first commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# `assets` — Bundled asset registry, resolver, CDN-clean check
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`assets` ships the Altair shell as a static-asset map keyed by logical
|
||||
filename. Every entry carries a content-type, the payload bytes, and a
|
||||
short SHA-256 hash derived once at module init so cache-bust URLs are
|
||||
stable across deploys with unchanged payloads. The shell is shipped
|
||||
inline (no FS reads at request time, no CDN fetches) which preserves
|
||||
the Altair plugin's hermetic-bundle guarantee — the package can boot in
|
||||
air-gapped, offline-first environments and still serve a usable UI.
|
||||
|
||||
## Features
|
||||
|
||||
- Default registry: `altair.js` + `altair.css`.
|
||||
- `defineAsset(name, contentType, body)` returns a frozen `BundledAsset`.
|
||||
- `resolveAsset(registry, uiEndpointBase, name)` produces a
|
||||
`ResolvedAsset` with a hash-busted URL.
|
||||
- Missing names throw `GraphqlUiAltairError` (`ASSET_MISSING`).
|
||||
- `findExternalHostReferences` is the CDN-clean conformance check.
|
||||
- Hash derivation is SHA-256, truncated to 16 hex chars — same
|
||||
cardinality as a `git rev-parse --short=16`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildDefaultAssetRegistry,
|
||||
resolveAsset,
|
||||
} from "@suaveplan/api-graphql-ui-altair/assets";
|
||||
|
||||
const registry = buildDefaultAssetRegistry();
|
||||
const jsAsset = resolveAsset(registry, "/graphql/ui", "altair.js");
|
||||
console.log(jsAsset.url); // /graphql/ui/assets/<hash>/altair.js
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildDefaultAssetRegistry,
|
||||
defineAsset,
|
||||
findExternalHostReferences,
|
||||
} from "@suaveplan/api-graphql-ui-altair/assets";
|
||||
|
||||
// Extend the registry with an operator-specific asset.
|
||||
const registry = new Map(buildDefaultAssetRegistry());
|
||||
registry.set(
|
||||
"logo.svg",
|
||||
defineAsset(
|
||||
"logo.svg",
|
||||
"image/svg+xml",
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" />'
|
||||
)
|
||||
);
|
||||
|
||||
const offenders = findExternalHostReferences(registry);
|
||||
if (offenders.length > 0) {
|
||||
throw new Error("CDN leak: " + offenders.join(", "));
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `BundledAsset` — `{ name, contentType, body, hash }`.
|
||||
- `ResolvedAsset extends BundledAsset` — adds `url`.
|
||||
- `defineAsset(name, contentType, body): BundledAsset`.
|
||||
- `buildDefaultAssetRegistry(): ReadonlyMap<string, BundledAsset>`.
|
||||
- `resolveAsset(registry, uiEndpointBase, name): ResolvedAsset` — throws
|
||||
`GraphqlUiAltairError` (`ASSET_MISSING`) on misses.
|
||||
- `findExternalHostReferences(registry): readonly string[]`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The asset bodies are held as JavaScript template literals so the build
|
||||
emits them as plain string constants — no FS access at boot, no
|
||||
`process.cwd()` dependence. The hash is computed via `node:crypto` to
|
||||
match the same hashing the rest of the project's static-asset
|
||||
pipeline uses; the truncation to 16 hex chars is intentional — a
|
||||
collision in a single deploy's asset set is vanishingly unlikely and
|
||||
the shorter URL is easier on the eyes in production logs.
|
||||
Reference in New Issue
Block a user