# `render-shell.ts` — Swagger UI HTML shell renderer _(no submodules)_ ## Purpose This module produces the deterministic HTML payload returned by `createSwaggerUiHandler`. It accepts the OpenAPI document URL, the parsed Swagger UI options, and a resolved asset descriptor, then renders the complete page — including a JSON configuration block consumed by the inline bootstrap script. The shell is intentionally small. It loads `swagger-ui.css` and `swagger-ui-bundle.js` from the resolved hrefs (CDN or bundled), defines a single `
` mount point, and ships a bootstrap snippet that reads the JSON config block and calls `SwaggerUIBundle({...})`. Every caller-supplied string is HTML-escaped before reaching the page, and configuration values never touch raw JS source text — they ride a `` break-out protection (`<` → `<`). - Wires `deepLinking`, `tryItOutEnabled`, `displayRequestDuration`, and `filter` straight into `SwaggerUIBundle`. - Defensive bootstrap: bails silently if the bundle has not finished loading. ## Usage: Basic ```ts import { renderShell } from "@suaveplan/api-openapi-ui-swagger/html"; import { parseSwaggerUiOptions } from "@suaveplan/api-openapi-ui-swagger/options"; import { resolveCdnAssets } from "@suaveplan/api-openapi-ui-swagger/assets"; const options = parseSwaggerUiOptions({}); const assets = resolveCdnAssets(options.version); const html = renderShell({ openapiUrl: "/openapi.json", options, assets, }); ``` ## Usage: Advanced ```ts import { buildSwaggerConfig, escapeHtml, renderShell, } from "@suaveplan/api-openapi-ui-swagger/html"; import { resolveBundledAssets } from "@suaveplan/api-openapi-ui-swagger/assets"; import { parseSwaggerUiOptions } from "@suaveplan/api-openapi-ui-swagger/options"; const options = parseSwaggerUiOptions({ title: "Acme — Customer API", theme: "dark", tryItOutEnabled: false, filter: true, }); const assets = resolveBundledAssets("/docs/ui/swagger/assets"); const html = renderShell({ openapiUrl: "/openapi.json", options, assets, }); // `buildSwaggerConfig` is exposed for callers that want to introspect // the JSON payload before it reaches the shell — useful in tests. const cfg = buildSwaggerConfig("/openapi.json", options); console.log(escapeHtml(cfg.url)); ``` ## API Reference - `escapeHtml(input: string): string` — five-character entity escape. - `buildSwaggerConfig(openapiUrl, options): SwaggerUiConfig` — JSON payload threaded into the page. - `renderShell({openapiUrl, options, assets}): string` — full HTML. - `interface SwaggerUiConfig { url; dom_id; deepLinking; tryItOutEnabled; displayRequestDuration; filter; theme }`. ## Implementation Notes The renderer is a pure function — it has no IO and no clock dependency. That makes it trivial to unit-test (input determinism is the test) and safe to call from a hot HTTP path (no per-request allocations beyond the returned string). The JSON payload uses the unicode-escape trick (`<` → `<`) to prevent any caller-supplied string from terminating the `` block. Without that protection a configurable URL or title containing `` could break out of the JSON island and inject inline scripts. The escape applies to every `<` character; JSON's own syntax never produces a literal `<` so the substitution is value-preserving.