first commit
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# `createScalarUiHandler` — Scalar HTTP handler factory
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The handler module owns the public `createScalarUiHandler` factory the
|
||||
host calls when `host.openapi.ui = "scalar"`. It validates the
|
||||
caller-supplied options through the Zod schema, eagerly resolves the
|
||||
Scalar runtime asset URL (CDN or bundled), constructs the OTel
|
||||
instrument bundle, and returns a frozen `ScalarUiHandlerBundle` whose
|
||||
`handler` field is the actual HTTP handler callable. The bundle also
|
||||
exposes an `assetHandler` for the bundled-mode `<uiEndpoint>/assets/`
|
||||
sub-route the host mounts alongside the main shell.
|
||||
|
||||
## Features
|
||||
|
||||
- Eager validation — invalid options or missing bundled assets fail at
|
||||
handler construction rather than at first request.
|
||||
- Strict CSP — `Content-Security-Policy` header value is derived once at
|
||||
construction time so request handling never has to recompute it. The
|
||||
policy widens to whitelist the CDN origin only when `assetMode = "cdn"`.
|
||||
- Per-request CSP nonce passthrough — the handler accepts an optional
|
||||
`nonce` on each request which the renderer threads onto the inline
|
||||
`<script>` blocks.
|
||||
- Idempotent — repeated handler invocations on the same request input
|
||||
return byte-identical body / headers.
|
||||
- OTel — every successful HTML response increments the
|
||||
`openapi.ui.requests_total` counter with `{ ui: "scalar" }`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createScalarUiHandler } from "@suaveplan/api-openapi-ui-scalar/handler";
|
||||
|
||||
const { handler } = createScalarUiHandler({
|
||||
openapiUrl: "/openapi.json",
|
||||
});
|
||||
|
||||
const response = handler({ method: "GET", path: "/docs/ui/scalar" });
|
||||
console.log(response.status); // 200
|
||||
console.log(response.headers["Content-Security-Policy"]);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { createScalarUiHandler } from "@suaveplan/api-openapi-ui-scalar/handler";
|
||||
|
||||
const bundle = createScalarUiHandler({
|
||||
openapiUrl: "/openapi.json",
|
||||
theme: "deepSpace",
|
||||
layout: "classic",
|
||||
assetMode: "bundled",
|
||||
});
|
||||
|
||||
// Per-request nonce — wire into your edge / Hono / Worker middleware.
|
||||
const html = bundle.handler({
|
||||
method: "GET",
|
||||
path: "/docs/ui/scalar",
|
||||
nonce: crypto.randomUUID(),
|
||||
});
|
||||
|
||||
// Bundled-mode hosts ALSO mount the asset handler at
|
||||
// `<uiEndpoint>/assets/:filename`.
|
||||
const js = bundle.assetHandler({
|
||||
method: "GET",
|
||||
path: `${bundle.options.uiEndpoint}/assets/scalar.js`,
|
||||
});
|
||||
console.log(js.headers["Content-Type"]); // "application/javascript; charset=utf-8"
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `CreateScalarUiHandlerInput` — `ScalarUiOptionsInput` plus
|
||||
`meterProvider?` and `bundledAssetProbe?` DI hooks.
|
||||
- `createScalarUiHandler(input)` — `ScalarUiHandlerBundle`.
|
||||
- `ScalarUiHandlerBundle` — `{ name, version, options, instruments,
|
||||
runtimeUrl, handler, assetHandler }` (frozen).
|
||||
- `ScalarUiHandler` — `(request) => ScalarHttpResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The factory pre-computes the CSP header value, the resolved runtime
|
||||
URL, and the OTel instrument bundle so request handling is a constant-
|
||||
time render + counter-increment. Mount-time validation catches every
|
||||
operator-config failure mode before the host accepts traffic. Errors
|
||||
raised here carry the stable `OPENAPI_UI_SCALAR.*` codes so host
|
||||
boot-up can discriminate without unpacking the underlying Zod issues.
|
||||
Reference in New Issue
Block a user