@craftainer/crud-sdk 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -116,9 +116,64 @@ await heroes.update(1, { name: "Ada" }, { ifMatch: etag }); // 412 -> Preconditi
116
116
  called once with `{ refresh: true }` and the request is retried only if
117
117
  the token changed. The cache scope is `"anon"` or the first 32 hex of the
118
118
  SHA-256 of the whole token (never decoded).
119
- - **Middleware.** `createClient({ middleware })` or `client.use(mw)` (added
120
- innermost). Each request carries a `RequestContext` (`resource`, `op`,
121
- `recordId`, `scope`, `apiVersion`, `signal`).
119
+ - **Cookie sessions (BFF).** With no bearer token in the browser, set
120
+ `cacheScope`, `credentials` and optionally `onUnauthorized`
121
+ (see [ADR 0003](docs/adrs/0003-cookie-sessions.md)):
122
+
123
+ ```ts
124
+ const csrf: Middleware = (ctx, next) => {
125
+ const headers = new Headers(ctx.request.headers);
126
+ headers.set("X-CSRF", "1");
127
+ return next({ ...ctx, request: new Request(ctx.request, { headers }) });
128
+ };
129
+ const client = createClient({
130
+ baseUrl: "/api/crud",
131
+ credentials: "same-origin", // sends the session cookie
132
+ cacheScope: () => currentUser.sub, // string, or sync/async function
133
+ onUnauthorized: () => location.assign("/bff/login"),
134
+ middleware: [csrf, cache.middleware],
135
+ });
136
+ ```
137
+
138
+ - `cacheScope` (string, or a function; it must resolve to a non-empty
139
+ string, else `ClientError`) **replaces** the token-hash / `"anon"` scope
140
+ everywhere: `client.scope()`, `namespace()` (TanStack keys), `ctx.scope`
141
+ and the `./cache` keys. It wins over `tokenProvider` when both are given
142
+ (the token still sets `Authorization`; it just no longer picks the
143
+ scope), and `scope()` does not call the `tokenProvider` then. A function
144
+ is evaluated per request (reused across that request's 401 retry), plus
145
+ once per `scope()` call. The resulting scope is internally namespaced (`s:` + the
146
+ percent-encoded value; treat it as opaque), so it never equals `anon` or
147
+ a token hash. The client sets `ctx.cacheScope` only when the option is
148
+ used, and the cache keys by it, else by the Authorization header as
149
+ before. **Middleware must carry context fields through** (build the next
150
+ context with `{ ...ctx, request }`); dropping `cacheScope` makes the cache
151
+ fall back to the header scope. Use something stable per user (the OIDC
152
+ `sub`), and a different value after logout/login as someone else.
153
+ - `credentials` is passed to every `fetch` as `RequestInit.credentials`.
154
+ - `credentials: "include"` over plain `http:` to a
155
+ non-loopback host is refused with `InsecureTransportError`, like an
156
+ `Authorization` header (`allowInsecureHttp: true` opts in).
157
+ - `onUnauthorized(response)` is awaited on each final `401` response, after
158
+ the `tokenProvider` refresh retry (if any); manifest and discovery
159
+ requests included. A call that makes several requests can fire it more
160
+ than once, so make it idempotent (for example a navigation). It gets a
161
+ clone of the response. The call then still rejects with `Unauthorized`; if
162
+ the callback throws, its error is the `Unauthorized`'s `cause`, or, when
163
+ the problem already has a `cause`, a non-enumerable `onUnauthorizedError`
164
+ property.
165
+ - There is no `headers` option: add static headers such as `X-CSRF` with a
166
+ middleware, as above.
167
+
168
+ - **Middleware.** > [!WARNING]
169
+ > Middleware must carry context fields through: build the next context with
170
+ > `{ ...ctx, request }`. A middleware that builds a fresh `RequestContext`
171
+ > drops `cacheScope`, and the `./cache` middleware then falls back to the
172
+ > Authorization-header scope (`anon` for cookie sessions), sharing entries
173
+ > between users.
174
+ > `createClient({ middleware })` or `client.use(mw)` (added
175
+ > innermost). Each request carries a `RequestContext` (`resource`, `op`,
176
+ > `recordId`, `scope`, `apiVersion`, `signal`).
122
177
  - **Response shape.** A call that expects an object or array and gets a `204`,
123
178
  an empty body or JSON of the wrong shape throws `ResponseError`, as a body
124
179
  that is not JSON does. `client.resource("heroes")` picks the highest
@@ -147,7 +202,8 @@ naming them.
147
202
 
148
203
  `createCache({ store, policies })` (from `./cache`) returns two `Middleware`:
149
204
  `cache.middleware` and `cache.autoIfMatch`. Put both **innermost**, after
150
- whatever sets `Authorization`: the cache scope is `anon` without an
205
+ whatever sets `Authorization`: unless `createClient({ cacheScope })` is set
206
+ (then that scope is used), the cache scope is `anon` without an
151
207
  Authorization header, else the first 32 hex characters of the SHA-256 of the
152
208
  whole credential, taken from the request as it is sent. `policies` maps
153
209
  manifest resource names to `cachePolicyFromManifest(resource)` (plain I1
@@ -1,5 +1,5 @@
1
- import { a as Middleware, i as MaybePromise, n as CacheStore, r as CrudEvent, t as CacheEntry } from "../interfaces-s73cVfIF.mjs";
2
- import { a as EventsConfig, c as subscribeEvents, d as cachePolicyFromManifest, f as tanstackCachePolicy, i as EventHubOptions, l as CachePolicy, n as EventHandlers, o as SubscribeEventsOptions, r as EventHub, s as getEventHub, t as EventConnection, u as ManifestCacheHints } from "../events-DdT0AkNE.mjs";
1
+ import { a as Middleware, i as MaybePromise, n as CacheStore, r as CrudEvent, t as CacheEntry } from "../interfaces-Ca8EDwyf.mjs";
2
+ import { a as EventsConfig, c as subscribeEvents, d as cachePolicyFromManifest, f as tanstackCachePolicy, i as EventHubOptions, l as CachePolicy, n as EventHandlers, o as SubscribeEventsOptions, r as EventHub, s as getEventHub, t as EventConnection, u as ManifestCacheHints } from "../events-CsFyFDMI.mjs";
3
3
  //#region src/cache/middleware.d.ts
4
4
  /**
5
5
  * A store may clear several prefixes in one pass (one walk, one transaction);
@@ -176,6 +176,16 @@ async function credentialScope(headers) {
176
176
  const credential = /^bearer\s+(.+)$/i.exec(value)?.[1] ?? value;
177
177
  return cacheScope(credential);
178
178
  }
179
+ /**
180
+ * The caller scope the cache keys by: `ctx.cacheScope` when the client was
181
+ * created with a `cacheScope` option (a cookie session sends no Authorization
182
+ * header, so the header cannot tell callers apart), otherwise
183
+ * {@link credentialScope} of the request as it is sent, so a middleware that
184
+ * sets its own Authorization is still covered.
185
+ */
186
+ async function contextScope(ctx) {
187
+ return ctx.cacheScope ?? credentialScope(ctx.request.headers);
188
+ }
179
189
  //#endregion
180
190
  //#region src/cache/middleware.ts
181
191
  const MIN_RETENTION_MS = 36e5;
@@ -275,7 +285,7 @@ function createCache(options) {
275
285
  };
276
286
  const begin = async (ctx) => {
277
287
  const { request, resource } = ctx;
278
- const scope = await credentialScope(request.headers);
288
+ const scope = await contextScope(ctx);
279
289
  const generationNow = generation(resource);
280
290
  if (request.method !== "GET") {
281
291
  if (!WRITE_METHODS.has(request.method)) return;
@@ -565,7 +575,7 @@ function createCache(options) {
565
575
  const autoIfMatch = async (ctx, next) => {
566
576
  const { request, resource, recordId } = ctx;
567
577
  if ((ctx.op === "update" || ctx.op === "delete") && recordId !== void 0 && resource !== void 0 && policyFor(resource, ctx.apiVersion)?.enabled === true && !request.headers.has("if-match")) {
568
- const etag = await lookupEtag(resource, recordId, await credentialScope(request.headers), ctx.apiVersion);
578
+ const etag = await lookupEtag(resource, recordId, await contextScope(ctx), ctx.apiVersion);
569
579
  if (etag !== void 0) {
570
580
  const headers = new Headers(request.headers);
571
581
  headers.set("if-match", etag);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cache/entry.ts","../../src/cache/keys.ts","../../src/cache/scope.ts","../../src/cache/middleware.ts","../../src/cache/memory.ts","../../node_modules/.pnpm/idb@8.0.4/node_modules/idb/build/index.js","../../src/cache/idb.ts","../../src/cache/wire.ts"],"sourcesContent":["/** Turning responses into `CacheEntry` values and back. */\nimport type { CacheEntry } from \"../interfaces.ts\";\n\n/** How the cache answered a read: see `cacheStatus`. */\nexport type CacheStatus = \"hit\" | \"stale\" | \"revalidated\" | \"miss\";\n\nconst statuses = new WeakMap<Response, CacheStatus>();\n\n/**\n * How the cache answered `response`: `hit` (fresh, no network), `stale`\n * (inside the stale-while-revalidate window, refreshed in the background),\n * `revalidated` (a `304` rebuilt from the stored entry) or `miss`.\n * `undefined` when the cache did not handle the request.\n */\nexport function cacheStatus(response: Response): CacheStatus | undefined {\n return statuses.get(response);\n}\n\n/** Tag `response` with how the cache answered. */\nexport function tagStatus(response: Response, status: CacheStatus): Response {\n statuses.set(response, status);\n return response;\n}\n\n// Describe the transfer, not the representation: the stored body is decoded.\nconst TRANSFER_HEADERS: ReadonlySet<string> = new Set([\n \"content-length\",\n \"content-encoding\",\n \"transfer-encoding\",\n \"connection\",\n \"keep-alive\",\n]);\nconst VARY_OK: ReadonlySet<string> = new Set([\n \"authorization\",\n \"accept\",\n \"accept-encoding\",\n]);\n\n/** The lower-cased directive names of the `Cache-Control` header. */\nexport function cacheControl(headers: Headers): ReadonlySet<string> {\n const names = (headers.get(\"cache-control\") ?? \"\")\n .split(\",\")\n .map((part) => (part.split(\"=\", 1)[0] as string).trim().toLowerCase())\n .filter((name) => name !== \"\");\n return new Set(names);\n}\n\n/** Whether the media type is JSON (`application/json` or `+json`). */\nexport function isJson(response: Response): boolean {\n const media = (response.headers.get(\"content-type\") ?? \"\")\n .split(\";\", 1)[0]!\n .trim()\n .toLowerCase();\n return media === \"application/json\" || media.endsWith(\"+json\");\n}\n\n/** `Vary` may only name headers the key already accounts for; `*` never caches. */\nexport function varyAllowsCaching(response: Response): boolean {\n const names = (response.headers.get(\"vary\") ?? \"\")\n .split(\",\")\n .map((part) => part.trim().toLowerCase())\n .filter((name) => name !== \"\");\n return names.every((name) => VARY_OK.has(name));\n}\n\nfunction storableHeaders(headers: Headers): Array<readonly [string, string]> {\n const items: Array<readonly [string, string]> = [];\n for (const [name, value] of headers) {\n if (!TRANSFER_HEADERS.has(name)) {\n items.push([name, value]);\n }\n }\n return items;\n}\n\nexport interface EntryTimes {\n readonly now: number;\n /** Milliseconds fresh. */\n readonly fresh: number;\n /** Milliseconds of stale-while-revalidate after `fresh`. */\n readonly stale: number;\n}\n\n/** Snapshot a read response whose body text was already read. */\nexport function buildEntry(\n response: Response,\n body: string,\n times: EntryTimes,\n keepEtag: boolean,\n): CacheEntry {\n const etag = keepEtag ? response.headers.get(\"etag\") : null;\n const base = {\n status: response.status,\n headers: storableHeaders(response.headers),\n body,\n storedAt: times.now,\n freshUntil: times.now + times.fresh,\n staleUntil: times.now + times.fresh + times.stale,\n };\n return etag === null ? base : { ...base, etag };\n}\n\n/** RFC 9111 4.3.4: headers of a `304` replace the stored ones of the same name. */\nexport function mergeHeaders(\n stored: CacheEntry[\"headers\"],\n update: Headers,\n): CacheEntry[\"headers\"] {\n const fresh = storableHeaders(update);\n const replaced = new Set(fresh.map(([name]) => name));\n return [...stored.filter(([name]) => !replaced.has(name)), ...fresh];\n}\n\n/** Rebuild a response from an entry, tagged with how the cache produced it. */\nexport function toResponse(entry: CacheEntry, status: CacheStatus): Response {\n const headers = new Headers();\n for (const [name, value] of entry.headers) {\n headers.append(name, value);\n }\n return tagStatus(\n new Response(entry.body, { status: entry.status, headers }),\n status,\n );\n}\n","/**\n * Cache keys and the invalidation prefixes.\n *\n * Keys are slash-separated:\n * `v1/{resource}/{kind}/{subject}/{apiVersion}/{scope}/{canonicalQuery}`.\n * Versions of one resource never share an entry, but they share one event\n * stream; because the version comes after kind and subject, one prefix such\n * as `v1/{resource}/record/{id}/` clears an entry in every version and for\n * every caller at once.\n */\nimport type { Op } from \"../interfaces.ts\";\n\n/** The ops that read and are cached (`manifest` has its own key). */\nexport type CacheKind =\n \"list\" | \"record\" | \"stats\" | \"predict\" | \"revisions\" | \"filters\";\n\nconst KINDS: ReadonlySet<string> = new Set<CacheKind>([\n \"list\",\n \"record\",\n \"stats\",\n \"predict\",\n \"revisions\",\n \"filters\",\n]);\nconst STATS_KINDS: ReadonlySet<string> = new Set([\"stats\", \"predict\"]);\nconst COLLECTION_KINDS = [\"list\", \"stats\", \"predict\"] as const;\nconst PER_RECORD_KINDS = [\"record\", \"revisions\"] as const;\n\n/** `true` when the op is a cacheable read kind. */\nexport function isCacheKind(op: Op): op is CacheKind {\n return KINDS.has(op);\n}\n\n/** `true` for `stats` and `predict`, which `stats_ttl` bounds. */\nexport function isStatsKind(kind: string): boolean {\n return STATS_KINDS.has(kind);\n}\n\n/**\n * Sorts the query pairs the client sent and drops `id` (it is the subject).\n * Values are kept verbatim: the client already emits canonical,\n * percent-encoded text, so nothing is decoded or re-encoded here.\n */\nexport function canonicalQuery(query: string): string {\n const text = query.startsWith(\"?\") ? query.slice(1) : query;\n const pairs = text\n .split(\"&\")\n .filter((part) => part !== \"\" && part.split(\"=\", 1)[0] !== \"id\")\n .map((part) => {\n const at = part.indexOf(\"=\");\n return at < 0\n ? ([part, \"\"] as const)\n : ([part.slice(0, at), part.slice(at)] as const);\n });\n pairs.sort(([an, av], [bn, bv]) =>\n an === bn ? (av < bv ? -1 : av > bv ? 1 : 0) : an < bn ? -1 : 1,\n );\n return pairs.map(([name, rest]) => name + rest).join(\"&\");\n}\n\nexport interface KeyParts {\n readonly resource: string;\n readonly kind: CacheKind;\n readonly recordId?: number | undefined;\n readonly scope: string;\n readonly query?: string;\n /** `null` (no version) renders as `_`. */\n readonly apiVersion: string | null;\n}\n\n/** The key of one cached response. */\nexport function buildKey(parts: KeyParts): string {\n const subject =\n (parts.kind === \"record\" || parts.kind === \"revisions\") &&\n parts.recordId !== undefined\n ? String(parts.recordId)\n : \"_\";\n const version = parts.apiVersion ?? \"_\";\n return `v1/${parts.resource}/${parts.kind}/${subject}/${version}/${parts.scope}/${canonicalQuery(parts.query ?? \"\")}`;\n}\n\n/** Key of the manifest document for one caller scope. */\nexport function manifestKey(scope: string): string {\n return `v1/_manifest/${scope}`;\n}\n\n/** Prefix of all entries of one kind for `resource` (every scope and subject). */\nexport function kindPrefix(resource: string, kind: CacheKind): string {\n return `v1/${resource}/${kind}/`;\n}\n\n/** Prefix of the `record` entries of one record (every version, scope, query). */\nexport function recordPrefix(resource: string, recordId: number): string {\n return `v1/${resource}/record/${recordId}/`;\n}\n\n/** Prefix of the `revisions` entries of one record. */\nexport function revisionsPrefix(resource: string, recordId: number): string {\n return `v1/${resource}/revisions/${recordId}/`;\n}\n\n/** The list, stats and predict prefixes of `resource`. */\nexport function collectionPrefixes(resource: string): string[] {\n return COLLECTION_KINDS.map((kind) => kindPrefix(resource, kind));\n}\n\n/** Everything cached for `resource` except `filters` (a reconnect or an unknown change). */\nexport function resourcePrefixes(resource: string): string[] {\n return [...COLLECTION_KINDS, ...PER_RECORD_KINDS].map((kind) =>\n kindPrefix(resource, kind),\n );\n}\n\n/**\n * Prefixes an SSE event clears, per the spec's table: `create` clears the\n * collection kinds; `update`/`delete`/`restore` also clear the record and its\n * revisions; an unknown action or a missing record id clears the resource.\n */\nexport function eventPrefixes(\n resource: string,\n action: string,\n recordId: number | undefined,\n): string[] {\n if (action === \"create\") {\n return collectionPrefixes(resource);\n }\n if (\n (action === \"update\" || action === \"delete\" || action === \"restore\") &&\n recordId !== undefined\n ) {\n return [\n ...collectionPrefixes(resource),\n recordPrefix(resource, recordId),\n revisionsPrefix(resource, recordId),\n ];\n }\n return resourcePrefixes(resource);\n}\n","import { ANON, cacheScope } from \"../auth.ts\";\n\n/**\n * `\"anon\"` when the request has no Authorization header; otherwise the first\n * 32 hex characters of the SHA-256 of the whole credential, the same digest as\n * the client's `cacheScope(token)` (a `Bearer ` prefix is dropped, any other value is hashed as it is). It is read from the\n * request the cache sees, so it must be the innermost middleware that matters:\n * whatever sets Authorization has to run before the cache. The raw credential\n * never enters a key or a log, and the unverified `sub` claim is never used.\n */\nexport async function credentialScope(headers: Headers): Promise<string> {\n const value = headers.get(\"authorization\");\n if (value === null) {\n return ANON;\n }\n const credential = /^bearer\\s+(.+)$/i.exec(value)?.[1] ?? value;\n return cacheScope(credential);\n}\n","/**\n * The caching middleware (`createCache`): an I3 `Middleware` plus the control\n * surface that events and the client use.\n *\n * It reads the resource, op, record id, API version and caller scope from\n * `RequestContext` and never touches the Authorization header. A read is\n * answered in one of four ways, see `cacheStatus`: `hit` (fresh, no\n * network), `stale` (inside the stale-while-revalidate window, refreshed in\n * the background), `revalidated` (a `304` rebuilt from the stored entry with\n * merged headers) or `miss`.\n *\n * Freshness beyond \"revalidate every time\" is granted only while the\n * resource's SSE stream is up (`setEventsActive`): events are the only thing\n * that can end a fresh window early. `stats` and `predict` are bounded by\n * `statsTtl` instead, as the server caches those bodies the same way. A\n * per-resource generation counter stops a response that was in flight when\n * an invalidation landed from being stored fresh.\n */\nimport type {\n CacheEntry,\n MaybePromise,\n CacheStore,\n Middleware,\n Next,\n RequestContext,\n} from \"../interfaces.ts\";\nimport {\n buildEntry,\n cacheControl,\n isJson,\n mergeHeaders,\n tagStatus,\n toResponse,\n varyAllowsCaching,\n} from \"./entry.ts\";\nimport {\n buildKey,\n collectionPrefixes,\n eventPrefixes,\n isCacheKind,\n isStatsKind,\n kindPrefix,\n manifestKey,\n recordPrefix,\n resourcePrefixes,\n revisionsPrefix,\n} from \"./keys.ts\";\nimport type { CacheKind } from \"./keys.ts\";\nimport { cachePolicyFromManifest } from \"./policy.ts\";\nimport { credentialScope } from \"./scope.ts\";\nimport type { CachePolicy } from \"./policy.ts\";\n\n/**\n * A store may clear several prefixes in one pass (one walk, one transaction);\n * the cache uses it when present and falls back to `clear` per prefix.\n */\nexport interface CacheStoreMany extends CacheStore {\n clearMany?(prefixes: readonly string[]): MaybePromise<void>;\n}\n\nexport interface CacheOptions {\n readonly store: CacheStoreMany;\n /**\n * Policy per manifest resource: `name@apiVersion` (built from that version's\n * manifest entry) or, for every version, plain `name`. A resource without\n * one is not cached. The event stream and the freshness gate are per name.\n */\n readonly policies: Readonly<Record<string, CachePolicy>>;\n /** Cache the manifest document per caller scope. Default `true`. */\n readonly cacheManifest?: boolean;\n /** Epoch milliseconds; injectable for tests. */\n readonly now?: () => number;\n}\n\nexport interface Cache {\n /** Put it in the client's middleware chain. */\n readonly middleware: Middleware;\n /** Record whether the SSE stream of `resource` is connected (gates fresh hits). */\n setEventsActive(resource: string, active: boolean): void;\n eventsActive(resource: string): boolean;\n /** Clear what an SSE event makes stale, in every API version. */\n invalidateEvent(\n resource: string,\n action: string,\n recordId: number | undefined,\n ): Promise<void>;\n /** Clear everything cached for `resource` except `filters` (after a reconnect). */\n clearResource(resource: string): Promise<void>;\n /** Clear keys under `prefix` (everything the store owns when omitted). */\n clear(prefix?: string): Promise<void>;\n /**\n * Adds `If-Match` from the cached record ETag to `PATCH`/`DELETE ?id=` that\n * have none. Put it innermost, next to `middleware`, so it sees the same\n * Authorization (and so scope) the request is sent with.\n */\n readonly autoIfMatch: Middleware;\n /** The stored ETag of a record of one API version, for `auto_if_match`. */\n cachedEtag(\n resource: string,\n recordId: number,\n scope: string,\n apiVersion: string | null,\n ): Promise<string | undefined>;\n /** Abort background revalidations and wait for them. */\n close(): Promise<void>;\n}\n\ntype Kind = CacheKind | \"manifest\";\ntype Decision = \"hit\" | \"stale\" | \"revalidate\" | \"miss\";\ntype StoreOp =\n | { readonly type: \"clear\"; readonly prefixes: readonly string[] }\n | {\n readonly type: \"set\";\n readonly key: string;\n readonly entry: CacheEntry;\n readonly ttlMs: number;\n }\n | { readonly type: \"delete\"; readonly key: string };\n\n/** What `begin` learned about one request. */\ninterface State {\n readonly ctx: RequestContext;\n readonly policy: CachePolicy;\n /** The cache kind of a read; `undefined` for a write. */\n readonly kind: Kind | undefined;\n readonly key: string | undefined;\n /** The resource's invalidation count when the request was made. */\n readonly generation: number;\n /** From the Authorization header of the request as the cache sees it. */\n readonly scope: string;\n readonly forceRevalidate: boolean;\n entry?: CacheEntry | undefined;\n /** Whether we put `If-None-Match` on the request. */\n validated?: boolean;\n}\n\nconst MIN_RETENTION_MS = 3_600_000;\nconst MANIFEST_POLICY: CachePolicy = cachePolicyFromManifest({});\nconst WRITE_METHODS: ReadonlySet<string> = new Set([\n \"POST\",\n \"PATCH\",\n \"PUT\",\n \"DELETE\",\n]);\nconst BULK_OPS: ReadonlySet<string> = new Set([\"bulk_update\", \"bulk_delete\"]);\nconst RECORD_WRITES: ReadonlySet<string> = new Set([\n \"delete\",\n \"publish\",\n \"restore\",\n]);\nconst CREATES: ReadonlySet<string> = new Set([\"create\", \"clone\", \"draft\"]);\n\nfunction isStorable(response: Response): boolean {\n return (\n isJson(response) &&\n !cacheControl(response.headers).has(\"no-store\") &&\n varyAllowsCaching(response)\n );\n}\n\nfunction retentionMs(freshMs: number, staleMs: number): number {\n return Math.max(freshMs + staleMs, MIN_RETENTION_MS);\n}\n\n/** The ids of a bulk response (`{matched, ids}`), or `undefined` if unreadable. */\nfunction idsFromBody(body: string): number[] | undefined {\n try {\n const ids = (JSON.parse(body) as { ids?: unknown }).ids;\n return Array.isArray(ids) && ids.every((id) => Number.isInteger(id))\n ? (ids as number[])\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The resource names in a manifest document, or none if unreadable. */\nfunction manifestResources(body: string): string[] {\n try {\n const resources = (JSON.parse(body) as { resources?: unknown }).resources;\n return Array.isArray(resources)\n ? resources\n .map((item: { name?: unknown }) => item.name)\n .filter((name): name is string => typeof name === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\nasync function safe<T>(call: () => T | Promise<T>): Promise<T | undefined> {\n try {\n return await call();\n } catch {\n return undefined; // a failing store is a miss, never a failed request\n }\n}\n\nexport function createCache(options: CacheOptions): Cache {\n const { store } = options;\n const policies = new Map(Object.entries(options.policies));\n const policyFor = (\n resource: string | undefined,\n apiVersion: string | null,\n ): CachePolicy | undefined =>\n resource === undefined\n ? undefined\n : (policies.get(`${resource}@${apiVersion}`) ?? policies.get(resource));\n const cacheManifest = options.cacheManifest ?? true;\n const now = options.now ?? Date.now;\n const eventsActive = new Set<string>();\n const generations = new Map<string, number>();\n const inflight = new Set<string>();\n const tasks = new Set<Promise<void>>();\n const background = new AbortController();\n\n let globalGeneration = 0; // bumped by `clear`, which may span resources\n const generation = (resource: string | undefined): number =>\n (generations.get(resource ?? \"\") ?? 0) + globalGeneration;\n const bump = (resource: string): void => {\n generations.set(resource, (generations.get(resource) ?? 0) + 1);\n };\n\n // -- freshness ---------------------------------------------------------------\n\n /**\n * Whether an entry of `kind` may be served without the network right now:\n * only while the stream is up, except `stats`/`predict`, which `stats_ttl`\n * bounds on its own. Never `filters` (events never clear them) or the manifest.\n */\n const gateOpen = (\n policy: CachePolicy,\n resource: string | undefined,\n kind: Kind,\n ): boolean =>\n !policy.revalidateOnly &&\n kind !== \"manifest\" &&\n kind !== \"filters\" &&\n (isStatsKind(kind) || eventsActive.has(resource as string));\n\n /** Stale-while-revalidate needs the stream for every kind, stats and predict included. */\n const staleOpen = (\n policy: CachePolicy,\n resource: string | undefined,\n kind: Kind,\n ): boolean =>\n gateOpen(policy, resource, kind) && eventsActive.has(resource as string);\n\n /** Milliseconds fresh and of stale-while-revalidate for a new entry; none if the response may predate an invalidation. */\n const entryTimes = (\n state: State,\n kind: Kind,\n ): { fresh: number; stale: number } => {\n const resource = state.ctx.resource;\n if (\n generation(resource) !== state.generation ||\n !gateOpen(state.policy, resource, kind)\n ) {\n return { fresh: 0, stale: 0 };\n }\n const fresh = isStatsKind(kind)\n ? state.policy.statsTtl\n : state.policy.maxAge;\n return {\n fresh: fresh * 1000,\n stale: staleOpen(state.policy, resource, kind)\n ? state.policy.staleWhileRevalidate * 1000\n : 0,\n };\n };\n\n const decide = (state: State, entry: CacheEntry | undefined): Decision => {\n if (entry === undefined) {\n return \"miss\";\n }\n if (\n !state.forceRevalidate &&\n gateOpen(state.policy, state.ctx.resource, state.kind as Kind)\n ) {\n const at = now();\n if (at < entry.freshUntil) {\n return \"hit\";\n }\n if (\n at < entry.staleUntil &&\n staleOpen(state.policy, state.ctx.resource, state.kind as Kind)\n ) {\n return \"stale\";\n }\n }\n return entry.etag !== undefined && state.policy.etag\n ? \"revalidate\"\n : \"miss\";\n };\n\n // -- request side ------------------------------------------------------------\n\n const begin = async (ctx: RequestContext): Promise<State | undefined> => {\n const { request, resource } = ctx;\n const scope = await credentialScope(request.headers);\n const generationNow = generation(resource);\n if (request.method !== \"GET\") {\n if (!WRITE_METHODS.has(request.method)) {\n return undefined; // HEAD, OPTIONS and the like change nothing\n }\n const policy = policyFor(resource, ctx.apiVersion);\n return policy?.enabled === true\n ? {\n ctx,\n policy,\n kind: undefined,\n key: undefined,\n generation: generationNow,\n scope,\n forceRevalidate: false,\n }\n : undefined;\n }\n let policy: CachePolicy | undefined;\n let kind: Kind;\n let key: string;\n if (ctx.op === \"manifest\") {\n if (!cacheManifest) {\n return undefined;\n }\n policy = MANIFEST_POLICY;\n kind = \"manifest\";\n key = manifestKey(scope);\n } else {\n policy = policyFor(resource, ctx.apiVersion);\n if (\n resource === undefined ||\n policy?.enabled !== true ||\n !isCacheKind(ctx.op)\n ) {\n return undefined;\n }\n kind = ctx.op;\n key = buildKey({\n resource,\n kind,\n recordId: ctx.recordId,\n scope,\n query: new URL(request.url).search,\n apiVersion: ctx.apiVersion,\n });\n }\n const control = cacheControl(request.headers);\n if (control.has(\"no-store\") || request.headers.has(\"if-none-match\")) {\n return undefined; // the caller forbids storing or manages validators itself\n }\n return {\n ctx,\n policy,\n kind,\n key,\n generation: generationNow,\n scope,\n forceRevalidate: control.has(\"no-cache\"),\n };\n };\n\n /** A copy of `ctx` whose request carries our validator (a `GET` has no body to lose). */\n const withValidator = (ctx: RequestContext, etag: string): RequestContext => {\n const headers = new Headers(ctx.request.headers);\n headers.set(\"if-none-match\", etag);\n return { ...ctx, request: new Request(ctx.request, { headers }) };\n };\n\n // -- response side -----------------------------------------------------------\n\n const readBody = async (\n state: State,\n response: Response,\n ): Promise<string | undefined> => {\n const { status } = response;\n const { op, recordId } = state.ctx;\n let wanted: boolean;\n if (state.kind !== undefined) {\n wanted = status === 200 && isStorable(response);\n } else if (op === \"update\") {\n wanted = status === 200 && recordId !== undefined && isStorable(response);\n } else {\n wanted =\n status >= 200 &&\n status < 300 &&\n (BULK_OPS.has(op) || (op === \"restore\" && recordId === undefined));\n }\n return wanted ? response.clone().text() : undefined;\n };\n\n /** One clear op for all `prefixes`: the store clears them in a single pass. */\n const clears = (prefixes: readonly string[]): StoreOp[] => [\n { type: \"clear\", prefixes },\n ];\n\n const manifestOps = (\n old: CacheEntry | undefined,\n entry: CacheEntry,\n ): StoreOp[] => {\n if (\n old === undefined ||\n (old.etag !== undefined && old.etag === entry.etag)\n ) {\n return [];\n }\n const names = new Set([\n ...[...policies.keys()].map((key) => key.replace(/@[^@]*$/, \"\")),\n ...manifestResources(old.body),\n ...manifestResources(entry.body),\n ]);\n return clears([...names].sort().map((name) => kindPrefix(name, \"filters\")));\n };\n\n const readPlan = (\n state: State,\n response: Response,\n body: string | undefined,\n ): { result: Response; ops: StoreOp[] } => {\n const key = state.key as string;\n const kind = state.kind as Kind;\n const { status } = response;\n const at = now();\n const { fresh, stale } = entryTimes(state, kind);\n const old = state.entry;\n if (status === 304 && state.validated === true && old !== undefined) {\n // `validated` means the request carried `old.etag`.\n const updated: CacheEntry = {\n ...old,\n headers: mergeHeaders(old.headers, response.headers),\n storedAt: at,\n freshUntil: at + fresh,\n staleUntil: at + fresh + stale,\n etag: response.headers.get(\"etag\") ?? (old.etag as string),\n };\n return {\n result: toResponse(updated, \"revalidated\"),\n ops: [\n {\n type: \"set\",\n key,\n entry: updated,\n ttlMs: retentionMs(fresh, stale),\n },\n ],\n };\n }\n if (status === 200 && body !== undefined) {\n tagStatus(response, \"miss\");\n const entry = buildEntry(\n response,\n body,\n { now: at, fresh, stale },\n state.policy.etag,\n );\n if (entry.etag === undefined && fresh <= 0) {\n // No validator and never fresh: drop the old entry.\n return { result: response, ops: [{ type: \"delete\", key }] };\n }\n const ops: StoreOp[] = [\n { type: \"set\", key, entry, ttlMs: retentionMs(fresh, stale) },\n ];\n if (kind === \"manifest\") {\n ops.push(...manifestOps(old, entry));\n }\n return { result: response, ops };\n }\n if (status === 200 || status === 404 || status === 410) {\n return { result: response, ops: [{ type: \"delete\", key }] };\n }\n return { result: response, ops: [] };\n };\n\n /** Store the PATCH response as the record's entry (it is the read view plus ETag). */\n const writeThrough = (\n state: State,\n response: Response,\n body: string | undefined,\n resource: string,\n recordId: number,\n ): StoreOp[] => {\n if (body === undefined) {\n return [];\n }\n const { fresh, stale } = entryTimes(state, \"record\");\n const entry = buildEntry(\n response,\n body,\n { now: now(), fresh, stale },\n state.policy.etag,\n );\n if (entry.etag === undefined && fresh <= 0) {\n return [];\n }\n const key = buildKey({\n resource,\n kind: \"record\",\n recordId,\n scope: state.scope,\n apiVersion: state.ctx.apiVersion,\n });\n return [{ type: \"set\", key, entry, ttlMs: retentionMs(fresh, stale) }];\n };\n\n /**\n * The store changes a write calls for. `response` is `undefined` when the\n * request threw: like a `5xx`, the outcome is unknown (the server may have\n * applied the write before the failure), so it clears what a success would\n * have, minus the write-through, and for a bulk op the whole resource (the\n * ids are unknown). Other failures changed nothing.\n */\n const writeOps = (\n state: State,\n response: Response | undefined,\n body: string | undefined,\n ): StoreOp[] => {\n const { op, recordId } = state.ctx;\n const resource = state.ctx.resource as string;\n const status = response?.status;\n if (status === 412) {\n return recordId === undefined\n ? []\n : clears([recordPrefix(resource, recordId)]);\n }\n if ((status === 404 || status === 410) && recordId !== undefined) {\n // The record is gone (or never was): drop what reads cached for it.\n return clears([\n recordPrefix(resource, recordId),\n revisionsPrefix(resource, recordId),\n ]);\n }\n const ok = status !== undefined && status >= 200 && status < 300;\n if (!ok && status !== undefined && status < 500) {\n return []; // refused: nothing changed\n }\n const collections = collectionPrefixes(resource);\n if (CREATES.has(op)) {\n return clears(collections);\n }\n if (recordId !== undefined && (RECORD_WRITES.has(op) || op === \"update\")) {\n return [\n ...clears([\n ...collections,\n revisionsPrefix(resource, recordId),\n recordPrefix(resource, recordId),\n ]),\n ...(op === \"update\" && response !== undefined && ok\n ? writeThrough(state, response, body, resource, recordId)\n : []),\n ];\n }\n if (ok && (BULK_OPS.has(op) || op === \"restore\")) {\n // Bulk ops and a bulk restore always have their body read (see `readBody`).\n const ids = idsFromBody(body as string);\n if (ids !== undefined) {\n return clears([\n ...collections,\n ...ids.flatMap((id) => [\n recordPrefix(resource, id),\n revisionsPrefix(resource, id),\n ]),\n ]);\n }\n }\n return clears(resourcePrefixes(resource));\n };\n\n /** One pass when the store can, else one `clear` per prefix; a failure is a miss. */\n const clearPrefixes = async (prefixes: readonly string[]): Promise<void> => {\n if (store.clearMany !== undefined) {\n await safe(() => store.clearMany?.(prefixes));\n return;\n }\n for (const prefix of prefixes) {\n await safe(() => store.clear(prefix));\n }\n };\n\n const apply = async (op: StoreOp): Promise<void> => {\n await safe(() =>\n op.type === \"clear\"\n ? clearPrefixes(op.prefixes)\n : op.type === \"set\"\n ? store.set(op.key, op.entry, op.ttlMs)\n : store.delete(op.key),\n );\n };\n\n /** Plan, then apply, the store changes a response calls for. */\n const finish = async (\n state: State,\n response: Response,\n ): Promise<Response> => {\n const body = await readBody(state, response);\n const { result, ops } =\n state.kind === undefined\n ? { result: response, ops: writeOps(state, response, body) }\n : readPlan(state, response, body);\n await applyOps(state, ops);\n return result;\n };\n\n /** Apply `ops`, guarding against an invalidation that lands meanwhile. */\n const applyOps = async (\n state: State,\n ops: readonly StoreOp[],\n ): Promise<void> => {\n if (state.kind === undefined && ops.some((op) => op.type === \"clear\")) {\n bump(state.ctx.resource as string); // before applying: reads now in flight must not store fresh\n }\n const expected = generation(state.ctx.resource);\n for (const op of ops) {\n await apply(op);\n }\n if (generation(state.ctx.resource) !== expected) {\n // An invalidation landed while storing: drop what was just stored.\n for (const op of ops.filter((candidate) => candidate.type === \"set\")) {\n await apply({ type: \"delete\", key: op.key });\n }\n }\n };\n\n // -- stale-while-revalidate --------------------------------------------------\n\n const schedule = (state: State, next: Next): void => {\n const key = state.key as string;\n if (background.signal.aborted || inflight.has(key)) {\n return;\n }\n inflight.add(key);\n const entry = state.entry as CacheEntry;\n const task = (async (): Promise<void> => {\n try {\n const signal = background.signal;\n const headers = new Headers(state.ctx.request.headers);\n if (entry.etag !== undefined) {\n headers.set(\"if-none-match\", entry.etag);\n }\n // Keeps the caller's init (credentials, redirect, ...) but is detached\n // from their signal: this outlives their request. The cache mode is\n // forced to \"no-store\": a caller's \"force-cache\" or \"only-if-cached\"\n // would be answered from the HTTP cache and never revalidate (Fetch,\n // HTTP-network-or-cache fetch step 16 does the same for a \"default\"\n // request that carries If-None-Match).\n const request = new Request(state.ctx.request, {\n headers,\n signal,\n cache: \"no-store\",\n });\n const fresh: State = {\n ...state,\n validated: entry.etag !== undefined,\n };\n const response = await next({ ...state.ctx, request, signal });\n const result = await finish(fresh, response);\n await result.body?.cancel();\n } catch {\n // Nothing to report to: the caller already has the stale answer.\n } finally {\n inflight.delete(key);\n }\n })();\n tasks.add(task);\n void task.then(() => tasks.delete(task));\n };\n\n // -- the middleware ----------------------------------------------------------\n\n const middleware: Middleware = async (ctx, next) => {\n const state = await begin(ctx);\n if (state === undefined) {\n return next(ctx);\n }\n if (state.kind === undefined) {\n const unsent = ctx.request.signal.aborted; // aborted mid-flight is unknown, not unsent\n let response: Response;\n try {\n response = await next(ctx);\n } catch (error) {\n // The write may have been applied before it failed: forget what it\n // touches, unless it was aborted before it could be sent. The\n // caller still gets the original error.\n if (!unsent) {\n await safe(() =>\n applyOps(state, writeOps(state, undefined, undefined)),\n );\n }\n throw error;\n }\n return finish(state, response);\n }\n state.entry = await safe(() => store.get(state.key as string));\n const decision = decide(state, state.entry);\n const entry = state.entry as CacheEntry;\n if (decision === \"hit\") {\n return toResponse(entry, \"hit\");\n }\n if (decision === \"stale\") {\n schedule(state, next);\n return toResponse(entry, \"stale\");\n }\n let sent = ctx;\n if (decision === \"revalidate\") {\n sent = withValidator(ctx, entry.etag as string);\n state.validated = true;\n }\n return finish(state, await next(sent));\n };\n\n // -- control surface ---------------------------------------------------------\n\n const autoIfMatch: Middleware = async (ctx, next) => {\n const { request, resource, recordId } = ctx;\n if (\n (ctx.op === \"update\" || ctx.op === \"delete\") &&\n recordId !== undefined &&\n resource !== undefined &&\n policyFor(resource, ctx.apiVersion)?.enabled === true &&\n !request.headers.has(\"if-match\")\n ) {\n const etag = await lookupEtag(\n resource,\n recordId,\n await credentialScope(request.headers),\n ctx.apiVersion,\n );\n if (etag !== undefined) {\n const headers = new Headers(request.headers);\n headers.set(\"if-match\", etag);\n return next({ ...ctx, request: new Request(request, { headers }) });\n }\n }\n return next(ctx);\n };\n\n const lookupEtag = async (\n resource: string,\n recordId: number,\n scope: string,\n apiVersion: string | null,\n ): Promise<string | undefined> => {\n const entry = await safe(() =>\n store.get(\n buildKey({ resource, kind: \"record\", recordId, scope, apiVersion }),\n ),\n );\n return entry?.etag;\n };\n\n return {\n middleware,\n autoIfMatch,\n setEventsActive(resource, active) {\n if (active) {\n eventsActive.add(resource);\n } else {\n eventsActive.delete(resource);\n }\n },\n eventsActive: (resource) => eventsActive.has(resource),\n async invalidateEvent(resource, action, recordId) {\n bump(resource);\n await clearPrefixes(eventPrefixes(resource, action, recordId));\n },\n async clearResource(resource) {\n bump(resource);\n await clearPrefixes(resourcePrefixes(resource));\n },\n async clear(prefix) {\n globalGeneration += 1; // reads in flight must not store fresh over the clear\n await safe(() => store.clear(prefix));\n },\n cachedEtag: lookupEtag,\n async close() {\n background.abort();\n await Promise.allSettled(tasks);\n },\n };\n}\n","/** In-memory cache store: LRU with a per-entry TTL. */\nimport type { CacheEntry, CacheStore } from \"../interfaces.ts\";\n\n/** How long a store keeps an entry when the caller gives no ttl (the spec's retention floor). */\nexport const DEFAULT_TTL_MS = 3_600_000;\n\nexport interface MemoryStoreOptions {\n /** Default 1024. */\n readonly maxEntries?: number;\n readonly defaultTtlMs?: number;\n /** Epoch milliseconds; injectable for tests. */\n readonly now?: () => number;\n}\n\n/** LRU store (default 1024 entries) that also honours each entry's ttl. */\nexport class MemoryStore implements CacheStore {\n private readonly data = new Map<\n string,\n { readonly expiresAt: number; readonly entry: CacheEntry }\n >();\n private readonly maxEntries: number;\n private readonly defaultTtlMs: number;\n private readonly now: () => number;\n\n constructor(options: MemoryStoreOptions = {}) {\n const maxEntries = options.maxEntries ?? 1024;\n if (!Number.isInteger(maxEntries) || maxEntries < 1) {\n throw new RangeError(\"maxEntries must be an integer of at least 1\");\n }\n this.maxEntries = maxEntries;\n this.defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n this.now = options.now ?? Date.now;\n }\n\n /** Number of stored entries (expired ones included until touched). */\n get size(): number {\n return this.data.size;\n }\n\n get(key: string): CacheEntry | undefined {\n const item = this.data.get(key);\n if (item === undefined) {\n return undefined;\n }\n if (item.expiresAt <= this.now()) {\n this.data.delete(key);\n return undefined;\n }\n this.data.delete(key); // re-insert: Map order is the recency order\n this.data.set(key, item);\n return item.entry;\n }\n\n set(key: string, entry: CacheEntry, ttlMs?: number): void {\n this.data.delete(key);\n this.data.set(key, {\n expiresAt: this.now() + (ttlMs ?? this.defaultTtlMs),\n entry,\n });\n while (this.data.size > this.maxEntries) {\n // The first key is the least recently used.\n this.data.delete(this.data.keys().next().value as string);\n }\n }\n\n delete(key: string): void {\n this.data.delete(key);\n }\n\n /** Remove every key under any of `prefixes` in one walk. */\n clearMany(prefixes: readonly string[]): void {\n for (const key of this.data.keys()) {\n if (prefixes.some((prefix) => key.startsWith(prefix))) {\n this.data.delete(key);\n }\n }\n }\n\n clear(prefix?: string): void {\n for (const key of this.data.keys()) {\n if (prefix === undefined || key.startsWith(prefix)) {\n this.data.delete(key);\n }\n }\n }\n}\n","const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c);\n\nlet idbProxyableTypes;\nlet cursorAdvanceMethods;\n// This is a function to prevent it throwing up in node environments.\nfunction getIdbProxyableTypes() {\n return (idbProxyableTypes ||\n (idbProxyableTypes = [\n IDBIndex,\n IDBObjectStore,\n IDBCursor,\n IDBDatabase,\n IDBTransaction,\n ]));\n}\n// This is a function to prevent it throwing up in node environments.\nfunction getCursorAdvanceMethods() {\n return (cursorAdvanceMethods ||\n (cursorAdvanceMethods = ['advance', 'continue', 'continuePrimaryKey'].map((method) => IDBCursor.prototype[method])));\n}\nconst transactionDoneMap = new WeakMap();\nconst transformCache = new WeakMap();\nconst reverseTransformCache = new WeakMap();\nfunction promisifyRequest(request) {\n const promise = new Promise((resolve, reject) => {\n const unlisten = () => {\n request.removeEventListener('success', success);\n request.removeEventListener('error', error);\n };\n const success = () => {\n resolve(wrap(request.result));\n unlisten();\n };\n const error = () => {\n reject(request.error);\n unlisten();\n };\n request.addEventListener('success', success);\n request.addEventListener('error', error);\n });\n // This mapping exists in reverseTransformCache but doesn't exist in transformCache. This\n // is because we create many promises from a single IDBRequest.\n reverseTransformCache.set(promise, request);\n return promise;\n}\nfunction cacheDonePromiseForTransaction(tx) {\n // Early bail if we've already created a done promise for this transaction.\n if (transactionDoneMap.has(tx))\n return;\n const done = new Promise((resolve, reject) => {\n const unlisten = () => {\n tx.removeEventListener('complete', complete);\n tx.removeEventListener('error', error);\n tx.removeEventListener('abort', error);\n };\n const complete = () => {\n resolve();\n unlisten();\n };\n const error = () => {\n reject(tx.error || new DOMException('AbortError', 'AbortError'));\n unlisten();\n };\n tx.addEventListener('complete', complete);\n tx.addEventListener('error', error);\n tx.addEventListener('abort', error);\n });\n // Cache it for later retrieval.\n transactionDoneMap.set(tx, done);\n}\nlet idbProxyTraps = {\n get(target, prop, receiver) {\n if (target instanceof IDBTransaction) {\n // Special handling for transaction.done.\n if (prop === 'done')\n return transactionDoneMap.get(target);\n // Make tx.store return the only store in the transaction, or undefined if there are many.\n if (prop === 'store') {\n return receiver.objectStoreNames[1]\n ? undefined\n : receiver.objectStore(receiver.objectStoreNames[0]);\n }\n }\n // Else transform whatever we get back.\n return wrap(target[prop]);\n },\n set(target, prop, value) {\n target[prop] = value;\n return true;\n },\n has(target, prop) {\n if (target instanceof IDBTransaction &&\n (prop === 'done' || prop === 'store')) {\n return true;\n }\n return prop in target;\n },\n};\nfunction replaceTraps(callback) {\n idbProxyTraps = callback(idbProxyTraps);\n}\nfunction wrapFunction(func) {\n // Due to expected object equality (which is enforced by the caching in `wrap`), we\n // only create one new func per func.\n // Cursor methods are special, as the behaviour is a little more different to standard IDB. In\n // IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the\n // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense\n // with real promises, so each advance methods returns a new promise for the cursor object, or\n // undefined if the end of the cursor has been reached.\n if (getCursorAdvanceMethods().includes(func)) {\n return function (...args) {\n // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use\n // the original object.\n func.apply(unwrap(this), args);\n return wrap(this.request);\n };\n }\n return function (...args) {\n // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use\n // the original object.\n return wrap(func.apply(unwrap(this), args));\n };\n}\nfunction transformCachableValue(value) {\n if (typeof value === 'function')\n return wrapFunction(value);\n // This doesn't return, it just creates a 'done' promise for the transaction,\n // which is later returned for transaction.done (see idbObjectHandler).\n if (value instanceof IDBTransaction)\n cacheDonePromiseForTransaction(value);\n if (instanceOfAny(value, getIdbProxyableTypes()))\n return new Proxy(value, idbProxyTraps);\n // Return the same value back if we're not going to transform it.\n return value;\n}\nfunction wrap(value) {\n // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because\n // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached.\n if (value instanceof IDBRequest)\n return promisifyRequest(value);\n // If we've already transformed this value before, reuse the transformed value.\n // This is faster, but it also provides object equality.\n if (transformCache.has(value))\n return transformCache.get(value);\n const newValue = transformCachableValue(value);\n // Not all types are transformed.\n // These may be primitive types, so they can't be WeakMap keys.\n if (!Object.is(newValue, value)) {\n transformCache.set(value, newValue);\n reverseTransformCache.set(newValue, value);\n }\n return newValue;\n}\nconst unwrap = (value) => reverseTransformCache.get(value);\n\n/**\n * Open a database.\n *\n * @param name Name of the database.\n * @param version Schema version.\n * @param callbacks Additional callbacks.\n */\nfunction openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) {\n const request = indexedDB.open(name, version);\n const openPromise = wrap(request);\n if (upgrade) {\n request.addEventListener('upgradeneeded', (event) => {\n upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event);\n });\n }\n if (blocked) {\n request.addEventListener('blocked', (event) => blocked(\n // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405\n event.oldVersion, event.newVersion, event));\n }\n openPromise\n .then((db) => {\n if (terminated)\n db.addEventListener('close', () => terminated());\n if (blocking) {\n db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event));\n }\n })\n .catch(() => { });\n return openPromise;\n}\n/**\n * Delete a database.\n *\n * @param name Name of the database.\n */\nfunction deleteDB(name, { blocked } = {}) {\n const request = indexedDB.deleteDatabase(name);\n if (blocked) {\n request.addEventListener('blocked', (event) => blocked(\n // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405\n event.oldVersion, event));\n }\n return wrap(request).then(() => undefined);\n}\n\nconst readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count'];\nconst writeMethods = ['put', 'add', 'delete', 'clear'];\nconst cachedMethods = {};\nfunction getMethod(target, prop) {\n if (!(target instanceof IDBDatabase &&\n !(prop in target) &&\n typeof prop === 'string')) {\n return;\n }\n if (cachedMethods[prop])\n return cachedMethods[prop];\n const targetFuncName = prop.replace(/FromIndex$/, '');\n const useIndex = prop !== targetFuncName;\n const isWrite = writeMethods.includes(targetFuncName);\n if (\n // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.\n !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) ||\n !(isWrite || readMethods.includes(targetFuncName))) {\n return;\n }\n const method = async function (storeName, ...args) {\n // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :(\n const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly');\n let target = tx.store;\n if (useIndex)\n target = target.index(args.shift());\n // Must reject if op rejects.\n // If it's a write operation, must reject if tx.done rejects.\n // Must reject with op rejection first.\n // Must resolve with op value.\n // Must handle both promises (no unhandled rejections)\n return (await Promise.all([\n target[targetFuncName](...args),\n isWrite && tx.done,\n ]))[0];\n };\n return (cachedMethods[prop] = method);\n}\nreplaceTraps((oldTraps) => ({\n ...oldTraps,\n get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver),\n has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop),\n}));\n\nconst advanceMethodProps = ['advance', 'continue', 'continuePrimaryKey'];\nconst methodMap = {};\nconst advanceResults = new WeakMap();\nconst ittrProxiedCursorToOriginalProxy = new WeakMap();\nconst cursorIteratorTraps = {\n get(target, prop) {\n if (!advanceMethodProps.includes(prop))\n return target[prop];\n let cachedFunc = methodMap[prop];\n if (!cachedFunc) {\n cachedFunc = methodMap[prop] = function (...args) {\n advanceResults.set(this, ittrProxiedCursorToOriginalProxy.get(this)[prop](...args));\n };\n }\n return cachedFunc;\n },\n};\nasync function* iterate(...args) {\n // tslint:disable-next-line:no-this-assignment\n let cursor = this;\n if (!(cursor instanceof IDBCursor)) {\n cursor = await cursor.openCursor(...args);\n }\n if (!cursor)\n return;\n cursor = cursor;\n const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);\n ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);\n // Map this double-proxy back to the original, so other cursor methods work.\n reverseTransformCache.set(proxiedCursor, unwrap(cursor));\n while (cursor) {\n yield proxiedCursor;\n // If one of the advancing methods was not called, call continue().\n cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());\n advanceResults.delete(proxiedCursor);\n }\n}\nfunction isIteratorProp(target, prop) {\n return ((prop === Symbol.asyncIterator &&\n instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor])) ||\n (prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore])));\n}\nreplaceTraps((oldTraps) => ({\n ...oldTraps,\n get(target, prop, receiver) {\n if (isIteratorProp(target, prop))\n return iterate;\n return oldTraps.get(target, prop, receiver);\n },\n has(target, prop) {\n return isIteratorProp(target, prop) || oldTraps.has(target, prop);\n },\n}));\n\nexport { deleteDB, openDB, unwrap, wrap };\n","/**\n * IndexedDB cache store (one object store, out-of-line string keys, an\n * `expiresAt` index).\n *\n * Database version 2 adds the `expiresAt` index; upgrading from version 1\n * keeps every entry (the index is built over the stored values). Expired\n * entries are swept once when the database is opened, and `maxEntries` evicts\n * the entries closest to expiry on `set`.\n *\n * Every call is wrapped: a blocked database, private mode or a missing\n * `indexedDB` is a miss (or a no-op), never a failed request. `clear(prefix)`\n * is one `IDBKeyRange.bound(prefix, prefix + \"\\uffff\")` delete.\n */\nimport { openDB } from \"idb\";\nimport type { IDBPDatabase } from \"idb\";\nimport type { CacheEntry, CacheStore } from \"../interfaces.ts\";\nimport { DEFAULT_TTL_MS } from \"./memory.ts\";\n\n// The package compiles without the DOM lib; this is the one global it needs.\ndeclare const IDBKeyRange: {\n bound(lower: string, upper: string): unknown;\n upperBound(upper: number): unknown;\n};\n\nconst STORE = \"entries\";\nconst BY_EXPIRY = \"expiresAt\";\nconst VERSION = 2;\n\ninterface Stored {\n readonly expiresAt: number;\n readonly entry: CacheEntry;\n}\n\n/**\n * Observes `tx.done` up front: when an operation throws, the aborted\n * transaction's `done` rejects with nobody left to await it, which would be\n * an unhandled rejection.\n */\nfunction guarded<T extends { readonly done: Promise<void> }>(tx: T): T {\n tx.done.catch(() => undefined);\n return tx;\n}\n\nfunction isInvalidState(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { name?: unknown }).name === \"InvalidStateError\"\n );\n}\n\nexport interface IndexedDbStoreOptions {\n /** Database name; default `craftainer-crud-cache`. */\n readonly name?: string;\n readonly defaultTtlMs?: number;\n /**\n * Upper bound on stored entries, a positive integer; default unbounded.\n * When a `set` leaves more than this many, the entries with the earliest\n * `expiresAt` are deleted until the cap holds (a just-written entry with a\n * short ttl can be among them). The cap is per database and per store\n * instance: two instances on one `name` with different caps each enforce\n * their own on their writes.\n */\n readonly maxEntries?: number;\n /** Epoch milliseconds; injectable for tests. */\n readonly now?: () => number;\n}\n\nexport class IndexedDbStore implements CacheStore {\n private readonly name: string;\n private readonly defaultTtlMs: number;\n private readonly maxEntries: number | undefined;\n private readonly now: () => number;\n private opening: Promise<IDBPDatabase> | undefined;\n\n constructor(options: IndexedDbStoreOptions = {}) {\n this.name = options.name ?? \"craftainer-crud-cache\";\n if (this.name === \"\") {\n // The name is the per-origin namespace of the store.\n throw new RangeError(\"IndexedDbStore: name must not be empty\");\n }\n this.defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n if (\n options.maxEntries !== undefined &&\n !(Number.isInteger(options.maxEntries) && options.maxEntries >= 1)\n ) {\n throw new RangeError(\n \"IndexedDbStore: maxEntries must be an integer >= 1\",\n );\n }\n this.maxEntries = options.maxEntries;\n this.now = options.now ?? Date.now;\n }\n\n private open(): Promise<IDBPDatabase> {\n if (this.opening === undefined) {\n // A newer version wants to upgrade: let it, and reopen on the next call.\n const opening: Promise<IDBPDatabase> = this.openAndSweep(() => {\n if (this.opening === opening) {\n this.opening = undefined;\n }\n });\n this.opening = opening;\n }\n return this.opening;\n }\n\n private async openAndSweep(onDropped: () => void): Promise<IDBPDatabase> {\n const db = await openDB(this.name, VERSION, {\n blocking() {\n db.close();\n onDropped();\n },\n // The browser closed the connection (storage cleared, evicted): drop it\n // so the next call opens a fresh one.\n terminated: onDropped,\n upgrade(upgrading, oldVersion, _newVersion, tx) {\n if (oldVersion < 1) {\n upgrading.createObjectStore(STORE);\n }\n // Version 1 had the store but no index; its values already carry expiresAt.\n tx.objectStore(STORE).createIndex(BY_EXPIRY, BY_EXPIRY);\n },\n });\n try {\n await this.sweep(db);\n } catch {\n // A failed sweep leaves the entries for `get` to expire one by one.\n }\n return db;\n }\n\n /** Deletes every entry that has expired (`expiresAt <= now`). */\n private async sweep(db: IDBPDatabase): Promise<void> {\n const tx = guarded(db.transaction(STORE, \"readwrite\"));\n let cursor = await tx.store\n .index(BY_EXPIRY)\n .openCursor(IDBKeyRange.upperBound(this.now()));\n while (cursor !== null) {\n await cursor.delete();\n cursor = await cursor.continue();\n }\n await tx.done;\n }\n\n /**\n * Run `work` on the database; a failure yields `fallback`. The handle is\n * shared by every in-flight call, so it is dropped (and closed) only when it\n * is itself unusable: it never opened, or it was closed under us\n * (`InvalidStateError`). Any other failure belongs to one operation (an\n * uncloneable value, a quota error) and leaves the handle to the others.\n */\n private async run<T>(\n fallback: T,\n work: (db: IDBPDatabase) => Promise<T>,\n ): Promise<T> {\n // The handle this call runs on: a concurrent call may already have replaced it.\n let used: Promise<IDBPDatabase> | undefined;\n let db: IDBPDatabase | undefined;\n try {\n used = this.open(); // may throw at once when indexedDB is unusable\n db = await used;\n return await work(db);\n } catch (error) {\n if (db === undefined || isInvalidState(error)) {\n // Drop only our own handle: a newer one belongs to other calls.\n if (this.opening === used) {\n this.opening = undefined;\n }\n // An orphaned connection can block an upgrade.\n db?.close();\n }\n return fallback;\n }\n }\n\n get(key: string): Promise<CacheEntry | undefined> {\n return this.run<CacheEntry | undefined>(undefined, async (db) => {\n const stored = (await db.get(STORE, key)) as Stored | undefined;\n if (stored === undefined) {\n return undefined;\n }\n if (stored.expiresAt <= this.now()) {\n await db.delete(STORE, key);\n return undefined;\n }\n return stored.entry;\n });\n }\n\n set(key: string, entry: CacheEntry, ttlMs?: number): Promise<void> {\n return this.run(undefined, async (db) => {\n const stored: Stored = {\n expiresAt: this.now() + (ttlMs ?? this.defaultTtlMs),\n entry,\n };\n const tx = guarded(db.transaction(STORE, \"readwrite\"));\n await tx.store.put(stored, key);\n if (this.maxEntries !== undefined) {\n let excess = (await tx.store.count()) - this.maxEntries;\n let cursor =\n excess > 0 ? await tx.store.index(BY_EXPIRY).openCursor() : null;\n while (cursor !== null && excess > 0) {\n await cursor.delete();\n excess -= 1;\n cursor = await cursor.continue();\n }\n }\n await tx.done;\n });\n }\n\n delete(key: string): Promise<void> {\n return this.run(undefined, (db) => db.delete(STORE, key));\n }\n\n clear(prefix?: string): Promise<void> {\n return this.run(undefined, (db) =>\n prefix === undefined\n ? db.clear(STORE)\n : db.delete(STORE, IDBKeyRange.bound(prefix, `${prefix}\\uffff`)),\n );\n }\n\n /** Clear several prefixes in one transaction (one bound range each). */\n clearMany(prefixes: readonly string[]): Promise<void> {\n return this.run(undefined, async (db) => {\n const tx = db.transaction(STORE, \"readwrite\");\n await Promise.all([\n ...prefixes.map((prefix) =>\n tx.store.delete(IDBKeyRange.bound(prefix, `${prefix}\\uffff`)),\n ),\n tx.done,\n ]);\n });\n }\n\n /** Close the database handle; a later call reopens it. */\n async close(): Promise<void> {\n const opening = this.opening;\n this.opening = undefined;\n try {\n (await opening)?.close();\n } catch {\n // Never opened.\n }\n }\n}\n","/** Wiring an event hub to a cache. */\nimport type { EventConnection, EventHub } from \"../events.ts\";\nimport { resetOnce } from \"../reset-once.ts\";\nimport type { Cache } from \"./middleware.ts\";\n\n/** The subscriptions that currently see a connected stream, per cache and resource. */\nconst connected = new WeakMap<Cache, Map<string, Set<object>>>();\n\n/**\n * Subscribe `cache` to `hub` for the manifest resource `resource`. Events\n * clear the spec's prefixes, a reconnect clears the resource, and fresh hits\n * are allowed only while the stream is connected. The cache is cleared once\n * per connection, whenever it subscribes: a subscription that joins an already\n * connected hub (or during the handshake) clears the resource first, unless\n * the cache was already cleared on that connection, and only then opens the\n * gate. Returns the unsubscribe (it also closes the freshness gate).\n */\nexport function invalidateOnEvents(\n cache: Cache,\n hub: EventHub,\n resource: string,\n): () => void {\n const byResource = connected.get(cache) ?? new Map<string, Set<object>>();\n connected.set(cache, byResource);\n const live = byResource.get(resource) ?? new Set<object>();\n byResource.set(resource, live);\n const self = {}; // this subscription's identity\n let latest = 0; // a status change supersedes a gate-opening that awaits its reset\n const reset = (connection: EventConnection): Promise<void> =>\n resetOnce(connection, cache, resource, () => cache.clearResource(resource));\n // The gate is open while any subscription sees a connected stream.\n const mark = (up: boolean): void => {\n if (up) {\n live.add(self);\n } else {\n live.delete(self);\n }\n cache.setEventsActive(resource, live.size > 0);\n };\n const unsubscribe = hub.subscribe({\n onEvent: (event) =>\n cache.invalidateEvent(resource, event.action, event.recordId),\n onReset: (connection) =>\n connection === undefined\n ? cache.clearResource(resource)\n : reset(connection),\n onStatus: async (up, connection) => {\n latest += 1;\n const mine = latest;\n if (up && connection !== undefined) {\n await reset(connection); // a late joiner clears before the gate opens\n if (mine !== latest) {\n return;\n }\n }\n mark(up);\n },\n });\n return () => {\n latest += 1;\n try {\n unsubscribe();\n } finally {\n mark(false); // idempotent; the gate stays open while another subscription is connected\n }\n };\n}\n"],"ignoreList":[5],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cache/entry.ts","../../src/cache/keys.ts","../../src/cache/scope.ts","../../src/cache/middleware.ts","../../src/cache/memory.ts","../../node_modules/.pnpm/idb@8.0.4/node_modules/idb/build/index.js","../../src/cache/idb.ts","../../src/cache/wire.ts"],"sourcesContent":["/** Turning responses into `CacheEntry` values and back. */\nimport type { CacheEntry } from \"../interfaces.ts\";\n\n/** How the cache answered a read: see `cacheStatus`. */\nexport type CacheStatus = \"hit\" | \"stale\" | \"revalidated\" | \"miss\";\n\nconst statuses = new WeakMap<Response, CacheStatus>();\n\n/**\n * How the cache answered `response`: `hit` (fresh, no network), `stale`\n * (inside the stale-while-revalidate window, refreshed in the background),\n * `revalidated` (a `304` rebuilt from the stored entry) or `miss`.\n * `undefined` when the cache did not handle the request.\n */\nexport function cacheStatus(response: Response): CacheStatus | undefined {\n return statuses.get(response);\n}\n\n/** Tag `response` with how the cache answered. */\nexport function tagStatus(response: Response, status: CacheStatus): Response {\n statuses.set(response, status);\n return response;\n}\n\n// Describe the transfer, not the representation: the stored body is decoded.\nconst TRANSFER_HEADERS: ReadonlySet<string> = new Set([\n \"content-length\",\n \"content-encoding\",\n \"transfer-encoding\",\n \"connection\",\n \"keep-alive\",\n]);\nconst VARY_OK: ReadonlySet<string> = new Set([\n \"authorization\",\n \"accept\",\n \"accept-encoding\",\n]);\n\n/** The lower-cased directive names of the `Cache-Control` header. */\nexport function cacheControl(headers: Headers): ReadonlySet<string> {\n const names = (headers.get(\"cache-control\") ?? \"\")\n .split(\",\")\n .map((part) => (part.split(\"=\", 1)[0] as string).trim().toLowerCase())\n .filter((name) => name !== \"\");\n return new Set(names);\n}\n\n/** Whether the media type is JSON (`application/json` or `+json`). */\nexport function isJson(response: Response): boolean {\n const media = (response.headers.get(\"content-type\") ?? \"\")\n .split(\";\", 1)[0]!\n .trim()\n .toLowerCase();\n return media === \"application/json\" || media.endsWith(\"+json\");\n}\n\n/** `Vary` may only name headers the key already accounts for; `*` never caches. */\nexport function varyAllowsCaching(response: Response): boolean {\n const names = (response.headers.get(\"vary\") ?? \"\")\n .split(\",\")\n .map((part) => part.trim().toLowerCase())\n .filter((name) => name !== \"\");\n return names.every((name) => VARY_OK.has(name));\n}\n\nfunction storableHeaders(headers: Headers): Array<readonly [string, string]> {\n const items: Array<readonly [string, string]> = [];\n for (const [name, value] of headers) {\n if (!TRANSFER_HEADERS.has(name)) {\n items.push([name, value]);\n }\n }\n return items;\n}\n\nexport interface EntryTimes {\n readonly now: number;\n /** Milliseconds fresh. */\n readonly fresh: number;\n /** Milliseconds of stale-while-revalidate after `fresh`. */\n readonly stale: number;\n}\n\n/** Snapshot a read response whose body text was already read. */\nexport function buildEntry(\n response: Response,\n body: string,\n times: EntryTimes,\n keepEtag: boolean,\n): CacheEntry {\n const etag = keepEtag ? response.headers.get(\"etag\") : null;\n const base = {\n status: response.status,\n headers: storableHeaders(response.headers),\n body,\n storedAt: times.now,\n freshUntil: times.now + times.fresh,\n staleUntil: times.now + times.fresh + times.stale,\n };\n return etag === null ? base : { ...base, etag };\n}\n\n/** RFC 9111 4.3.4: headers of a `304` replace the stored ones of the same name. */\nexport function mergeHeaders(\n stored: CacheEntry[\"headers\"],\n update: Headers,\n): CacheEntry[\"headers\"] {\n const fresh = storableHeaders(update);\n const replaced = new Set(fresh.map(([name]) => name));\n return [...stored.filter(([name]) => !replaced.has(name)), ...fresh];\n}\n\n/** Rebuild a response from an entry, tagged with how the cache produced it. */\nexport function toResponse(entry: CacheEntry, status: CacheStatus): Response {\n const headers = new Headers();\n for (const [name, value] of entry.headers) {\n headers.append(name, value);\n }\n return tagStatus(\n new Response(entry.body, { status: entry.status, headers }),\n status,\n );\n}\n","/**\n * Cache keys and the invalidation prefixes.\n *\n * Keys are slash-separated:\n * `v1/{resource}/{kind}/{subject}/{apiVersion}/{scope}/{canonicalQuery}`.\n * Versions of one resource never share an entry, but they share one event\n * stream; because the version comes after kind and subject, one prefix such\n * as `v1/{resource}/record/{id}/` clears an entry in every version and for\n * every caller at once.\n */\nimport type { Op } from \"../interfaces.ts\";\n\n/** The ops that read and are cached (`manifest` has its own key). */\nexport type CacheKind =\n \"list\" | \"record\" | \"stats\" | \"predict\" | \"revisions\" | \"filters\";\n\nconst KINDS: ReadonlySet<string> = new Set<CacheKind>([\n \"list\",\n \"record\",\n \"stats\",\n \"predict\",\n \"revisions\",\n \"filters\",\n]);\nconst STATS_KINDS: ReadonlySet<string> = new Set([\"stats\", \"predict\"]);\nconst COLLECTION_KINDS = [\"list\", \"stats\", \"predict\"] as const;\nconst PER_RECORD_KINDS = [\"record\", \"revisions\"] as const;\n\n/** `true` when the op is a cacheable read kind. */\nexport function isCacheKind(op: Op): op is CacheKind {\n return KINDS.has(op);\n}\n\n/** `true` for `stats` and `predict`, which `stats_ttl` bounds. */\nexport function isStatsKind(kind: string): boolean {\n return STATS_KINDS.has(kind);\n}\n\n/**\n * Sorts the query pairs the client sent and drops `id` (it is the subject).\n * Values are kept verbatim: the client already emits canonical,\n * percent-encoded text, so nothing is decoded or re-encoded here.\n */\nexport function canonicalQuery(query: string): string {\n const text = query.startsWith(\"?\") ? query.slice(1) : query;\n const pairs = text\n .split(\"&\")\n .filter((part) => part !== \"\" && part.split(\"=\", 1)[0] !== \"id\")\n .map((part) => {\n const at = part.indexOf(\"=\");\n return at < 0\n ? ([part, \"\"] as const)\n : ([part.slice(0, at), part.slice(at)] as const);\n });\n pairs.sort(([an, av], [bn, bv]) =>\n an === bn ? (av < bv ? -1 : av > bv ? 1 : 0) : an < bn ? -1 : 1,\n );\n return pairs.map(([name, rest]) => name + rest).join(\"&\");\n}\n\nexport interface KeyParts {\n readonly resource: string;\n readonly kind: CacheKind;\n readonly recordId?: number | undefined;\n readonly scope: string;\n readonly query?: string;\n /** `null` (no version) renders as `_`. */\n readonly apiVersion: string | null;\n}\n\n/** The key of one cached response. */\nexport function buildKey(parts: KeyParts): string {\n const subject =\n (parts.kind === \"record\" || parts.kind === \"revisions\") &&\n parts.recordId !== undefined\n ? String(parts.recordId)\n : \"_\";\n const version = parts.apiVersion ?? \"_\";\n return `v1/${parts.resource}/${parts.kind}/${subject}/${version}/${parts.scope}/${canonicalQuery(parts.query ?? \"\")}`;\n}\n\n/** Key of the manifest document for one caller scope. */\nexport function manifestKey(scope: string): string {\n return `v1/_manifest/${scope}`;\n}\n\n/** Prefix of all entries of one kind for `resource` (every scope and subject). */\nexport function kindPrefix(resource: string, kind: CacheKind): string {\n return `v1/${resource}/${kind}/`;\n}\n\n/** Prefix of the `record` entries of one record (every version, scope, query). */\nexport function recordPrefix(resource: string, recordId: number): string {\n return `v1/${resource}/record/${recordId}/`;\n}\n\n/** Prefix of the `revisions` entries of one record. */\nexport function revisionsPrefix(resource: string, recordId: number): string {\n return `v1/${resource}/revisions/${recordId}/`;\n}\n\n/** The list, stats and predict prefixes of `resource`. */\nexport function collectionPrefixes(resource: string): string[] {\n return COLLECTION_KINDS.map((kind) => kindPrefix(resource, kind));\n}\n\n/** Everything cached for `resource` except `filters` (a reconnect or an unknown change). */\nexport function resourcePrefixes(resource: string): string[] {\n return [...COLLECTION_KINDS, ...PER_RECORD_KINDS].map((kind) =>\n kindPrefix(resource, kind),\n );\n}\n\n/**\n * Prefixes an SSE event clears, per the spec's table: `create` clears the\n * collection kinds; `update`/`delete`/`restore` also clear the record and its\n * revisions; an unknown action or a missing record id clears the resource.\n */\nexport function eventPrefixes(\n resource: string,\n action: string,\n recordId: number | undefined,\n): string[] {\n if (action === \"create\") {\n return collectionPrefixes(resource);\n }\n if (\n (action === \"update\" || action === \"delete\" || action === \"restore\") &&\n recordId !== undefined\n ) {\n return [\n ...collectionPrefixes(resource),\n recordPrefix(resource, recordId),\n revisionsPrefix(resource, recordId),\n ];\n }\n return resourcePrefixes(resource);\n}\n","import { ANON, cacheScope } from \"../auth.ts\";\nimport type { RequestContext } from \"../interfaces.ts\";\n\n/**\n * `\"anon\"` when the request has no Authorization header; otherwise the first\n * 32 hex characters of the SHA-256 of the whole credential, the same digest as\n * the client's `cacheScope(token)` (a `Bearer ` prefix is dropped, any other value is hashed as it is). It is read from the\n * request the cache sees, so it must be the innermost middleware that matters:\n * whatever sets Authorization has to run before the cache. The raw credential\n * never enters a key or a log, and the unverified `sub` claim is never used.\n */\nexport async function credentialScope(headers: Headers): Promise<string> {\n const value = headers.get(\"authorization\");\n if (value === null) {\n return ANON;\n }\n const credential = /^bearer\\s+(.+)$/i.exec(value)?.[1] ?? value;\n return cacheScope(credential);\n}\n\n/**\n * The caller scope the cache keys by: `ctx.cacheScope` when the client was\n * created with a `cacheScope` option (a cookie session sends no Authorization\n * header, so the header cannot tell callers apart), otherwise\n * {@link credentialScope} of the request as it is sent, so a middleware that\n * sets its own Authorization is still covered.\n */\nexport async function contextScope(ctx: RequestContext): Promise<string> {\n return ctx.cacheScope ?? credentialScope(ctx.request.headers);\n}\n","/**\n * The caching middleware (`createCache`): an I3 `Middleware` plus the control\n * surface that events and the client use.\n *\n * It reads the resource, op, record id, API version and caller scope from\n * `RequestContext` and never touches the Authorization header. A read is\n * answered in one of four ways, see `cacheStatus`: `hit` (fresh, no\n * network), `stale` (inside the stale-while-revalidate window, refreshed in\n * the background), `revalidated` (a `304` rebuilt from the stored entry with\n * merged headers) or `miss`.\n *\n * Freshness beyond \"revalidate every time\" is granted only while the\n * resource's SSE stream is up (`setEventsActive`): events are the only thing\n * that can end a fresh window early. `stats` and `predict` are bounded by\n * `statsTtl` instead, as the server caches those bodies the same way. A\n * per-resource generation counter stops a response that was in flight when\n * an invalidation landed from being stored fresh.\n */\nimport type {\n CacheEntry,\n MaybePromise,\n CacheStore,\n Middleware,\n Next,\n RequestContext,\n} from \"../interfaces.ts\";\nimport {\n buildEntry,\n cacheControl,\n isJson,\n mergeHeaders,\n tagStatus,\n toResponse,\n varyAllowsCaching,\n} from \"./entry.ts\";\nimport {\n buildKey,\n collectionPrefixes,\n eventPrefixes,\n isCacheKind,\n isStatsKind,\n kindPrefix,\n manifestKey,\n recordPrefix,\n resourcePrefixes,\n revisionsPrefix,\n} from \"./keys.ts\";\nimport type { CacheKind } from \"./keys.ts\";\nimport { cachePolicyFromManifest } from \"./policy.ts\";\nimport { contextScope } from \"./scope.ts\";\nimport type { CachePolicy } from \"./policy.ts\";\n\n/**\n * A store may clear several prefixes in one pass (one walk, one transaction);\n * the cache uses it when present and falls back to `clear` per prefix.\n */\nexport interface CacheStoreMany extends CacheStore {\n clearMany?(prefixes: readonly string[]): MaybePromise<void>;\n}\n\nexport interface CacheOptions {\n readonly store: CacheStoreMany;\n /**\n * Policy per manifest resource: `name@apiVersion` (built from that version's\n * manifest entry) or, for every version, plain `name`. A resource without\n * one is not cached. The event stream and the freshness gate are per name.\n */\n readonly policies: Readonly<Record<string, CachePolicy>>;\n /** Cache the manifest document per caller scope. Default `true`. */\n readonly cacheManifest?: boolean;\n /** Epoch milliseconds; injectable for tests. */\n readonly now?: () => number;\n}\n\nexport interface Cache {\n /** Put it in the client's middleware chain. */\n readonly middleware: Middleware;\n /** Record whether the SSE stream of `resource` is connected (gates fresh hits). */\n setEventsActive(resource: string, active: boolean): void;\n eventsActive(resource: string): boolean;\n /** Clear what an SSE event makes stale, in every API version. */\n invalidateEvent(\n resource: string,\n action: string,\n recordId: number | undefined,\n ): Promise<void>;\n /** Clear everything cached for `resource` except `filters` (after a reconnect). */\n clearResource(resource: string): Promise<void>;\n /** Clear keys under `prefix` (everything the store owns when omitted). */\n clear(prefix?: string): Promise<void>;\n /**\n * Adds `If-Match` from the cached record ETag to `PATCH`/`DELETE ?id=` that\n * have none. Put it innermost, next to `middleware`, so it sees the same\n * Authorization (and so scope) the request is sent with.\n */\n readonly autoIfMatch: Middleware;\n /** The stored ETag of a record of one API version, for `auto_if_match`. */\n cachedEtag(\n resource: string,\n recordId: number,\n scope: string,\n apiVersion: string | null,\n ): Promise<string | undefined>;\n /** Abort background revalidations and wait for them. */\n close(): Promise<void>;\n}\n\ntype Kind = CacheKind | \"manifest\";\ntype Decision = \"hit\" | \"stale\" | \"revalidate\" | \"miss\";\ntype StoreOp =\n | { readonly type: \"clear\"; readonly prefixes: readonly string[] }\n | {\n readonly type: \"set\";\n readonly key: string;\n readonly entry: CacheEntry;\n readonly ttlMs: number;\n }\n | { readonly type: \"delete\"; readonly key: string };\n\n/** What `begin` learned about one request. */\ninterface State {\n readonly ctx: RequestContext;\n readonly policy: CachePolicy;\n /** The cache kind of a read; `undefined` for a write. */\n readonly kind: Kind | undefined;\n readonly key: string | undefined;\n /** The resource's invalidation count when the request was made. */\n readonly generation: number;\n /** From the Authorization header of the request as the cache sees it. */\n readonly scope: string;\n readonly forceRevalidate: boolean;\n entry?: CacheEntry | undefined;\n /** Whether we put `If-None-Match` on the request. */\n validated?: boolean;\n}\n\nconst MIN_RETENTION_MS = 3_600_000;\nconst MANIFEST_POLICY: CachePolicy = cachePolicyFromManifest({});\nconst WRITE_METHODS: ReadonlySet<string> = new Set([\n \"POST\",\n \"PATCH\",\n \"PUT\",\n \"DELETE\",\n]);\nconst BULK_OPS: ReadonlySet<string> = new Set([\"bulk_update\", \"bulk_delete\"]);\nconst RECORD_WRITES: ReadonlySet<string> = new Set([\n \"delete\",\n \"publish\",\n \"restore\",\n]);\nconst CREATES: ReadonlySet<string> = new Set([\"create\", \"clone\", \"draft\"]);\n\nfunction isStorable(response: Response): boolean {\n return (\n isJson(response) &&\n !cacheControl(response.headers).has(\"no-store\") &&\n varyAllowsCaching(response)\n );\n}\n\nfunction retentionMs(freshMs: number, staleMs: number): number {\n return Math.max(freshMs + staleMs, MIN_RETENTION_MS);\n}\n\n/** The ids of a bulk response (`{matched, ids}`), or `undefined` if unreadable. */\nfunction idsFromBody(body: string): number[] | undefined {\n try {\n const ids = (JSON.parse(body) as { ids?: unknown }).ids;\n return Array.isArray(ids) && ids.every((id) => Number.isInteger(id))\n ? (ids as number[])\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The resource names in a manifest document, or none if unreadable. */\nfunction manifestResources(body: string): string[] {\n try {\n const resources = (JSON.parse(body) as { resources?: unknown }).resources;\n return Array.isArray(resources)\n ? resources\n .map((item: { name?: unknown }) => item.name)\n .filter((name): name is string => typeof name === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\nasync function safe<T>(call: () => T | Promise<T>): Promise<T | undefined> {\n try {\n return await call();\n } catch {\n return undefined; // a failing store is a miss, never a failed request\n }\n}\n\nexport function createCache(options: CacheOptions): Cache {\n const { store } = options;\n const policies = new Map(Object.entries(options.policies));\n const policyFor = (\n resource: string | undefined,\n apiVersion: string | null,\n ): CachePolicy | undefined =>\n resource === undefined\n ? undefined\n : (policies.get(`${resource}@${apiVersion}`) ?? policies.get(resource));\n const cacheManifest = options.cacheManifest ?? true;\n const now = options.now ?? Date.now;\n const eventsActive = new Set<string>();\n const generations = new Map<string, number>();\n const inflight = new Set<string>();\n const tasks = new Set<Promise<void>>();\n const background = new AbortController();\n\n let globalGeneration = 0; // bumped by `clear`, which may span resources\n const generation = (resource: string | undefined): number =>\n (generations.get(resource ?? \"\") ?? 0) + globalGeneration;\n const bump = (resource: string): void => {\n generations.set(resource, (generations.get(resource) ?? 0) + 1);\n };\n\n // -- freshness ---------------------------------------------------------------\n\n /**\n * Whether an entry of `kind` may be served without the network right now:\n * only while the stream is up, except `stats`/`predict`, which `stats_ttl`\n * bounds on its own. Never `filters` (events never clear them) or the manifest.\n */\n const gateOpen = (\n policy: CachePolicy,\n resource: string | undefined,\n kind: Kind,\n ): boolean =>\n !policy.revalidateOnly &&\n kind !== \"manifest\" &&\n kind !== \"filters\" &&\n (isStatsKind(kind) || eventsActive.has(resource as string));\n\n /** Stale-while-revalidate needs the stream for every kind, stats and predict included. */\n const staleOpen = (\n policy: CachePolicy,\n resource: string | undefined,\n kind: Kind,\n ): boolean =>\n gateOpen(policy, resource, kind) && eventsActive.has(resource as string);\n\n /** Milliseconds fresh and of stale-while-revalidate for a new entry; none if the response may predate an invalidation. */\n const entryTimes = (\n state: State,\n kind: Kind,\n ): { fresh: number; stale: number } => {\n const resource = state.ctx.resource;\n if (\n generation(resource) !== state.generation ||\n !gateOpen(state.policy, resource, kind)\n ) {\n return { fresh: 0, stale: 0 };\n }\n const fresh = isStatsKind(kind)\n ? state.policy.statsTtl\n : state.policy.maxAge;\n return {\n fresh: fresh * 1000,\n stale: staleOpen(state.policy, resource, kind)\n ? state.policy.staleWhileRevalidate * 1000\n : 0,\n };\n };\n\n const decide = (state: State, entry: CacheEntry | undefined): Decision => {\n if (entry === undefined) {\n return \"miss\";\n }\n if (\n !state.forceRevalidate &&\n gateOpen(state.policy, state.ctx.resource, state.kind as Kind)\n ) {\n const at = now();\n if (at < entry.freshUntil) {\n return \"hit\";\n }\n if (\n at < entry.staleUntil &&\n staleOpen(state.policy, state.ctx.resource, state.kind as Kind)\n ) {\n return \"stale\";\n }\n }\n return entry.etag !== undefined && state.policy.etag\n ? \"revalidate\"\n : \"miss\";\n };\n\n // -- request side ------------------------------------------------------------\n\n const begin = async (ctx: RequestContext): Promise<State | undefined> => {\n const { request, resource } = ctx;\n const scope = await contextScope(ctx);\n const generationNow = generation(resource);\n if (request.method !== \"GET\") {\n if (!WRITE_METHODS.has(request.method)) {\n return undefined; // HEAD, OPTIONS and the like change nothing\n }\n const policy = policyFor(resource, ctx.apiVersion);\n return policy?.enabled === true\n ? {\n ctx,\n policy,\n kind: undefined,\n key: undefined,\n generation: generationNow,\n scope,\n forceRevalidate: false,\n }\n : undefined;\n }\n let policy: CachePolicy | undefined;\n let kind: Kind;\n let key: string;\n if (ctx.op === \"manifest\") {\n if (!cacheManifest) {\n return undefined;\n }\n policy = MANIFEST_POLICY;\n kind = \"manifest\";\n key = manifestKey(scope);\n } else {\n policy = policyFor(resource, ctx.apiVersion);\n if (\n resource === undefined ||\n policy?.enabled !== true ||\n !isCacheKind(ctx.op)\n ) {\n return undefined;\n }\n kind = ctx.op;\n key = buildKey({\n resource,\n kind,\n recordId: ctx.recordId,\n scope,\n query: new URL(request.url).search,\n apiVersion: ctx.apiVersion,\n });\n }\n const control = cacheControl(request.headers);\n if (control.has(\"no-store\") || request.headers.has(\"if-none-match\")) {\n return undefined; // the caller forbids storing or manages validators itself\n }\n return {\n ctx,\n policy,\n kind,\n key,\n generation: generationNow,\n scope,\n forceRevalidate: control.has(\"no-cache\"),\n };\n };\n\n /** A copy of `ctx` whose request carries our validator (a `GET` has no body to lose). */\n const withValidator = (ctx: RequestContext, etag: string): RequestContext => {\n const headers = new Headers(ctx.request.headers);\n headers.set(\"if-none-match\", etag);\n return { ...ctx, request: new Request(ctx.request, { headers }) };\n };\n\n // -- response side -----------------------------------------------------------\n\n const readBody = async (\n state: State,\n response: Response,\n ): Promise<string | undefined> => {\n const { status } = response;\n const { op, recordId } = state.ctx;\n let wanted: boolean;\n if (state.kind !== undefined) {\n wanted = status === 200 && isStorable(response);\n } else if (op === \"update\") {\n wanted = status === 200 && recordId !== undefined && isStorable(response);\n } else {\n wanted =\n status >= 200 &&\n status < 300 &&\n (BULK_OPS.has(op) || (op === \"restore\" && recordId === undefined));\n }\n return wanted ? response.clone().text() : undefined;\n };\n\n /** One clear op for all `prefixes`: the store clears them in a single pass. */\n const clears = (prefixes: readonly string[]): StoreOp[] => [\n { type: \"clear\", prefixes },\n ];\n\n const manifestOps = (\n old: CacheEntry | undefined,\n entry: CacheEntry,\n ): StoreOp[] => {\n if (\n old === undefined ||\n (old.etag !== undefined && old.etag === entry.etag)\n ) {\n return [];\n }\n const names = new Set([\n ...[...policies.keys()].map((key) => key.replace(/@[^@]*$/, \"\")),\n ...manifestResources(old.body),\n ...manifestResources(entry.body),\n ]);\n return clears([...names].sort().map((name) => kindPrefix(name, \"filters\")));\n };\n\n const readPlan = (\n state: State,\n response: Response,\n body: string | undefined,\n ): { result: Response; ops: StoreOp[] } => {\n const key = state.key as string;\n const kind = state.kind as Kind;\n const { status } = response;\n const at = now();\n const { fresh, stale } = entryTimes(state, kind);\n const old = state.entry;\n if (status === 304 && state.validated === true && old !== undefined) {\n // `validated` means the request carried `old.etag`.\n const updated: CacheEntry = {\n ...old,\n headers: mergeHeaders(old.headers, response.headers),\n storedAt: at,\n freshUntil: at + fresh,\n staleUntil: at + fresh + stale,\n etag: response.headers.get(\"etag\") ?? (old.etag as string),\n };\n return {\n result: toResponse(updated, \"revalidated\"),\n ops: [\n {\n type: \"set\",\n key,\n entry: updated,\n ttlMs: retentionMs(fresh, stale),\n },\n ],\n };\n }\n if (status === 200 && body !== undefined) {\n tagStatus(response, \"miss\");\n const entry = buildEntry(\n response,\n body,\n { now: at, fresh, stale },\n state.policy.etag,\n );\n if (entry.etag === undefined && fresh <= 0) {\n // No validator and never fresh: drop the old entry.\n return { result: response, ops: [{ type: \"delete\", key }] };\n }\n const ops: StoreOp[] = [\n { type: \"set\", key, entry, ttlMs: retentionMs(fresh, stale) },\n ];\n if (kind === \"manifest\") {\n ops.push(...manifestOps(old, entry));\n }\n return { result: response, ops };\n }\n if (status === 200 || status === 404 || status === 410) {\n return { result: response, ops: [{ type: \"delete\", key }] };\n }\n return { result: response, ops: [] };\n };\n\n /** Store the PATCH response as the record's entry (it is the read view plus ETag). */\n const writeThrough = (\n state: State,\n response: Response,\n body: string | undefined,\n resource: string,\n recordId: number,\n ): StoreOp[] => {\n if (body === undefined) {\n return [];\n }\n const { fresh, stale } = entryTimes(state, \"record\");\n const entry = buildEntry(\n response,\n body,\n { now: now(), fresh, stale },\n state.policy.etag,\n );\n if (entry.etag === undefined && fresh <= 0) {\n return [];\n }\n const key = buildKey({\n resource,\n kind: \"record\",\n recordId,\n scope: state.scope,\n apiVersion: state.ctx.apiVersion,\n });\n return [{ type: \"set\", key, entry, ttlMs: retentionMs(fresh, stale) }];\n };\n\n /**\n * The store changes a write calls for. `response` is `undefined` when the\n * request threw: like a `5xx`, the outcome is unknown (the server may have\n * applied the write before the failure), so it clears what a success would\n * have, minus the write-through, and for a bulk op the whole resource (the\n * ids are unknown). Other failures changed nothing.\n */\n const writeOps = (\n state: State,\n response: Response | undefined,\n body: string | undefined,\n ): StoreOp[] => {\n const { op, recordId } = state.ctx;\n const resource = state.ctx.resource as string;\n const status = response?.status;\n if (status === 412) {\n return recordId === undefined\n ? []\n : clears([recordPrefix(resource, recordId)]);\n }\n if ((status === 404 || status === 410) && recordId !== undefined) {\n // The record is gone (or never was): drop what reads cached for it.\n return clears([\n recordPrefix(resource, recordId),\n revisionsPrefix(resource, recordId),\n ]);\n }\n const ok = status !== undefined && status >= 200 && status < 300;\n if (!ok && status !== undefined && status < 500) {\n return []; // refused: nothing changed\n }\n const collections = collectionPrefixes(resource);\n if (CREATES.has(op)) {\n return clears(collections);\n }\n if (recordId !== undefined && (RECORD_WRITES.has(op) || op === \"update\")) {\n return [\n ...clears([\n ...collections,\n revisionsPrefix(resource, recordId),\n recordPrefix(resource, recordId),\n ]),\n ...(op === \"update\" && response !== undefined && ok\n ? writeThrough(state, response, body, resource, recordId)\n : []),\n ];\n }\n if (ok && (BULK_OPS.has(op) || op === \"restore\")) {\n // Bulk ops and a bulk restore always have their body read (see `readBody`).\n const ids = idsFromBody(body as string);\n if (ids !== undefined) {\n return clears([\n ...collections,\n ...ids.flatMap((id) => [\n recordPrefix(resource, id),\n revisionsPrefix(resource, id),\n ]),\n ]);\n }\n }\n return clears(resourcePrefixes(resource));\n };\n\n /** One pass when the store can, else one `clear` per prefix; a failure is a miss. */\n const clearPrefixes = async (prefixes: readonly string[]): Promise<void> => {\n if (store.clearMany !== undefined) {\n await safe(() => store.clearMany?.(prefixes));\n return;\n }\n for (const prefix of prefixes) {\n await safe(() => store.clear(prefix));\n }\n };\n\n const apply = async (op: StoreOp): Promise<void> => {\n await safe(() =>\n op.type === \"clear\"\n ? clearPrefixes(op.prefixes)\n : op.type === \"set\"\n ? store.set(op.key, op.entry, op.ttlMs)\n : store.delete(op.key),\n );\n };\n\n /** Plan, then apply, the store changes a response calls for. */\n const finish = async (\n state: State,\n response: Response,\n ): Promise<Response> => {\n const body = await readBody(state, response);\n const { result, ops } =\n state.kind === undefined\n ? { result: response, ops: writeOps(state, response, body) }\n : readPlan(state, response, body);\n await applyOps(state, ops);\n return result;\n };\n\n /** Apply `ops`, guarding against an invalidation that lands meanwhile. */\n const applyOps = async (\n state: State,\n ops: readonly StoreOp[],\n ): Promise<void> => {\n if (state.kind === undefined && ops.some((op) => op.type === \"clear\")) {\n bump(state.ctx.resource as string); // before applying: reads now in flight must not store fresh\n }\n const expected = generation(state.ctx.resource);\n for (const op of ops) {\n await apply(op);\n }\n if (generation(state.ctx.resource) !== expected) {\n // An invalidation landed while storing: drop what was just stored.\n for (const op of ops.filter((candidate) => candidate.type === \"set\")) {\n await apply({ type: \"delete\", key: op.key });\n }\n }\n };\n\n // -- stale-while-revalidate --------------------------------------------------\n\n const schedule = (state: State, next: Next): void => {\n const key = state.key as string;\n if (background.signal.aborted || inflight.has(key)) {\n return;\n }\n inflight.add(key);\n const entry = state.entry as CacheEntry;\n const task = (async (): Promise<void> => {\n try {\n const signal = background.signal;\n const headers = new Headers(state.ctx.request.headers);\n if (entry.etag !== undefined) {\n headers.set(\"if-none-match\", entry.etag);\n }\n // Keeps the caller's init (credentials, redirect, ...) but is detached\n // from their signal: this outlives their request. The cache mode is\n // forced to \"no-store\": a caller's \"force-cache\" or \"only-if-cached\"\n // would be answered from the HTTP cache and never revalidate (Fetch,\n // HTTP-network-or-cache fetch step 16 does the same for a \"default\"\n // request that carries If-None-Match).\n const request = new Request(state.ctx.request, {\n headers,\n signal,\n cache: \"no-store\",\n });\n const fresh: State = {\n ...state,\n validated: entry.etag !== undefined,\n };\n const response = await next({ ...state.ctx, request, signal });\n const result = await finish(fresh, response);\n await result.body?.cancel();\n } catch {\n // Nothing to report to: the caller already has the stale answer.\n } finally {\n inflight.delete(key);\n }\n })();\n tasks.add(task);\n void task.then(() => tasks.delete(task));\n };\n\n // -- the middleware ----------------------------------------------------------\n\n const middleware: Middleware = async (ctx, next) => {\n const state = await begin(ctx);\n if (state === undefined) {\n return next(ctx);\n }\n if (state.kind === undefined) {\n const unsent = ctx.request.signal.aborted; // aborted mid-flight is unknown, not unsent\n let response: Response;\n try {\n response = await next(ctx);\n } catch (error) {\n // The write may have been applied before it failed: forget what it\n // touches, unless it was aborted before it could be sent. The\n // caller still gets the original error.\n if (!unsent) {\n await safe(() =>\n applyOps(state, writeOps(state, undefined, undefined)),\n );\n }\n throw error;\n }\n return finish(state, response);\n }\n state.entry = await safe(() => store.get(state.key as string));\n const decision = decide(state, state.entry);\n const entry = state.entry as CacheEntry;\n if (decision === \"hit\") {\n return toResponse(entry, \"hit\");\n }\n if (decision === \"stale\") {\n schedule(state, next);\n return toResponse(entry, \"stale\");\n }\n let sent = ctx;\n if (decision === \"revalidate\") {\n sent = withValidator(ctx, entry.etag as string);\n state.validated = true;\n }\n return finish(state, await next(sent));\n };\n\n // -- control surface ---------------------------------------------------------\n\n const autoIfMatch: Middleware = async (ctx, next) => {\n const { request, resource, recordId } = ctx;\n if (\n (ctx.op === \"update\" || ctx.op === \"delete\") &&\n recordId !== undefined &&\n resource !== undefined &&\n policyFor(resource, ctx.apiVersion)?.enabled === true &&\n !request.headers.has(\"if-match\")\n ) {\n const etag = await lookupEtag(\n resource,\n recordId,\n await contextScope(ctx),\n ctx.apiVersion,\n );\n if (etag !== undefined) {\n const headers = new Headers(request.headers);\n headers.set(\"if-match\", etag);\n return next({ ...ctx, request: new Request(request, { headers }) });\n }\n }\n return next(ctx);\n };\n\n const lookupEtag = async (\n resource: string,\n recordId: number,\n scope: string,\n apiVersion: string | null,\n ): Promise<string | undefined> => {\n const entry = await safe(() =>\n store.get(\n buildKey({ resource, kind: \"record\", recordId, scope, apiVersion }),\n ),\n );\n return entry?.etag;\n };\n\n return {\n middleware,\n autoIfMatch,\n setEventsActive(resource, active) {\n if (active) {\n eventsActive.add(resource);\n } else {\n eventsActive.delete(resource);\n }\n },\n eventsActive: (resource) => eventsActive.has(resource),\n async invalidateEvent(resource, action, recordId) {\n bump(resource);\n await clearPrefixes(eventPrefixes(resource, action, recordId));\n },\n async clearResource(resource) {\n bump(resource);\n await clearPrefixes(resourcePrefixes(resource));\n },\n async clear(prefix) {\n globalGeneration += 1; // reads in flight must not store fresh over the clear\n await safe(() => store.clear(prefix));\n },\n cachedEtag: lookupEtag,\n async close() {\n background.abort();\n await Promise.allSettled(tasks);\n },\n };\n}\n","/** In-memory cache store: LRU with a per-entry TTL. */\nimport type { CacheEntry, CacheStore } from \"../interfaces.ts\";\n\n/** How long a store keeps an entry when the caller gives no ttl (the spec's retention floor). */\nexport const DEFAULT_TTL_MS = 3_600_000;\n\nexport interface MemoryStoreOptions {\n /** Default 1024. */\n readonly maxEntries?: number;\n readonly defaultTtlMs?: number;\n /** Epoch milliseconds; injectable for tests. */\n readonly now?: () => number;\n}\n\n/** LRU store (default 1024 entries) that also honours each entry's ttl. */\nexport class MemoryStore implements CacheStore {\n private readonly data = new Map<\n string,\n { readonly expiresAt: number; readonly entry: CacheEntry }\n >();\n private readonly maxEntries: number;\n private readonly defaultTtlMs: number;\n private readonly now: () => number;\n\n constructor(options: MemoryStoreOptions = {}) {\n const maxEntries = options.maxEntries ?? 1024;\n if (!Number.isInteger(maxEntries) || maxEntries < 1) {\n throw new RangeError(\"maxEntries must be an integer of at least 1\");\n }\n this.maxEntries = maxEntries;\n this.defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n this.now = options.now ?? Date.now;\n }\n\n /** Number of stored entries (expired ones included until touched). */\n get size(): number {\n return this.data.size;\n }\n\n get(key: string): CacheEntry | undefined {\n const item = this.data.get(key);\n if (item === undefined) {\n return undefined;\n }\n if (item.expiresAt <= this.now()) {\n this.data.delete(key);\n return undefined;\n }\n this.data.delete(key); // re-insert: Map order is the recency order\n this.data.set(key, item);\n return item.entry;\n }\n\n set(key: string, entry: CacheEntry, ttlMs?: number): void {\n this.data.delete(key);\n this.data.set(key, {\n expiresAt: this.now() + (ttlMs ?? this.defaultTtlMs),\n entry,\n });\n while (this.data.size > this.maxEntries) {\n // The first key is the least recently used.\n this.data.delete(this.data.keys().next().value as string);\n }\n }\n\n delete(key: string): void {\n this.data.delete(key);\n }\n\n /** Remove every key under any of `prefixes` in one walk. */\n clearMany(prefixes: readonly string[]): void {\n for (const key of this.data.keys()) {\n if (prefixes.some((prefix) => key.startsWith(prefix))) {\n this.data.delete(key);\n }\n }\n }\n\n clear(prefix?: string): void {\n for (const key of this.data.keys()) {\n if (prefix === undefined || key.startsWith(prefix)) {\n this.data.delete(key);\n }\n }\n }\n}\n","const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c);\n\nlet idbProxyableTypes;\nlet cursorAdvanceMethods;\n// This is a function to prevent it throwing up in node environments.\nfunction getIdbProxyableTypes() {\n return (idbProxyableTypes ||\n (idbProxyableTypes = [\n IDBIndex,\n IDBObjectStore,\n IDBCursor,\n IDBDatabase,\n IDBTransaction,\n ]));\n}\n// This is a function to prevent it throwing up in node environments.\nfunction getCursorAdvanceMethods() {\n return (cursorAdvanceMethods ||\n (cursorAdvanceMethods = ['advance', 'continue', 'continuePrimaryKey'].map((method) => IDBCursor.prototype[method])));\n}\nconst transactionDoneMap = new WeakMap();\nconst transformCache = new WeakMap();\nconst reverseTransformCache = new WeakMap();\nfunction promisifyRequest(request) {\n const promise = new Promise((resolve, reject) => {\n const unlisten = () => {\n request.removeEventListener('success', success);\n request.removeEventListener('error', error);\n };\n const success = () => {\n resolve(wrap(request.result));\n unlisten();\n };\n const error = () => {\n reject(request.error);\n unlisten();\n };\n request.addEventListener('success', success);\n request.addEventListener('error', error);\n });\n // This mapping exists in reverseTransformCache but doesn't exist in transformCache. This\n // is because we create many promises from a single IDBRequest.\n reverseTransformCache.set(promise, request);\n return promise;\n}\nfunction cacheDonePromiseForTransaction(tx) {\n // Early bail if we've already created a done promise for this transaction.\n if (transactionDoneMap.has(tx))\n return;\n const done = new Promise((resolve, reject) => {\n const unlisten = () => {\n tx.removeEventListener('complete', complete);\n tx.removeEventListener('error', error);\n tx.removeEventListener('abort', error);\n };\n const complete = () => {\n resolve();\n unlisten();\n };\n const error = () => {\n reject(tx.error || new DOMException('AbortError', 'AbortError'));\n unlisten();\n };\n tx.addEventListener('complete', complete);\n tx.addEventListener('error', error);\n tx.addEventListener('abort', error);\n });\n // Cache it for later retrieval.\n transactionDoneMap.set(tx, done);\n}\nlet idbProxyTraps = {\n get(target, prop, receiver) {\n if (target instanceof IDBTransaction) {\n // Special handling for transaction.done.\n if (prop === 'done')\n return transactionDoneMap.get(target);\n // Make tx.store return the only store in the transaction, or undefined if there are many.\n if (prop === 'store') {\n return receiver.objectStoreNames[1]\n ? undefined\n : receiver.objectStore(receiver.objectStoreNames[0]);\n }\n }\n // Else transform whatever we get back.\n return wrap(target[prop]);\n },\n set(target, prop, value) {\n target[prop] = value;\n return true;\n },\n has(target, prop) {\n if (target instanceof IDBTransaction &&\n (prop === 'done' || prop === 'store')) {\n return true;\n }\n return prop in target;\n },\n};\nfunction replaceTraps(callback) {\n idbProxyTraps = callback(idbProxyTraps);\n}\nfunction wrapFunction(func) {\n // Due to expected object equality (which is enforced by the caching in `wrap`), we\n // only create one new func per func.\n // Cursor methods are special, as the behaviour is a little more different to standard IDB. In\n // IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the\n // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense\n // with real promises, so each advance methods returns a new promise for the cursor object, or\n // undefined if the end of the cursor has been reached.\n if (getCursorAdvanceMethods().includes(func)) {\n return function (...args) {\n // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use\n // the original object.\n func.apply(unwrap(this), args);\n return wrap(this.request);\n };\n }\n return function (...args) {\n // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use\n // the original object.\n return wrap(func.apply(unwrap(this), args));\n };\n}\nfunction transformCachableValue(value) {\n if (typeof value === 'function')\n return wrapFunction(value);\n // This doesn't return, it just creates a 'done' promise for the transaction,\n // which is later returned for transaction.done (see idbObjectHandler).\n if (value instanceof IDBTransaction)\n cacheDonePromiseForTransaction(value);\n if (instanceOfAny(value, getIdbProxyableTypes()))\n return new Proxy(value, idbProxyTraps);\n // Return the same value back if we're not going to transform it.\n return value;\n}\nfunction wrap(value) {\n // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because\n // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached.\n if (value instanceof IDBRequest)\n return promisifyRequest(value);\n // If we've already transformed this value before, reuse the transformed value.\n // This is faster, but it also provides object equality.\n if (transformCache.has(value))\n return transformCache.get(value);\n const newValue = transformCachableValue(value);\n // Not all types are transformed.\n // These may be primitive types, so they can't be WeakMap keys.\n if (!Object.is(newValue, value)) {\n transformCache.set(value, newValue);\n reverseTransformCache.set(newValue, value);\n }\n return newValue;\n}\nconst unwrap = (value) => reverseTransformCache.get(value);\n\n/**\n * Open a database.\n *\n * @param name Name of the database.\n * @param version Schema version.\n * @param callbacks Additional callbacks.\n */\nfunction openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) {\n const request = indexedDB.open(name, version);\n const openPromise = wrap(request);\n if (upgrade) {\n request.addEventListener('upgradeneeded', (event) => {\n upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event);\n });\n }\n if (blocked) {\n request.addEventListener('blocked', (event) => blocked(\n // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405\n event.oldVersion, event.newVersion, event));\n }\n openPromise\n .then((db) => {\n if (terminated)\n db.addEventListener('close', () => terminated());\n if (blocking) {\n db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event));\n }\n })\n .catch(() => { });\n return openPromise;\n}\n/**\n * Delete a database.\n *\n * @param name Name of the database.\n */\nfunction deleteDB(name, { blocked } = {}) {\n const request = indexedDB.deleteDatabase(name);\n if (blocked) {\n request.addEventListener('blocked', (event) => blocked(\n // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405\n event.oldVersion, event));\n }\n return wrap(request).then(() => undefined);\n}\n\nconst readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count'];\nconst writeMethods = ['put', 'add', 'delete', 'clear'];\nconst cachedMethods = {};\nfunction getMethod(target, prop) {\n if (!(target instanceof IDBDatabase &&\n !(prop in target) &&\n typeof prop === 'string')) {\n return;\n }\n if (cachedMethods[prop])\n return cachedMethods[prop];\n const targetFuncName = prop.replace(/FromIndex$/, '');\n const useIndex = prop !== targetFuncName;\n const isWrite = writeMethods.includes(targetFuncName);\n if (\n // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.\n !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) ||\n !(isWrite || readMethods.includes(targetFuncName))) {\n return;\n }\n const method = async function (storeName, ...args) {\n // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :(\n const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly');\n let target = tx.store;\n if (useIndex)\n target = target.index(args.shift());\n // Must reject if op rejects.\n // If it's a write operation, must reject if tx.done rejects.\n // Must reject with op rejection first.\n // Must resolve with op value.\n // Must handle both promises (no unhandled rejections)\n return (await Promise.all([\n target[targetFuncName](...args),\n isWrite && tx.done,\n ]))[0];\n };\n return (cachedMethods[prop] = method);\n}\nreplaceTraps((oldTraps) => ({\n ...oldTraps,\n get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver),\n has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop),\n}));\n\nconst advanceMethodProps = ['advance', 'continue', 'continuePrimaryKey'];\nconst methodMap = {};\nconst advanceResults = new WeakMap();\nconst ittrProxiedCursorToOriginalProxy = new WeakMap();\nconst cursorIteratorTraps = {\n get(target, prop) {\n if (!advanceMethodProps.includes(prop))\n return target[prop];\n let cachedFunc = methodMap[prop];\n if (!cachedFunc) {\n cachedFunc = methodMap[prop] = function (...args) {\n advanceResults.set(this, ittrProxiedCursorToOriginalProxy.get(this)[prop](...args));\n };\n }\n return cachedFunc;\n },\n};\nasync function* iterate(...args) {\n // tslint:disable-next-line:no-this-assignment\n let cursor = this;\n if (!(cursor instanceof IDBCursor)) {\n cursor = await cursor.openCursor(...args);\n }\n if (!cursor)\n return;\n cursor = cursor;\n const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);\n ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);\n // Map this double-proxy back to the original, so other cursor methods work.\n reverseTransformCache.set(proxiedCursor, unwrap(cursor));\n while (cursor) {\n yield proxiedCursor;\n // If one of the advancing methods was not called, call continue().\n cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());\n advanceResults.delete(proxiedCursor);\n }\n}\nfunction isIteratorProp(target, prop) {\n return ((prop === Symbol.asyncIterator &&\n instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor])) ||\n (prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore])));\n}\nreplaceTraps((oldTraps) => ({\n ...oldTraps,\n get(target, prop, receiver) {\n if (isIteratorProp(target, prop))\n return iterate;\n return oldTraps.get(target, prop, receiver);\n },\n has(target, prop) {\n return isIteratorProp(target, prop) || oldTraps.has(target, prop);\n },\n}));\n\nexport { deleteDB, openDB, unwrap, wrap };\n","/**\n * IndexedDB cache store (one object store, out-of-line string keys, an\n * `expiresAt` index).\n *\n * Database version 2 adds the `expiresAt` index; upgrading from version 1\n * keeps every entry (the index is built over the stored values). Expired\n * entries are swept once when the database is opened, and `maxEntries` evicts\n * the entries closest to expiry on `set`.\n *\n * Every call is wrapped: a blocked database, private mode or a missing\n * `indexedDB` is a miss (or a no-op), never a failed request. `clear(prefix)`\n * is one `IDBKeyRange.bound(prefix, prefix + \"\\uffff\")` delete.\n */\nimport { openDB } from \"idb\";\nimport type { IDBPDatabase } from \"idb\";\nimport type { CacheEntry, CacheStore } from \"../interfaces.ts\";\nimport { DEFAULT_TTL_MS } from \"./memory.ts\";\n\n// The package compiles without the DOM lib; this is the one global it needs.\ndeclare const IDBKeyRange: {\n bound(lower: string, upper: string): unknown;\n upperBound(upper: number): unknown;\n};\n\nconst STORE = \"entries\";\nconst BY_EXPIRY = \"expiresAt\";\nconst VERSION = 2;\n\ninterface Stored {\n readonly expiresAt: number;\n readonly entry: CacheEntry;\n}\n\n/**\n * Observes `tx.done` up front: when an operation throws, the aborted\n * transaction's `done` rejects with nobody left to await it, which would be\n * an unhandled rejection.\n */\nfunction guarded<T extends { readonly done: Promise<void> }>(tx: T): T {\n tx.done.catch(() => undefined);\n return tx;\n}\n\nfunction isInvalidState(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { name?: unknown }).name === \"InvalidStateError\"\n );\n}\n\nexport interface IndexedDbStoreOptions {\n /** Database name; default `craftainer-crud-cache`. */\n readonly name?: string;\n readonly defaultTtlMs?: number;\n /**\n * Upper bound on stored entries, a positive integer; default unbounded.\n * When a `set` leaves more than this many, the entries with the earliest\n * `expiresAt` are deleted until the cap holds (a just-written entry with a\n * short ttl can be among them). The cap is per database and per store\n * instance: two instances on one `name` with different caps each enforce\n * their own on their writes.\n */\n readonly maxEntries?: number;\n /** Epoch milliseconds; injectable for tests. */\n readonly now?: () => number;\n}\n\nexport class IndexedDbStore implements CacheStore {\n private readonly name: string;\n private readonly defaultTtlMs: number;\n private readonly maxEntries: number | undefined;\n private readonly now: () => number;\n private opening: Promise<IDBPDatabase> | undefined;\n\n constructor(options: IndexedDbStoreOptions = {}) {\n this.name = options.name ?? \"craftainer-crud-cache\";\n if (this.name === \"\") {\n // The name is the per-origin namespace of the store.\n throw new RangeError(\"IndexedDbStore: name must not be empty\");\n }\n this.defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n if (\n options.maxEntries !== undefined &&\n !(Number.isInteger(options.maxEntries) && options.maxEntries >= 1)\n ) {\n throw new RangeError(\n \"IndexedDbStore: maxEntries must be an integer >= 1\",\n );\n }\n this.maxEntries = options.maxEntries;\n this.now = options.now ?? Date.now;\n }\n\n private open(): Promise<IDBPDatabase> {\n if (this.opening === undefined) {\n // A newer version wants to upgrade: let it, and reopen on the next call.\n const opening: Promise<IDBPDatabase> = this.openAndSweep(() => {\n if (this.opening === opening) {\n this.opening = undefined;\n }\n });\n this.opening = opening;\n }\n return this.opening;\n }\n\n private async openAndSweep(onDropped: () => void): Promise<IDBPDatabase> {\n const db = await openDB(this.name, VERSION, {\n blocking() {\n db.close();\n onDropped();\n },\n // The browser closed the connection (storage cleared, evicted): drop it\n // so the next call opens a fresh one.\n terminated: onDropped,\n upgrade(upgrading, oldVersion, _newVersion, tx) {\n if (oldVersion < 1) {\n upgrading.createObjectStore(STORE);\n }\n // Version 1 had the store but no index; its values already carry expiresAt.\n tx.objectStore(STORE).createIndex(BY_EXPIRY, BY_EXPIRY);\n },\n });\n try {\n await this.sweep(db);\n } catch {\n // A failed sweep leaves the entries for `get` to expire one by one.\n }\n return db;\n }\n\n /** Deletes every entry that has expired (`expiresAt <= now`). */\n private async sweep(db: IDBPDatabase): Promise<void> {\n const tx = guarded(db.transaction(STORE, \"readwrite\"));\n let cursor = await tx.store\n .index(BY_EXPIRY)\n .openCursor(IDBKeyRange.upperBound(this.now()));\n while (cursor !== null) {\n await cursor.delete();\n cursor = await cursor.continue();\n }\n await tx.done;\n }\n\n /**\n * Run `work` on the database; a failure yields `fallback`. The handle is\n * shared by every in-flight call, so it is dropped (and closed) only when it\n * is itself unusable: it never opened, or it was closed under us\n * (`InvalidStateError`). Any other failure belongs to one operation (an\n * uncloneable value, a quota error) and leaves the handle to the others.\n */\n private async run<T>(\n fallback: T,\n work: (db: IDBPDatabase) => Promise<T>,\n ): Promise<T> {\n // The handle this call runs on: a concurrent call may already have replaced it.\n let used: Promise<IDBPDatabase> | undefined;\n let db: IDBPDatabase | undefined;\n try {\n used = this.open(); // may throw at once when indexedDB is unusable\n db = await used;\n return await work(db);\n } catch (error) {\n if (db === undefined || isInvalidState(error)) {\n // Drop only our own handle: a newer one belongs to other calls.\n if (this.opening === used) {\n this.opening = undefined;\n }\n // An orphaned connection can block an upgrade.\n db?.close();\n }\n return fallback;\n }\n }\n\n get(key: string): Promise<CacheEntry | undefined> {\n return this.run<CacheEntry | undefined>(undefined, async (db) => {\n const stored = (await db.get(STORE, key)) as Stored | undefined;\n if (stored === undefined) {\n return undefined;\n }\n if (stored.expiresAt <= this.now()) {\n await db.delete(STORE, key);\n return undefined;\n }\n return stored.entry;\n });\n }\n\n set(key: string, entry: CacheEntry, ttlMs?: number): Promise<void> {\n return this.run(undefined, async (db) => {\n const stored: Stored = {\n expiresAt: this.now() + (ttlMs ?? this.defaultTtlMs),\n entry,\n };\n const tx = guarded(db.transaction(STORE, \"readwrite\"));\n await tx.store.put(stored, key);\n if (this.maxEntries !== undefined) {\n let excess = (await tx.store.count()) - this.maxEntries;\n let cursor =\n excess > 0 ? await tx.store.index(BY_EXPIRY).openCursor() : null;\n while (cursor !== null && excess > 0) {\n await cursor.delete();\n excess -= 1;\n cursor = await cursor.continue();\n }\n }\n await tx.done;\n });\n }\n\n delete(key: string): Promise<void> {\n return this.run(undefined, (db) => db.delete(STORE, key));\n }\n\n clear(prefix?: string): Promise<void> {\n return this.run(undefined, (db) =>\n prefix === undefined\n ? db.clear(STORE)\n : db.delete(STORE, IDBKeyRange.bound(prefix, `${prefix}\\uffff`)),\n );\n }\n\n /** Clear several prefixes in one transaction (one bound range each). */\n clearMany(prefixes: readonly string[]): Promise<void> {\n return this.run(undefined, async (db) => {\n const tx = db.transaction(STORE, \"readwrite\");\n await Promise.all([\n ...prefixes.map((prefix) =>\n tx.store.delete(IDBKeyRange.bound(prefix, `${prefix}\\uffff`)),\n ),\n tx.done,\n ]);\n });\n }\n\n /** Close the database handle; a later call reopens it. */\n async close(): Promise<void> {\n const opening = this.opening;\n this.opening = undefined;\n try {\n (await opening)?.close();\n } catch {\n // Never opened.\n }\n }\n}\n","/** Wiring an event hub to a cache. */\nimport type { EventConnection, EventHub } from \"../events.ts\";\nimport { resetOnce } from \"../reset-once.ts\";\nimport type { Cache } from \"./middleware.ts\";\n\n/** The subscriptions that currently see a connected stream, per cache and resource. */\nconst connected = new WeakMap<Cache, Map<string, Set<object>>>();\n\n/**\n * Subscribe `cache` to `hub` for the manifest resource `resource`. Events\n * clear the spec's prefixes, a reconnect clears the resource, and fresh hits\n * are allowed only while the stream is connected. The cache is cleared once\n * per connection, whenever it subscribes: a subscription that joins an already\n * connected hub (or during the handshake) clears the resource first, unless\n * the cache was already cleared on that connection, and only then opens the\n * gate. Returns the unsubscribe (it also closes the freshness gate).\n */\nexport function invalidateOnEvents(\n cache: Cache,\n hub: EventHub,\n resource: string,\n): () => void {\n const byResource = connected.get(cache) ?? new Map<string, Set<object>>();\n connected.set(cache, byResource);\n const live = byResource.get(resource) ?? new Set<object>();\n byResource.set(resource, live);\n const self = {}; // this subscription's identity\n let latest = 0; // a status change supersedes a gate-opening that awaits its reset\n const reset = (connection: EventConnection): Promise<void> =>\n resetOnce(connection, cache, resource, () => cache.clearResource(resource));\n // The gate is open while any subscription sees a connected stream.\n const mark = (up: boolean): void => {\n if (up) {\n live.add(self);\n } else {\n live.delete(self);\n }\n cache.setEventsActive(resource, live.size > 0);\n };\n const unsubscribe = hub.subscribe({\n onEvent: (event) =>\n cache.invalidateEvent(resource, event.action, event.recordId),\n onReset: (connection) =>\n connection === undefined\n ? cache.clearResource(resource)\n : reset(connection),\n onStatus: async (up, connection) => {\n latest += 1;\n const mine = latest;\n if (up && connection !== undefined) {\n await reset(connection); // a late joiner clears before the gate opens\n if (mine !== latest) {\n return;\n }\n }\n mark(up);\n },\n });\n return () => {\n latest += 1;\n try {\n unsubscribe();\n } finally {\n mark(false); // idempotent; the gate stays open while another subscription is connected\n }\n };\n}\n"],"ignoreList":[5],"mappings":"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"}