Zudoku Documentation

repository·main·Indexed 20 days ago

https://github.com/zuplo/zudoku

An open-source, customizable API documentation framework for creating developer experiences using OpenAPI or GraphQL schemas. Zudoku features integrated API playgrounds, MDX-based custom pages, and support for OpenID or OAuth2 authentication. It can be deployed via AWS Lambda SSR, Cloudflare Workers SSR, or used as a standalone React component via CDN. The framework includes a CLI for project scaffolding and a set of UI components such as Alert and Badge.

Tokens
199.6K
Snippets
671
Records
878
Agent score
68%

What's inside Zudoku

  1. Overview of Cosmo Cargo shipping services

    main

    Cosmo Cargo provides logistics and shipping integration via API. Key capabilities include:

    • Shipment Management: Create and manage shipments, and generate shipping labels and stamps.
    • Real-time Tracking: Track shipments in real-time via the API.
    • Webhooks: Receive automated webhook notifications for shipment status updates.
    • Carrier Integration: Access multiple shipping carriers through a single integration point.
  2. Overview of Zudoku features

    main

    Zudoku is an open-source API documentation framework designed to build developer experiences around OpenAPI (and upcoming GraphQL) schemas. Key features include:

    • OpenAPI Powered: Automatically generate documentation from single or multiple OpenAPI schemas.
    • MDX Support: Create custom documentation pages using MDX.
    • Integrated Playground: Allows users to test API calls directly within the docs, including support for authentication.
    • Authentication: Integrate user authentication via OpenID or OAuth2.
    • Dark Mode: Built-in support for dark mode.
  3. International Shipping Features in Cosmo Cargo

    main

    Cosmo Cargo's API provides several features for managing global logistics:

    • Global Coverage: Support for over 200 countries and territories.
    • Multi-Carrier Support: A single API interface to access multiple international carriers.
    • Customs Documentation: Automated generation of customs forms and declarations.
    • Duty & Tax Calculation: Real-time estimates for duties and taxes in USD, EUR, or GBP.
    • International Tracking: End-to-end visibility for cross-border shipments.
    • Multi-Language Support: Local language support for shipping labels and documentation.
    • Currency Conversion: Automatic conversion for shipping costs.
  4. How SSR asset protection works in Zudoku

    main

    Zudoku provides bundle-level protection for sensitive routes. While protectedRoutes in your configuration prevents unauthenticated users from rendering content via the RouteGuard, it does not inherently stop a user from downloading the underlying JavaScript chunks from your static host.

    To prevent this, Zudoku implements a pipeline that physically isolates the JavaScript for protected routes into a separate directory (_protected/) that is not served by the static host. Instead, these chunks are served through the SSR worker (e.g., AWS Lambda, Cloudflare Worker, or Node.js), which performs an authentication check before responding with the file. This ensures that the JS for gated routes is only accessible after a successful auth check.

  5. How protected chunks are routed and resolved

    main

    During the build process (zudoku build --ssr), Zudoku identifies protected modules and re-routes them to a specific directory defined by PROTECTED_CHUNK_DIR (which is _protected).

    Chunk Routing: In the Rolldown configuration, chunks identified as protected are assigned a filename pattern like ${PROTECTED_CHUNK_DIR}/[name]-[hash].js instead of the standard assets/[name]-[hash].js.

    Runtime URL Resolution: To ensure the browser fetches these chunks through the SSR origin (where the auth check happens) rather than a public CDN, Zudoku uses experimental.renderBuiltUrl to rewrite the URLs. Any filename starting with _protected/ is rewritten to resolve through the basePath of the SSR application.

    // Internal logic used for URL rewriting
    if (filename.startsWith(`${PROTECTED_CHUNK_DIR}/`)) {
      return joinUrl(config.basePath, `/${filename}`);
    }
  6. Understand Zudoku deployment modes (SSR vs SSG)

    main

    Zudoku operates in two distinct deployment modes that determine how authentication state is managed and how cookies are handled. The mode is selected at build time.

    SSR (Server-Side Rendering)

    • Runtime Server: Required (e.g., Node, Vercel, Cloudflare).
    • Cookie Endpoint: The /__z/auth/session endpoint is available.
    • Auth State: The first paint is server-rendered using cookies. Authentication is more secure because secrets are stored in httpOnly cookies that JavaScript cannot access.
    • Build Command: zudoku build --ssr (retains the server bundle).

    SSG (Static Site Generation)

    • Runtime Server: None (static hosting).
    • Cookie Endpoint: Absent (the server bundle is deleted after prerendering).
    • Auth State: Authentication is client-only and hydrated from localStorage. This is a weaker security posture as tokens in localStorage are vulnerable to XSS.
    • Build Command: zudoku build (prerenders and drops the server bundle).

    Runtime Detection

    The client detects the mode at runtime via the window.ZUDOKU_SSR_AUTH object. The SSR server injects this object into the HTML whenever auth is configured. In SSG mode, this object is never injected.

  7. The rule for protecting routes: Use dynamic imports

    main

    To ensure a route is physically isolated into a protected chunk, you must load it via a dynamic import (() => import(...)). This dynamic import creates the 'lazy-boundary' that the build system uses to split the code into a protected chunk.

    What is NOT protected:

    • Inline JSX elements: If you define a route like {path: "/admin", element: <Admin />} but Admin is statically imported at the top of the file, the code for Admin will be included in the public bundle. The build will fail with an error from assertProtectedPatternsCovered to prevent this leak.
    • Raw inline OpenAPI: Using type: "raw" inlines the schema into the main bundle, making it public.
    • Dynamically-generated paths: If the path cannot be statically resolved by the AST scanner, the protection might not be applied automatically.
    // CORRECT: Protected via dynamic import
    { path: "/admin", component: () => import("./Admin") }
    
    // INCORRECT: Leaks into public bundle via static import
    import { Admin } from "./Admin";
    const routes = [{ path: "/admin", element: <Admin /> }];
  8. Use JSX components in headings

    main

    You can include JSX components inside Markdown headings. These components will render in both the sidebar navigation and the table of contents.

    Note: For components to render correctly in headings, they must be registered via the mdx.components configuration option.

    # My Page <Badge>New</Badge>
  9. How Zudoku redirects behave (Server vs Client)

    main

    Redirect behavior depends on how the visitor accesses the URL:

    Server-side behavior

    When a visitor loads a redirect path directly (e.g., via browser address bar or external link):

    • Zuplo and Vercel: Returns an HTTP 301 (Moved Permanently) with a Location header. This is ideal for SEO.
    • SSR deployments: The server returns a real HTTP 301 from the router loader.
    • Other static hosts (Netlify, Cloudflare Pages, GitHub Pages, S3, nginx, etc.): Zudoku prerenders a small HTML file containing a JavaScript redirect. This returns a 200 response, and the redirect occurs once the script runs. JavaScript must be enabled.

    Client-side behavior

    When a visitor clicks an internal link pointing to a redirect path, Zudoku handles the redirect entirely in the browser using client-side routing. This provides a seamless transition without a full page reload.

  10. Auto-detecting protected route shapes for chunking

    main

    Zudoku uses a Vite transform to scan your code for specific patterns to determine which modules belong to which routes. To ensure your content is correctly isolated into protected chunks, use one of these two supported shapes:

    Shape A: Object literal with path property

    Any object containing a string path property. All dynamic import() calls within the other properties of this object are registered as subtree-scoped to that path.

    { path: "/admin", lazy: () => import("./AdminPage") }

    Shape B: Dictionary keyed by route path

    An object where the keys are route-path strings (starting with / and containing no .) and the values are arrow functions calling import().

    const fileImports = {
      "/docs/intro": () => import("./intro.mdx"),
      "/docs/guides": () => import("./guides.mdx"),
    };
    // Shape A
    { path: "/admin", lazy: () => import("./AdminPage") }
    
    // Shape B
    const fileImports = {
      "/docs/intro": () => import("./intro.mdx"),
      "/docs/guides": () => import("./guides.mdx"),
    };