Files
suaveplan/packages/api-stack/api/api-openapi-ui-scalar/src/assets/assets.md
2026-05-27 23:29:53 +01:00

3.3 KiB

Scalar bundled-asset registry

(no submodules)

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

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

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.