first commit
This commit is contained in:
83
docs/api-stack/api/api-openapi-ui-redoc/README.md
Normal file
83
docs/api-stack/api/api-openapi-ui-redoc/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# @suaveplan/api-openapi-ui-redoc — ReDoc UI adapter for the Suaveplan api-host. Mounts the ReDoc HTML shell at /docs/ui/redoc (or any caller-chosen path) via createRedocUiHandler({ openapiUrl, title, theme, expandResponses }) and at /docs/{plugin}/ui/redoc via createPerPluginRedocUiHandler. Validates RedocUiOptions through a strict Zod schema, resolves the ReDoc runtime bundle from a cdn / bundled / inline strategy, embeds the configured options into the shell as a JSON island, and raises the OPENAPI_UI_REDOC_* error-code family on collision, missing asset, or invalid options. Structural sibling of api-graphql-ui-altair and api-graphql-ui-graphiql.
|
||||
|
||||
Stage-8 ReDoc UI adapter for the Suaveplan api-host. Mounts the ReDoc HTML
|
||||
shell at `/docs/ui/redoc` (or any caller-chosen path) via
|
||||
`createRedocUiHandler` and at `/docs/{plugin}/ui/redoc` via
|
||||
`createPerPluginRedocUiHandler`. Validates options through a strict Zod
|
||||
schema, resolves the runtime bundle from a cdn / bundled / inline strategy,
|
||||
and raises the `OPENAPI_UI_REDOC_*` error-code family on missing assets or
|
||||
invalid options.
|
||||
|
||||
Structural sibling of `@suaveplan/api-graphql-ui-altair` and
|
||||
`@suaveplan/api-graphql-ui-graphiql`.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [assets](./src/assets/assets.md) — This module resolves the runtime asset URL the ReDoc HTML shell references. The package supports three asset strategies…
|
||||
- [assets](./src/assets/assets.md) — This module resolves the runtime asset URL the ReDoc HTML shell references. The package supports three asset strategies…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module defines the frozen error-code namespace shared by the ReDoc UI adapter. Every fault the package raises carr…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module defines the frozen error-code namespace shared by the ReDoc UI adapter. Every fault the package raises carr…
|
||||
- [handler](./src/handler/handler.md) — This module exposes `createRedocUiHandler`, the factory the api-host calls to obtain the HTTP handler serving the ReDoc…
|
||||
- [handler](./src/handler/handler.md) — This module exposes `createRedocUiHandler`, the factory the api-host calls to obtain the HTTP handler serving the ReDoc…
|
||||
- [html](./src/html/render-shell.md) — This module exposes `renderRedocShell`, the pure function that produces the ReDoc HTML shell consumed by both the globa…
|
||||
- [render-shell](./src/html/render-shell.md) — This module exposes `renderRedocShell`, the pure function that produces the ReDoc HTML shell consumed by both the globa…
|
||||
- [options](./src/options/options.md) — This module defines the Zod schema and parser for `RedocUiOptions`, the configuration bundle the ReDoc UI handler accep…
|
||||
- [options](./src/options/options.md) — This module defines the Zod schema and parser for `RedocUiOptions`, the configuration bundle the ReDoc UI handler accep…
|
||||
- [per-plugin](./src/per-plugin/per-plugin.md) — This module exposes `createPerPluginRedocUiHandler`, the sibling of `createRedocUiHandler` for hosts that mount per-plu…
|
||||
- [per-plugin](./src/per-plugin/per-plugin.md) — This module exposes `createPerPluginRedocUiHandler`, the sibling of `createRedocUiHandler` for hosts that mount per-plu…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/api-openapi-ui-redoc
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { createRedocUiHandler } from "@suaveplan/api-openapi-ui-redoc";
|
||||
|
||||
const handler = createRedocUiHandler({
|
||||
openapiUrl: "/openapi.json",
|
||||
title: "Acme API",
|
||||
theme: "dark",
|
||||
expandResponses: "200,201",
|
||||
});
|
||||
|
||||
const res = handler();
|
||||
// res.body is the rendered HTML shell, ready to send.
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
| Module | Subpath | Description |
|
||||
| ---------------------------- | --------------------------------------------- | ----------- |
|
||||
| `handler/handler.ts` | `@suaveplan/api-openapi-ui-redoc/handler` | `createRedocUiHandler` + `RedocHttpResponse` |
|
||||
| `per-plugin/per-plugin.ts` | `@suaveplan/api-openapi-ui-redoc/per-plugin` | `createPerPluginRedocUiHandler` |
|
||||
| `options/options.ts` | `@suaveplan/api-openapi-ui-redoc/options` | `RedocUiOptions` Zod schema + parser |
|
||||
| `assets/assets.ts` | `@suaveplan/api-openapi-ui-redoc/assets` | cdn / bundled / inline strategy resolver |
|
||||
| `html/render-shell.ts` | `@suaveplan/api-openapi-ui-redoc/html` | Pure HTML shell renderer |
|
||||
| `error-codes/error-codes.ts` | `@suaveplan/api-openapi-ui-redoc/error-codes` | `OPENAPI_UI_REDOC_*` registry |
|
||||
|
||||
## Asset strategies
|
||||
|
||||
- **`cdn`** (default) — point the shell at the canonical Redocly CDN URL
|
||||
(`https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js`). Zero-byte
|
||||
deploy; one network round-trip per first-paint.
|
||||
- **`bundled`** — operators supply the runtime bytes; the package serves
|
||||
them from a cache-busted local URL.
|
||||
- **`inline`** — embed the runtime body directly into the HTML shell. One
|
||||
network round-trip total, at the cost of a larger first-paint payload.
|
||||
|
||||
## Error codes
|
||||
|
||||
- `OPENAPI_UI_REDOC_ASSETS_MISSING` — `bundled` or `inline` mode requested
|
||||
without a payload.
|
||||
- `OPENAPI_UI_REDOC_INVALID_OPTIONS` — Zod rejected the option bundle.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
81
docs/api-stack/api/api-openapi-ui-redoc/src/assets/assets.md
Normal file
81
docs/api-stack/api/api-openapi-ui-redoc/src/assets/assets.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# `assets` — ReDoc runtime asset strategy
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module resolves the runtime asset URL the ReDoc HTML shell references.
|
||||
The package supports three asset strategies — `cdn`, `bundled`, and `inline` —
|
||||
so operators can choose between zero-byte deploys (CDN), air-gapped
|
||||
deployments (bundled), and single-request first-paint (inline) without
|
||||
changing the handler call site. The resolver returns a typed
|
||||
`ResolvedRedocAsset` whose `url` slots straight into the shell renderer.
|
||||
|
||||
## Features
|
||||
|
||||
- `REDOC_CDN_URL` — the canonical Redocly CDN URL for the `redoc.standalone.js`
|
||||
bundle.
|
||||
- `defineRedocAsset(name, contentType, body)` builds a bundled-asset record
|
||||
with a SHA-256-prefix cache-bust hash derived from the payload bytes.
|
||||
- `resolveRedocAsset({ strategy, bundled, inline })` returns the asset
|
||||
descriptor for the chosen strategy. Missing payloads raise
|
||||
`OPENAPI_UI_REDOC_ASSETS_MISSING`.
|
||||
- Bundled URLs are cache-busted (`./assets/<hash>/<name>`) so re-deploys with
|
||||
unchanged payloads keep their CDN warm-cache entries.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { resolveRedocAsset, REDOC_CDN_URL } from "@suaveplan/api-openapi-ui-redoc/assets";
|
||||
|
||||
const cdn = resolveRedocAsset({ strategy: "cdn" });
|
||||
console.log(cdn.url === REDOC_CDN_URL); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Ship the runtime alongside the host binary for air-gapped operation:
|
||||
|
||||
```ts
|
||||
import { readFileSync } from "node:fs";
|
||||
import {
|
||||
defineRedocAsset,
|
||||
resolveRedocAsset,
|
||||
} from "@suaveplan/api-openapi-ui-redoc/assets";
|
||||
|
||||
const bundle = readFileSync("./vendor/redoc.standalone.js", "utf8");
|
||||
const asset = defineRedocAsset(
|
||||
"redoc.standalone.js",
|
||||
"application/javascript; charset=utf-8",
|
||||
bundle
|
||||
);
|
||||
|
||||
const resolved = resolveRedocAsset({ strategy: "bundled", bundled: asset });
|
||||
// resolved.url === `./assets/${asset.hash}/redoc.standalone.js`
|
||||
```
|
||||
|
||||
The host then wires a `GET ./assets/:hash/:name` route that serves the
|
||||
asset's `body` with its `contentType` and `Cache-Control: public, max-age=31536000, immutable`.
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
| ------------------------- | ------ | ----------- |
|
||||
| `REDOC_CDN_URL` | const | Upstream Redocly CDN URL |
|
||||
| `REDOC_BUNDLED_URL` | const | Conventional relative URL for bundled mode |
|
||||
| `RedocAssetStrategy` | type | `"cdn" \| "bundled" \| "inline"` |
|
||||
| `BundledRedocAsset` | type | `{ name, contentType, body, hash }` |
|
||||
| `ResolvedRedocAsset` | type | `{ strategy, url, inlineBody? }` |
|
||||
| `defineRedocAsset` | fn | Build a bundled-asset record with a content-derived cache-bust hash |
|
||||
| `resolveRedocAsset` | fn | Resolve `{ strategy, bundled?, inline? }` → `ResolvedRedocAsset` |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`defineRedocAsset` hashes the payload via `node:crypto` SHA-256 and keeps the
|
||||
first 16 hex characters — collision-resistant for cache-busting and short
|
||||
enough to keep URLs readable. The `inline` strategy returns a sentinel
|
||||
`data:inline` URL plus the body itself; the shell renderer detects this
|
||||
sentinel and inlines a `<script>` tag rather than emitting a `<script src=...>`.
|
||||
This avoids leaking the literal payload into a query string.
|
||||
@@ -0,0 +1,83 @@
|
||||
# `error-codes` — `OPENAPI_UI_REDOC.*` registry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module defines the frozen error-code namespace shared by the ReDoc UI
|
||||
adapter. Every fault the package raises carries one of the two codes registered
|
||||
here, so operators can map ReDoc-adapter failures to dashboards, alerts, and
|
||||
runbooks without spelunking through stack traces. The registry is wired into
|
||||
the cross-package `defineCodes` registry so callers that bare-import the
|
||||
package automatically surface the namespace alongside every other suaveplan
|
||||
code prefix.
|
||||
|
||||
## Features
|
||||
|
||||
- Two stable codes: `OPENAPI_UI_REDOC_ASSETS_MISSING` for asset-strategy
|
||||
faults and `OPENAPI_UI_REDOC_INVALID_OPTIONS` for Zod-rejected option
|
||||
bundles.
|
||||
- A concrete `OpenapiUiRedocError` subclass that narrows `code` so consumers
|
||||
can `instanceof`-discriminate the package's faults from other Suaveplan
|
||||
errors.
|
||||
- A `redocCode` getter that re-exposes the code at a typed alias for
|
||||
pattern-matching in dashboards.
|
||||
- Frozen tuple via `defineCodes(...)` so the literal types survive bundling
|
||||
and tree-shaking.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
OPENAPI_UI_REDOC_CODES,
|
||||
OpenapiUiRedocError,
|
||||
} from "@suaveplan/api-openapi-ui-redoc/error-codes";
|
||||
|
||||
throw new OpenapiUiRedocError(
|
||||
OPENAPI_UI_REDOC_CODES.ASSETS_MISSING,
|
||||
"uiAssets='bundled' but no payload supplied"
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Pattern-match codes in a host fault sink to send INVALID_OPTIONS to the
|
||||
operator dashboard and ASSETS_MISSING to the deploy-pipeline alarm:
|
||||
|
||||
```ts
|
||||
import {
|
||||
OPENAPI_UI_REDOC_CODES,
|
||||
OpenapiUiRedocError,
|
||||
} from "@suaveplan/api-openapi-ui-redoc/error-codes";
|
||||
|
||||
function handleFault(err: unknown): void {
|
||||
if (!(err instanceof OpenapiUiRedocError)) return;
|
||||
switch (err.redocCode) {
|
||||
case OPENAPI_UI_REDOC_CODES.ASSETS_MISSING:
|
||||
alertDeployPipeline(err);
|
||||
return;
|
||||
case OPENAPI_UI_REDOC_CODES.INVALID_OPTIONS:
|
||||
alertOperatorConsole(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
| -------------------------- | ------ | ---------------------------------------- |
|
||||
| `OPENAPI_UI_REDOC_CODES` | const | Frozen `Record<"ASSETS_MISSING"\|"INVALID_OPTIONS", string>` |
|
||||
| `OpenapiUiRedocErrorCode` | type | Union of the two namespaced literals |
|
||||
| `OpenapiUiRedocError` | class | `SuaveplanError<OpenapiUiRedocErrorCode>` subclass with `redocCode` |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Codes are registered exactly once at module init via `defineCodes("OPENAPI_UI_REDOC", […])`.
|
||||
Importing the package's root barrel surfaces this module as a side-effect so
|
||||
subpath consumers (e.g. `…/handler`) still benefit from the registration even
|
||||
if they never reach the top-level export. The concrete class re-stores the
|
||||
typed `redocCode` so narrowed `instanceof` checks expose the code on a typed
|
||||
property rather than the loosely-typed `SuaveplanError.code`.
|
||||
@@ -0,0 +1,96 @@
|
||||
# `handler` — `createRedocUiHandler`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module exposes `createRedocUiHandler`, the factory the api-host calls
|
||||
to obtain the HTTP handler serving the ReDoc HTML shell. The returned
|
||||
handler is a zero-argument function the host invokes per request. It is
|
||||
intentionally router-agnostic — adapters in the api-host layer translate
|
||||
the returned `RedocHttpResponse` into Hono / api-router envelopes — so the
|
||||
package can be wired into any HTTP surface without dragging a router peer
|
||||
dependency through every consumer.
|
||||
|
||||
## Features
|
||||
|
||||
- Validates options at construction; invalid input raises
|
||||
`OPENAPI_UI_REDOC_INVALID_OPTIONS` before any handler runs.
|
||||
- Resolves the runtime asset URL up-front; missing bundled / inline payloads
|
||||
raise `OPENAPI_UI_REDOC_ASSETS_MISSING` at construction, not at request time.
|
||||
- Pre-renders the HTML shell once at construction — every subsequent request
|
||||
is a constant-time return of the frozen response object.
|
||||
- The response carries `Content-Type: text/html; charset=utf-8`,
|
||||
`Cache-Control: no-cache`, and `X-Content-Type-Options: nosniff`.
|
||||
- The handler is idempotent — the same call always returns the same frozen
|
||||
object, so adapters can safely cache it.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createRedocUiHandler } from "@suaveplan/api-openapi-ui-redoc/handler";
|
||||
|
||||
const handler = createRedocUiHandler({ openapiUrl: "/openapi.json" });
|
||||
const res = handler();
|
||||
// res.body is the rendered HTML shell.
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Wire the bundled-asset strategy into a Hono app, including the cache-busted
|
||||
asset route:
|
||||
|
||||
```ts
|
||||
import { Hono } from "hono";
|
||||
import {
|
||||
createRedocUiHandler,
|
||||
defineRedocAsset,
|
||||
} from "@suaveplan/api-openapi-ui-redoc";
|
||||
|
||||
const asset = defineRedocAsset(
|
||||
"redoc.standalone.js",
|
||||
"application/javascript; charset=utf-8",
|
||||
runtimeBody
|
||||
);
|
||||
|
||||
const handler = createRedocUiHandler({
|
||||
openapiUrl: "/openapi.json",
|
||||
uiAssets: "bundled",
|
||||
bundledAsset: asset,
|
||||
title: "Acme API",
|
||||
expandResponses: "200,201",
|
||||
theme: "dark",
|
||||
});
|
||||
|
||||
const app = new Hono();
|
||||
app.get("/docs/ui/redoc", (c) => {
|
||||
const r = handler();
|
||||
Object.entries(r.headers).forEach(([k, v]) => c.header(k, v));
|
||||
return c.body(r.body, r.status);
|
||||
});
|
||||
app.get(`/docs/ui/redoc/assets/${asset.hash}/${asset.name}`, (c) => {
|
||||
c.header("Content-Type", asset.contentType);
|
||||
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
||||
return c.body(asset.body as string);
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
| --------------------------- | ---- | ----------- |
|
||||
| `RedocHttpResponse` | type | `{ status, headers, body }` — router-agnostic response |
|
||||
| `RedocHttpHandler` | type | `() => RedocHttpResponse` |
|
||||
| `CreateRedocUiHandlerInput` | type | `RedocUiOptionsInput & { bundledAsset?, inlineAsset? }` |
|
||||
| `createRedocUiHandler` | fn | Build the handler; pre-renders the shell |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The handler pre-renders the HTML once at construction. This keeps the
|
||||
per-request hot path a constant-time return of a frozen object — the same
|
||||
shape the api-router would otherwise have to allocate per call. Pre-rendering
|
||||
also means every option-level fault surfaces at host start-up, not at the
|
||||
first request, which is the failure mode operators want for misconfigured
|
||||
deploys.
|
||||
@@ -0,0 +1,81 @@
|
||||
# `html/render-shell` — ReDoc HTML shell renderer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module exposes `renderRedocShell`, the pure function that produces the
|
||||
ReDoc HTML shell consumed by both the global and per-plugin handlers. It
|
||||
embeds the OpenAPI document URL into a `<redoc spec-url="…">` custom element,
|
||||
ships a JSON option island for the runtime to read, and injects the runtime
|
||||
runtime asset reference based on the chosen asset strategy. Keeping the
|
||||
renderer separate from the handler factories lets host operators embed the
|
||||
shell in custom adapters (e.g. SSR, Edge functions) without reaching for the
|
||||
handler builder.
|
||||
|
||||
## Features
|
||||
|
||||
- HTML-escapes every user-supplied value (`title`, `openapiUrl`, etc.) so
|
||||
option strings cannot break out of attribute or text-node scope.
|
||||
- Inlines the runtime body when `strategy === "inline"` and emits a
|
||||
`<script src="…">` tag for `cdn` / `bundled` strategies.
|
||||
- Embeds `theme`, `hideDownloadButton`, `nativeScrollbars`, and
|
||||
`expandResponses` into the JSON options island the runtime reads.
|
||||
- Tags the body element with `data-redoc-theme` so external stylesheets can
|
||||
override the colour scheme.
|
||||
- Pure — no I/O, no clocks, no globals.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { renderRedocShell } from "@suaveplan/api-openapi-ui-redoc/html";
|
||||
import {
|
||||
parseRedocUiOptions,
|
||||
resolveRedocAsset,
|
||||
} from "@suaveplan/api-openapi-ui-redoc";
|
||||
|
||||
const html = renderRedocShell({
|
||||
options: parseRedocUiOptions({ openapiUrl: "/openapi.json" }),
|
||||
resolvedAsset: resolveRedocAsset({ strategy: "cdn" }),
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Pre-render the shell for a multi-tenant host that picks up the OpenAPI URL
|
||||
from the request path:
|
||||
|
||||
```ts
|
||||
import { renderRedocShell } from "@suaveplan/api-openapi-ui-redoc/html";
|
||||
|
||||
const options = parseRedocUiOptions({ openapiUrl: "/openapi.json", theme: "dark" });
|
||||
const resolvedAsset = resolveRedocAsset({ strategy: "cdn" });
|
||||
|
||||
function htmlForTenant(tenant: string): string {
|
||||
return renderRedocShell({
|
||||
options,
|
||||
resolvedAsset,
|
||||
openapiUrlOverride: `/${tenant}/openapi.json`,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
| ------------------------ | ---- | ----------- |
|
||||
| `RenderRedocShellInput` | type | `{ options, resolvedAsset, openapiUrlOverride? }` |
|
||||
| `renderRedocShell` | fn | Render the HTML shell as a string |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The escape function covers the five HTML special characters (`&`, `<`, `>`,
|
||||
`"`, `'`) plus the apostrophe — the same surface as the WHATWG HTML
|
||||
serialization-attribute rules. The JSON island is serialised via
|
||||
`JSON.stringify` and HTML-escaped so embedded `</script>` sequences in
|
||||
option values cannot escape the `<script>` context. The shell's bottom-of-
|
||||
body initialiser calls `window.Redoc.init` when the runtime defines it,
|
||||
falling back gracefully when the runtime is unavailable (e.g. during DOM
|
||||
unit tests that don't load the standalone bundle).
|
||||
@@ -0,0 +1,82 @@
|
||||
# `options` — `RedocUiOptions` schema + parser
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module defines the Zod schema and parser for `RedocUiOptions`, the
|
||||
configuration bundle the ReDoc UI handler accepts. It is the single boundary
|
||||
where caller-supplied option values transition from untrusted input to a
|
||||
fully-defaulted, immutable typed record the rest of the package treats as
|
||||
ground truth. Refusing malformed input here means the rendered HTML shell
|
||||
never has to defend against half-validated values at request time.
|
||||
|
||||
## Features
|
||||
|
||||
- Strict schema — unknown keys are refused so typos surface immediately
|
||||
instead of silently producing a misconfigured shell.
|
||||
- Sensible defaults so the bare `createRedocUiHandler()` call still produces
|
||||
a working `/docs/ui/redoc` shell against `/openapi.json`.
|
||||
- `expandResponses` is a regex-validated comma-separated list of three-character
|
||||
HTTP status codes (or `"all"`), matching the official ReDoc option.
|
||||
- `uiAssets` strategy literal — one of `"cdn"`, `"bundled"`, `"inline"`.
|
||||
- Zod failures are wrapped as `OpenapiUiRedocError(INVALID_OPTIONS)` with the
|
||||
original `ZodError` attached as `cause`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { parseRedocUiOptions } from "@suaveplan/api-openapi-ui-redoc/options";
|
||||
|
||||
const opts = parseRedocUiOptions({
|
||||
openapiUrl: "/openapi.json",
|
||||
theme: "dark",
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Combine with the host-config layer to validate `host.openapi.ui = "redoc"`
|
||||
options at ingestion time, before any handler is constructed:
|
||||
|
||||
```ts
|
||||
import {
|
||||
parseRedocUiOptions,
|
||||
redocUiOptionsSchema,
|
||||
type RedocUiOptionsInput,
|
||||
} from "@suaveplan/api-openapi-ui-redoc/options";
|
||||
|
||||
const hostConfigSchema = z.object({
|
||||
openapi: z.object({
|
||||
ui: z.literal("redoc"),
|
||||
redoc: redocUiOptionsSchema.partial().default({}),
|
||||
}),
|
||||
});
|
||||
|
||||
function buildHandler(raw: unknown) {
|
||||
const cfg = hostConfigSchema.parse(raw);
|
||||
const opts: RedocUiOptionsInput = cfg.openapi.redoc;
|
||||
return parseRedocUiOptions(opts);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
| ------------------------- | ------ | ----------- |
|
||||
| `redocUiOptionsSchema` | const | `z.ZodObject` of the strict options shape |
|
||||
| `RedocUiOptions` | type | `z.infer<typeof redocUiOptionsSchema>` (defaulted) |
|
||||
| `RedocUiOptionsInput` | type | `z.input<typeof redocUiOptionsSchema>` (caller-facing) |
|
||||
| `parseRedocUiOptions` | fn | `(input?) => RedocUiOptions` — throws `OpenapiUiRedocError(INVALID_OPTIONS)` |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The schema is `.strict()` so additional keys raise the catch-all
|
||||
`INVALID_OPTIONS` code rather than silently passing through. We use
|
||||
`safeParse` internally so we can wrap the error rather than re-throw the raw
|
||||
`ZodError`; the wrapper carries the registry code and stable message but the
|
||||
underlying issue list remains accessible via `err.cause`. Defaults are baked
|
||||
into the schema so a `parseRedocUiOptions({})` call returns a fully-formed
|
||||
options record without callers having to reach for any other layer.
|
||||
@@ -0,0 +1,70 @@
|
||||
# `per-plugin` — per-plugin ReDoc handler factory
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module exposes `createPerPluginRedocUiHandler`, the sibling of
|
||||
`createRedocUiHandler` for hosts that mount per-plugin documentation surfaces
|
||||
at `/docs/{plugin}/ui/redoc`. The rendered shell references the plugin-filtered
|
||||
OpenAPI URL (typically `/openapi.json?plugin=foo`) so a single host can serve
|
||||
both the global aggregate documentation and per-plugin slices through the
|
||||
same UI adapter.
|
||||
|
||||
## Features
|
||||
|
||||
- Plugin-specific URL resolver — operators inject a custom `openapiUrlForPlugin`
|
||||
if their host filters by path segment rather than query string.
|
||||
- Sensible default resolver appends `?plugin=<encoded>` to `options.openapiUrl`
|
||||
(or `&plugin=…` if the URL already has a query string).
|
||||
- Validates options through the same Zod schema as the global handler.
|
||||
- Pre-renders the shell once per plugin handler so per-request cost is zero.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createPerPluginRedocUiHandler } from "@suaveplan/api-openapi-ui-redoc/per-plugin";
|
||||
|
||||
const handler = createPerPluginRedocUiHandler({
|
||||
plugin: "contacts",
|
||||
openapiUrl: "/openapi.json",
|
||||
});
|
||||
// handler().body references "/openapi.json?plugin=contacts"
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Use a path-segment resolver when the host serves filtered documents at
|
||||
`/openapi/<plugin>.json`:
|
||||
|
||||
```ts
|
||||
const handler = createPerPluginRedocUiHandler({
|
||||
plugin: "billing",
|
||||
openapiUrl: "/openapi.json",
|
||||
openapiUrlForPlugin: (p) => `/openapi/${p}.json`,
|
||||
title: "Billing API",
|
||||
expandResponses: "all",
|
||||
});
|
||||
```
|
||||
|
||||
The api-host typically lazy-builds one per-plugin handler per request, but
|
||||
because construction validates options + asset strategy up-front, callers can
|
||||
also pre-build the per-plugin handlers at start-up and dispatch by slug.
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
| ------------------------------------- | ---- | ----------- |
|
||||
| `OpenapiUrlForPlugin` | type | `(plugin: string) => string` |
|
||||
| `CreatePerPluginRedocUiHandlerInput` | type | `RedocUiOptionsInput & { plugin, openapiUrlForPlugin?, bundledAsset?, inlineAsset? }` |
|
||||
| `createPerPluginRedocUiHandler` | fn | Build the per-plugin handler |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The default resolver URL-encodes the plugin slug via `encodeURIComponent` so
|
||||
slugs containing `/` or other delimiters cannot break out of the query
|
||||
parameter. The shell is rendered with the resolved URL substituted in via
|
||||
`openapiUrlOverride` on `renderRedocShell`, leaving the rest of the options
|
||||
record (theme, expandResponses, …) untouched.
|
||||
Reference in New Issue
Block a user