# @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/`. ## 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/ ## 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/` 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' []; style-src 'self' 'unsafe-inline' []; connect-src 'self' []`. Inline scripts are NEVER permitted — the Scalar bootstrap is loaded as an external `