Files
skills/docs/api-stack/api/api-openapi-ui-swagger/src/html/render-shell.md
2026-05-29 08:20:56 +01:00

3.7 KiB

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 <div id="swagger-ui"> 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 <script type="application/json"> block that the bootstrap parses at runtime.

Features

  • Deterministic output — identical inputs always yield identical bytes.
  • HTML escapes the document title, theme attribute, and both asset URLs.
  • Embeds caller config via <script type="application/json"> with </script> 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

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

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 </script> block. Without that protection a configurable URL or title containing </script> 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.