Initial Commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# `render-shell.ts` — Swagger UI HTML shell renderer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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
|
||||
|
||||
```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 `</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.
|
||||
Reference in New Issue
Block a user