@craftainer/crud-sdk 0.1.0 → 0.3.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
@@ -65,7 +65,18 @@ await heroes.update(1, { name: "Ada" }, { ifMatch: etag }); // 412 -> Preconditi
65
65
  order `id`, filters sorted by (field, operator), sort, skip, limit,
66
66
  `include_archived`, `include_unpublished`; everything but `A-Za-z0-9-._~`
67
67
  percent-encoded; datetimes are `Date`s sent as UTC with milliseconds
68
- trimmed; `date` fields take `YYYY-MM-DD` strings.
68
+ trimmed; `date` fields take `YYYY-MM-DD` strings. `only_archived` is
69
+ emitted right after `include_archived`, and only when `true`.
70
+ - **Archived rows.** `list` and `iterAll` take `includeArchived` and
71
+ `onlyArchived`. `onlyArchived: true` returns only rows with `archived_at`
72
+ set (the server's reserved `only_archived` list parameter; it implies
73
+ `include_archived` and combines with filters, sort and paging). It is not
74
+ sent when `false` or absent. Combined with `includeArchived: false` it
75
+ rejects with `InvalidQueryError` before any request (the server would
76
+ answer `422`). It is a list option only (not on `stats`, writes, `restore`
77
+ or `get`); a non-archivable resource ignores it. The wire query is part of
78
+ the HTTP cache key, and in the TanStack layer put it in the list query (`listOptions(ns, "heroes", api, { onlyArchived: true })`), so
79
+ archived-only and normal lists never share a cache entry.
69
80
  - **Errors.** `ProblemError` (RFC 9457) with subclasses `BadRequest`,
70
81
  `Unauthorized`, `Forbidden`, `NotFound`, `PreconditionFailed`,
71
82
  `ValidationFailed` (`errors`), `Locked`, `RateLimited` (`retryAfter`),
@@ -117,15 +128,46 @@ events }), "heroes")`; `fetch` must be authenticated. Events invalidate and
117
128
  `./tanstack` has no runtime dependency on `@tanstack/query-core`: `crudKeys`,
118
129
  `listOptions`/`recordOptions`/`statsOptions`/`predictOptions`/
119
130
  `revisionsOptions`/`manifestOptions`, `infiniteListOptions`, the
120
- `create`/`update`/`deleteMutationOptions` and `bridgeEvents(queryClient, hub,
121
- resource)`. Use `tanstackCachePolicy` so the SDK cache only revalidates and
122
- TanStack's `staleTime` owns freshness. The factories take structural slices (`list(query, { signal })`, ...), which
131
+ `create`/`update`/`deleteMutationOptions`, `invalidateResource`/
132
+ `invalidateCollections`/`invalidateRecord` (each takes `{ client, resource }`)
133
+ and `bridgeEvents(queryClient, hub, { client, resource })`, where `client` is
134
+ the id string (`client.id`) and `resource` the same string the keys use. Use
135
+ `tanstackCachePolicy` so the SDK cache only revalidates and TanStack's
136
+ `staleTime` owns freshness. The factories take structural slices (`list(query, { signal })`, ...), which
123
137
  the client's options-object methods do not match for `list`, `stats`,
124
138
  `predict` and the manifest. `tanstackApi(client, resource)` (from
125
139
  `./tanstack`) binds one `ResourceClient` to all of them; a query is the
126
140
  client's `ListOptions` (`{ filters, sort, skip, limit, ... }`) without
127
141
  `signal`, which TanStack's `signal` fills in.
128
142
 
143
+ **Query keys** are namespaced by client and caller scope, so clients or
144
+ callers sharing one `QueryClient` never see each other's data:
145
+
146
+ ```
147
+ ["crud", client, resource, scope, kind, ...rest] kind: list | record | stats | predict | revisions
148
+ ["crud", client, "_manifest", scope]
149
+ ```
150
+
151
+ `client` is `CrudClient.id` (`createClient({ id })`, default `baseUrl`; keep it
152
+ stable across reloads of a persisted cache). `scope` is `client.scope()`.
153
+ `await client.namespace()` returns the `{ client, scope }` that every factory
154
+ takes as its first argument (`listOptions(ns, resource, api, query)`,
155
+ `createMutationOptions(queryClient, ns, resource, api)`). `resource` is the
156
+ manifest name, or `name@apiVersion` for a pinned version (a different
157
+ string is a different resource: pass the same one to the invalidate helpers
158
+ and `bridgeEvents`; `_manifest` is reserved). The namespace is a snapshot: a
159
+ token change (login, logout, refresh to another identity) changes the scope,
160
+ so resolve it again then, for example in a provider keyed on the token.
161
+ Clients that share a `baseUrl` but differ in anything else need distinct ids.
162
+
163
+ Because `resource` precedes `scope`, "everything of resource X on client C" is
164
+ one prefix (`crudKeys.resource(c, x)`, or `invalidateResource(queryClient, c,
165
+ x)`), and `crudKeys.client(c)` clears a whole client. Narrowing by scope, kind
166
+ or record id uses `crudKeys.filter({ client, resource?, scope?, kinds?, id? })`,
167
+ which returns `{ queryKey }` or `{ predicate }` to spread into
168
+ `invalidateQueries`/`removeQueries`. Mutations and `bridgeEvents` invalidate
169
+ every scope of the client's resource, since a write changes what every caller sees.
170
+
129
171
  ```ts
130
172
  import { createClient } from "@craftainer/crud-sdk";
131
173
  import {
@@ -144,7 +186,7 @@ const client = createClient({
144
186
  middleware: [cache.middleware, cache.autoIfMatch],
145
187
  });
146
188
  const api = tanstackApi(client, await client.resource("heroes"));
147
- useQuery(listOptions("heroes", api, { limit: 50 }));
189
+ useQuery(listOptions(await client.namespace(), "heroes", api, { limit: 50 }));
148
190
  ```
149
191
 
150
192
  Cache keys carry no server origin, so a store shared between servers needs a
@@ -879,13 +879,14 @@ var IndexedDbStore = class {
879
879
  }
880
880
  /** Run `work` on the database; a failure drops the handle (so a later call retries) and yields `fallback`. */
881
881
  async run(fallback, work) {
882
+ let used;
882
883
  try {
883
- return await work(await this.open());
884
+ used = this.open();
885
+ return await work(await used);
884
886
  } catch {
885
- const dropped = this.opening;
886
- this.opening = void 0;
887
+ if (this.opening === used) this.opening = void 0;
887
888
  try {
888
- (await dropped)?.close();
889
+ (await used)?.close();
889
890
  } catch {}
890
891
  return fallback;
891
892
  }
@@ -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, generation(resource) + 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 const writeOps = (\n state: State,\n response: Response,\n body: string | undefined,\n ): StoreOp[] => {\n const { op, recordId } = state.ctx;\n const resource = state.ctx.resource as string;\n const { status } = response;\n if (status === 412) {\n return recordId === undefined\n ? []\n : clears([recordPrefix(resource, recordId)]);\n }\n if (status < 200 || status >= 300) {\n return [];\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\"\n ? writeThrough(state, response, body, resource, recordId)\n : []),\n ];\n }\n if (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 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 return result;\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 // Detached from the caller's signal: this outlives their request.\n const request = new Request(state.ctx.request.url, {\n method: \"GET\",\n headers,\n signal,\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 return finish(state, await next(ctx));\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).\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};\n\nconst STORE = \"entries\";\n\ninterface Stored {\n readonly expiresAt: number;\n readonly entry: CacheEntry;\n}\n\nexport interface IndexedDbStoreOptions {\n /** Database name; default `craftainer-crud-cache`. */\n readonly name?: string;\n readonly defaultTtlMs?: 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 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 this.now = options.now ?? Date.now;\n }\n\n private open(): Promise<IDBPDatabase> {\n this.opening ??= openDB(this.name, 1, {\n upgrade(db) {\n db.createObjectStore(STORE);\n },\n });\n return this.opening;\n }\n\n /** Run `work` on the database; a failure drops the handle (so a later call retries) and yields `fallback`. */\n private async run<T>(\n fallback: T,\n work: (db: IDBPDatabase) => Promise<T>,\n ): Promise<T> {\n try {\n return await work(await this.open());\n } catch {\n // Close the handle we drop: an orphaned connection can block an upgrade.\n const dropped = this.opening;\n this.opening = undefined;\n try {\n (await dropped)?.close();\n } catch {\n // It never opened.\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 await db.put(STORE, stored, key);\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 { EventHub } from \"../events.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. Returns the unsubscribe\n * (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 // 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: () => cache.clearResource(resource),\n onStatus: mark,\n });\n return () => {\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":";;;AAMA,MAAM,2BAAW,IAAI,QAA+B;;;;;;;AAQpD,SAAgB,YAAY,UAA6C;CACvE,OAAO,SAAS,IAAI,QAAQ;AAC9B;;AAGA,SAAgB,UAAU,UAAoB,QAA+B;CAC3E,SAAS,IAAI,UAAU,MAAM;CAC7B,OAAO;AACT;AAGA,MAAM,mCAAwC,IAAI,IAAI;CACpD;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,0BAA+B,IAAI,IAAI;CAC3C;CACA;CACA;AACF,CAAC;;AAGD,SAAgB,aAAa,SAAuC;CAClE,MAAM,SAAS,QAAQ,IAAI,eAAe,KAAK,GAAA,CAC5C,MAAM,GAAG,CAAC,CACV,KAAK,SAAU,KAAK,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,CAAY,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CACrE,QAAQ,SAAS,SAAS,EAAE;CAC/B,OAAO,IAAI,IAAI,KAAK;AACtB;;AAGA,SAAgB,OAAO,UAA6B;CAClD,MAAM,SAAS,SAAS,QAAQ,IAAI,cAAc,KAAK,GAAA,CACpD,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,CAChB,KAAK,CAAC,CACN,YAAY;CACf,OAAO,UAAU,sBAAsB,MAAM,SAAS,OAAO;AAC/D;;AAGA,SAAgB,kBAAkB,UAA6B;CAK7D,QAJe,SAAS,QAAQ,IAAI,MAAM,KAAK,GAAA,CAC5C,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CACxC,QAAQ,SAAS,SAAS,EAClB,CAAC,CAAC,OAAO,SAAS,QAAQ,IAAI,IAAI,CAAC;AAChD;AAEA,SAAS,gBAAgB,SAAoD;CAC3E,MAAM,QAA0C,CAAC;CACjD,KAAK,MAAM,CAAC,MAAM,UAAU,SAC1B,IAAI,CAAC,iBAAiB,IAAI,IAAI,GAC5B,MAAM,KAAK,CAAC,MAAM,KAAK,CAAC;CAG5B,OAAO;AACT;;AAWA,SAAgB,WACd,UACA,MACA,OACA,UACY;CACZ,MAAM,OAAO,WAAW,SAAS,QAAQ,IAAI,MAAM,IAAI;CACvD,MAAM,OAAO;EACX,QAAQ,SAAS;EACjB,SAAS,gBAAgB,SAAS,OAAO;EACzC;EACA,UAAU,MAAM;EAChB,YAAY,MAAM,MAAM,MAAM;EAC9B,YAAY,MAAM,MAAM,MAAM,QAAQ,MAAM;CAC9C;CACA,OAAO,SAAS,OAAO,OAAO;EAAE,GAAG;EAAM;CAAK;AAChD;;AAGA,SAAgB,aACd,QACA,QACuB;CACvB,MAAM,QAAQ,gBAAgB,MAAM;CACpC,MAAM,WAAW,IAAI,IAAI,MAAM,KAAK,CAAC,UAAU,IAAI,CAAC;CACpD,OAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,UAAU,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,GAAG,KAAK;AACrE;;AAGA,SAAgB,WAAW,OAAmB,QAA+B;CAC3E,MAAM,UAAU,IAAI,QAAQ;CAC5B,KAAK,MAAM,CAAC,MAAM,UAAU,MAAM,SAChC,QAAQ,OAAO,MAAM,KAAK;CAE5B,OAAO,UACL,IAAI,SAAS,MAAM,MAAM;EAAE,QAAQ,MAAM;EAAQ;CAAQ,CAAC,GAC1D,MACF;AACF;;;AC1GA,MAAM,wBAA6B,IAAI,IAAe;CACpD;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,8BAAmC,IAAI,IAAI,CAAC,SAAS,SAAS,CAAC;AACrE,MAAM,mBAAmB;CAAC;CAAQ;CAAS;AAAS;AACpD,MAAM,mBAAmB,CAAC,UAAU,WAAW;;AAG/C,SAAgB,YAAY,IAAyB;CACnD,OAAO,MAAM,IAAI,EAAE;AACrB;;AAGA,SAAgB,YAAY,MAAuB;CACjD,OAAO,YAAY,IAAI,IAAI;AAC7B;;;;;;AAOA,SAAgB,eAAe,OAAuB;CAEpD,MAAM,SADO,MAAM,WAAW,GAAG,IAAI,MAAM,MAAM,CAAC,IAAI,MAAA,CAEnD,MAAM,GAAG,CAAC,CACV,QAAQ,SAAS,SAAS,MAAM,KAAK,MAAM,KAAK,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAC/D,KAAK,SAAS;EACb,MAAM,KAAK,KAAK,QAAQ,GAAG;EAC3B,OAAO,KAAK,IACP,CAAC,MAAM,EAAE,IACT,CAAC,KAAK,MAAM,GAAG,EAAE,GAAG,KAAK,MAAM,EAAE,CAAC;CACzC,CAAC;CACH,MAAM,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,QACzB,OAAO,KAAM,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,IAAK,KAAK,KAAK,KAAK,CAChE;CACA,OAAO,MAAM,KAAK,CAAC,MAAM,UAAU,OAAO,IAAI,CAAC,CAAC,KAAK,GAAG;AAC1D;;AAaA,SAAgB,SAAS,OAAyB;CAChD,MAAM,WACH,MAAM,SAAS,YAAY,MAAM,SAAS,gBAC3C,MAAM,aAAa,KAAA,IACf,OAAO,MAAM,QAAQ,IACrB;CACN,MAAM,UAAU,MAAM,cAAc;CACpC,OAAO,MAAM,MAAM,SAAS,GAAG,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,MAAM,GAAG,eAAe,MAAM,SAAS,EAAE;AACpH;;AAGA,SAAgB,YAAY,OAAuB;CACjD,OAAO,gBAAgB;AACzB;;AAGA,SAAgB,WAAW,UAAkB,MAAyB;CACpE,OAAO,MAAM,SAAS,GAAG,KAAK;AAChC;;AAGA,SAAgB,aAAa,UAAkB,UAA0B;CACvE,OAAO,MAAM,SAAS,UAAU,SAAS;AAC3C;;AAGA,SAAgB,gBAAgB,UAAkB,UAA0B;CAC1E,OAAO,MAAM,SAAS,aAAa,SAAS;AAC9C;;AAGA,SAAgB,mBAAmB,UAA4B;CAC7D,OAAO,iBAAiB,KAAK,SAAS,WAAW,UAAU,IAAI,CAAC;AAClE;;AAGA,SAAgB,iBAAiB,UAA4B;CAC3D,OAAO,CAAC,GAAG,kBAAkB,GAAG,gBAAgB,CAAC,CAAC,KAAK,SACrD,WAAW,UAAU,IAAI,CAC3B;AACF;;;;;;AAOA,SAAgB,cACd,UACA,QACA,UACU;CACV,IAAI,WAAW,UACb,OAAO,mBAAmB,QAAQ;CAEpC,KACG,WAAW,YAAY,WAAW,YAAY,WAAW,cAC1D,aAAa,KAAA,GAEb,OAAO;EACL,GAAG,mBAAmB,QAAQ;EAC9B,aAAa,UAAU,QAAQ;EAC/B,gBAAgB,UAAU,QAAQ;CACpC;CAEF,OAAO,iBAAiB,QAAQ;AAClC;;;;;;;;;;;AC/HA,eAAsB,gBAAgB,SAAmC;CACvE,MAAM,QAAQ,QAAQ,IAAI,eAAe;CACzC,IAAI,UAAU,MACZ,OAAO;CAET,MAAM,aAAa,mBAAmB,KAAK,KAAK,CAAC,GAAG,MAAM;CAC1D,OAAO,WAAW,UAAU;AAC9B;;;ACuHA,MAAM,mBAAmB;AACzB,MAAM,kBAA+B,wBAAwB,CAAC,CAAC;AAC/D,MAAM,gCAAqC,IAAI,IAAI;CACjD;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,2BAAgC,IAAI,IAAI,CAAC,eAAe,aAAa,CAAC;AAC5E,MAAM,gCAAqC,IAAI,IAAI;CACjD;CACA;CACA;AACF,CAAC;AACD,MAAM,0BAA+B,IAAI,IAAI;CAAC;CAAU;CAAS;AAAO,CAAC;AAEzE,SAAS,WAAW,UAA6B;CAC/C,OACE,OAAO,QAAQ,KACf,CAAC,aAAa,SAAS,OAAO,CAAC,CAAC,IAAI,UAAU,KAC9C,kBAAkB,QAAQ;AAE9B;AAEA,SAAS,YAAY,SAAiB,SAAyB;CAC7D,OAAO,KAAK,IAAI,UAAU,SAAS,gBAAgB;AACrD;;AAGA,SAAS,YAAY,MAAoC;CACvD,IAAI;EACF,MAAM,MAAO,KAAK,MAAM,IAAI,CAAC,CAAuB;EACpD,OAAO,MAAM,QAAQ,GAAG,KAAK,IAAI,OAAO,OAAO,OAAO,UAAU,EAAE,CAAC,IAC9D,MACD,KAAA;CACN,QAAQ;EACN;CACF;AACF;;AAGA,SAAS,kBAAkB,MAAwB;CACjD,IAAI;EACF,MAAM,YAAa,KAAK,MAAM,IAAI,CAAC,CAA6B;EAChE,OAAO,MAAM,QAAQ,SAAS,IAC1B,UACG,KAAK,SAA6B,KAAK,IAAI,CAAC,CAC5C,QAAQ,SAAyB,OAAO,SAAS,QAAQ,IAC5D,CAAC;CACP,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,eAAe,KAAQ,MAAoD;CACzE,IAAI;EACF,OAAO,MAAM,KAAK;CACpB,QAAQ;EACN;CACF;AACF;AAEA,SAAgB,YAAY,SAA8B;CACxD,MAAM,EAAE,UAAU;CAClB,MAAM,WAAW,IAAI,IAAI,OAAO,QAAQ,QAAQ,QAAQ,CAAC;CACzD,MAAM,aACJ,UACA,eAEA,aAAa,KAAA,IACT,KAAA,IACC,SAAS,IAAI,GAAG,SAAS,GAAG,YAAY,KAAK,SAAS,IAAI,QAAQ;CACzE,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,+BAAe,IAAI,IAAY;CACrC,MAAM,8BAAc,IAAI,IAAoB;CAC5C,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,wBAAQ,IAAI,IAAmB;CACrC,MAAM,aAAa,IAAI,gBAAgB;CAEvC,IAAI,mBAAmB;CACvB,MAAM,cAAc,cACjB,YAAY,IAAI,YAAY,EAAE,KAAK,KAAK;CAC3C,MAAM,QAAQ,aAA2B;EACvC,YAAY,IAAI,UAAU,WAAW,QAAQ,IAAI,CAAC;CACpD;;;;;;CASA,MAAM,YACJ,QACA,UACA,SAEA,CAAC,OAAO,kBACR,SAAS,cACT,SAAS,cACR,YAAY,IAAI,KAAK,aAAa,IAAI,QAAkB;;CAG3D,MAAM,aACJ,QACA,UACA,SAEA,SAAS,QAAQ,UAAU,IAAI,KAAK,aAAa,IAAI,QAAkB;;CAGzE,MAAM,cACJ,OACA,SACqC;EACrC,MAAM,WAAW,MAAM,IAAI;EAC3B,IACE,WAAW,QAAQ,MAAM,MAAM,cAC/B,CAAC,SAAS,MAAM,QAAQ,UAAU,IAAI,GAEtC,OAAO;GAAE,OAAO;GAAG,OAAO;EAAE;EAK9B,OAAO;GACL,QAJY,YAAY,IAAI,IAC1B,MAAM,OAAO,WACb,MAAM,OAAO,UAEA;GACf,OAAO,UAAU,MAAM,QAAQ,UAAU,IAAI,IACzC,MAAM,OAAO,uBAAuB,MACpC;EACN;CACF;CAEA,MAAM,UAAU,OAAc,UAA4C;EACxE,IAAI,UAAU,KAAA,GACZ,OAAO;EAET,IACE,CAAC,MAAM,mBACP,SAAS,MAAM,QAAQ,MAAM,IAAI,UAAU,MAAM,IAAY,GAC7D;GACA,MAAM,KAAK,IAAI;GACf,IAAI,KAAK,MAAM,YACb,OAAO;GAET,IACE,KAAK,MAAM,cACX,UAAU,MAAM,QAAQ,MAAM,IAAI,UAAU,MAAM,IAAY,GAE9D,OAAO;EAEX;EACA,OAAO,MAAM,SAAS,KAAA,KAAa,MAAM,OAAO,OAC5C,eACA;CACN;CAIA,MAAM,QAAQ,OAAO,QAAoD;EACvE,MAAM,EAAE,SAAS,aAAa;EAC9B,MAAM,QAAQ,MAAM,gBAAgB,QAAQ,OAAO;EACnD,MAAM,gBAAgB,WAAW,QAAQ;EACzC,IAAI,QAAQ,WAAW,OAAO;GAC5B,IAAI,CAAC,cAAc,IAAI,QAAQ,MAAM,GACnC;GAEF,MAAM,SAAS,UAAU,UAAU,IAAI,UAAU;GACjD,OAAO,QAAQ,YAAY,OACvB;IACE;IACA;IACA,MAAM,KAAA;IACN,KAAK,KAAA;IACL,YAAY;IACZ;IACA,iBAAiB;GACnB,IACA,KAAA;EACN;EACA,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,IAAI,OAAO,YAAY;GACzB,IAAI,CAAC,eACH;GAEF,SAAS;GACT,OAAO;GACP,MAAM,YAAY,KAAK;EACzB,OAAO;GACL,SAAS,UAAU,UAAU,IAAI,UAAU;GAC3C,IACE,aAAa,KAAA,KACb,QAAQ,YAAY,QACpB,CAAC,YAAY,IAAI,EAAE,GAEnB;GAEF,OAAO,IAAI;GACX,MAAM,SAAS;IACb;IACA;IACA,UAAU,IAAI;IACd;IACA,OAAO,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC;IAC5B,YAAY,IAAI;GAClB,CAAC;EACH;EACA,MAAM,UAAU,aAAa,QAAQ,OAAO;EAC5C,IAAI,QAAQ,IAAI,UAAU,KAAK,QAAQ,QAAQ,IAAI,eAAe,GAChE;EAEF,OAAO;GACL;GACA;GACA;GACA;GACA,YAAY;GACZ;GACA,iBAAiB,QAAQ,IAAI,UAAU;EACzC;CACF;;CAGA,MAAM,iBAAiB,KAAqB,SAAiC;EAC3E,MAAM,UAAU,IAAI,QAAQ,IAAI,QAAQ,OAAO;EAC/C,QAAQ,IAAI,iBAAiB,IAAI;EACjC,OAAO;GAAE,GAAG;GAAK,SAAS,IAAI,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC;EAAE;CAClE;CAIA,MAAM,WAAW,OACf,OACA,aACgC;EAChC,MAAM,EAAE,WAAW;EACnB,MAAM,EAAE,IAAI,aAAa,MAAM;EAC/B,IAAI;EACJ,IAAI,MAAM,SAAS,KAAA,GACjB,SAAS,WAAW,OAAO,WAAW,QAAQ;OACzC,IAAI,OAAO,UAChB,SAAS,WAAW,OAAO,aAAa,KAAA,KAAa,WAAW,QAAQ;OAExE,SACE,UAAU,OACV,SAAS,QACR,SAAS,IAAI,EAAE,KAAM,OAAO,aAAa,aAAa,KAAA;EAE3D,OAAO,SAAS,SAAS,MAAM,CAAC,CAAC,KAAK,IAAI,KAAA;CAC5C;;CAGA,MAAM,UAAU,aAA2C,CACzD;EAAE,MAAM;EAAS;CAAS,CAC5B;CAEA,MAAM,eACJ,KACA,UACc;EACd,IACE,QAAQ,KAAA,KACP,IAAI,SAAS,KAAA,KAAa,IAAI,SAAS,MAAM,MAE9C,OAAO,CAAC;EAEV,MAAM,wBAAQ,IAAI,IAAI;GACpB,GAAG,CAAC,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,QAAQ,WAAW,EAAE,CAAC;GAC/D,GAAG,kBAAkB,IAAI,IAAI;GAC7B,GAAG,kBAAkB,MAAM,IAAI;EACjC,CAAC;EACD,OAAO,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,SAAS,WAAW,MAAM,SAAS,CAAC,CAAC;CAC5E;CAEA,MAAM,YACJ,OACA,UACA,SACyC;EACzC,MAAM,MAAM,MAAM;EAClB,MAAM,OAAO,MAAM;EACnB,MAAM,EAAE,WAAW;EACnB,MAAM,KAAK,IAAI;EACf,MAAM,EAAE,OAAO,UAAU,WAAW,OAAO,IAAI;EAC/C,MAAM,MAAM,MAAM;EAClB,IAAI,WAAW,OAAO,MAAM,cAAc,QAAQ,QAAQ,KAAA,GAAW;GAEnE,MAAM,UAAsB;IAC1B,GAAG;IACH,SAAS,aAAa,IAAI,SAAS,SAAS,OAAO;IACnD,UAAU;IACV,YAAY,KAAK;IACjB,YAAY,KAAK,QAAQ;IACzB,MAAM,SAAS,QAAQ,IAAI,MAAM,KAAM,IAAI;GAC7C;GACA,OAAO;IACL,QAAQ,WAAW,SAAS,aAAa;IACzC,KAAK,CACH;KACE,MAAM;KACN;KACA,OAAO;KACP,OAAO,YAAY,OAAO,KAAK;IACjC,CACF;GACF;EACF;EACA,IAAI,WAAW,OAAO,SAAS,KAAA,GAAW;GACxC,UAAU,UAAU,MAAM;GAC1B,MAAM,QAAQ,WACZ,UACA,MACA;IAAE,KAAK;IAAI;IAAO;GAAM,GACxB,MAAM,OAAO,IACf;GACA,IAAI,MAAM,SAAS,KAAA,KAAa,SAAS,GAEvC,OAAO;IAAE,QAAQ;IAAU,KAAK,CAAC;KAAE,MAAM;KAAU;IAAI,CAAC;GAAE;GAE5D,MAAM,MAAiB,CACrB;IAAE,MAAM;IAAO;IAAK;IAAO,OAAO,YAAY,OAAO,KAAK;GAAE,CAC9D;GACA,IAAI,SAAS,YACX,IAAI,KAAK,GAAG,YAAY,KAAK,KAAK,CAAC;GAErC,OAAO;IAAE,QAAQ;IAAU;GAAI;EACjC;EACA,IAAI,WAAW,OAAO,WAAW,OAAO,WAAW,KACjD,OAAO;GAAE,QAAQ;GAAU,KAAK,CAAC;IAAE,MAAM;IAAU;GAAI,CAAC;EAAE;EAE5D,OAAO;GAAE,QAAQ;GAAU,KAAK,CAAC;EAAE;CACrC;;CAGA,MAAM,gBACJ,OACA,UACA,MACA,UACA,aACc;EACd,IAAI,SAAS,KAAA,GACX,OAAO,CAAC;EAEV,MAAM,EAAE,OAAO,UAAU,WAAW,OAAO,QAAQ;EACnD,MAAM,QAAQ,WACZ,UACA,MACA;GAAE,KAAK,IAAI;GAAG;GAAO;EAAM,GAC3B,MAAM,OAAO,IACf;EACA,IAAI,MAAM,SAAS,KAAA,KAAa,SAAS,GACvC,OAAO,CAAC;EASV,OAAO,CAAC;GAAE,MAAM;GAAO,KAPX,SAAS;IACnB;IACA,MAAM;IACN;IACA,OAAO,MAAM;IACb,YAAY,MAAM,IAAI;GACxB,CACyB;GAAG;GAAO,OAAO,YAAY,OAAO,KAAK;EAAE,CAAC;CACvE;CAEA,MAAM,YACJ,OACA,UACA,SACc;EACd,MAAM,EAAE,IAAI,aAAa,MAAM;EAC/B,MAAM,WAAW,MAAM,IAAI;EAC3B,MAAM,EAAE,WAAW;EACnB,IAAI,WAAW,KACb,OAAO,aAAa,KAAA,IAChB,CAAC,IACD,OAAO,CAAC,aAAa,UAAU,QAAQ,CAAC,CAAC;EAE/C,IAAI,SAAS,OAAO,UAAU,KAC5B,OAAO,CAAC;EAEV,MAAM,cAAc,mBAAmB,QAAQ;EAC/C,IAAI,QAAQ,IAAI,EAAE,GAChB,OAAO,OAAO,WAAW;EAE3B,IAAI,aAAa,KAAA,MAAc,cAAc,IAAI,EAAE,KAAK,OAAO,WAC7D,OAAO,CACL,GAAG,OAAO;GACR,GAAG;GACH,gBAAgB,UAAU,QAAQ;GAClC,aAAa,UAAU,QAAQ;EACjC,CAAC,GACD,GAAI,OAAO,WACP,aAAa,OAAO,UAAU,MAAM,UAAU,QAAQ,IACtD,CAAC,CACP;EAEF,IAAI,SAAS,IAAI,EAAE,KAAK,OAAO,WAAW;GAExC,MAAM,MAAM,YAAY,IAAc;GACtC,IAAI,QAAQ,KAAA,GACV,OAAO,OAAO,CACZ,GAAG,aACH,GAAG,IAAI,SAAS,OAAO,CACrB,aAAa,UAAU,EAAE,GACzB,gBAAgB,UAAU,EAAE,CAC9B,CAAC,CACH,CAAC;EAEL;EACA,OAAO,OAAO,iBAAiB,QAAQ,CAAC;CAC1C;;CAGA,MAAM,gBAAgB,OAAO,aAA+C;EAC1E,IAAI,MAAM,cAAc,KAAA,GAAW;GACjC,MAAM,WAAW,MAAM,YAAY,QAAQ,CAAC;GAC5C;EACF;EACA,KAAK,MAAM,UAAU,UACnB,MAAM,WAAW,MAAM,MAAM,MAAM,CAAC;CAExC;CAEA,MAAM,QAAQ,OAAO,OAA+B;EAClD,MAAM,WACJ,GAAG,SAAS,UACR,cAAc,GAAG,QAAQ,IACzB,GAAG,SAAS,QACV,MAAM,IAAI,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,IACpC,MAAM,OAAO,GAAG,GAAG,CAC3B;CACF;;CAGA,MAAM,SAAS,OACb,OACA,aACsB;EACtB,MAAM,OAAO,MAAM,SAAS,OAAO,QAAQ;EAC3C,MAAM,EAAE,QAAQ,QACd,MAAM,SAAS,KAAA,IACX;GAAE,QAAQ;GAAU,KAAK,SAAS,OAAO,UAAU,IAAI;EAAE,IACzD,SAAS,OAAO,UAAU,IAAI;EACpC,IAAI,MAAM,SAAS,KAAA,KAAa,IAAI,MAAM,OAAO,GAAG,SAAS,OAAO,GAClE,KAAK,MAAM,IAAI,QAAkB;EAEnC,MAAM,WAAW,WAAW,MAAM,IAAI,QAAQ;EAC9C,KAAK,MAAM,MAAM,KACf,MAAM,MAAM,EAAE;EAEhB,IAAI,WAAW,MAAM,IAAI,QAAQ,MAAM,UAErC,KAAK,MAAM,MAAM,IAAI,QAAQ,cAAc,UAAU,SAAS,KAAK,GACjE,MAAM,MAAM;GAAE,MAAM;GAAU,KAAK,GAAG;EAAI,CAAC;EAG/C,OAAO;CACT;CAIA,MAAM,YAAY,OAAc,SAAqB;EACnD,MAAM,MAAM,MAAM;EAClB,IAAI,WAAW,OAAO,WAAW,SAAS,IAAI,GAAG,GAC/C;EAEF,SAAS,IAAI,GAAG;EAChB,MAAM,QAAQ,MAAM;EACpB,MAAM,QAAQ,YAA2B;GACvC,IAAI;IACF,MAAM,SAAS,WAAW;IAC1B,MAAM,UAAU,IAAI,QAAQ,MAAM,IAAI,QAAQ,OAAO;IACrD,IAAI,MAAM,SAAS,KAAA,GACjB,QAAQ,IAAI,iBAAiB,MAAM,IAAI;IAGzC,MAAM,UAAU,IAAI,QAAQ,MAAM,IAAI,QAAQ,KAAK;KACjD,QAAQ;KACR;KACA;IACF,CAAC;IACD,MAAM,QAAe;KACnB,GAAG;KACH,WAAW,MAAM,SAAS,KAAA;IAC5B;IACA,MAAM,WAAW,MAAM,KAAK;KAAE,GAAG,MAAM;KAAK;KAAS;IAAO,CAAC;IAE7D,OAAM,MADe,OAAO,OAAO,QAAQ,EAAA,CAC9B,MAAM,OAAO;GAC5B,QAAQ,CAER,UAAU;IACR,SAAS,OAAO,GAAG;GACrB;EACF,EAAA,CAAG;EACH,MAAM,IAAI,IAAI;EACd,KAAU,WAAW,MAAM,OAAO,IAAI,CAAC;CACzC;CAIA,MAAM,aAAyB,OAAO,KAAK,SAAS;EAClD,MAAM,QAAQ,MAAM,MAAM,GAAG;EAC7B,IAAI,UAAU,KAAA,GACZ,OAAO,KAAK,GAAG;EAEjB,IAAI,MAAM,SAAS,KAAA,GACjB,OAAO,OAAO,OAAO,MAAM,KAAK,GAAG,CAAC;EAEtC,MAAM,QAAQ,MAAM,WAAW,MAAM,IAAI,MAAM,GAAa,CAAC;EAC7D,MAAM,WAAW,OAAO,OAAO,MAAM,KAAK;EAC1C,MAAM,QAAQ,MAAM;EACpB,IAAI,aAAa,OACf,OAAO,WAAW,OAAO,KAAK;EAEhC,IAAI,aAAa,SAAS;GACxB,SAAS,OAAO,IAAI;GACpB,OAAO,WAAW,OAAO,OAAO;EAClC;EACA,IAAI,OAAO;EACX,IAAI,aAAa,cAAc;GAC7B,OAAO,cAAc,KAAK,MAAM,IAAc;GAC9C,MAAM,YAAY;EACpB;EACA,OAAO,OAAO,OAAO,MAAM,KAAK,IAAI,CAAC;CACvC;CAIA,MAAM,cAA0B,OAAO,KAAK,SAAS;EACnD,MAAM,EAAE,SAAS,UAAU,aAAa;EACxC,KACG,IAAI,OAAO,YAAY,IAAI,OAAO,aACnC,aAAa,KAAA,KACb,aAAa,KAAA,KACb,UAAU,UAAU,IAAI,UAAU,CAAC,EAAE,YAAY,QACjD,CAAC,QAAQ,QAAQ,IAAI,UAAU,GAC/B;GACA,MAAM,OAAO,MAAM,WACjB,UACA,UACA,MAAM,gBAAgB,QAAQ,OAAO,GACrC,IAAI,UACN;GACA,IAAI,SAAS,KAAA,GAAW;IACtB,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;IAC3C,QAAQ,IAAI,YAAY,IAAI;IAC5B,OAAO,KAAK;KAAE,GAAG;KAAK,SAAS,IAAI,QAAQ,SAAS,EAAE,QAAQ,CAAC;IAAE,CAAC;GACpE;EACF;EACA,OAAO,KAAK,GAAG;CACjB;CAEA,MAAM,aAAa,OACjB,UACA,UACA,OACA,eACgC;EAMhC,QAAO,MALa,WAClB,MAAM,IACJ,SAAS;GAAE;GAAU,MAAM;GAAU;GAAU;GAAO;EAAW,CAAC,CACpE,CACF,EAAA,EACc;CAChB;CAEA,OAAO;EACL;EACA;EACA,gBAAgB,UAAU,QAAQ;GAChC,IAAI,QACF,aAAa,IAAI,QAAQ;QAEzB,aAAa,OAAO,QAAQ;EAEhC;EACA,eAAe,aAAa,aAAa,IAAI,QAAQ;EACrD,MAAM,gBAAgB,UAAU,QAAQ,UAAU;GAChD,KAAK,QAAQ;GACb,MAAM,cAAc,cAAc,UAAU,QAAQ,QAAQ,CAAC;EAC/D;EACA,MAAM,cAAc,UAAU;GAC5B,KAAK,QAAQ;GACb,MAAM,cAAc,iBAAiB,QAAQ,CAAC;EAChD;EACA,MAAM,MAAM,QAAQ;GAClB,oBAAoB;GACpB,MAAM,WAAW,MAAM,MAAM,MAAM,CAAC;EACtC;EACA,YAAY;EACZ,MAAM,QAAQ;GACZ,WAAW,MAAM;GACjB,MAAM,QAAQ,WAAW,KAAK;EAChC;CACF;AACF;;AChtBA,IAAa,cAAb,MAA+C;CAC7C,uBAAwB,IAAI,IAG1B;CACF;CACA;CACA;CAEA,YAAY,UAA8B,CAAC,GAAG;EAC5C,MAAM,aAAa,QAAQ,cAAc;EACzC,IAAI,CAAC,OAAO,UAAU,UAAU,KAAK,aAAa,GAChD,MAAM,IAAI,WAAW,6CAA6C;EAEpE,KAAK,aAAa;EAClB,KAAK,eAAe,QAAQ,gBAAA;EAC5B,KAAK,MAAM,QAAQ,OAAO,KAAK;CACjC;;CAGA,IAAI,OAAe;EACjB,OAAO,KAAK,KAAK;CACnB;CAEA,IAAI,KAAqC;EACvC,MAAM,OAAO,KAAK,KAAK,IAAI,GAAG;EAC9B,IAAI,SAAS,KAAA,GACX;EAEF,IAAI,KAAK,aAAa,KAAK,IAAI,GAAG;GAChC,KAAK,KAAK,OAAO,GAAG;GACpB;EACF;EACA,KAAK,KAAK,OAAO,GAAG;EACpB,KAAK,KAAK,IAAI,KAAK,IAAI;EACvB,OAAO,KAAK;CACd;CAEA,IAAI,KAAa,OAAmB,OAAsB;EACxD,KAAK,KAAK,OAAO,GAAG;EACpB,KAAK,KAAK,IAAI,KAAK;GACjB,WAAW,KAAK,IAAI,KAAK,SAAS,KAAK;GACvC;EACF,CAAC;EACD,OAAO,KAAK,KAAK,OAAO,KAAK,YAE3B,KAAK,KAAK,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAe;CAE5D;CAEA,OAAO,KAAmB;EACxB,KAAK,KAAK,OAAO,GAAG;CACtB;;CAGA,UAAU,UAAmC;EAC3C,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,GAC/B,IAAI,SAAS,MAAM,WAAW,IAAI,WAAW,MAAM,CAAC,GAClD,KAAK,KAAK,OAAO,GAAG;CAG1B;CAEA,MAAM,QAAuB;EAC3B,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,GAC/B,IAAI,WAAW,KAAA,KAAa,IAAI,WAAW,MAAM,GAC/C,KAAK,KAAK,OAAO,GAAG;CAG1B;AACF;;;ACrFA,MAAM,iBAAiB,QAAQ,iBAAiB,aAAa,MAAM,MAAM,kBAAkB,CAAC;AAE5F,IAAI;AACJ,IAAI;AAEJ,SAAS,uBAAuB;CAC5B,OAAQ,sBACH,oBAAoB;EACjB;EACA;EACA;EACA;EACA;CACJ;AACR;AAEA,SAAS,0BAA0B;CAC/B,OAAQ,yBACH,uBAAuB;EAAC;EAAW;EAAY;CAAoB,CAAC,CAAC,KAAK,WAAW,UAAU,UAAU,OAAO;AACzH;AACA,MAAM,qCAAqB,IAAI,QAAQ;AACvC,MAAM,iCAAiB,IAAI,QAAQ;AACnC,MAAM,wCAAwB,IAAI,QAAQ;AAC1C,SAAS,iBAAiB,SAAS;CAC/B,MAAM,UAAU,IAAI,SAAS,SAAS,WAAW;EAC7C,MAAM,iBAAiB;GACnB,QAAQ,oBAAoB,WAAW,OAAO;GAC9C,QAAQ,oBAAoB,SAAS,KAAK;EAC9C;EACA,MAAM,gBAAgB;GAClB,QAAQ,KAAK,QAAQ,MAAM,CAAC;GAC5B,SAAS;EACb;EACA,MAAM,cAAc;GAChB,OAAO,QAAQ,KAAK;GACpB,SAAS;EACb;EACA,QAAQ,iBAAiB,WAAW,OAAO;EAC3C,QAAQ,iBAAiB,SAAS,KAAK;CAC3C,CAAC;CAGD,sBAAsB,IAAI,SAAS,OAAO;CAC1C,OAAO;AACX;AACA,SAAS,+BAA+B,IAAI;CAExC,IAAI,mBAAmB,IAAI,EAAE,GACzB;CACJ,MAAM,OAAO,IAAI,SAAS,SAAS,WAAW;EAC1C,MAAM,iBAAiB;GACnB,GAAG,oBAAoB,YAAY,QAAQ;GAC3C,GAAG,oBAAoB,SAAS,KAAK;GACrC,GAAG,oBAAoB,SAAS,KAAK;EACzC;EACA,MAAM,iBAAiB;GACnB,QAAQ;GACR,SAAS;EACb;EACA,MAAM,cAAc;GAChB,OAAO,GAAG,SAAS,IAAI,aAAa,cAAc,YAAY,CAAC;GAC/D,SAAS;EACb;EACA,GAAG,iBAAiB,YAAY,QAAQ;EACxC,GAAG,iBAAiB,SAAS,KAAK;EAClC,GAAG,iBAAiB,SAAS,KAAK;CACtC,CAAC;CAED,mBAAmB,IAAI,IAAI,IAAI;AACnC;AACA,IAAI,gBAAgB;CAChB,IAAI,QAAQ,MAAM,UAAU;EACxB,IAAI,kBAAkB,gBAAgB;GAElC,IAAI,SAAS,QACT,OAAO,mBAAmB,IAAI,MAAM;GAExC,IAAI,SAAS,SACT,OAAO,SAAS,iBAAiB,KAC3B,KAAA,IACA,SAAS,YAAY,SAAS,iBAAiB,EAAE;EAE/D;EAEA,OAAO,KAAK,OAAO,KAAK;CAC5B;CACA,IAAI,QAAQ,MAAM,OAAO;EACrB,OAAO,QAAQ;EACf,OAAO;CACX;CACA,IAAI,QAAQ,MAAM;EACd,IAAI,kBAAkB,mBACjB,SAAS,UAAU,SAAS,UAC7B,OAAO;EAEX,OAAO,QAAQ;CACnB;AACJ;AACA,SAAS,aAAa,UAAU;CAC5B,gBAAgB,SAAS,aAAa;AAC1C;AACA,SAAS,aAAa,MAAM;CAQxB,IAAI,wBAAwB,CAAC,CAAC,SAAS,IAAI,GACvC,OAAO,SAAU,GAAG,MAAM;EAGtB,KAAK,MAAM,OAAO,IAAI,GAAG,IAAI;EAC7B,OAAO,KAAK,KAAK,OAAO;CAC5B;CAEJ,OAAO,SAAU,GAAG,MAAM;EAGtB,OAAO,KAAK,KAAK,MAAM,OAAO,IAAI,GAAG,IAAI,CAAC;CAC9C;AACJ;AACA,SAAS,uBAAuB,OAAO;CACnC,IAAI,OAAO,UAAU,YACjB,OAAO,aAAa,KAAK;CAG7B,IAAI,iBAAiB,gBACjB,+BAA+B,KAAK;CACxC,IAAI,cAAc,OAAO,qBAAqB,CAAC,GAC3C,OAAO,IAAI,MAAM,OAAO,aAAa;CAEzC,OAAO;AACX;AACA,SAAS,KAAK,OAAO;CAGjB,IAAI,iBAAiB,YACjB,OAAO,iBAAiB,KAAK;CAGjC,IAAI,eAAe,IAAI,KAAK,GACxB,OAAO,eAAe,IAAI,KAAK;CACnC,MAAM,WAAW,uBAAuB,KAAK;CAG7C,IAAI,CAAC,OAAO,GAAG,UAAU,KAAK,GAAG;EAC7B,eAAe,IAAI,OAAO,QAAQ;EAClC,sBAAsB,IAAI,UAAU,KAAK;CAC7C;CACA,OAAO;AACX;AACA,MAAM,UAAU,UAAU,sBAAsB,IAAI,KAAK;;;;;;;;AASzD,SAAS,OAAO,MAAM,SAAS,EAAE,SAAS,SAAS,UAAU,eAAe,CAAC,GAAG;CAC5E,MAAM,UAAU,UAAU,KAAK,MAAM,OAAO;CAC5C,MAAM,cAAc,KAAK,OAAO;CAChC,IAAI,SACA,QAAQ,iBAAiB,kBAAkB,UAAU;EACjD,QAAQ,KAAK,QAAQ,MAAM,GAAG,MAAM,YAAY,MAAM,YAAY,KAAK,QAAQ,WAAW,GAAG,KAAK;CACtG,CAAC;CAEL,IAAI,SACA,QAAQ,iBAAiB,YAAY,UAAU,QAE/C,MAAM,YAAY,MAAM,YAAY,KAAK,CAAC;CAE9C,YACK,MAAM,OAAO;EACd,IAAI,YACA,GAAG,iBAAiB,eAAe,WAAW,CAAC;EACnD,IAAI,UACA,GAAG,iBAAiB,kBAAkB,UAAU,SAAS,MAAM,YAAY,MAAM,YAAY,KAAK,CAAC;CAE3G,CAAC,CAAC,CACG,YAAY,CAAE,CAAC;CACpB,OAAO;AACX;AAgBA,MAAM,cAAc;CAAC;CAAO;CAAU;CAAU;CAAc;AAAO;AACrE,MAAM,eAAe;CAAC;CAAO;CAAO;CAAU;AAAO;AACrD,MAAM,gBAAgB,CAAC;AACvB,SAAS,UAAU,QAAQ,MAAM;CAC7B,IAAI,EAAE,kBAAkB,eACpB,EAAE,QAAQ,WACV,OAAO,SAAS,WAChB;CAEJ,IAAI,cAAc,OACd,OAAO,cAAc;CACzB,MAAM,iBAAiB,KAAK,QAAQ,cAAc,EAAE;CACpD,MAAM,WAAW,SAAS;CAC1B,MAAM,UAAU,aAAa,SAAS,cAAc;CACpD,IAEA,EAAE,mBAAmB,WAAW,WAAW,eAAA,CAAgB,cACvD,EAAE,WAAW,YAAY,SAAS,cAAc,IAChD;CAEJ,MAAM,SAAS,eAAgB,WAAW,GAAG,MAAM;EAE/C,MAAM,KAAK,KAAK,YAAY,WAAW,UAAU,cAAc,UAAU;EACzE,IAAI,SAAS,GAAG;EAChB,IAAI,UACA,SAAS,OAAO,MAAM,KAAK,MAAM,CAAC;EAMtC,QAAQ,MAAM,QAAQ,IAAI,CACtB,OAAO,eAAe,CAAC,GAAG,IAAI,GAC9B,WAAW,GAAG,IAClB,CAAC,EAAA,CAAG;CACR;CACA,OAAQ,cAAc,QAAQ;AAClC;AACA,cAAc,cAAc;CACxB,GAAG;CACH,MAAM,QAAQ,MAAM,aAAa,UAAU,QAAQ,IAAI,KAAK,SAAS,IAAI,QAAQ,MAAM,QAAQ;CAC/F,MAAM,QAAQ,SAAS,CAAC,CAAC,UAAU,QAAQ,IAAI,KAAK,SAAS,IAAI,QAAQ,IAAI;AACjF,EAAE;AAEF,MAAM,qBAAqB;CAAC;CAAW;CAAY;AAAoB;AACvE,MAAM,YAAY,CAAC;AACnB,MAAM,iCAAiB,IAAI,QAAQ;AACnC,MAAM,mDAAmC,IAAI,QAAQ;AACrD,MAAM,sBAAsB,EACxB,IAAI,QAAQ,MAAM;CACd,IAAI,CAAC,mBAAmB,SAAS,IAAI,GACjC,OAAO,OAAO;CAClB,IAAI,aAAa,UAAU;CAC3B,IAAI,CAAC,YACD,aAAa,UAAU,QAAQ,SAAU,GAAG,MAAM;EAC9C,eAAe,IAAI,MAAM,iCAAiC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;CACtF;CAEJ,OAAO;AACX,EACJ;AACA,gBAAgB,QAAQ,GAAG,MAAM;CAE7B,IAAI,SAAS;CACb,IAAI,EAAE,kBAAkB,YACpB,SAAS,MAAM,OAAO,WAAW,GAAG,IAAI;CAE5C,IAAI,CAAC,QACD;CACJ,SAAS;CACT,MAAM,gBAAgB,IAAI,MAAM,QAAQ,mBAAmB;CAC3D,iCAAiC,IAAI,eAAe,MAAM;CAE1D,sBAAsB,IAAI,eAAe,OAAO,MAAM,CAAC;CACvD,OAAO,QAAQ;EACX,MAAM;EAEN,SAAS,OAAO,eAAe,IAAI,aAAa,KAAK,OAAO,SAAS;EACrE,eAAe,OAAO,aAAa;CACvC;AACJ;AACA,SAAS,eAAe,QAAQ,MAAM;CAClC,OAAS,SAAS,OAAO,iBACrB,cAAc,QAAQ;EAAC;EAAU;EAAgB;CAAS,CAAC,KAC1D,SAAS,aAAa,cAAc,QAAQ,CAAC,UAAU,cAAc,CAAC;AAC/E;AACA,cAAc,cAAc;CACxB,GAAG;CACH,IAAI,QAAQ,MAAM,UAAU;EACxB,IAAI,eAAe,QAAQ,IAAI,GAC3B,OAAO;EACX,OAAO,SAAS,IAAI,QAAQ,MAAM,QAAQ;CAC9C;CACA,IAAI,QAAQ,MAAM;EACd,OAAO,eAAe,QAAQ,IAAI,KAAK,SAAS,IAAI,QAAQ,IAAI;CACpE;AACJ,EAAE;;;;;;;;;;ACxRF,MAAM,QAAQ;AAed,IAAa,iBAAb,MAAkD;CAChD;CACA;CACA;CACA;CAEA,YAAY,UAAiC,CAAC,GAAG;EAC/C,KAAK,OAAO,QAAQ,QAAQ;EAC5B,IAAI,KAAK,SAAS,IAEhB,MAAM,IAAI,WAAW,wCAAwC;EAE/D,KAAK,eAAe,QAAQ,gBAAA;EAC5B,KAAK,MAAM,QAAQ,OAAO,KAAK;CACjC;CAEA,OAAsC;EACpC,KAAK,YAAY,OAAO,KAAK,MAAM,GAAG,EACpC,QAAQ,IAAI;GACV,GAAG,kBAAkB,KAAK;EAC5B,EACF,CAAC;EACD,OAAO,KAAK;CACd;;CAGA,MAAc,IACZ,UACA,MACY;EACZ,IAAI;GACF,OAAO,MAAM,KAAK,MAAM,KAAK,KAAK,CAAC;EACrC,QAAQ;GAEN,MAAM,UAAU,KAAK;GACrB,KAAK,UAAU,KAAA;GACf,IAAI;IACF,CAAC,MAAM,QAAA,EAAU,MAAM;GACzB,QAAQ,CAER;GACA,OAAO;EACT;CACF;CAEA,IAAI,KAA8C;EAChD,OAAO,KAAK,IAA4B,KAAA,GAAW,OAAO,OAAO;GAC/D,MAAM,SAAU,MAAM,GAAG,IAAI,OAAO,GAAG;GACvC,IAAI,WAAW,KAAA,GACb;GAEF,IAAI,OAAO,aAAa,KAAK,IAAI,GAAG;IAClC,MAAM,GAAG,OAAO,OAAO,GAAG;IAC1B;GACF;GACA,OAAO,OAAO;EAChB,CAAC;CACH;CAEA,IAAI,KAAa,OAAmB,OAA+B;EACjE,OAAO,KAAK,IAAI,KAAA,GAAW,OAAO,OAAO;GACvC,MAAM,SAAiB;IACrB,WAAW,KAAK,IAAI,KAAK,SAAS,KAAK;IACvC;GACF;GACA,MAAM,GAAG,IAAI,OAAO,QAAQ,GAAG;EACjC,CAAC;CACH;CAEA,OAAO,KAA4B;EACjC,OAAO,KAAK,IAAI,KAAA,IAAY,OAAO,GAAG,OAAO,OAAO,GAAG,CAAC;CAC1D;CAEA,MAAM,QAAgC;EACpC,OAAO,KAAK,IAAI,KAAA,IAAY,OAC1B,WAAW,KAAA,IACP,GAAG,MAAM,KAAK,IACd,GAAG,OAAO,OAAO,YAAY,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,CACnE;CACF;;CAGA,UAAU,UAA4C;EACpD,OAAO,KAAK,IAAI,KAAA,GAAW,OAAO,OAAO;GACvC,MAAM,KAAK,GAAG,YAAY,OAAO,WAAW;GAC5C,MAAM,QAAQ,IAAI,CAChB,GAAG,SAAS,KAAK,WACf,GAAG,MAAM,OAAO,YAAY,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,CAC9D,GACA,GAAG,IACL,CAAC;EACH,CAAC;CACH;;CAGA,MAAM,QAAuB;EAC3B,MAAM,UAAU,KAAK;EACrB,KAAK,UAAU,KAAA;EACf,IAAI;GACF,CAAC,MAAM,QAAA,EAAU,MAAM;EACzB,QAAQ,CAER;CACF;AACF;;;;ACnIA,MAAM,4BAAY,IAAI,QAAyC;;;;;;;AAQ/D,SAAgB,mBACd,OACA,KACA,UACY;CACZ,MAAM,aAAa,UAAU,IAAI,KAAK,qBAAK,IAAI,IAAyB;CACxE,UAAU,IAAI,OAAO,UAAU;CAC/B,MAAM,OAAO,WAAW,IAAI,QAAQ,qBAAK,IAAI,IAAY;CACzD,WAAW,IAAI,UAAU,IAAI;CAC7B,MAAM,OAAO,CAAC;CAEd,MAAM,QAAQ,OAAsB;EAClC,IAAI,IACF,KAAK,IAAI,IAAI;OAEb,KAAK,OAAO,IAAI;EAElB,MAAM,gBAAgB,UAAU,KAAK,OAAO,CAAC;CAC/C;CACA,MAAM,cAAc,IAAI,UAAU;EAChC,UAAU,UACR,MAAM,gBAAgB,UAAU,MAAM,QAAQ,MAAM,QAAQ;EAC9D,eAAe,MAAM,cAAc,QAAQ;EAC3C,UAAU;CACZ,CAAC;CACD,aAAa;EACX,IAAI;GACF,YAAY;EACd,UAAU;GACR,KAAK,KAAK;EACZ;CACF;AACF"}
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, generation(resource) + 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 const writeOps = (\n state: State,\n response: Response,\n body: string | undefined,\n ): StoreOp[] => {\n const { op, recordId } = state.ctx;\n const resource = state.ctx.resource as string;\n const { status } = response;\n if (status === 412) {\n return recordId === undefined\n ? []\n : clears([recordPrefix(resource, recordId)]);\n }\n if (status < 200 || status >= 300) {\n return [];\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\"\n ? writeThrough(state, response, body, resource, recordId)\n : []),\n ];\n }\n if (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 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 return result;\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 // Detached from the caller's signal: this outlives their request.\n const request = new Request(state.ctx.request.url, {\n method: \"GET\",\n headers,\n signal,\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 return finish(state, await next(ctx));\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).\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};\n\nconst STORE = \"entries\";\n\ninterface Stored {\n readonly expiresAt: number;\n readonly entry: CacheEntry;\n}\n\nexport interface IndexedDbStoreOptions {\n /** Database name; default `craftainer-crud-cache`. */\n readonly name?: string;\n readonly defaultTtlMs?: 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 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 this.now = options.now ?? Date.now;\n }\n\n private open(): Promise<IDBPDatabase> {\n this.opening ??= openDB(this.name, 1, {\n upgrade(db) {\n db.createObjectStore(STORE);\n },\n });\n return this.opening;\n }\n\n /** Run `work` on the database; a failure drops the handle (so a later call retries) and yields `fallback`. */\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 try {\n used = this.open(); // may throw at once when indexedDB is unusable\n return await work(await used);\n } catch {\n // Drop only our own handle, and close it: an orphaned connection can\n // block an upgrade, while a newer handle belongs to other calls.\n if (this.opening === used) {\n this.opening = undefined;\n }\n try {\n (await used)?.close();\n } catch {\n // It never opened.\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 await db.put(STORE, stored, key);\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 { EventHub } from \"../events.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. Returns the unsubscribe\n * (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 // 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: () => cache.clearResource(resource),\n onStatus: mark,\n });\n return () => {\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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"}
@@ -142,6 +142,12 @@ interface QueryParams {
142
142
  readonly limitParam?: string | undefined;
143
143
  readonly sortParam?: string | undefined;
144
144
  readonly includeArchived?: boolean | undefined;
145
+ /**
146
+ * Only rows with `archived_at` set (list only; implies `include_archived`).
147
+ * Emitted only when `true`; combining `true` with `includeArchived: false`
148
+ * throws `InvalidQueryError`, as the server answers 422.
149
+ */
150
+ readonly onlyArchived?: boolean | undefined;
145
151
  readonly includeUnpublished?: boolean | undefined;
146
152
  }
147
153
  /** Percent-encodes everything but the RFC 3986 unreserved characters `A-Za-z0-9-._~`. */
@@ -157,10 +163,11 @@ declare function formatScalar(field: QueryField, value: unknown): string;
157
163
  /**
158
164
  * The query string (no leading `?`) for a list, get or bulk call. Order of
159
165
  * parameters: `id`, filters sorted by (field, operator), sort, skip, limit,
160
- * `include_archived`, `include_unpublished`. The sort/skip/limit names are
166
+ * `include_archived`, `only_archived`, `include_unpublished`. The sort/skip/limit names are
161
167
  * `sortParam`/`skipParam`/`limitParam` (the manifest's `pagination`); the
162
168
  * `id` parameter is always `id`. Flags are emitted whenever they are not
163
- * `undefined`.
169
+ * `undefined`, except `only_archived`, which is emitted only when `true`
170
+ * (absent means false; it is a reserved key, not a filter).
164
171
  */
165
172
  declare function buildQuery(fields: readonly QueryField[], params?: QueryParams): string;
166
173
  //#endregion
@@ -169,20 +176,26 @@ declare function buildQuery(fields: readonly QueryField[], params?: QueryParams)
169
176
  interface RequestOptions {
170
177
  readonly signal?: AbortSignal | undefined;
171
178
  }
172
- /** Options of `list` and `iterAll`. */
179
+ /**
180
+ * Options of `list` and `iterAll`. `onlyArchived: true` returns only archived
181
+ * rows (it implies `includeArchived`; with `includeArchived: false` the call
182
+ * throws `InvalidQueryError` before any request). Non-archivable resources
183
+ * ignore it.
184
+ */
173
185
  interface ListOptions extends RequestOptions {
174
186
  readonly filters?: Filters | undefined;
175
187
  readonly sort?: Sort | undefined;
176
188
  readonly skip?: number | undefined;
177
189
  readonly limit?: number | undefined;
178
190
  readonly includeArchived?: boolean | undefined;
191
+ readonly onlyArchived?: boolean | undefined;
179
192
  readonly includeUnpublished?: boolean | undefined;
180
193
  }
181
194
  /** Options of `update` and `delete`: `ifMatch` is an ETag (`412` when stale). */
182
195
  interface WriteOptions extends RequestOptions {
183
196
  readonly ifMatch?: string | undefined;
184
197
  }
185
- /** Options of `stats`. */
198
+ /** Options of `stats`. There is no `onlyArchived` (the server rejects it on `/stats`). */
186
199
  interface StatsOptions extends RequestOptions {
187
200
  readonly bucket?: string | undefined;
188
201
  readonly includeArchived?: boolean | undefined;
@@ -277,11 +290,19 @@ interface ClientOptions {
277
290
  readonly middleware?: readonly Middleware[] | undefined;
278
291
  /** Skips api-catalog discovery. */
279
292
  readonly manifestUrl?: string | undefined;
293
+ /**
294
+ * Stable identity of this client in TanStack query keys. Defaults to
295
+ * `baseUrl`; set it when two clients share a `baseUrl` but must not share
296
+ * cached data, and keep it constant across reloads of a persisted cache.
297
+ */
298
+ readonly id?: string | undefined;
280
299
  }
281
300
  /** Discovers the manifest and hands out {@link ResourceClient}s. */
282
301
  declare class CrudClient {
283
302
  #private;
284
303
  readonly baseUrl: string;
304
+ /** Stable identity in TanStack query keys (`ClientOptions.id`, else `baseUrl`). */
305
+ readonly id: string;
285
306
  constructor(options: ClientOptions);
286
307
  /** Adds middleware after the existing ones (innermost); it sees every later request. */
287
308
  use(middleware: Middleware): this;
@@ -289,6 +310,11 @@ declare class CrudClient {
289
310
  token(refresh?: boolean): Promise<string | null>;
290
311
  /** The cache scope of the current caller (`"anon"` or a hash of the token). */
291
312
  scope(): Promise<string>;
313
+ /** The `{ client, scope }` namespace the `./tanstack` key factory takes. */
314
+ namespace(): Promise<{
315
+ readonly client: string;
316
+ readonly scope: string;
317
+ }>;
292
318
  /** The manifest for this caller, loaded once per caller scope (`refresh: true` reloads). */
293
319
  manifest(options?: {
294
320
  readonly refresh?: boolean;
@@ -300,4 +326,4 @@ declare class CrudClient {
300
326
  declare function createClient(options: ClientOptions): CrudClient;
301
327
  //#endregion
302
328
  export { Pagination as A, Capabilities as C, JsonObject as D, GetJson as E, manifestUrlFromCatalog as F, parseManifest as I, Schemas as M, findResource as N, Manifest as O, loadManifest as P, CacheHints as S, FieldSpec as T, encodeParams as _, PredictOptions as a, AuthSpec as b, RestoreOptions as c, createClient as d, Filters as f, buildQuery as g, Sort as h, ListOptions as i, Resource as j, PROFILE as k, StatsOptions as l, QueryParams as m, ClientOptions as n, RequestOptions as o, QueryField as p, CrudClient as r, ResourceClient as s, BulkResult as t, WriteOptions as u, formatScalar as v, EventsSpec as w, CATALOG_PATH as x, quote as y };
303
- //# sourceMappingURL=client-BLd126Jt.d.mts.map
329
+ //# sourceMappingURL=client-BU9rPr8h.d.mts.map