first commit
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# content-address
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Content-addressed URL builder and cache-invalidation helpers for packed
|
||||
archives. Each archive's canonical URL is derived from a BLAKE3 Merkle
|
||||
root over its entries plus the format-version byte, so any payload or
|
||||
wire-format change produces a different URL and the Service Worker can
|
||||
cleanly invalidate the prior version on next install. This module is
|
||||
the foundation for the `/packed/<hash>.bundle.asset` URL form and the
|
||||
SW `activate`-time cache pruning.
|
||||
|
||||
## Features
|
||||
|
||||
- `computeArchiveContentHash(entries, formatVersion)` — deterministic
|
||||
32-byte BLAKE3 root that mixes the format-version byte into the leaf
|
||||
set so a wire-format bump is reflected in the URL.
|
||||
- `contentHashHex(hash)` — lowercase-hex serialiser used by URL builders
|
||||
and cache-key emitters.
|
||||
- `buildContentAddressedUrl(prefix, hash, ext?)` — emits the canonical
|
||||
`<prefix>/<hash>.<ext>` URL (default ext `bundle.asset` matches the
|
||||
Cluster A SW pattern).
|
||||
- `cleanupOutdatedCaches(caches, prefix, keep)` — Service Worker helper
|
||||
that deletes every cache name with a given prefix not in `keep`.
|
||||
- `PACKER_BROWSER_FLAGS.URL_CONTENT_ADDRESSED` — stable feature-flag
|
||||
string registered against the manager's `FlagProvider`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildContentAddressedUrl,
|
||||
computeArchiveContentHash,
|
||||
contentHashHex,
|
||||
} from "@suaveplan/packer-browser";
|
||||
|
||||
const entries = [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])];
|
||||
const hash = computeArchiveContentHash(entries, /* formatVersion */ 1);
|
||||
const url = buildContentAddressedUrl("/packed", contentHashHex(hash));
|
||||
// → "/packed/<64-hex>.bundle.asset"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
cleanupOutdatedCaches,
|
||||
runPackedResourceServiceWorker,
|
||||
} from "@suaveplan/packer-browser";
|
||||
|
||||
// Inside the Service Worker bootstrap:
|
||||
runPackedResourceServiceWorker(self as unknown as ServiceWorkerGlobalScope, {
|
||||
cacheName: `packed-resources-${currentHash}`,
|
||||
contentAddressed: true,
|
||||
});
|
||||
|
||||
// `cleanupOutdatedCaches` is invoked by the SW's activate handler
|
||||
// automatically when `contentAddressed` is on. You can also call it
|
||||
// from a maintenance message handler:
|
||||
self.addEventListener("message", async (ev) => {
|
||||
if ((ev as MessageEvent).data?.type === "GC") {
|
||||
const deleted = await cleanupOutdatedCaches(
|
||||
self.caches,
|
||||
"packed-resources-",
|
||||
[`packed-resources-${currentHash}`]
|
||||
);
|
||||
console.info("Pruned", deleted);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `computeArchiveContentHash(entries: ReadonlyArray<Uint8Array>, formatVersion: number): Uint8Array`
|
||||
Throws `RangeError` if `formatVersion` is not an integer in [0, 255].
|
||||
- `contentHashHex(hash: Uint8Array): string` Throws `RangeError` on
|
||||
non-32-byte input.
|
||||
- `buildContentAddressedUrl(prefix: string, hash: string, ext?: string): string`
|
||||
- `cleanupOutdatedCaches(caches: CacheStorageLike, prefix: string, keep: ReadonlyArray<string>): Promise<string[]>`
|
||||
Returns the list of cache names that were deleted.
|
||||
- `PACKER_BROWSER_FLAGS.URL_CONTENT_ADDRESSED: string` — `"packer-browser.url.content-addressed"`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The format-version byte is appended as a synthetic trailing entry
|
||||
rather than mixed via HKDF so the implementation stays simple and
|
||||
every transformation is auditable via the existing
|
||||
`entryDigestBlake3` + `merkleRootBlake3` primitives.
|
||||
- `cleanupOutdatedCaches` accepts any object with `keys()` and
|
||||
`delete()` methods; both the production `globalThis.caches` and the
|
||||
in-memory `ShimCacheStorage` satisfy the contract, which keeps the SW
|
||||
unit tests honest.
|
||||
- `buildContentAddressedUrl` trims a trailing slash from the prefix so
|
||||
callers can pass `/packed` or `/packed/` interchangeably.
|
||||
- The Cluster A SW pattern `/\.packed$/` continues to match the
|
||||
canonical content-addressed form; the Cluster B
|
||||
`RunServiceWorkerOptions.legacyUrlPattern` slot accepts any other
|
||||
RegExp callers need for backwards compatibility (e.g. legacy
|
||||
named-archive URLs).
|
||||
Reference in New Issue
Block a user