first commit
This commit is contained in:
130
docs/api-stack/api/api-openapi-ui-scalar/README.md
Normal file
130
docs/api-stack/api/api-openapi-ui-scalar/README.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# @suaveplan/api-openapi-ui-scalar — Scalar UI adapter for the Suaveplan OpenAPI plugin system. Exposes createScalarUiHandler() and createPerPluginScalarUiHandler() — factories returning HTTP handlers that serve a self-contained Scalar HTML shell with strict CSP. Default OpenAPI explorer when operators want Scalar; sibling api-openapi-ui-swagger and api-openapi-ui-redoc cover the alternatives.
|
||||
|
||||
Scalar UI adapter for the Suaveplan OpenAPI plugin system.
|
||||
|
||||
`createScalarUiHandler(opts)` returns a frozen handler bundle that
|
||||
serves a self-contained Scalar HTML shell at `/docs/ui/scalar` (or any
|
||||
configured endpoint). The bundle is router-agnostic — host adapters
|
||||
(`api-router-hono`, `api-router-in-memory`, worker bridge) wrap the
|
||||
handler with their native request context. `createPerPluginScalarUiHandler`
|
||||
is the per-plugin variant mounted under `/docs/ui/scalar/<plugin>`.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [assets](./src/assets/assets.md) — The assets module owns the in-package bundled Scalar runtime that ships with the package — a small self-contained JS bo…
|
||||
- [assets](./src/assets/assets.md) — The assets module owns the in-package bundled Scalar runtime that ships with the package — a small self-contained JS bo…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module owns the stable error-code registry for the `@suaveplan/api-openapi-ui-scalar` package. The registry is bui…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module owns the stable error-code registry for the `@suaveplan/api-openapi-ui-scalar` package. The registry is bui…
|
||||
- [handler](./src/handler/handler.md) — The handler module owns the public `createScalarUiHandler` factory the host calls when `host.openapi.ui = "scalar"`. It…
|
||||
- [handler](./src/handler/handler.md) — The handler module owns the public `createScalarUiHandler` factory the host calls when `host.openapi.ui = "scalar"`. It…
|
||||
- [headers](./src/headers/csp.md) — The headers module builds the `Content-Security-Policy` header value emitted on every HTML response from the Scalar UI…
|
||||
- [csp](./src/headers/csp.md) — The headers module builds the `Content-Security-Policy` header value emitted on every HTML response from the Scalar UI…
|
||||
- [html](./src/html/render-shell.md) — The render-shell module emits the HTML body returned by the Scalar UI handler. It embeds the configured OpenAPI documen…
|
||||
- [render-shell](./src/html/render-shell.md) — The render-shell module emits the HTML body returned by the Scalar UI handler. It embeds the configured OpenAPI documen…
|
||||
- [instruments](./src/instruments/instruments.md) — The instruments module wires the Scalar UI handler's OTel instruments into a frozen bundle constructed once at handler-…
|
||||
- [instruments](./src/instruments/instruments.md) — The instruments module wires the Scalar UI handler's OTel instruments into a frozen bundle constructed once at handler-…
|
||||
- [options](./src/options/options.md) — The options module owns the single Zod schema consumed by every Scalar UI handler factory. It is exposed both directly…
|
||||
- [options](./src/options/options.md) — The options module owns the single Zod schema consumed by every Scalar UI handler factory. It is exposed both directly…
|
||||
- [per-plugin](./src/per-plugin/per-plugin.md) — The per-plugin module exposes `createPerPluginScalarUiHandler`, the variant the host mounts under `/docs/ui/scalar/<plu…
|
||||
- [per-plugin](./src/per-plugin/per-plugin.md) — The per-plugin module exposes `createPerPluginScalarUiHandler`, the variant the host mounts under `/docs/ui/scalar/<plu…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Why opt-in
|
||||
|
||||
Per the design doc (`docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`
|
||||
§7) the API foundation reserves `/docs/ui/<vendor>` for an interactive
|
||||
OpenAPI explorer but does not bundle one by default — UI choice is
|
||||
operator-chosen so the runtime image stays minimal for production
|
||||
deployments that don't need it.
|
||||
|
||||
Operators wire ONE of:
|
||||
|
||||
- `@suaveplan/api-openapi-ui-scalar` — canonical Scalar (this package).
|
||||
- `@suaveplan/api-openapi-ui-swagger` — Swagger UI alternative.
|
||||
- `@suaveplan/api-openapi-ui-redoc` — ReDoc alternative.
|
||||
|
||||
The OpenAPI emitter (`api-openapi`) does not require a UI plugin — the
|
||||
JSON / YAML document is always available at `/openapi.json` regardless.
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { createScalarUiHandler } from "@suaveplan/api-openapi-ui-scalar";
|
||||
|
||||
const { handler, options } = createScalarUiHandler({
|
||||
openapiUrl: "/openapi.json",
|
||||
});
|
||||
|
||||
// GET /docs/ui/scalar -> the explorer
|
||||
const response = handler({ method: "GET", path: options.uiEndpoint });
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- `handler` — `createScalarUiHandler` factory and HTTP handler shape.
|
||||
- `per-plugin` — `createPerPluginScalarUiHandler` variant.
|
||||
- `options` — `scalarUiOptionsSchema` + `parseScalarUiOptions`.
|
||||
- `assets` — bundled JS / CSS registry and `resolveAssetUrl` resolver.
|
||||
- `headers` — `buildCspHeader` strict CSP builder.
|
||||
- `html` — `renderShell` HTML shell renderer.
|
||||
- `instruments` — OTel counter wiring.
|
||||
- `error-codes` — `OPENAPI_UI_SCALAR.*` registry.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Default | Notes |
|
||||
|---|---|---|
|
||||
| `openapiUrl` | `/openapi.json` | Embedded into the Scalar config block. |
|
||||
| `uiEndpoint` | `/docs/ui/scalar` | Must start with `/`. |
|
||||
| `title` | `"API Reference"` | HTML-escaped before embedding. |
|
||||
| `theme` | `"default"` | One of 11 built-in Scalar themes. |
|
||||
| `layout` | `"modern"` | `"modern"` / `"classic"`. |
|
||||
| `assetMode` | `"cdn"` | `"cdn"` / `"bundled"`. |
|
||||
| `cdnUrl` | JSDelivr URL | Whitelisted in CSP when `assetMode = "cdn"`. |
|
||||
| `defaultOpenAllTags` | `false` | Expand every tag on load. |
|
||||
| `hideSearch` | `false` | Hide the Scalar search box. |
|
||||
| `darkMode` | `false` | Force dark mode. |
|
||||
| `editorOptions` | `{}` | Open passthrough bag to Scalar. |
|
||||
| `meterProvider` | OTel global | OTel `MeterProvider` for the counter. |
|
||||
|
||||
## Security posture
|
||||
|
||||
- **Strict CSP.** `default-src 'self'; script-src 'self' [<cdn>]; style-src 'self' 'unsafe-inline' [<cdn>]; connect-src 'self' [<api-origin>]`.
|
||||
Inline scripts are NEVER permitted — the Scalar bootstrap is loaded as
|
||||
an external `<script src>` so `'unsafe-inline'` would be a regression.
|
||||
- **Per-request nonce passthrough.** Hosts running nonce-based CSP pass
|
||||
`nonce` on each request and the renderer threads it onto the inline
|
||||
config block.
|
||||
- **Bundled-mode hardening.** `assetMode = "bundled"` keeps every fetch
|
||||
inside the host origin. A missing bundled asset raises
|
||||
`OPENAPI_UI_SCALAR.ASSETS_MISSING` at handler construction — no silent
|
||||
fall-back to CDN.
|
||||
- **Cache-immutable assets.** Bundled JS / CSS carries
|
||||
`Cache-Control: public, max-age=31536000, immutable`.
|
||||
- **Stale-hash tolerance.** Hashed filenames resolve any matching stem +
|
||||
ext to the current asset bytes — rolling deploys don't 404 mid-flight.
|
||||
|
||||
## OTel
|
||||
|
||||
- `openapi.ui.requests_total { ui: "scalar" }` — counter incremented on
|
||||
each successful HTML response.
|
||||
- `openapi.ui.requests_total { ui: "scalar", plugin: "<name>" }` — per-
|
||||
plugin fan-out emitted by the per-plugin handler in addition to the
|
||||
baseline observation.
|
||||
|
||||
## Error codes
|
||||
|
||||
- `OPENAPI_UI_SCALAR.ASSETS_MISSING` — bundled mode without a bundle.
|
||||
- `OPENAPI_UI_SCALAR.INVALID_OPTIONS` — Zod schema rejected the config.
|
||||
- `OPENAPI_UI_SCALAR.ASSET_NOT_REGISTERED` — internal asset lookup miss.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-openapi-ui-scalar
|
||||
```
|
||||
|
||||
100% statements / branches / functions / lines required on touched
|
||||
modules (`provider: istanbul`).
|
||||
|
||||
Reference in New Issue
Block a user