Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
# Scalar bundled-asset registry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The assets module owns the in-package bundled Scalar runtime that
ships with the package — a small self-contained JS bootstrap and a tiny
companion stylesheet — plus the asset-URL resolver consumed by the
handler factory. Air-gapped hosts toggle `assetMode = "bundled"` and the
shell loads JS / CSS from `<uiEndpoint>/assets/<hash>.<ext>`; hosts on
the public internet toggle `assetMode = "cdn"` and the shell loads the
upstream `@scalar/api-reference` bundle from JSDelivr. Either way the
shell embeds the bootstrap config block at render time so the runtime
target is fixed at handler construction.
## Features
- `SCALAR_ASSETS` — frozen registry keyed by logical asset name, every
entry carrying its bytes, MIME type, and an FNV-1a-32 cache-busting
hash.
- `hashedAssetPath(name)` — formats the `<stem>.<hash>.<ext>` filename
embedded in the rendered shell.
- `resolveAsset(filename)` — reverse lookup tolerating stale hashes
(rolling deploy) and missing-hash bare filenames.
- `resolveAssetUrl({ mode, cdnUrl, uiEndpoint, probe })` — returns the
Scalar runtime URL the shell should embed. `bundled` + missing asset
raises `OPENAPI_UI_SCALAR.ASSETS_MISSING`.
- `defaultBundledAssetProbe` — checks the in-package registry is
non-empty (default behaviour); tests inject a stub to simulate the
stripped-bundle case.
## Usage: Basic
```ts
import { resolveAssetUrl } from "@suaveplan/api-openapi-ui-scalar/assets";
const runtime = resolveAssetUrl({
mode: "cdn",
cdnUrl: "https://cdn.jsdelivr.net/npm/@scalar/api-reference",
uiEndpoint: "/docs/ui/scalar",
});
console.log(runtime.url);
```
## Usage: Advanced
```ts
import {
resolveAssetUrl,
type BundledAssetProbe,
} from "@suaveplan/api-openapi-ui-scalar/assets";
const stubbedProbe: BundledAssetProbe = { hasBundle: () => false };
// Simulate the air-gapped "asset stripped from dist" failure case —
// `resolveAssetUrl` throws OPENAPI_UI_SCALAR.ASSETS_MISSING.
try {
resolveAssetUrl({
mode: "bundled",
cdnUrl: "",
uiEndpoint: "/docs/ui/scalar",
probe: stubbedProbe,
});
} catch (err) {
console.error("bundled asset is missing", err);
}
```
## API Reference
- `ScalarAsset` interface — `{ name, content, contentType, hash }`.
- `SCALAR_ASSETS` `ReadonlyMap<string, ScalarAsset>` — frozen registry.
- `fnv1a32(input)` — 8-char hex FNV-1a-32 of the asset bytes.
- `hashedAssetPath(name)` — throws `ASSET_NOT_REGISTERED` when missing.
- `resolveAsset(filename)` — returns the asset entry or throws
`ASSET_NOT_REGISTERED`.
- `resolveAssetUrl(input)` — returns `{ mode, url }`; throws
`ASSETS_MISSING` in bundled mode without a bundle.
- `listScalarAssetNames()` — listing of registered names for tests.
## Implementation Notes
The bundled JS bootstrap is intentionally minimal — it parses the
`<script id="scalar-config" type="application/json">` block and renders
a status line + path listing so air-gapped hosts have a functional
fallback. Operators that need the full Scalar React UI install the
upstream `@scalar/api-reference` and overwrite `dist/assets/scalar.js`
at build time; the FNV-1a hash refreshes automatically. The hash is NOT
security-relevant — it exists only to invalidate browser caches.