Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user