Initial Commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# `assets`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Bundled static-asset registry for the GraphiQL UI plugin. The HTML shell
|
||||
references two assets (`graphiql.js`, `graphiql.css`) via hashed filenames
|
||||
under `<uiEndpoint>/assets/`. Hash bytes come from FNV-1a-32 of the asset
|
||||
content — non-cryptographic, used purely for cache-busting on deploy.
|
||||
|
||||
The asset payloads are shipped inline as TypeScript template strings so
|
||||
the package has zero filesystem requirements at runtime. Operators who
|
||||
want a richer GraphiQL distribution (the full React app) can swap
|
||||
`GRAPHIQL_ASSETS` content on a fork without touching the rest of the
|
||||
plugin.
|
||||
|
||||
## Features
|
||||
|
||||
- `GRAPHIQL_ASSETS` — frozen `Map<name, asset>` exposing every shipped
|
||||
static.
|
||||
- `hashedAssetPath(name)` — builds `<stem>.<hash>.<ext>` for embedding in
|
||||
the HTML shell.
|
||||
- `resolveAsset(filename)` — server-side lookup. Tolerates stale hashes
|
||||
during rolling deploys (resolves any matching stem+ext to the current
|
||||
asset).
|
||||
- `fnv1a32(input)` — public so callers can hash their own additions in
|
||||
consistent shape.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { GRAPHIQL_ASSETS, resolveAsset } from "@suaveplan/api-graphql-ui-graphiql";
|
||||
|
||||
for (const asset of GRAPHIQL_ASSETS.values()) {
|
||||
console.log(asset.name, asset.hash, asset.contentType);
|
||||
}
|
||||
const css = resolveAsset("graphiql.css");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { fnv1a32, hashedAssetPath, resolveAsset } from "@suaveplan/api-graphql-ui-graphiql";
|
||||
|
||||
// Build the served filename for the JS asset.
|
||||
const path = `/graphql/ui/assets/${hashedAssetPath("graphiql.js")}`;
|
||||
|
||||
// Verify integrity in a custom asset middleware.
|
||||
const asset = resolveAsset("graphiql.js");
|
||||
if (fnv1a32(asset.content) !== asset.hash) {
|
||||
throw new Error("asset content drift");
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface GraphiqlAsset { name, content, contentType, hash }`.
|
||||
- `GRAPHIQL_ASSETS: ReadonlyMap<string, GraphiqlAsset>`.
|
||||
- `fnv1a32(input: string): string` — 8-char lowercase hex hash.
|
||||
- `hashedAssetPath(name: string): string` — throws `ASSET_MISSING` if
|
||||
`name` is not registered.
|
||||
- `resolveAsset(filename: string): GraphiqlAsset` — throws
|
||||
`ASSET_MISSING` on lookup failure.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The stale-hash tolerance is intentional. Picture a CDN-cached `index.html`
|
||||
served before a deploy with the new asset bundle — that `index.html`
|
||||
references `graphiql.<old-hash>.js`. Returning 404 on the old hash would
|
||||
break the in-flight session for ~CDN-TTL seconds. The resolver instead
|
||||
serves the new bytes under the old name, traded against a slightly larger
|
||||
attack surface (an adversary who knows the asset name+ext can fetch the
|
||||
asset at any hash). The Cache-Control header on the response is
|
||||
`immutable` for the matched name, so the browser still caches the
|
||||
content correctly.
|
||||
Reference in New Issue
Block a user