@cloudbitmaps/core 0.11.2 → 0.13.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.
Files changed (44) hide show
  1. package/dist/{chunk-5OMC5U7U.js → chunk-5W3XS2IU.js} +292 -22
  2. package/dist/chunk-5W3XS2IU.js.map +7 -0
  3. package/dist/core/audit.d.ts +6 -0
  4. package/dist/core/chunk-window.d.ts +24 -0
  5. package/dist/core/codec.d.ts +33 -1
  6. package/dist/core/consistency.d.ts +21 -2
  7. package/dist/core/cost.d.ts +91 -10
  8. package/dist/core/crbm/format.d.ts +42 -3
  9. package/dist/core/crbm/reader.d.ts +45 -3
  10. package/dist/core/crbm/writer.d.ts +32 -3
  11. package/dist/core/crbm-storage-source.d.ts +204 -23
  12. package/dist/core/crypto.d.ts +11 -7
  13. package/dist/core/determinism.d.ts +7 -0
  14. package/dist/core/engine.d.ts +41 -8
  15. package/dist/core/erase-id.d.ts +23 -3
  16. package/dist/core/erasure.d.ts +8 -4
  17. package/dist/core/generation-gc.d.ts +102 -4
  18. package/dist/core/load-input.d.ts +55 -0
  19. package/dist/core/load.d.ts +65 -10
  20. package/dist/core/metadata.d.ts +42 -0
  21. package/dist/core/metrics.d.ts +7 -1
  22. package/dist/core/pinned-storage-source.d.ts +2 -1
  23. package/dist/core/ports.d.ts +119 -13
  24. package/dist/core/registry-scan.d.ts +6 -0
  25. package/dist/core/retention-sweep.d.ts +47 -2
  26. package/dist/core/retry.d.ts +14 -0
  27. package/dist/core/rollback.d.ts +34 -4
  28. package/dist/core/summary.d.ts +61 -0
  29. package/dist/core/token.d.ts +36 -0
  30. package/dist/core/validate.d.ts +2 -0
  31. package/dist/driver-kit.js +39 -20
  32. package/dist/driver-kit.js.map +2 -2
  33. package/dist/drivers/_shared/entropy.d.ts +5 -0
  34. package/dist/drivers/_shared/object-registry.d.ts +36 -6
  35. package/dist/drivers/_shared/registry.d.ts +47 -19
  36. package/dist/drivers/localfs/registry.d.ts +10 -0
  37. package/dist/drivers/memory.d.ts +9 -3
  38. package/dist/drivers/retry/retrying-chunk-source.d.ts +2 -1
  39. package/dist/index.d.ts +4 -3
  40. package/dist/index.js +1878 -501
  41. package/dist/index.js.map +4 -4
  42. package/dist/testing/fuzz-core.d.ts +7 -6
  43. package/package.json +1 -1
  44. package/dist/chunk-5OMC5U7U.js.map +0 -7
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/core/bit-route.ts", "../src/core/budget.ts", "../src/core/keys.ts", "../src/core/metrics.ts", "../src/core/engine.ts", "../src/core/lru.ts", "../src/core/reader-defaults.ts", "../src/core/cost.ts", "../src/core/due-index.ts", "../src/core/registry-scan.ts", "../src/core/codec.ts", "../src/export/index.ts", "../src/core/discover.ts", "../src/drivers/backends.ts", "../src/drivers/memory.ts", "../src/core/blob.ts", "../src/drivers/localfs/storage.ts", "../src/drivers/localfs/paths.ts", "../src/drivers/localfs/fs-util.ts", "../src/drivers/localfs/registry.ts", "../src/core/audit.ts", "../src/core/cooperative.ts", "../src/core/crypto.ts", "../src/core/crbm/crc32c.ts", "../src/core/crbm/varint.ts", "../src/core/crbm/format.ts", "../src/core/crbm/reader.ts", "../src/core/crbm/writer.ts", "../src/core/crbm-storage-source.ts", "../src/core/pinned-storage-source.ts", "../src/core/generation-gc.ts", "../src/core/load.ts", "../src/core/rollback.ts", "../src/core/erase-id.ts", "../src/drivers/crypto.ts", "../src/core/erasure.ts", "../src/core/retention.ts", "../src/core/retention-sweep.ts", "../src/core/consistency.ts", "../src/core/retry.ts", "../src/drivers/retry/retrying-chunk-source.ts"],
4
- "sourcesContent": ["/**\n * BitRouteSplitter \u2014 maps a 32-bit id to its chunk (top 16 bits) and remainder\n * (bottom 16 bits), per the data model.\n */\nimport { ValidationError } from './errors';\n\nexport const U32_MAX = 0xffff_ffff;\nexport const CHUNK_COUNT = 0x1_0000; // 65,536 possible chunk keys\nconst LOW_16 = 0xffff;\n\nexport interface BitRoute {\n /** Top 16 bits \u2014 the chunk key (0..65535). */\n readonly chunkKey: number;\n /** Bottom 16 bits \u2014 the value within the chunk (0..65535). */\n readonly remainder: number;\n}\n\n/** Split a u32 id into `{ chunkKey, remainder }`. Throws `ValidationError` for non-u32 input. */\nexport function splitId(id: number): BitRoute {\n if (!Number.isInteger(id) || id < 0 || id > U32_MAX) {\n throw new ValidationError(`id must be an integer in 0..${U32_MAX}; got ${id}`);\n }\n return { chunkKey: id >>> 16, remainder: id & LOW_16 };\n}\n\n/**\n * Recombine a chunk key + remainder back into the u32 id. Precondition: `chunkKey` is a validated\n * 16-bit value (tier-derived keys are validated upstream in the engine before reaching here, per\n * invariant 5); `remainder` is masked to 16 bits.\n */\n/**\n * Largest legal value inside a chunk payload. A chunk covers `CHUNK_COUNT` ids, so remainders run\n * `[0, CHUNK_COUNT - 1]`. Exported so the engine can assert it on untrusted tier bytes.\n */\nexport const MAX_REMAINDER = CHUNK_COUNT - 1;\n\nexport function joinId(chunkKey: number, remainder: number): number {\n return (((chunkKey & LOW_16) << 16) >>> 0) + (remainder & LOW_16);\n}\n", "/**\n * Per-op denial-of-wallet budget \u2014 hard invariant 6 (bounded memory and cost). A read/admin op is refused\n * **before it fans out** if its projected fan-out would exceed `maxRequests` \u2014 so one runaway\n * `count`/`iterate`/`intersect`/`subjectReport`/`eraseSubject` can't drive unbounded RCU/GET cost on a shared\n * backend. The check is O(1) (a single comparison against the already-known fan-out size), so the hot path is\n * untouched. Each request's payload is separately size-capped (the safe-deserialize ceiling), so bounding the\n * fan-out count transitively bounds bytes \u2014 the budget is intentionally count-based, not a second byte\n * accumulator that would add per-chunk work.\n *\n * **What the count is.** For the read ops it is the number of Storage **chunk fetches** (`count`/`iterate`: one per\n * effective chunk; `intersect`: surviving keys \u00D7 operands). For the admin scans it is the number of **segments\n * fanned out to** (`subjectReport`: one `has()` each; `eraseSubject`: one per segment) \u2014 i.e. it bounds the\n * *breadth* of the fan-out, not the request total of each segment's own rewrite. That's the right lever for a\n * runaway (a million-segment sweep); it is not a byte/request meter. One addition: a segment `eraseSubject` has to\n * search generation by generation is also charged one unit for each generation it opens beyond the current one,\n * since that is O(generations) work the segment count alone does not show.\n */\n\nimport { BudgetExceededError, ValidationError } from './errors';\n\nexport interface Budget {\n /** Max units of fan-out (Storage chunk fetches, or segments scanned) one op may issue before it is refused. */\n readonly maxRequests: number;\n}\n\n/**\n * Default: **on but generous** \u2014 even a full single-segment scan (\u2264 65 536 chunks) or a large single-namespace\n * admin scan stays well under it; it exists to catch a true runaway (a million-segment scan, a pathological\n * intersection). Tune it down for tighter multi-tenant control, or set `budget: false` to disable.\n */\nexport const DEFAULT_BUDGET: Budget = { maxRequests: 1_000_000 };\n\n/** A store/per-op budget option: partial overrides, or `false` to disable entirely. */\nexport type BudgetOption = Partial<Budget> | false;\n\n/** Resolve a budget option against a fallback to a concrete `Budget`, or `null` when disabled. Validates. */\nexport function resolveBudget(opt: BudgetOption | undefined, fallback: Budget): Budget | null {\n if (opt === false) return null; // explicitly disabled\n const maxRequests = opt?.maxRequests ?? fallback.maxRequests;\n if (!Number.isInteger(maxRequests) || maxRequests < 1) {\n throw new ValidationError(`budget.maxRequests must be a positive integer; got ${maxRequests}`);\n }\n return { maxRequests };\n}\n\n/**\n * Resolve a **per-op** budget override against the store's own resolved budget (never the raw global default):\n * `undefined` inherits the store budget as-is (including a disabled `null`); a partial `{}` / omitted\n * `maxRequests` inherits the store's *tightening* (so a per-op passthrough of an absent config value can't\n * silently lift a tight tenant ceiling back to the generous default); `{ maxRequests }` replaces it; `false`\n * disables. Falls back to {@link DEFAULT_BUDGET} only when the store itself has no budget (disabled).\n */\nexport function resolvePerOpBudget(\n opt: BudgetOption | undefined,\n storeBudget: Budget | null,\n): Budget | null {\n if (opt === undefined) return storeBudget;\n return resolveBudget(opt, storeBudget ?? DEFAULT_BUDGET);\n}\n\n/**\n * Refuse an op whose projected fan-out exceeds the budget. **Call before fan-out**, with the exact fan-out size\n * the op is about to issue (Storage chunk fetches, or segments to scan). O(1). No-op when `budget` is `null`\n * (disabled).\n */\nexport function checkBudget(budget: Budget | null, projectedRequests: number, op: string): void {\n if (budget !== null && projectedRequests > budget.maxRequests) {\n throw new BudgetExceededError(\n `${op} would fan out to ${projectedRequests} units, over the per-op budget of ` +\n `${budget.maxRequests} \u2014 raise \\`budget.maxRequests\\`, override it per-op, or set \\`budget: false\\``,\n );\n }\n}\n\n/**\n * Drain an async iterable into an array, refusing **as soon as** the budget is exceeded.\n *\n * This exists because the obvious spelling is wrong in a way that is easy to miss:\n *\n * ```ts\n * for await (const rec of registry.list(ns)) recs.push(rec);\n * checkBudget(budget, recs.length, op); // \u2190 too late; the memory is already spent\n * ```\n *\n * That pattern bounds the **fan-out** but not the **enumeration**, so a tight budget refuses the work only\n * after paying for the list \u2014 which defeats the purpose. It was measured buffering 20,000 registry records\n * under `budget: { maxRequests: 2 }` before throwing. Draining incrementally bounds resident memory to\n * `O(budget)` instead of `O(fleet)`.\n *\n * **The error message is deliberately vaguer than `checkBudget`'s.** It reports \"more than N\" rather than an\n * exact total, because knowing the exact total requires finishing the scan \u2014 which is precisely the cost being\n * refused. An honest approximation beats an expensive exact number.\n *\n * A `null` budget (`budget: false`) means the caller has explicitly opted out of fan-out limits, so this\n * degrades to a plain drain.\n */\nexport async function collectWithinBudget<T>(\n source: AsyncIterable<T>,\n budget: Budget | null,\n op: string,\n): Promise<T[]> {\n const out: T[] = [];\n for await (const item of source) {\n out.push(item);\n // Checked INSIDE the loop, and `>` not `>=`, so the threshold matches `checkBudget` exactly: a budget of N\n // admits exactly N units and refuses the N+1'th.\n if (budget !== null && out.length > budget.maxRequests) {\n throw new BudgetExceededError(\n `${op} would fan out to more than ${budget.maxRequests} units, over the per-op budget \u2014 the scan was ` +\n `abandoned at that point rather than completed, so no exact total is available. Raise ` +\n `\\`budget.maxRequests\\`, override it per-op, or set \\`budget: false\\``,\n );\n }\n }\n return out;\n}\n", "/**\n * Canonical key encoding for a segment / chunk \u2014 one source of truth, used by both the storage\n * drivers and the cache (DRY). The encoding is collision-proof and injection-proof.\n *\n * **The name parts are encoded, and that is what makes the delimiters safe.** A name is any non-empty string,\n * so it may contain a space or a `/`, and with the parts left literal two distinct segments could produce one\n * key:\n *\n * ```\n * segmentKey({ segment: 's' }) \u2192 \"/ s\"\n * segmentKey({ namespace: '/', segment: 's' }) \u2192 \"/ s\" \u2190 the same key\n * segmentKey({ namespace: 'a', segment: 'b c' }) \u2192 \"a b c\"\n * segmentKey({ namespace: 'a b', segment: 'c' }) \u2192 \"a b c\" \u2190 the same key\n * ```\n *\n * These keys reach the reader LRU and the decoded-chunk cache, so a collision is one segment serving\n * another's data on the read path, on every backend \u2014 invariant 3, and a tenant-isolation break.\n *\n * `encodeNameForKey` escapes both delimiters (`/` \u2192 `%2F`, space \u2192 `%20`) along with control characters, so\n * neither can be injected across a field \u2014 a property of the encoding, not of what a caller chooses to name\n * things. The absent-namespace sentinel keeps the same ours-vs-theirs asymmetry the\n * storage keys use: it is emitted literally while a caller's namespace is encoded, so a namespace actually\n * named `/` encodes to `%2F` and cannot impersonate it.\n *\n * These keys are in-memory and driver-internal; nothing on disk is named by them.\n */\nimport { encodeNameForKey } from './name-codec';\nimport type { ChunkRef, SegmentRef } from './ports';\n\nconst FIELD = ' ';\nconst NO_NAMESPACE = '/';\n\nexport function segmentKey(ref: SegmentRef): string {\n const ns = ref.namespace === undefined ? NO_NAMESPACE : encodeNameForKey(ref.namespace);\n return `${ns}${FIELD}${encodeNameForKey(ref.segment)}`;\n}\n\nexport function segmentPrefix(ref: SegmentRef): string {\n return `${segmentKey(ref)}${FIELD}`;\n}\n\nexport function chunkRefKey(ref: ChunkRef): string {\n return `${segmentPrefix(ref)}${ref.chunkKey}`;\n}\n\n/**\n * Cache key for a chunk scoped to a specific **version** of the segment \u2014 {@link chunkRefKey} plus that\n * version (space-delimited, injection-proof exactly as above \u2014 the name parts are already encoded). A publish bumps the version, so it naturally\n * misses the cache instead of serving a stale superseded chunk, and the superseded entries age out under the\n * LRU ceiling (no active purge needed).\n *\n * `version` is whatever the Storage source reports as identifying the bytes a read will see \u2014 its\n * `currentVersion` where it has one, otherwise the bare generation number. **A generation number alone is not\n * an identity**: a load's generation number restarts at 0 once a registry row is purged and the bucket emptied, so a\n * retired-and-re-created name serves different data at the same `currentGen`, and a key built on the number\n * hands the new incarnation the old one's decoded chunks.\n */\nexport function chunkGenKey(ref: ChunkRef, version: string | number): string {\n return `${chunkRefKey(ref)}${FIELD}${version}`;\n}\n\n/**\n * Deterministic FNV-1a shard assignment for a segment key \u2014 dependency-free, stable across workers and\n * restarts. One definition, so every sharded fleet operation (today the retention sweep) agrees on it exactly:\n * two definitions that disagreed would make the union across workers neither disjoint nor complete.\n */\nexport function shardOf(key: string, totalShards: number): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x0100_0193);\n }\n return (h >>> 0) % totalShards;\n}\n", "/**\n * Metrics seam \u2014 an injected sink the engine pushes typed events to, exactly like the `Clock`/driver seams\n * keep `core/` pure and storage-agnostic.\n *\n * Design: the library emits **neutral domain events** and stays vendor-agnostic \u2014 you (or a ~12-line\n * adapter) map them to OpenTelemetry / Datadog / a log line. The default is a **no-op**, and emission is\n * skipped entirely when no sink is wired (near-zero overhead, no telemetry dependency). Events carry **raw\n * observations** (bytes, counts, ms); turning those into billable units / dollars is the cost model's job,\n * which keeps rates pluggable.\n *\n * Two caveats for sink authors: `onEvent` runs **synchronously on the I/O path**, so keep it cheap and\n * non-blocking (offload batching/network to your own async queue); and `segment`/`namespace` are\n * caller-controlled strings that may be PII and are **unbounded-cardinality** \u2014 do not map them to\n * per-series metric labels unless they're known low-cardinality and PII-free. See the observability\n * guide.\n */\n\n/** The segment operations that emit an `op` latency event (timed with the injected clock, at the facade). */\nexport type MetricOpName = 'has' | 'count' | 'intersectInto' | 'unionInto' | 'andNotInto';\n\n/**\n * One observability event. A discriminated union on `kind` \u2014 new variants can be added over time without\n * breaking consumers (they switch on the kinds they care about). `namespace` is the segment's namespace\n * (absent \u21D2 the default namespace). Segment names are identifiers, never bitmap contents (threat model:\n * never log PII / bitmap bits).\n */\nexport type MetricEvent =\n | {\n readonly kind: 'storage.get';\n readonly namespace?: string;\n readonly segment: string;\n /** Bytes returned (0 if the chunk was absent \u2014 a GET still happened). */\n readonly bytes: number;\n /**\n * Elapsed wall time of the read \u2014 includes any transient-retry backoff on the storage call. From the\n * injected clock, clamped to \u2265 0 (0 under the no-wait test clock).\n */\n readonly ms: number;\n }\n | { readonly kind: 'cache'; readonly hit: boolean }\n | {\n readonly kind: 'retry';\n /** Infrastructure-fault backoff (throttling, 5xx, a dropped connection) \u2014 the one kind of retry the store does. */\n readonly reason: 'transient';\n /** 1-based number of the attempt about to be retried. */\n readonly attempt: number;\n readonly delayMs: number;\n }\n | {\n readonly kind: 'intersect';\n /**\n * Which chunk-aligned combine this was. **Optional** \u2014 absent means `'intersect'`.\n *\n * For `'union'` over its *include* operands alone, `skippedChunks` is 0 by construction \u2014 union reads\n * every chunk of every operand, so there is nothing to prune. It can still be non-zero when a union\n * carries `exclude` operands, because a suppression list may hold keys no include does and those are\n * legitimately never fetched. So: 0 is the expected shape for a plain union, not a guarantee.\n */\n readonly op?: 'intersect' | 'union' | 'andNot';\n /** Operand count, including any `exclude` (suppression) operands. */\n readonly operands: number;\n /**\n * Distinct chunk **keys** selected to fetch \u2014 for `'intersect'`, those present in every operand.\n *\n * **Keys, not requests.** The actual storage GETs are roughly `fetchedChunks \u00D7 operands`, plus one per\n * `exclude` that holds a given key, which is why this number is smaller than what the per-op budget\n * charges for the same call. The two are different units by design; if you are reconciling a bill, the\n * budget's accounting is the one that models requests.\n */\n readonly fetchedChunks: number;\n /**\n * **Distinct** chunk-keys pruned \u2014 never fetched (the chunk-skipping saving). Counts distinct keys,\n * not per-operand GETs, so it under-states the true GET saving when 3+ operands partially overlap.\n *\n * On a read bounded by `after` / `through`, this and `fetchedChunks` count only the keys inside the range:\n * the keys outside it are never considered, so they are not counted as pruned either. Both count the keys the\n * call would fetch if read to the end, so a page that stops early reports every key up to `through`, or to the\n * end of the segment when `through` is left out.\n */\n readonly skippedChunks: number;\n }\n | { readonly kind: 'op'; readonly name: MetricOpName; readonly ms: number };\n\n/**\n * The sink you plug in. One method, synchronous, fire-and-forget. It must never throw back into the\n * caller \u2014 `CloudRoaring` wraps a user sink with {@link safeMetrics} so a buggy sink can't break I/O.\n */\nexport interface IMetricsSink {\n onEvent(event: MetricEvent): void;\n}\n\n/** The default sink: discards everything, zero overhead. Used whenever no sink is wired. */\nexport const NOOP_METRICS: IMetricsSink = {\n onEvent(): void {\n /* discard */\n },\n};\n\n/**\n * Wrap a sink so a throwing/buggy `onEvent` can never break the data path \u2014 observability is strictly\n * best-effort. `CloudRoaring` applies this to the user-supplied sink at construction.\n */\nexport function safeMetrics(sink: IMetricsSink): IMetricsSink {\n if (sink === NOOP_METRICS) return sink;\n return {\n onEvent(event: MetricEvent): void {\n try {\n sink.onEvent(event);\n } catch {\n /* swallow \u2014 a metrics sink must never break a read */\n }\n },\n };\n}\n\n/** Accumulated totals \u2014 the shape returned by {@link CountingMetricsSink.snapshot}. */\nexport interface MetricsSnapshot {\n readonly storage: { readonly gets: number; readonly bytes: number; readonly totalMs: number };\n readonly cache: { readonly hits: number; readonly misses: number };\n readonly retries: { readonly transient: number };\n readonly intersect: {\n readonly calls: number;\n readonly fetchedChunks: number;\n readonly skippedChunks: number;\n };\n readonly ops: Readonly<\n Record<MetricOpName, { readonly count: number; readonly totalMs: number }>\n >;\n}\n\nconst OP_NAMES: readonly MetricOpName[] = [\n 'has',\n 'count',\n 'intersectInto',\n 'unionInto',\n 'andNotInto',\n];\n\n/**\n * A ready-made sink that tallies events into a {@link MetricsSnapshot} \u2014 handy for tests, quick scripts,\n * and as the grounded-request-cost source behind `costReport()`. `snapshot()` returns an independent copy;\n * `reset()` zeroes the counters.\n */\nexport class CountingMetricsSink implements IMetricsSink {\n private storage = { gets: 0, bytes: 0, totalMs: 0 };\n private cache = { hits: 0, misses: 0 };\n private retries = { transient: 0 };\n private intersect = { calls: 0, fetchedChunks: 0, skippedChunks: 0 };\n private ops: Record<MetricOpName, { count: number; totalMs: number }> =\n CountingMetricsSink.zeroOps();\n\n private static zeroOps(): Record<MetricOpName, { count: number; totalMs: number }> {\n // null-proto so a type-violating event.name (e.g. '__proto__') can never reach Object.prototype\n const ops = Object.create(null) as Record<MetricOpName, { count: number; totalMs: number }>;\n for (const name of OP_NAMES) ops[name] = { count: 0, totalMs: 0 };\n return ops;\n }\n\n onEvent(event: MetricEvent): void {\n switch (event.kind) {\n case 'storage.get':\n this.storage.gets += 1;\n this.storage.bytes += event.bytes;\n this.storage.totalMs += event.ms;\n break;\n case 'cache':\n if (event.hit) this.cache.hits += 1;\n else this.cache.misses += 1;\n break;\n case 'retry':\n this.retries.transient += 1;\n break;\n case 'intersect':\n this.intersect.calls += 1;\n this.intersect.fetchedChunks += event.fetchedChunks;\n this.intersect.skippedChunks += event.skippedChunks;\n break;\n case 'op': {\n // A name outside `OP_NAMES` (a JS caller) is counted nowhere rather than crashing the sink.\n const op = this.ops[event.name] as { count: number; totalMs: number } | undefined;\n if (op === undefined) break;\n op.count += 1;\n op.totalMs += event.ms;\n break;\n }\n default: {\n const exhaustive: never = event;\n return exhaustive;\n }\n }\n }\n\n snapshot(): MetricsSnapshot {\n const ops = Object.create(null) as Record<MetricOpName, { count: number; totalMs: number }>;\n for (const name of OP_NAMES) ops[name] = { ...this.ops[name] };\n return {\n storage: { ...this.storage },\n cache: { ...this.cache },\n retries: { ...this.retries },\n intersect: { ...this.intersect },\n ops,\n };\n }\n\n reset(): void {\n this.storage = { gets: 0, bytes: 0, totalMs: 0 };\n this.cache = { hits: 0, misses: 0 };\n this.retries = { transient: 0 };\n this.intersect = { calls: 0, fetchedChunks: 0, skippedChunks: 0 };\n this.ops = CountingMetricsSink.zeroOps();\n }\n}\n", "/**\n * SegmentEngine \u2014 the read side of the loaded store: id routing, the chunk-skipping combines, and the cache\n * of decoded Storage chunks, over the {@link StorageChunkSource} port.\n *\n * **Read-only by design.** Every write in this library is a new immutable generation \u2014 a load, the facade's\n * `*Into` verbs, `eraseIdFromSegment` \u2014 published through the registry pointer. Nothing here mutates\n * stored bytes, so a read never merges tiers: one chunk read is one whole, checksum-verified, immutable chunk of\n * one generation. Storage-agnostic and time/random-free (the determinism seam): all I/O is via the injected\n * source; the cache carries its own `Clock`.\n */\nimport { splitId, joinId, CHUNK_COUNT, MAX_REMAINDER, U32_MAX } from './bit-route';\nimport type { CodecBitmap, CodecInterface } from './codec';\nimport { checkBudget, DEFAULT_BUDGET, resolvePerOpBudget } from './budget';\nimport type { Budget, BudgetOption } from './budget';\nimport type { Clock } from './determinism';\nimport { IntegrityError, ValidationError } from './errors';\nimport { chunkGenKey, chunkRefKey, segmentPrefix } from './keys';\nimport type { BoundedLru } from './lru';\nimport { NOOP_METRICS, safeMetrics } from './metrics';\nimport type { IMetricsSink } from './metrics';\nimport type { ChunkRef, StorageChunkSource, SegmentRef, SegmentSize } from './ports';\n\nconst DEFAULT_MAX_BITMAP_BYTES = 1 << 20; // 1 MiB per bitmap \u2014 generous; real chunks are far smaller\n/** Max overlapping-chunk intersections in flight \u2014 bounds memory + concurrent reads (invariant 6). */\nconst DEFAULT_INTERSECT_CONCURRENCY = 8;\n\n/** A clock that always reads 0 \u2014 used when nothing is injected, so the `storage.get` latency metric reports 0 ms. */\nconst ZERO_CLOCK: Pick<Clock, 'now'> = { now: () => 0 };\n\nexport interface EngineDeps {\n readonly storage: StorageChunkSource;\n /** Optional cache of decoded (immutable) Storage chunks. */\n readonly cache?: BoundedLru<string, CodecBitmap>;\n readonly maxBitmapBytes?: number;\n /**\n * The bitmap codec \u2014 **required**. `core/` is codec-agnostic: it can have no default, because the concrete codec\n * lives in a *flavor* package that depends on core (a default here would invert that arrow). A flavor's facade\n * injects it \u2014 `@cloudbitmaps/roaring` passes the roaring codec \u2014 so applications never see this.\n */\n readonly codec: CodecInterface;\n /** Time source for the `storage.get` latency metric only; defaults to a clock that reads 0. */\n readonly clock?: Pick<Clock, 'now'>;\n /** Observability sink; defaults to a no-op. Assumed exception-safe (the facade wraps it). */\n readonly metrics?: IMetricsSink;\n /**\n * Per-op denial-of-wallet budget, already resolved by the facade: a {@link Budget} to enforce, or `null` to\n * disable. Undefined \u21D2 {@link DEFAULT_BUDGET} (a direct engine construction still gets the generous default).\n * Read ops refuse before fan-out if they'd exceed it.\n */\n readonly budget?: Budget | null;\n}\n\n/**\n * A range of ids for a read to yield: those greater than `after` and no greater than `through`, `(after, through]`.\n *\n * Built for keyset paging: `after` is the last id the caller already has, `through` the end of its window. Each is\n * optional: left out, the read starts at the first id or runs to the last. Each is an integer in `0..4294967295`, or\n * the read throws {@link ValidationError} when it is first read. `after >= through` is an empty range, not an error,\n * because a cursor that has reached the end of its window is normal; an empty range reads nothing, not even whether\n * a combine's operands exist.\n *\n * A read with a range fetches only the chunks the range overlaps, on a combine for every operand and every\n * `exclude`. The per-op budget is charged once, before the first fetch, for every chunk in the range, so with\n * `after` alone it is charged to the end of the segment however early the caller stops; pass `through` to bound it.\n */\nexport interface IdRange {\n /** Exclusive lower bound: the read yields only ids greater than this. */\n readonly after?: number | undefined;\n /** Inclusive upper bound: the read yields only ids up to and including this. */\n readonly through?: number | undefined;\n}\n\n/** Options common to the chunk-aligned combines. */\nexport interface CombineOptions extends IdRange {\n /** Max chunk keys resolved concurrently \u2014 bounds the Storage footprint. A positive integer. */\n readonly concurrency?: number;\n /** Override the store's per-op budget for this call (`false` lifts it). */\n readonly budget?: BudgetOption;\n /** Segments whose ids are subtracted from the result. */\n readonly exclude?: readonly SegmentRef[];\n /**\n * Allow an operand that names a segment which does not exist. Default `false` \u2014 a combine refuses one,\n * because a misspelled or mis-namespaced operand is indistinguishable from a correct one in the result.\n * Set `true` when you genuinely intend to combine against a name that may not have been created yet.\n */\n readonly allowAbsentOperands?: boolean;\n}\n\n/** One resolved operand of a chunk-aligned combine: its chunk-key set + pinned generation. */\ninterface Operand {\n readonly seg: SegmentRef;\n /** The operand's chunk keys, inside the read's range when it has one. */\n readonly keys: Set<number>;\n /** Whether the segment has no chunks at all, whatever the range: what the absent-operand check judges. */\n readonly chunkless: boolean;\n /** The cache-key component \u2014 a `currentVersion` string, a generation number, or absent. */\n readonly gen: string | number | null | undefined;\n}\n\n/** A validated {@link IdRange}: inclusive id bounds, and the chunk key and remainder at each end. */\ninterface IdWindow {\n readonly loKey: number;\n readonly loRem: number;\n readonly hiKey: number;\n readonly hiRem: number;\n}\n\n/**\n * Validate a read's range. `null` when none was asked for, so the read does no extra work; `'empty'` when no id can\n * be in it.\n */\nfunction windowOf(range: IdRange | undefined): IdWindow | 'empty' | null {\n const after = range?.after;\n const through = range?.through;\n if (after === undefined && through === undefined) return null;\n for (const [name, bound] of [\n ['after', after],\n ['through', through],\n ] as const) {\n if (bound !== undefined && (!Number.isInteger(bound) || bound < 0 || bound > U32_MAX)) {\n const got = typeof bound === 'number' ? String(bound) : `a ${typeof bound}`;\n throw new ValidationError(`${name} must be an integer in 0..${U32_MAX}; got ${got}`);\n }\n }\n const lo = after === undefined ? 0 : after + 1;\n const hi = through ?? U32_MAX;\n if (lo > hi) return 'empty';\n return {\n loKey: Math.floor(lo / CHUNK_COUNT),\n loRem: lo % CHUNK_COUNT,\n hiKey: Math.floor(hi / CHUNK_COUNT),\n hiRem: hi % CHUNK_COUNT,\n };\n}\n\n/** The window a range with neither bound set describes: every id. */\nconst WHOLE_ID_SPACE: IdWindow = {\n loKey: 0,\n loRem: 0,\n hiKey: CHUNK_COUNT - 1,\n hiRem: MAX_REMAINDER,\n};\n\n/**\n * Whether the range cuts inside `chunkKey`: one of its two edge chunks, and only where the bound falls inside the\n * chunk rather than on its boundary. A chunk the range covers whole is yielded by the plain loop.\n */\nconst isEdge = (chunkKey: number, w: IdWindow): boolean =>\n (chunkKey === w.loKey && w.loRem > 0) || (chunkKey === w.hiKey && w.hiRem < MAX_REMAINDER);\n\n/** The ids of an edge chunk that lie inside the window. The chunk iterates ascending, so it stops at the top. */\nfunction* edgeIds(chunk: CodecBitmap, chunkKey: number, w: IdWindow): Generator<number> {\n const from = chunkKey === w.loKey ? w.loRem : 0;\n const to = chunkKey === w.hiKey ? w.hiRem : MAX_REMAINDER;\n for (const remainder of chunk) {\n if (remainder > to) return;\n if (remainder >= from) yield joinId(chunkKey, remainder);\n }\n}\n\n/** The ascending `keys` inside the window's chunk span: two binary searches, no scan. */\nfunction keysWithin(keys: readonly number[], w: IdWindow): number[] {\n const firstAtLeast = (key: number): number => {\n let lo = 0;\n let hi = keys.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (keys[mid]! < key) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n return keys.slice(firstAtLeast(w.loKey), firstAtLeast(w.hiKey + 1));\n}\n\n/**\n * An ordered window of chunk reads: up to `max` reads open ahead of the one being consumed, handed back in key\n * order. With `ramp`, the window opens 1, 2, 4, 8 wide instead of `max` at once, so a read that stops after a few\n * ids has fetched a handful of chunks, not a full window.\n *\n * Each read is wrapped to resolve and never reject, so a read nobody consumes (the consumer stopped, or an\n * earlier chunk failed) cannot raise an unhandled rejection; its error surfaces from {@link take} only if the\n * read reaches that chunk. Memory is bounded by the window: at most `max` decoded chunks are held ahead.\n */\nclass ChunkWindow {\n private readonly open: Array<Promise<{ chunk: CodecBitmap | null; error?: { cause: unknown } }>> =\n [];\n private launched = 0;\n private taken = 0;\n\n constructor(\n private readonly keys: readonly number[],\n private readonly fetch: (chunkKey: number) => Promise<CodecBitmap | null>,\n private readonly max: number,\n private readonly ramp: boolean,\n ) {}\n\n /** The next chunk in key order (null if the source holds none), or the error its read raised. */\n async take(): Promise<CodecBitmap | null> {\n const width = this.ramp ? Math.min(this.max, 2 ** Math.min(this.taken, 30)) : this.max;\n while (this.launched < this.keys.length && this.launched - this.taken < width) {\n this.open.push(\n this.fetch(this.keys[this.launched++]!).then(\n (chunk) => ({ chunk }),\n (cause: unknown) => ({ chunk: null, error: { cause } }),\n ),\n );\n }\n const slot = await this.open.shift()!;\n this.taken += 1;\n if (slot.error) throw slot.error.cause;\n return slot.chunk;\n }\n}\n\nexport class SegmentEngine {\n private readonly storage: StorageChunkSource;\n private readonly cache: BoundedLru<string, CodecBitmap> | undefined;\n private readonly codec: CodecInterface;\n private readonly maxBitmapBytes: number;\n private readonly clock: Pick<Clock, 'now'>;\n private readonly metrics: IMetricsSink;\n private readonly metricsOn: boolean;\n /** Resolved per-op budget (null = disabled); undefined deps \u21D2 the generous default. */\n private readonly budget: Budget | null;\n\n constructor(deps: EngineDeps) {\n this.storage = deps.storage;\n this.cache = deps.cache;\n this.codec = deps.codec;\n this.maxBitmapBytes = deps.maxBitmapBytes ?? DEFAULT_MAX_BITMAP_BYTES;\n this.clock = deps.clock ?? ZERO_CLOCK;\n // undefined \u21D2 the generous default; null \u21D2 explicitly disabled (must not be re-defaulted by `??`).\n this.budget = deps.budget === undefined ? DEFAULT_BUDGET : deps.budget;\n // Wrap for defense-in-depth (idempotent for the no-op) so even a direct engine construction with a\n // throwing sink can't break the data path; `metricsOn` short-circuits all emission when unused.\n this.metrics = safeMetrics(deps.metrics ?? NOOP_METRICS);\n this.metricsOn = this.metrics !== NOOP_METRICS;\n }\n\n /** Membership: one chunk lookup \u2014 the cache, else one Storage fetch of that chunk. */\n async has(seg: SegmentRef, id: number): Promise<boolean> {\n const { chunkKey, remainder } = splitId(id);\n const chunk = await this.storageChunk({ ...seg, chunkKey }, await this.cacheVersion(seg));\n return chunk ? chunk.has(remainder) : false;\n }\n\n /**\n * Cardinality. When the Storage source can serve per-chunk cardinality from its `.crbm` index (the `.crbm`\n * source can), the count is summed from the index with **zero payload reads**. A source without that\n * capability (the in-memory source) falls back to fetching every chunk.\n *\n * The index sum is trusted, not confirmed: the reader checked the index for internal consistency when it opened\n * the object, and a corrupt index that is still internally consistent yields a wrong count. `iterate()` and the\n * combines decode the payloads.\n */\n async count(seg: SegmentRef): Promise<number> {\n const cardinalities = this.storage.cardinalities ? await this.storage.cardinalities(seg) : null;\n if (cardinalities) {\n let total = 0;\n for (const [k, n] of cardinalities) {\n this.assertChunkKeyInRange(k);\n total += n;\n }\n return total;\n }\n const chunkKeys = await this.chunkKeys(seg);\n checkBudget(this.budget, chunkKeys.length, 'count'); // one storage fetch per chunk (before fan-out)\n const gen = await this.cacheVersion(seg); // after the shape read \u2014 see `combine`\n // Order does not matter to a sum, but the window hands chunks back in key order: no more state than that.\n const window = this.chunkWindow(seg, chunkKeys, gen, false);\n let total = 0;\n for (let i = 0; i < chunkKeys.length; i++) total += (await window.take())?.size ?? 0;\n return total;\n }\n\n /** Whether the Storage source can measure segment size (for grounded cost); false \u21D2 storage isn't grounded. */\n get supportsStorageSize(): boolean {\n return typeof this.storage.sizeOf === 'function';\n }\n\n /** How often the Storage source re-reads a pointer while reading (cost reporting); undefined if it does not say. */\n get pointerRefreshMs(): number | undefined {\n return this.storage.pointerRefreshMs;\n }\n\n /** Grounded Storage size of a segment's current generation (cost reporting), or null if it has no generation. */\n segmentSize(seg: SegmentRef): Promise<SegmentSize | null> {\n return this.storage.sizeOf ? this.storage.sizeOf(seg) : Promise.resolve(null);\n }\n\n /** The chunks of `seg` at `chunkKeys`, read through a window of {@link DEFAULT_INTERSECT_CONCURRENCY}. */\n private chunkWindow(\n seg: SegmentRef,\n chunkKeys: readonly number[],\n gen: string | number | null | undefined,\n ramp: boolean,\n ): ChunkWindow {\n return new ChunkWindow(\n chunkKeys,\n (chunkKey) => this.storageChunk({ ...seg, chunkKey }, gen),\n DEFAULT_INTERSECT_CONCURRENCY,\n ramp,\n );\n }\n\n /**\n * Every id, ascending, reading ahead through a window of up to 8 chunk fetches that opens 1, 2, 4, 8 wide, so a\n * read that stops early has fetched only a handful of chunks past the one it stopped in; with `range`, only the ids in `(after, through]`, fetching only the\n * chunks the range overlaps (see {@link IdRange}).\n *\n * Two generators, not one with a branch: a second `yield` site in the full read's generator grows its frame, and\n * every id of a read that asked for no range paid for it (measured at about 5% per id). The full read keeps the\n * loop it always had.\n */\n iterate(seg: SegmentRef, range?: IdRange): AsyncGenerator<number> {\n return range === undefined ? this.iterateAll(seg) : this.iterateRange(seg, range);\n }\n\n private async *iterateAll(seg: SegmentRef): AsyncGenerator<number> {\n const chunkKeys = await this.chunkKeys(seg);\n checkBudget(this.budget, chunkKeys.length, 'iterate'); // one storage fetch per chunk (before fan-out)\n const gen = await this.cacheVersion(seg); // after the shape read \u2014 see `combine`\n const window = this.chunkWindow(seg, chunkKeys, gen, true);\n for (const chunkKey of chunkKeys) {\n const chunk = await window.take();\n if (chunk === null) continue;\n // Read straight off the (possibly cached) instance: iteration does not mutate it.\n for (const remainder of chunk) yield joinId(chunkKey, remainder);\n }\n }\n\n private async *iterateRange(seg: SegmentRef, range: IdRange): AsyncGenerator<number> {\n const w = windowOf(range) ?? WHOLE_ID_SPACE;\n if (w === 'empty') return;\n const chunkKeys = keysWithin(await this.chunkKeys(seg), w);\n checkBudget(this.budget, chunkKeys.length, 'iterate'); // one storage fetch per chunk (before fan-out)\n const gen = await this.cacheVersion(seg); // after the shape read \u2014 see `combine`\n const window = this.chunkWindow(seg, chunkKeys, gen, true);\n for (const chunkKey of chunkKeys) {\n const chunk = await window.take();\n if (chunk === null) continue;\n if (isEdge(chunkKey, w)) {\n for (const id of edgeIds(chunk, chunkKey, w)) yield id;\n } else {\n for (const remainder of chunk) yield joinId(chunkKey, remainder);\n }\n }\n }\n\n /**\n * `segs[0] \u2229 segs[1] \u2229 \u2026`, minus every id in `options.exclude` \u2014 streamed ascending.\n *\n * The `exclude` operands are what make suppression **compose without materialising**. Applying suppression\n * afterwards (intersect into a temp segment, then subtract) writes an intermediate segment that nobody wants;\n * applying it here folds it into the same chunk-aligned pass. It is also cheaper than it looks: an exclude\n * operand is read **only at the keys that survived the intersection** \u2014 the cost is bounded by the\n * audience's surviving key count and **never scales with the size of the suppression list**. Subtracting a\n * 61,000-chunk global opt-out list costs at most one read per surviving key, not 61,000.\n *\n * ---\n *\n * Chunk-skipping intersection. Aligns each segment's chunk-key set (read from the `.crbm` index, no payload),\n * keeps only keys present in **all** operands (a key missing from any operand can't contribute \u2192 its Storage\n * chunks are never fetched \u2014 the core saving), then for each surviving key fetches the operands' chunks in\n * parallel and hands them to the codec for the AND, streaming results through a bounded in-flight window.\n *\n * **Memory:** the Storage payload footprint is bounded by the window (`concurrency \u00D7 operands \u00D7 chunk`), not by\n * segment size \u2014 that's the Lambda-friendly property.\n *\n * Generation-consistent within the call (normal case): each operand's current generation is resolved **once**\n * up front (before the fan-out) and threaded into every chunk read, so a concurrent load can't corrupt or tear\n * the result \u2014 every chunk read is a whole, checksum-verified, immutable generation. The edge a *long* call can\n * hit is invariant 3's: if it straddles a mid-call `cache.genTtlMs` boundary and a load has published, an\n * operand's not-yet-requested chunks may re-resolve forward to the newer generation (a generation hop within one long\n * call) \u2014 the call never crashes or returns a torn object, but may mix generations. Three things hop it without\n * waiting for the TTL, so a shorter call can meet them too: **the reader cache evicting an operand mid-call**\n * (`maxOpenSegments`), whose re-read re-resolves fresh; a sweep deleting the generation it was reading, which\n * heals the read forward; and an invalidation, which this store's own `load`, `rollback` and `eraseSubject` make\n * and `invalidate()` makes on request. Still whole/immutable per read, never torn.\n */\n intersect(segs: readonly SegmentRef[], options?: CombineOptions): AsyncGenerator<number> {\n return this.combine(segs, options?.exclude ?? [], 'all', 'intersect', options);\n }\n\n /**\n * `segs[0] \u222A segs[1] \u222A \u2026`, minus every id in `options.exclude` \u2014 streamed ascending.\n *\n * **Union is the one composite read with no chunk-skipping**, and that is a property of the operation rather\n * than of this implementation: an id in *any* operand belongs to the result, so every chunk of every operand\n * has to be read. `intersect` can prune a key absent from any operand; union cannot prune anything. It is\n * budgeted exactly like `intersect` so a wide union is refused rather than quietly billed.\n */\n union(segs: readonly SegmentRef[], options?: CombineOptions): AsyncGenerator<number> {\n return this.combine(segs, options?.exclude ?? [], 'any', 'union', options);\n }\n\n /**\n * `seg \\ (excludes[0] \u222A excludes[1] \u222A \u2026)` \u2014 streamed ascending. Suppression on its own.\n *\n * Reads every chunk of `seg` (any of them may survive) but each exclude **only where it overlaps `seg`**, so\n * the cost scales with the segment being filtered rather than with the size of the suppression list.\n */\n andNot(\n seg: SegmentRef,\n excludes: readonly SegmentRef[],\n options?: Omit<CombineOptions, 'exclude'>,\n ): AsyncGenerator<number> {\n return this.combine([seg], excludes, 'all', 'andNot', options);\n }\n\n private async *combine(\n segs: readonly SegmentRef[],\n excludeSegs: readonly SegmentRef[],\n mode: 'all' | 'any',\n op: 'intersect' | 'union' | 'andNot',\n options?: Omit<CombineOptions, 'exclude'>,\n ): AsyncGenerator<number> {\n // Validated HERE rather than in the public wrappers, deliberately. `combine` is an async generator, so a\n // throw surfaces when the caller first iterates \u2014 which is the behaviour `intersect` has always had, and\n // `await expect(collect(engine.intersect([]))).rejects` in the suite depends on it. Guarding in the\n // wrappers would make them throw synchronously instead, a silent breaking change for anyone whose\n // try/catch sits around the iteration. Delegating through `yield*` would restore it at the cost of a\n // microtask per id on the hottest streaming path, which is not a trade worth making for an error case.\n if (segs.length === 0) {\n throw new ValidationError(`${op} requires at least one segment`);\n }\n if (op === 'andNot' && excludeSegs.length === 0) {\n throw new ValidationError('andNot requires at least one segment to subtract');\n }\n const limit = options?.concurrency ?? DEFAULT_INTERSECT_CONCURRENCY;\n if (!Number.isInteger(limit) || limit < 1) {\n throw new ValidationError(\n `concurrency must be a positive integer; got ${options?.concurrency}`,\n );\n }\n // Per-op budget override (else the store budget); resolved before any I/O so a bad value fails fast. A\n // partial override inherits the store's tightening (not the generous default) \u2014 see resolvePerOpBudget.\n const budget = resolvePerOpBudget(options?.budget, this.budget);\n // The range, if any, cuts every operand's keys once, here, so what follows \u2014 alignment, the budget, the\n // metrics, the fan-out \u2014 sees only chunks inside it. Only the two edge chunks are cut again, where ids leave.\n const w = windowOf(options);\n if (w === 'empty') return;\n\n // \u2460 Index-map extraction: each operand's chunk-key set + its current generation, resolved ONCE here (not\n // per chunk) so the fan-out below adds no per-chunk generation re-resolve. Only metadata so far.\n //\n // Generation resolution ordering: resolve `gen` AFTER the shape read (`listChunkKeys`). A non-empty shape\n // read means the source resolved and cached a non-null snapshot, so `gen` cannot then come back null while\n // storage data is present, and `storageChunk` cannot skip every chunk on a stale null.\n //\n // Honest note on how much this ordering buys, so a future reader does not take an untested claim for a tested\n // one. Against a `CrbmStorageChunkSource` \u2014 the\n // only source with a generation to resolve \u2014 both reads go through the same resolved-reader memo, so the\n // pathological state (`keys` non-empty, `gen` null) does not arise and swapping the two is observably\n // identical. The ordering is kept because it is the order that is correct for *any* source\n // satisfying the port \u2014 a custom source that resolves its shape and its generation independently can still\n // produce that state \u2014 not because a test can currently tell the difference.\n //\n // Within a call shorter than `cache.genTtlMs` the two share one snapshot, so the read is\n // generation-consistent \u2014 absent cache-pressure eviction (see `intersect`).\n const extract = async (seg: SegmentRef): Promise<Operand> => {\n const all = await this.chunkKeys(seg);\n const gen = await this.cacheVersion(seg);\n const keys = w === null ? all : keysWithin(all, w);\n return { seg, keys: new Set(keys), chunkless: all.length === 0, gen };\n };\n const [operands, excludes] = await Promise.all([\n Promise.all(segs.map(extract)),\n Promise.all(excludeSegs.map(extract)),\n ]);\n await this.refuseAbsentOperands([...operands, ...excludes], options?.allowAbsentOperands, op);\n\n // \u2461 Key alignment. Candidate keys come from the INCLUDE operands only \u2014 an exclude can subtract ids from a\n // chunk but can never introduce one, so a key no include holds cannot appear in the result however large\n // the suppression list is. That asymmetry is what makes suppression cheap here.\n //\n // 'all' (intersect / andNot): keys present in EVERY include. Start from the smallest set to minimize the\n // scan \u2014 a key missing from any operand cannot survive the AND.\n // 'any' (union): keys present in ANY include. No pruning is possible; see `union`'s note.\n const candidates: number[] = [];\n if (mode === 'all') {\n const pivot = operands.reduce((a, b) => (a.keys.size <= b.keys.size ? a : b));\n for (const k of pivot.keys) {\n if (operands.every((o) => o.keys.has(k))) candidates.push(k);\n }\n } else {\n const seen = new Set<number>();\n for (const o of operands) for (const k of o.keys) seen.add(k);\n for (const k of seen) candidates.push(k);\n }\n candidates.sort((a, b) => a - b); // defensive: `chunkKeys` already returns sorted; don't rely on it\n const common = candidates;\n\n // Denial-of-wallet budget (before the fan-out AND before we emit a \"work done\" metric): the heavy cost is\n // one storage fetch per surviving key per operand \u2014 refuse up front if that would exceed the budget. Placed\n // above the metric so a refused intersect doesn't report chunks it never fetched.\n //\n // Units are the chunk reads this will actually issue, counted per key: every include present at that key\n // (all of them under 'all'), plus only those excludes that hold the key. Counting excludes as always-present\n // would refuse work that costs nothing \u2014 the whole point of the asymmetry above. For a plain `intersect`\n // with no excludes this reduces to `common.length * operands.length`.\n let units = 0;\n for (const key of common) {\n for (const o of operands) if (mode === 'all' || o.keys.has(key)) units += 1;\n for (const e of excludes) if (e.keys.has(key)) units += 1;\n }\n checkBudget(budget, units, op);\n\n // Emit the chunk-skipping efficiency: `fetched` = shared keys we'll read, `skipped` = distinct keys\n // pruned across all operands (never fetched \u2014 the core saving). Bounded metadata work, no extra I/O.\n if (this.metricsOn) {\n const distinctKeys = new Set<number>();\n for (const o of operands) for (const k of o.keys) distinctKeys.add(k);\n for (const e of excludes) for (const k of e.keys) distinctKeys.add(k);\n // `fetchedChunks` counts **distinct chunk keys**, not per-operand reads \u2014 its documented unit, asserted by\n // the bench anchors and the metrics tests. It deliberately differs from the budget's request count: they\n // measure different things on purpose (keys vs billable requests). The\n // relationship is documented on the event instead.\n this.metrics.onEvent({\n kind: 'intersect',\n op,\n operands: segs.length + excludes.length,\n fetchedChunks: common.length,\n // Never negative: under 'any' every candidate key came from some operand, so `distinctKeys` is a\n // superset of `common`; excludes only ever add keys here.\n skippedChunks: distinctKeys.size - common.length,\n });\n }\n\n // \u2462\u2013\u2464 Surgical streaming AND through a bounded, order-preserving window: fetch+intersect at most\n // `limit` keys concurrently, yield each key's ids before priming far ahead (bounded Storage footprint).\n // Each task resolves to a value (never rejects) so an error on one key can't leave the other in-flight\n // promises unhandled \u2014 we surface it, in key order, when its slot is drained.\n type Slot = { key: number; result: CodecBitmap | null; error?: unknown };\n const startAt = (key: number): Promise<Slot> =>\n this.combineChunk(operands, excludes, mode, key).then(\n (result) => ({ key, result }),\n (error: unknown) => ({ key, result: null, error }),\n );\n\n const inFlight: Array<Promise<Slot>> = [];\n let next = 0;\n while (next < common.length && inFlight.length < limit) inFlight.push(startAt(common[next++]!));\n while (inFlight.length > 0) {\n const slot = await inFlight.shift()!; // FIFO over ascending keys \u21D2 ascending output\n if (next < common.length) inFlight.push(startAt(common[next++]!));\n if (slot.error !== undefined) throw slot.error;\n if (slot.result && !slot.result.isEmpty) {\n if (w !== null && isEdge(slot.key, w)) {\n for (const id of edgeIds(slot.result, slot.key, w)) yield id;\n } else {\n for (const remainder of slot.result) yield joinId(slot.key, remainder);\n }\n }\n }\n }\n\n /**\n * The AND (or OR) of one chunk key across all operands, minus the excludes that hold it, or `null` if empty.\n * Operand chunks are fetched **in parallel** (the spec's parallel byte-range reads).\n *\n * The accumulator is a **clone** of the first operand's chunk, and that clone is load-bearing: every other\n * bitmap here is the cached, shared Storage instance and must never be mutated, or the cache is poisoned for\n * every later reader. The remaining operands and the excludes are only *read* by the in-place ops, so they are\n * used as-is \u2014 one clone per key, not one per operand.\n */\n private async combineChunk(\n operands: ReadonlyArray<Operand>,\n excludes: ReadonlyArray<Operand>,\n mode: 'all' | 'any',\n chunkKey: number,\n ): Promise<CodecBitmap | null> {\n // Under 'any' an include may simply not hold this key; skip it rather than fetching a certainly-empty\n // chunk. Under 'all' every include holds every candidate key by construction, so the filter is a no-op.\n const present = mode === 'all' ? operands : operands.filter((o) => o.keys.has(chunkKey));\n if (present.length === 0) return null;\n const chunks = await Promise.all(\n present.map((op) => this.storageChunk({ ...op.seg, chunkKey }, op.gen)),\n );\n // A key the index lists but the source cannot produce bytes for reads as empty \u2014 and under AND an empty\n // operand empties the result.\n if (mode === 'all' && chunks.some((c) => c === null)) return null;\n const first = chunks[0];\n const acc = first ? first.clone() : this.codec.empty();\n for (let i = 1; i < chunks.length; i++) {\n const other = chunks[i];\n if (other === null || other === undefined) continue; // 'any' only \u2014 'all' returned above\n if (mode === 'all') {\n acc.andInPlace(other);\n if (acc.isEmpty) return null; // once empty, the remaining operands can't revive it\n } else {\n acc.orInPlace(other);\n }\n }\n if (acc.isEmpty) return null;\n\n // Suppression, folded into the same pass. Only excludes that actually hold this key are fetched \u2014 this is\n // where a large global opt-out list stops being expensive. Fetched lazily rather than alongside the\n // includes above, because an AND that collapsed to empty means there is nothing left to suppress.\n const relevant = excludes.filter((e) => e.keys.has(chunkKey));\n if (relevant.length > 0) {\n const cuts = await Promise.all(\n relevant.map((e) => this.storageChunk({ ...e.seg, chunkKey }, e.gen)),\n );\n for (const cut of cuts) {\n if (cut === null) continue;\n acc.andNotInPlace(cut);\n if (acc.isEmpty) return null;\n }\n }\n return acc;\n }\n\n /** The segment's chunk keys, ascending \u2014 a shape read off the index, no payload. Keys are untrusted (invariant 5). */\n private async chunkKeys(seg: SegmentRef): Promise<number[]> {\n const keys = [...(await this.storage.listChunkKeys(seg))];\n for (const k of keys) this.assertChunkKeyInRange(k);\n keys.sort((a, b) => a - b);\n // A key listed twice would be read and yielded twice; the `.crbm` reader refuses one, a custom source may not.\n for (let i = 1; i < keys.length; i++) {\n if (keys[i] === keys[i - 1]) {\n throw new IntegrityError(`chunk key from a tier is listed twice: ${keys[i]}`);\n }\n }\n return keys;\n }\n\n /**\n * Tier-derived keys are untrusted (invariant 5) \u2014 fail fast on a corrupt/out-of-range key rather than\n * letting it flow into the id-routing step and produce a bogus id.\n */\n private assertChunkKeyInRange(k: number): void {\n if (!Number.isInteger(k) || k < 0 || k >= CHUNK_COUNT) {\n throw new IntegrityError(`chunk key from a tier is out of range: ${k}`);\n }\n }\n\n /**\n * The other half of invariant 5: a chunk payload holds **remainders** \u2014 16-bit offsets within one chunk \u2014 so\n * every value must be `<= 0xffff`.\n *\n * Nothing upstream establishes that. The byte cap bounds *size*, and CRC/AEAD prove the bytes are the bytes\n * that were written \u2014 which anyone able to write the bucket satisfies trivially. A value `>= 65536` would then\n * reach the id-routing step, which masks it (`remainder & 0xffff`) and emits a **fabricated id belonging to a different\n * chunk's id space**: indistinguishable from real data, inflating `count()` and creating spurious `intersect`\n * matches.\n *\n * Costs one `maximum()` per chunk, not per id \u2014 `maximum` is optional on the codec seam precisely so a codec\n * that cannot answer in O(1) opts out instead of making the read path walk every value.\n *\n * One value stands for all of them only because the decode is structurally checked. Roaring answers `maximum()`\n * from its last container, so a payload listing its containers out of order would report the wrong one's\n * largest value and pass here while holding values above 65,535. The codec's `safeDeserialize` refuses that\n * payload, and every other shape that would make this answer wrong, before it reaches this check.\n */\n private assertChunkPayloadInRange(bitmap: CodecBitmap, chunkKey: number): void {\n const max = bitmap.maximum?.();\n if (max !== undefined && max > MAX_REMAINDER) {\n throw new IntegrityError(\n `chunk ${chunkKey} payload holds value ${max}, outside the 16-bit remainder range ` +\n `[0, ${MAX_REMAINDER}] \u2014 the stored object is corrupt or was not written by this codec`,\n );\n }\n }\n\n /**\n * Refuse a combine whose operand names a segment that was never created.\n *\n * A segment that resolves to nothing is ambiguous in a way that matters only here. Read it directly and\n * \"empty\" is the right answer either way. Pass it as an **operand** and the two states diverge: a\n * suppression list with nobody on it correctly suppresses nothing, while one whose namespace you omitted\n * *silently* suppresses nothing \u2014 and the result is not empty and obviously wrong, it is the full audience\n * and plausibly right. That direction is the dangerous one: the failure mode is mailing the people who\n * opted out.\n *\n * The asymmetry is why this is checked rather than left to the caller. A mistyped **include** collapses an\n * intersect to nothing, which you notice; a mistyped **exclude** removes a safeguard, which you do not. Both\n * are refused, because both are wiring errors and one rule is easier to trust than two.\n *\n * **Costs nothing on a normal combine.** `exists` is consulted only for an operand that resolved to zero\n * chunks \u2014 a segment with data is self-evidently registered \u2014 so the check is a registry read exactly when\n * a combine was about to do something suspicious, and no reads at all otherwise. A source that cannot answer\n * `exists` (it has no notion of registration) skips the check rather than guessing. An operand is judged by\n * all its chunks, not those inside a range: one with nothing in range but data elsewhere is not suspicious.\n */\n private async refuseAbsentOperands(\n resolved: readonly Operand[],\n allow: boolean | undefined,\n op: string,\n ): Promise<void> {\n if (allow === true || this.storage.exists === undefined) return;\n const empty = resolved.filter((o) => o.chunkless);\n if (empty.length === 0) return;\n const checked = await Promise.all(\n empty.map(async (o) => ({ seg: o.seg, exists: await this.storage.exists!(o.seg) })),\n );\n const absent = checked.filter((c) => !c.exists).map((c) => c.seg);\n if (absent.length === 0) return;\n const named = absent\n .map((s) => (s.namespace === undefined ? `\"${s.segment}\"` : `\"${s.namespace}/${s.segment}\"`))\n .join(', ');\n throw new ValidationError(\n `${op}: operand ${named} names a segment that does not exist, so it would contribute nothing \u2014 ` +\n `an exclude would suppress no ids and an include would contribute none. Check the name and the ` +\n `namespace (a segment addressed without its \\`namespace\\` is a DIFFERENT segment). ` +\n `Pass \\`allowAbsentOperands: true\\` if you meant it.`,\n );\n }\n\n /**\n * Drop every piece of state this engine derived from `ref`, and tell the Storage source to do the same.\n *\n * The decoded-chunk cache is keyed by generation, which handles a *publish* (the new generation misses) but\n * not a *destruction*: a segment that was erased from, dropped, shredded or retired has no newer generation\n * whose key would miss, so its decoded chunks stay resident and keep answering. That is why the erasing\n * process itself could report `erased: true` and then still answer `true` for the same id, out of RAM, with\n * no storage read to intercept.\n *\n * Called by the destructive verbs on the facade. Synchronous and best-effort \u2014 forgetting cannot fail.\n */\n invalidate(ref: SegmentRef): void {\n const prefix = segmentPrefix(ref);\n this.cache?.deleteWhere((key) => key.startsWith(prefix));\n this.storage.invalidate?.(ref);\n }\n\n /** The segment's current generation, resolved once per op (`undefined` \u21D2 source can't report it). */\n private currentGen(seg: SegmentRef): Promise<number | null | undefined> {\n return this.storage.currentGeneration\n ? this.storage.currentGeneration(seg)\n : Promise.resolve(undefined);\n }\n\n /**\n * The cache-key component identifying **which bytes** this op will read: the source's `currentVersion` when\n * it has one, else the generation alone.\n *\n * The generation alone is not an identity. A load's generation number restarts at 0 once a row is purged\n * and the bucket emptied, so a retired-and-re-created name serves different data at the same `currentGen` \u2014 and a\n * decoded chunk cached under `(segment, chunk, 0)` is handed straight back to the new incarnation. That is an\n * erased id reappearing with no read to intercept it, which is why this is keyed on the version rather than\n * the number.\n */\n private async cacheVersion(seg: SegmentRef): Promise<string | number | null | undefined> {\n if (this.storage.currentVersion) return this.storage.currentVersion(seg);\n return this.currentGen(seg);\n }\n\n /**\n * Decode a Storage chunk. `gen` is the segment's current generation, resolved **once per op** by the caller (not\n * per chunk \u2014 that would put a registry re-resolve on every chunk of a count/intersect). The cache is\n * keyed by it, so a load that advances the generation misses the cache and re-reads the new bytes instead of\n * serving a stale decoded chunk (an erased id can't resurrect from a cached superseded chunk). `gen === null`\n * \u21D2 the source reports no current generation \u21D2 no storage bytes for any chunk, so skip the fetch entirely.\n * `gen === undefined` \u21D2 the source can't report a generation (it only ever serves one) \u21D2 the key stays\n * generation-free. Superseded-generation entries age out under the LRU ceiling \u2014 no active purge.\n *\n * The returned instance is **shared** (it may be the cached one): callers read it or clone it, never mutate it.\n */\n private async storageChunk(\n ref: ChunkRef,\n gen: string | number | null | undefined,\n ): Promise<CodecBitmap | null> {\n if (gen === null) return null;\n const cacheKey = gen === undefined ? chunkRefKey(ref) : chunkGenKey(ref, gen);\n if (this.cache) {\n const cached = this.cache.get(cacheKey);\n if (cached) {\n if (this.metricsOn) this.metrics.onEvent({ kind: 'cache', hit: true });\n return cached;\n }\n if (this.metricsOn) this.metrics.onEvent({ kind: 'cache', hit: false });\n }\n const startedAt = this.metricsOn ? this.clock.now() : 0;\n const bytes = await this.storage.getChunk(ref);\n if (this.metricsOn) {\n this.metrics.onEvent({\n kind: 'storage.get',\n namespace: ref.namespace,\n segment: ref.segment,\n bytes: bytes ? bytes.length : 0,\n ms: Math.max(0, this.clock.now() - startedAt),\n });\n }\n if (!bytes) return null;\n const bitmap = this.codec.safeDeserialize(bytes, this.maxBitmapBytes);\n this.assertChunkPayloadInRange(bitmap, ref.chunkKey);\n this.cache?.set(cacheKey, bitmap);\n return bitmap;\n }\n}\n", "/**\n * Bounded LRU cache with optional TTL \u2014 the cache tier's memory ceiling.\n *\n * Time comes from an injected `Clock` (never `Date.now()`) so the cache is deterministic\n * under simulation.\n *\n * The cache can be bounded by entry **count** (`maxEntries`, always) and, optionally, by an aggregate\n * **byte weight** (`maxBytes`). The byte bound exists for values whose footprint is large and uneven \u2014 the\n * storage reader cache, where one entry pins a fully-parsed `.crbm` index that can be ~6 MB for a wide segment\n * yet a few hundred bytes for a small one (a count-only bound lets 1024 wide indices blow a Lambda's\n * memory even though the count is \"in bounds\"). Weights arrive **after** insertion (a cached reader is a\n * pending promise; its size is known only once it resolves), so weight is reported via {@link setWeight} and\n * eviction re-runs then.\n */\nimport type { Clock } from './determinism';\nimport { ValidationError } from './errors';\n\nexport interface LruOptions {\n /** Hard ceiling on entries; the least-recently-used is evicted past it. */\n readonly maxEntries: number;\n /**\n * Optional aggregate byte ceiling across all entries' reported weights (see {@link BoundedLru.setWeight}).\n * When set, the least-recently-used entries are evicted until the total is within budget \u2014 in addition to\n * the `maxEntries` bound. Omit for a pure count-bounded cache (weights stay 0, no byte eviction).\n */\n readonly maxBytes?: number;\n /** Optional time-to-live in ms; an entry older than this is treated as absent. */\n readonly ttlMs?: number;\n /** Only `now()` is used (never `sleep`), so a count-only cache can pass a minimal time source. */\n readonly clock: Pick<Clock, 'now'>;\n}\n\ninterface Entry<V> {\n readonly value: V;\n readonly expiresAt: number; // Infinity when no TTL\n bytes: number; // reported weight (0 until setWeight); summed against maxBytes\n}\n\nexport class BoundedLru<K, V> {\n private readonly map = new Map<K, Entry<V>>();\n private readonly maxEntries: number;\n private readonly maxBytes: number | undefined;\n private readonly ttlMs: number | undefined;\n private readonly clock: Pick<Clock, 'now'>;\n private totalBytes = 0;\n\n constructor(options: LruOptions) {\n if (!Number.isInteger(options.maxEntries) || options.maxEntries < 1) {\n throw new ValidationError(`maxEntries must be a positive integer; got ${options.maxEntries}`);\n }\n if (\n options.maxBytes !== undefined &&\n (!(options.maxBytes > 0) || !Number.isFinite(options.maxBytes))\n ) {\n throw new ValidationError(\n `maxBytes must be a positive finite number; got ${options.maxBytes}`,\n );\n }\n if (options.ttlMs !== undefined && (!(options.ttlMs > 0) || !Number.isFinite(options.ttlMs))) {\n throw new ValidationError(`ttlMs must be a positive finite number; got ${options.ttlMs}`);\n }\n this.maxEntries = options.maxEntries;\n this.maxBytes = options.maxBytes;\n this.ttlMs = options.ttlMs;\n this.clock = options.clock;\n }\n\n get(key: K): V | undefined {\n const entry = this.map.get(key);\n if (entry === undefined) return undefined;\n if (entry.expiresAt <= this.clock.now()) {\n this.drop(key, entry);\n return undefined;\n }\n // Refresh recency: re-insert so it becomes most-recently-used.\n this.map.delete(key);\n this.map.set(key, entry);\n return entry.value;\n }\n\n /** Read a value **without** counting as an access (recency unchanged, no purge on expiry). */\n peek(key: K): V | undefined {\n return this.map.get(key)?.value;\n }\n\n set(key: K, value: V): void {\n const prev = this.map.get(key);\n if (prev !== undefined) this.totalBytes -= prev.bytes;\n this.map.delete(key);\n const expiresAt = this.ttlMs === undefined ? Infinity : this.clock.now() + this.ttlMs;\n this.map.set(key, { value, expiresAt, bytes: 0 });\n while (this.map.size > this.maxEntries) {\n const oldest = this.map.keys().next();\n if (oldest.done === true) break;\n this.dropKey(oldest.value);\n }\n this.evictToBudget(key);\n }\n\n /**\n * Report an entry's byte weight (typically once a cached promise resolves and its size is known). Updates\n * the running total and evicts the least-recently-used entries if the aggregate now exceeds `maxBytes`.\n * Recency is **not** changed. The `key` being weighted is protected from this eviction pass (we just\n * populated it) \u2014 so a single entry larger than the whole budget is kept rather than immediately dropped.\n */\n setWeight(key: K, bytes: number): void {\n if (!(bytes >= 0) || !Number.isFinite(bytes)) {\n // Guard the running total against a NaN/negative weight, which no removal path could ever restore.\n throw new ValidationError(`weight must be a non-negative finite number; got ${bytes}`);\n }\n const entry = this.map.get(key);\n if (entry === undefined) return;\n this.totalBytes += bytes - entry.bytes;\n entry.bytes = bytes;\n this.evictToBudget(key);\n }\n\n private evictToBudget(protectKey: K): void {\n if (this.maxBytes === undefined) return;\n while (this.totalBytes > this.maxBytes && this.map.size > 1) {\n const oldest = this.oldestExcept(protectKey);\n if (oldest === undefined) break; // only the protected key remains\n this.dropKey(oldest);\n }\n }\n\n /** The least-recently-used key that is not `protectKey`, or undefined if none other exists. */\n private oldestExcept(protectKey: K): K | undefined {\n for (const key of this.map.keys()) {\n if (key !== protectKey) return key;\n }\n return undefined;\n }\n\n private dropKey(key: K): void {\n const entry = this.map.get(key);\n if (entry !== undefined) this.drop(key, entry);\n }\n\n private drop(key: K, entry: Entry<V>): void {\n this.totalBytes -= entry.bytes;\n this.map.delete(key);\n }\n\n /** Membership test that does NOT count as an access (recency is unchanged). Purges if expired. */\n has(key: K): boolean {\n const entry = this.map.get(key);\n if (entry === undefined) return false;\n if (entry.expiresAt <= this.clock.now()) {\n this.drop(key, entry);\n return false;\n }\n return true;\n }\n\n delete(key: K): boolean {\n const entry = this.map.get(key);\n if (entry === undefined) return false;\n this.drop(key, entry);\n return true;\n }\n\n /**\n * Delete every entry whose key matches `pred`, returning how many went. Used to drop one segment's decoded\n * chunks when something destroys the generation they came from \u2014 a deletion the generation-keyed cache\n * cannot express on its own, because a *destroyed* segment has no newer generation whose key would miss.\n * Linear in the cache size; called on admin paths (erasure, drop, shred, retirement), never on a read.\n */\n deleteWhere(pred: (key: K) => boolean): number {\n let dropped = 0;\n // Snapshot the keys first: `drop` mutates the map, and deleting during its own iteration is fragile.\n for (const key of [...this.map.keys()]) {\n if (!pred(key)) continue;\n const entry = this.map.get(key);\n if (entry === undefined) continue;\n this.drop(key, entry);\n dropped += 1;\n }\n return dropped;\n }\n\n clear(): void {\n this.map.clear();\n this.totalBytes = 0;\n }\n\n /** Current entry count (may include not-yet-evicted expired entries). */\n get size(): number {\n return this.map.size;\n }\n\n /** Current aggregate reported weight (bytes) across all entries. */\n get weightBytes(): number {\n return this.totalBytes;\n }\n}\n", "/**\n * The storage reader's defaults, stated once for the two modules that need them: the reader, which applies them,\n * and the cost model, which prices what they cause. The cost model once imported them from the reader itself,\n * which pulled the whole read path into a module that is otherwise arithmetic.\n */\n\n/** Default TTL (ms) for re-resolving a segment's `currentGen` \u2014 the bound on post-publish read staleness. */\nexport const DEFAULT_CURRENT_GEN_TTL_MS = 2000;\n\n/**\n * How long (ms) a reader keeps serving after a pointer refresh failed transiently, before the next read asks the\n * registry again. Short, so an outage ends the stale serving soon after the registry answers; fixed, so a registry\n * that is down is asked at most once per this interval per segment being read (the refresh is coalesced per\n * segment). Never longer than the TTL itself.\n */\nexport const REFRESH_RETRY_MS = 500;\n\n/** Default ceiling on cached segment readers (each holds a parsed `.crbm` index) \u2014 the steady-state count bound. */\nexport const DEFAULT_MAX_OPEN_SEGMENTS = 1024;\n\n/** Default aggregate ceiling (bytes) on resident parsed indices in the reader cache \u2014 the steady-state byte bound. */\nexport const DEFAULT_MAX_OPEN_INDEX_BYTES = 64 * 1024 * 1024;\n", "/**\n * Cost model & estimator \u2014 pure, `core/`-safe (no I/O, time, or SDK). Turns the verified economics of the\n * loaded store into a first-class library API:\n *\n * - {@link estimateCost} \u2014 **planning** mode: pure what-if from segment sizes + a workload (sizing/sales).\n * - `segment.costReport()` (wired in the facade) \u2014 **grounded** mode: the segment's real size from the `.crbm`\n * index (free + exact, no payload reads) + a supplied workload for request rates. (Whole-store aggregation is\n * a later phase.)\n *\n * Guiding split: **formulas are the spec; rates are a pluggable {@link PricingProfile}.** The report always emits\n * a {@link CostReport.verdict} that includes the lose-zone \u2014 it never hides where an always-on cache (Redis)\n * wins.\n *\n * What the model covers, and states in `assumptions.notes`: object-store GETs for point reads, for\n * intersections (each operand's pointer and index as well as its chunks) and for the pointer refresh a long-lived\n * reader pays; the requests of a load (the object's write, and the listings and pointer reads and write\n * `store.load()` makes around it); and storage. Same-region egress is treated as free and internet egress is not\n * modeled; request cost is derived from the supplied workload rates (deriving it from live metrics counters is a\n * later refinement). There is no per-write term because the loaded store has no per-id write: data arrives as\n * generations, and a generation is a load.\n *\n * Every request count below is one the engine makes, and `tests/core/cost.test.ts` holds each to the engine by\n * counting what it sends: the model is only as honest as those counts, and they move when the engine does. They\n * are for a single-bucket store, where the pointer is an object beside the data, which is the topology that\n * ships. A load is more than the object's PUT, and an intersect more than its chunk reads: pricing either as the\n * one alone under-quotes a load by more than half and leaves out a pointer read and an index read for every\n * operand.\n *\n * The counts are S3's (and GCS's), and one reader process's. On Azure Blob a read that needs the object's size \u2014 a\n * pointer read, and a segment's tail read \u2014 is two requests, the properties and then the bytes, which\n * {@link PricingProfile} carries as `requestsPerSizedRead`. A fleet of reader processes pays the pointer refresh\n * once per process, which {@link Workload.readerProcesses} carries. Where the model still quotes low is listed on\n * {@link Workload.hotSegments} and {@link Workload.chunksPerIntersect}.\n */\nimport { ValidationError } from './errors';\nimport { DEFAULT_CURRENT_GEN_TTL_MS, DEFAULT_MAX_OPEN_SEGMENTS } from './reader-defaults';\n\n/**\n * Freeze a constant and everything in it, so a caller that changes one \u2014 a `push` onto a catalogue's rows \u2014 cannot\n * change the default for every other caller in the process: the change throws in strict mode, as in every ES module,\n * and is ignored in a sloppy-mode script.\n */\nfunction deepFreeze<T>(value: T): Readonly<T> {\n if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const inner of Object.values(value)) deepFreeze(inner);\n }\n return value;\n}\n\n/** Pluggable rate card. Rates differ by cloud/region/term and drift over time; the formulas don't. */\nexport interface PricingProfile {\n readonly name: string;\n readonly storage: {\n /** Object GET (per **million** requests). A ranged GET bills as a full GET. */\n readonly getPerMillion: number;\n /** Object PUT (per million) \u2014 what a load pays, per request. */\n readonly putPerMillion: number;\n readonly storagePerGiBMonth: number;\n /**\n * Requests one read costs when it needs the object's size first: a pointer read, and a segment's tail read.\n * Default **1**, S3's, whose suffix-range GET returns the size with the bytes. **2** on Azure Blob,\n * which reads the properties and then the bytes; GCS answers in one request, like S3. A chunk read knows its range, and is one request everywhere.\n */\n readonly requestsPerSizedRead?: number;\n };\n /**\n * The always-on Redis the verdict compares against: exactly one of the two. `sizedToData`, the default's, prices\n * the cheapest cluster that holds the report's stored bytes; `monthlyUSD` is one cluster you name, whatever the\n * data size. A profile carrying both is refused rather than read one way.\n */\n readonly redis:\n | { readonly monthlyUSD: number; readonly sizedToData?: never }\n | { readonly sizedToData: RedisSizing; readonly monthlyUSD?: never };\n}\n\n/** One node type a Redis cluster can be built from, as its cloud prices it. */\nexport interface RedisNodeType {\n readonly name: string;\n /** Memory, in GiB, as the cloud publishes it for the node type. */\n readonly memoryGiB: number;\n /**\n * SSD, in GiB, on a data-tiering node (ElastiCache's `r6gd`): keys stay in memory, and the values read least\n * recently move to the SSD. Counted in full beside the memory, as AWS counts a data-tiering node's capacity.\n */\n readonly ssdGiB?: number;\n /** Price per node-hour, in USD. */\n readonly hourlyUSD: number;\n /**\n * The most shards a cluster of this type is priced at. The default catalogue prices its burstable nodes as one\n * shard: a cluster sharded across them is not how data anyone queries is held. Default: no limit.\n */\n readonly maxShards?: number;\n}\n\n/**\n * Redis sized to hold the data: enough shards for the bytes, each a primary and its replicas, on whichever node\n * type in `nodeTypes` makes that cheapest. Prices within a millionth of a dollar of the cheapest are a tie, which\n * fewer nodes win, then the lower price, then the name, so the answer never depends on the order of the rows. The data is held at its stored,\n * compressed size, which is a floor on the memory Redis needs for it: a native Redis bitmap is sized by its highest\n * id, not by how many ids it holds, so sparse ids need more.\n */\nexport interface RedisSizing {\n /** Where the prices come from, and when; the report's notes quote it. */\n readonly source: string;\n readonly nodeTypes: readonly RedisNodeType[];\n /** Replicas per shard. AWS's best practice is 2; Multi-AZ needs at least 1. */\n readonly replicasPerShard: number;\n /** The share of each node's memory kept back from data: ElastiCache's `reserved-memory-percent`, 25% by default. */\n readonly reservedMemoryFraction: number;\n}\n\n/**\n * **ElastiCache for Redis OSS in us-east-1, on-demand**: node prices from AWS's public price list, version\n * 20260914063714 (published 2026-09-14, effective 2026-09-01,\n * https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonElastiCache/20260914063714/us-east-1/index.json), and the\n * memory AWS publishes for each node type. Every shard is a primary and two replicas, AWS's best practice, with\n * ElastiCache's default 25% of each node's memory reserved, and the burstable `t4g` nodes are priced as one shard.\n *\n * It lists only rows that are the cheapest fit for some size of data: the data-tiering `r6gd` rows above about\n * 30 GiB, and the larger in-memory `r6g` rows, `2xlarge` and up, when those are left out. The current `m7g` and `r7g` nodes are not here,\n * because an `m6g` or `r6g` with the same memory costs less. It is the cheapest cluster of THIS kind; Redis bought\n * another way costs less \u2014 one replica a shard a third less, ElastiCache for Valkey 20% less a node, reserved nodes\n * less again \u2014 so against those the saving the verdict reports is smaller. Pass their prices to compare with them.\n */\nexport const ELASTICACHE_REDIS_US_EAST_1_ONDEMAND: RedisSizing = deepFreeze({\n source: 'ElastiCache for Redis OSS, us-east-1 on-demand, AWS price list 20260914063714',\n replicasPerShard: 2,\n reservedMemoryFraction: 0.25,\n nodeTypes: [\n { name: 'cache.t4g.micro', memoryGiB: 0.5, hourlyUSD: 0.016, maxShards: 1 },\n { name: 'cache.t4g.small', memoryGiB: 1.37, hourlyUSD: 0.032, maxShards: 1 },\n { name: 'cache.t4g.medium', memoryGiB: 3.09, hourlyUSD: 0.065, maxShards: 1 },\n { name: 'cache.m6g.large', memoryGiB: 6.38, hourlyUSD: 0.149 },\n { name: 'cache.r6g.large', memoryGiB: 13.07, hourlyUSD: 0.206 },\n { name: 'cache.r6g.xlarge', memoryGiB: 26.32, hourlyUSD: 0.411 },\n { name: 'cache.r6g.2xlarge', memoryGiB: 52.82, hourlyUSD: 0.821 },\n { name: 'cache.r6g.4xlarge', memoryGiB: 105.81, hourlyUSD: 1.642 },\n { name: 'cache.r6g.8xlarge', memoryGiB: 209.55, hourlyUSD: 3.284 },\n { name: 'cache.r6g.12xlarge', memoryGiB: 317.77, hourlyUSD: 4.925 },\n { name: 'cache.r6g.16xlarge', memoryGiB: 419.09, hourlyUSD: 6.567 },\n { name: 'cache.r6gd.xlarge', memoryGiB: 26.32, ssdGiB: 99.33, hourlyUSD: 0.781 },\n { name: 'cache.r6gd.2xlarge', memoryGiB: 52.82, ssdGiB: 199.07, hourlyUSD: 1.56 },\n { name: 'cache.r6gd.4xlarge', memoryGiB: 105.81, ssdGiB: 398.14, hourlyUSD: 3.12 },\n { name: 'cache.r6gd.8xlarge', memoryGiB: 209.55, ssdGiB: 796.28, hourlyUSD: 6.24 },\n { name: 'cache.r6gd.12xlarge', memoryGiB: 317.77, ssdGiB: 1194.42, hourlyUSD: 9.358 },\n { name: 'cache.r6gd.16xlarge', memoryGiB: 419.09, ssdGiB: 1592.56, hourlyUSD: 12.477 },\n ],\n});\n\n/**\n * One ElastiCache HA cluster, a primary and two replicas of cache.m7g.large \u2014 the m7g family's smallest node \u2014\n * at $0.158 an hour each: 3 \u00D7 730 h \u00D7 $0.158 = $346.02, published to the dollar. The per-request crossovers on the\n * benchmarks page are drawn against this one cluster, whatever the data size; pass it as `pricing.redis` to compare\n * with it. It is not the cheapest cluster for any size of data: three cache.m6g.large hold the same memory for less.\n */\n// The trailing comment below is parsed by scripts/site-figures.cjs, and the literal by bench/lib/calibration-figures.cjs.\nexport const ONE_REDIS_HA_CLUSTER = deepFreeze({ monthlyUSD: 346 }); // ElastiCache HA: 1 primary + 2 replicas (cache.m7g.large); ~$115 single-node\n\n/**\n * Default profile \u2014 **AWS us-east-1, on-demand**, mid-2026, from the fact-checked published pricing rather than\n * copied from a blog post, with Redis sized to the data. Override it for your region, cloud, or committed term.\n */\nexport const AWS_US_EAST_1_ONDEMAND: PricingProfile = deepFreeze({\n name: 'aws-us-east-1-ondemand',\n storage: { getPerMillion: 0.4, putPerMillion: 5.0, storagePerGiBMonth: 0.023 },\n redis: { sizedToData: ELASTICACHE_REDIS_US_EAST_1_ONDEMAND },\n});\n\n/** Sustained access pattern. All rates default to 0; unspecified \u21D2 that op contributes nothing. */\nexport interface Workload {\n /**\n * Point reads (`has`) per second. Each cache miss is one object GET, once the segment's pointer and index are\n * read.\n */\n readonly readsPerSec?: number;\n readonly intersectsPerSec?: number;\n /** Cache hit rate in `[0, 1]` \u2014 hits are free; only misses cost. Default 0. */\n readonly cacheHitRate?: number;\n /**\n * Storage chunks one intersection fetches, summed over its operands: the chunk-skipping survivors. Default 1.\n * Two segments sharing `k` chunks fetch `2k`. The model adds each operand's pointer and index reads itself (see\n * {@link Workload.operandsPerIntersect}), so count chunks only. One more GET per operand whose index outgrows\n * the reader's tail read (256 KiB by default), which then reads the index whole, belongs here too, and so does a\n * pointer re-read by an intersect slow enough to outlive {@link Workload.genTtlMs}.\n */\n readonly chunksPerIntersect?: number;\n /**\n * Segments each intersection reads, `exclude` operands included; at least 1. Default 2. An intersection is priced\n * **cold**: before its chunks, each operand's pointer is read, then its index, in one read of the object's tail \u2014\n * 2 GETs an operand, so a cold intersect of two segments sharing `k` chunks is `4 + 2k` GETs. `cacheHitRate`\n * does not apply to intersections, so a long-lived reader that answers a repeat from its cache pays less. Other\n * combines read their operands the same way and can be priced here too, with the chunks they fetch.\n */\n readonly operandsPerIntersect?: number;\n /**\n * Generations published per month across the modeled data \u2014 the write side of a loaded store. Default **0**\n * \u21D2 loads are not modeled and the report *discloses* the omission rather than silently under-reporting.\n */\n readonly loadsPerMonth?: number;\n /**\n * PUT-class requests one load's object write issues, each priced at the PUT rate. Default **1** (a single-object\n * PUT). A multipart write of `P` parts bills `P + 2` (initiate, the parts, complete) \u2014 set it when you know your\n * object sizes. The model adds what `store.load()` does around the write: two listings and the pointer's write,\n * PUT-class on S3, and nine GETs, the pointer read eight times and the current generation's index once. That is\n * a segment with two generations behind it; its first load makes two fewer GETs, and its second one fewer. On\n * S3 at the default prices a single-part `store.load()` is then about $23.60 per million. A segment whose index\n * outgrows the tail read makes one more GET, and a publish that loses a race to another writer reads the\n * pointer again.\n */\n readonly requestsPerLoad?: number;\n /**\n * Segments each long-lived reader process keeps reading. A reader re-reads a segment's pointer when it reads the\n * segment after {@link Workload.genTtlMs} has passed, so each costs at most one GET per `genTtlMs` in each process\n * that reads it \u2014 1,314,000 GETs a month at the default 2 s \u2014 and the whole term at most one GET per point read\n * (`readsPerSec`, across the fleet). Intersections are priced cold and pay their own pointer reads. Default **0**\n * \u21D2 the refresh is not modeled, and the report *discloses* the omission when there are reads to refresh for.\n *\n * It assumes each hot segment stays open in the reader's cache (`cache.readerMax`, 1,024 segments by default,\n * and `cache.readerMaxBytes`, 64 MiB of parsed index). A read of a segment the cache evicted opens it again, a\n * pointer read and a tail read, which is not priced here, and neither is the re-open every reader makes after\n * each load, for the new generation's index. Size those caches to keep the hot set open; the report says so when\n * `hotSegments` is more than a store keeps open by default.\n */\n readonly hotSegments?: number;\n /**\n * Long-lived reader processes, each keeping its own {@link Workload.hotSegments} open and refreshing their\n * pointers on its own: ten processes reading the same hundred segments pay ten times one process's refresh.\n * At least 1. Default 1.\n */\n readonly readerProcesses?: number;\n /**\n * How long the reader trusts a pointer, in ms: the store's `cache.genTtlMs`. Default 2000, the store's own\n * default; `segment.costReport()` uses the store's. `0` turns the timed refresh off, and the model bills none: a\n * reader then re-reads a pointer only when its cache evicts the segment, a read finds its generation swept, or a\n * write invalidates it, none of which this term prices.\n */\n readonly genTtlMs?: number;\n}\n\n/** One (group of) segment(s) for planning. `count` = how many like this (default 1). */\nexport interface SegmentSizing {\n readonly sizeBytes?: number;\n readonly cardinality?: number;\n /** Number of segments with these characteristics. Default 1. */\n readonly count?: number;\n}\n\nexport interface EstimateInput {\n readonly segments: readonly SegmentSizing[];\n readonly workload?: Workload;\n readonly pricing?: PricingProfile;\n}\n\nexport interface CostReport {\n readonly monthlyUSD: {\n readonly byOp: {\n readonly reads: number;\n readonly intersects: number;\n readonly storage: number;\n /**\n * Loads: each its object's `requestsPerLoad` PUT-class requests plus what `store.load()` adds around them.\n * 0 unless `loadsPerMonth` is set.\n */\n readonly loads: number;\n /** The pointer refresh of {@link Workload.hotSegments}. 0 unless `hotSegments` is set. */\n readonly pointerRefresh: number;\n };\n readonly total: number;\n };\n /**\n * The Redis the verdict compares against. Sized, the default, it is the cheapest cluster in\n * `pricing.redis.sizedToData` that holds this report's stored bytes; fixed, it is `pricing.redis.monthlyUSD` as\n * given.\n *\n * **It is not additive across reports.** Each report sizes Redis to its own bytes, so a per-segment report\n * (`segment.costReport()`) compares with a cluster holding that one segment alone, and the baselines of a store's\n * segments do not sum to the store's. To judge a store, price all its segments in one `estimateCost`. To alarm on\n * one, sum `monthlyUSD.total` over its segments and compare the sum with the Redis you would run for it: a\n * per-segment verdict against that whole price fires only when one segment alone costs more than all of it.\n */\n readonly redisBaseline:\n | { readonly basis: 'fixed'; readonly monthlyUSD: number }\n | {\n readonly basis: 'sized-to-data';\n readonly monthlyUSD: number;\n /** The node type, the shards, the nodes (every shard's primary and replicas), and whether they tier to SSD. */\n readonly cluster: {\n readonly nodeType: string;\n readonly shards: number;\n readonly nodes: number;\n readonly dataTiering: boolean;\n };\n };\n /**\n * Sustained read rate at which the pay-per-use model's cost passes {@link CostReport.redisBaseline}, **evaluated\n * at this report's `cacheHitRate`** \u2014 so a higher cache-hit rate raises it (cache hits are free) \u2014 with the other\n * request axes at 0, and with what keeping the data readable costs taken out of the baseline first: storage, and\n * this report's pointer refresh. Loads, the write side, are left out of it. `Infinity` means it never crosses (a\n * 100% cache-hit rate). The published anchor (~329 reads/s) is against {@link ONE_REDIS_HA_CLUSTER}, at\n * `cacheHitRate: 0` with no refresh modeled.\n */\n readonly redisCrossover: { readonly readsPerSec: number };\n readonly verdict: 'win-big' | 'win' | 'lose-zone';\n readonly rationale: string;\n readonly assumptions: {\n readonly cacheHitRate: number;\n readonly pricingName: string;\n /** True when segment **sizes** were real (grounded `costReport`), false for a pure `estimateCost`. */\n readonly grounded: boolean;\n /** What the model assumed and what it left out, in words. The last says how the Redis was priced. */\n readonly notes: readonly string[];\n };\n}\n\nconst HOURS_PER_MONTH = 730; // AWS's own convention: 365 days \u00D7 24 hours \u00F7 12 months\nconst SECONDS_PER_MONTH = HOURS_PER_MONTH * 3600; // 2,628,000\nconst GIB = 1024 ** 3;\n\n/**\n * A cold intersection's reads for each operand before its chunks: the pointer, then the index in one tail read.\n * Both need the object's size, so each costs `requestsPerSizedRead` requests.\n */\nconst SIZED_READS_PER_COLD_OPERAND = 2;\n\n/**\n * What `store.load()` adds to its object's write, as the engine makes the requests on a segment with two\n * generations behind it: PUT-class, two listings (one to number the generation, one to collect after the publish)\n * and the pointer's write; reads, eight of the pointer and one of the current generation's index, each a sized\n * read. `tests/core/cost.test.ts` holds these to the engine, and counts a segment's first load at seven reads and\n * its second at eight.\n */\nconst STORE_LOAD_PUT_CLASS = 3;\nconst STORE_LOAD_SIZED_READS = 9;\n\n/** Fail-fast at the boundary: reject non-finite / negative inputs rather than leak NaN into the report. */\nfunction requireFiniteNonNeg(n: number | undefined, field: string): number {\n if (n === undefined || !Number.isFinite(n) || n < 0) {\n throw new ValidationError(`${field} must be a finite number >= 0; got ${n}`);\n }\n return n;\n}\n\nfunction clamp01(n: number): number {\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n\n/** Two cluster prices closer than this are the same price. */\nconst TIE_USD = 1e-6;\n\ninterface SizedCluster {\n readonly nodeType: RedisNodeType;\n readonly shards: number;\n readonly nodes: number;\n readonly monthlyUSD: number;\n}\n\n/** The node's share of the data: its memory less the reserve, and all of a data-tiering node's SSD. */\nfunction usableGiB(sizing: RedisSizing, nodeType: RedisNodeType): number {\n return nodeType.memoryGiB * (1 - sizing.reservedMemoryFraction) + (nodeType.ssdGiB ?? 0);\n}\n\n/** Fail-fast on a caller-supplied catalogue: a bad row would otherwise win on NaN, or never fit anything. */\nfunction validateSizing(sizing: RedisSizing | undefined): asserts sizing is RedisSizing {\n const at = 'pricing.redis.sizedToData';\n if (sizing === null || typeof sizing !== 'object') {\n throw new ValidationError(`${at} must be a RedisSizing`);\n }\n if (typeof sizing.source !== 'string' || sizing.source === '') {\n throw new ValidationError(`${at}.source must say where the prices come from`);\n }\n if (!Array.isArray(sizing.nodeTypes) || sizing.nodeTypes.length === 0) {\n throw new ValidationError(`${at}.nodeTypes must list at least one node type`);\n }\n if (!Number.isInteger(sizing.replicasPerShard) || sizing.replicasPerShard < 0) {\n throw new ValidationError(\n `${at}.replicasPerShard must be an integer >= 0; got ${sizing.replicasPerShard}`,\n );\n }\n const reserved = requireFiniteNonNeg(\n sizing.reservedMemoryFraction,\n `${at}.reservedMemoryFraction`,\n );\n if (reserved >= 1) {\n throw new ValidationError(`${at}.reservedMemoryFraction must be below 1; got ${reserved}`);\n }\n const names = new Set<string>();\n for (const n of sizing.nodeTypes) {\n if (n === null || typeof n !== 'object' || typeof n.name !== 'string' || n.name === '') {\n throw new ValidationError(`${at}.nodeTypes: every node type needs a name`);\n }\n const row = `${at}.nodeTypes[${JSON.stringify(n.name)}]`;\n if (names.has(n.name)) throw new ValidationError(`${row} is listed twice`);\n names.add(n.name);\n if (!(requireFiniteNonNeg(n.memoryGiB, `${row}.memoryGiB`) > 0)) {\n throw new ValidationError(`${row}.memoryGiB must be above 0`);\n }\n requireFiniteNonNeg(n.ssdGiB ?? 0, `${row}.ssdGiB`);\n requireFiniteNonNeg(n.hourlyUSD, `${row}.hourlyUSD`);\n if (n.maxShards !== undefined && (!Number.isInteger(n.maxShards) || n.maxShards < 1)) {\n throw new ValidationError(`${row}.maxShards must be an integer >= 1; got ${n.maxShards}`);\n }\n // A memory so small that the reserve leaves nothing would size any data at infinite shards.\n if (!(usableGiB(sizing, n) > 0)) {\n throw new ValidationError(\n `${row} leaves no memory for data once ${reserved} of it is reserved`,\n );\n }\n }\n}\n\n/**\n * The cheapest cluster in `sizing` that holds `dataGiB`: for each node type, enough shards for the data in what each\n * node leaves free, every shard a primary and its replicas. See {@link RedisSizing} for how a tie is broken.\n */\nfunction cheapestCluster(sizing: RedisSizing, dataGiB: number): SizedCluster {\n const fits: SizedCluster[] = [];\n for (const nodeType of sizing.nodeTypes) {\n const usable = usableGiB(sizing, nodeType);\n let shards = Math.max(1, Math.ceil(dataGiB / usable));\n // The division can round an exact multiple up by one: a shard fewer that still holds the data is the answer.\n if (shards > 1 && (shards - 1) * usable >= dataGiB) shards -= 1;\n if (nodeType.maxShards !== undefined && shards > nodeType.maxShards) continue;\n const nodes = shards * (1 + sizing.replicasPerShard);\n fits.push({\n nodeType,\n shards,\n nodes,\n monthlyUSD: nodes * nodeType.hourlyUSD * HOURS_PER_MONTH,\n });\n }\n const size = bytesWords(dataGiB * GIB);\n if (fits.length === 0) {\n throw new ValidationError(\n `no node type in pricing.redis.sizedToData.nodeTypes holds ${size} within its maxShards`,\n );\n }\n // Prices within TIE_USD of the cheapest are one price reached by different roundings. Chosen among them by a rule\n // that never looks at where a row sits in the list: fewer nodes, then the lower price, then the name.\n const cheapest = fits.reduce((m, c) => Math.min(m, c.monthlyUSD), Infinity);\n const [best] = fits\n .filter((c) => c.monthlyUSD <= cheapest + TIE_USD)\n .sort(\n (a, b) =>\n a.nodes - b.nodes ||\n a.monthlyUSD - b.monthlyUSD ||\n (a.nodeType.name < b.nodeType.name ? -1 : a.nodeType.name > b.nodeType.name ? 1 : 0),\n );\n if (best === undefined || !Number.isFinite(best.monthlyUSD)) {\n throw new ValidationError(\n `pricing.redis.sizedToData prices ${size} at no finite cost; check its node types`,\n );\n }\n return best;\n}\n\n/**\n * A plain copy of a caller's sizing, each value read once, so what is validated is what is priced: a getter or a\n * Proxy that answered differently the second time could otherwise price a row it never validated. Anything not\n * shaped like a sizing is passed through for {@link validateSizing} to refuse.\n */\nfunction snapshotSizing(sizing: RedisSizing | undefined): RedisSizing | undefined {\n if (sizing === null || typeof sizing !== 'object') return sizing;\n const { source, nodeTypes, replicasPerShard, reservedMemoryFraction } = sizing;\n const row = (n: RedisNodeType): RedisNodeType => {\n if (n === null || typeof n !== 'object') return n;\n const { name, memoryGiB, ssdGiB, hourlyUSD, maxShards } = n;\n return {\n name,\n memoryGiB,\n hourlyUSD,\n ...(ssdGiB === undefined ? {} : { ssdGiB }),\n ...(maxShards === undefined ? {} : { maxShards }),\n };\n };\n return {\n source,\n replicasPerShard,\n reservedMemoryFraction,\n nodeTypes: Array.isArray(nodeTypes) ? Array.from(nodeTypes, row) : nodeTypes,\n };\n}\n\n/** What a report compares against: a fixed price, or the cluster sized to its bytes, and the sizing that chose it. */\ninterface Baseline {\n readonly monthlyUSD: number;\n readonly sized?: { readonly sizing: RedisSizing; readonly cluster: SizedCluster };\n}\n\n/** Read `pricing.redis`, which must be exactly one of the two shapes, and price it for `storedGiB`. */\nfunction resolveBaseline(redis: PricingProfile['redis'], storedGiB: number): Baseline {\n if (redis === null || typeof redis !== 'object') {\n throw new ValidationError('pricing.redis must be { monthlyUSD } or { sizedToData }');\n }\n // A shape is given when its key has a value, however the value is reached: an inherited price, or one behind a\n // getter, is as given as an own property, and a key set to undefined gives nothing. Each is read once.\n const { monthlyUSD, sizedToData } = redis;\n const fixed = monthlyUSD !== undefined;\n const sized = sizedToData !== undefined;\n if (fixed === sized) {\n throw new ValidationError(\n 'pricing.redis must be exactly one of { monthlyUSD } and { sizedToData }' +\n (fixed\n ? ', not both: spreading AWS_US_EAST_1_ONDEMAND.redis keeps its sizedToData, so pass redis: { monthlyUSD } alone'\n : ''),\n );\n }\n if (sized) {\n const sizing = snapshotSizing(sizedToData);\n validateSizing(sizing);\n const cluster = cheapestCluster(sizing, storedGiB);\n return { monthlyUSD: cluster.monthlyUSD, sized: { sizing, cluster } };\n }\n return { monthlyUSD: requireFiniteNonNeg(monthlyUSD, 'pricing.redis.monthlyUSD') };\n}\n\n/**\n * A byte count in the largest binary unit it fills \u2014 \"18.63 GiB\", \"190.73 MiB\", \"20 bytes\" \u2014 since a report on one\n * segment is usually far under a GiB, and \"0.00 GiB\" would read as nothing at all.\n */\nfunction bytesWords(bytes: number): string {\n const units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];\n let value = bytes;\n let unit = 0;\n // Rounded before the unit is chosen, so 1,048,575 bytes reads \"1.00 MiB\", not \"1024.00 KiB\".\n const shown = (v: number, u: number): number => (u === 0 ? Math.round(v) : Number(v.toFixed(2)));\n while (unit < units.length - 1 && shown(value, unit) >= 1024) {\n value /= 1024;\n unit += 1;\n }\n if (unit > 0) return `${value.toFixed(2)} ${units[unit]}`;\n const n = Math.round(value);\n return `${n} byte${n === 1 ? '' : 's'}`;\n}\n\n/** \"1 shard of 3 cache.r6g.xlarge nodes\", or \"95 shards, 285 cache.r6g.xlarge nodes\". */\nfunction clusterWords(c: SizedCluster): string {\n const count = (n: number): string => n.toLocaleString('en-US');\n const nodes = `${count(c.nodes)} ${c.nodeType.name} node${c.nodes === 1 ? '' : 's'}`;\n return c.shards === 1 ? `1 shard of ${nodes}` : `${count(c.shards)} shards, ${nodes}`;\n}\n\n/** Rough bytes for a segment when only cardinality is known: 2 B/value (array-container upper bound). */\nfunction sizingBytes(spec: SegmentSizing): number {\n if (spec.sizeBytes !== undefined) return requireFiniteNonNeg(spec.sizeBytes, 'segment.sizeBytes');\n if (spec.cardinality !== undefined) {\n return requireFiniteNonNeg(spec.cardinality, 'segment.cardinality') * 2;\n }\n return 0;\n}\n\n/** Core report builder shared by planning + grounded modes. `storageBytes` is total across all segments. */\nfunction buildReport(input: {\n readonly storageBytes: number;\n readonly workload: Workload;\n readonly pricing: PricingProfile;\n readonly grounded: boolean;\n readonly extraNotes?: readonly string[];\n /** Some sizes were derived from cardinality, an upper bound: a Redis sized to them may be larger than needed. */\n readonly sizesFromCardinality?: boolean;\n}): CostReport {\n const { pricing, grounded } = input;\n const { storage, redis } = pricing;\n const S = SECONDS_PER_MONTH;\n\n // The pricing profile is a public, caller-supplied boundary input too \u2014 validate every rate that feeds\n // the report so a malformed profile fails fast rather than leaking NaN/Infinity dollars + a bogus verdict.\n requireFiniteNonNeg(storage.getPerMillion, 'pricing.storage.getPerMillion');\n requireFiniteNonNeg(storage.putPerMillion, 'pricing.storage.putPerMillion');\n requireFiniteNonNeg(storage.storagePerGiBMonth, 'pricing.storage.storagePerGiBMonth');\n const sizedRead = requireFiniteNonNeg(\n storage.requestsPerSizedRead ?? 1,\n 'pricing.storage.requestsPerSizedRead',\n );\n\n const storageBytes = requireFiniteNonNeg(input.storageBytes, 'storageBytes');\n const storedGiB = storageBytes / GIB;\n\n // The Redis to compare against: the cheapest cluster that holds the stored bytes, or the one cluster named.\n const baseline = resolveBaseline(redis, storedGiB);\n const baselineUSD = baseline.monthlyUSD;\n\n const cacheHitRate = clamp01(\n requireFiniteNonNeg(input.workload.cacheHitRate ?? 0, 'cacheHitRate'),\n );\n const readsPerSec = requireFiniteNonNeg(input.workload.readsPerSec ?? 0, 'readsPerSec');\n const intersectsPerSec = requireFiniteNonNeg(\n input.workload.intersectsPerSec ?? 0,\n 'intersectsPerSec',\n );\n const chunksPerIntersect = requireFiniteNonNeg(\n input.workload.chunksPerIntersect ?? 1,\n 'chunksPerIntersect',\n );\n const loadsPerMonth = requireFiniteNonNeg(input.workload.loadsPerMonth ?? 0, 'loadsPerMonth');\n const requestsPerLoad = requireFiniteNonNeg(\n input.workload.requestsPerLoad ?? 1,\n 'requestsPerLoad',\n );\n const operandsPerIntersect = requireFiniteNonNeg(\n input.workload.operandsPerIntersect ?? 2,\n 'operandsPerIntersect',\n );\n if (operandsPerIntersect < 1) {\n throw new ValidationError(\n `operandsPerIntersect must be at least 1, since an intersection reads its operands; got ${operandsPerIntersect}`,\n );\n }\n const hotSegments = requireFiniteNonNeg(input.workload.hotSegments ?? 0, 'hotSegments');\n const readerProcesses = requireFiniteNonNeg(\n input.workload.readerProcesses ?? 1,\n 'readerProcesses',\n );\n if (readerProcesses < 1) {\n throw new ValidationError(`readerProcesses must be at least 1; got ${readerProcesses}`);\n }\n const genTtlMs = requireFiniteNonNeg(\n input.workload.genTtlMs ?? DEFAULT_CURRENT_GEN_TTL_MS,\n 'genTtlMs',\n );\n\n // Per-request unit costs (USD). Same-region egress is free; internet egress not modeled.\n const storageGetUSD = storage.getPerMillion / 1e6;\n const putUSD = storage.putPerMillion / 1e6;\n\n // Monthly volumes.\n const missFraction = 1 - cacheHitRate;\n const readMisses = readsPerSec * S * missFraction;\n const intersects = intersectsPerSec * S;\n\n // A reader re-reads a pointer only when it reads the segment after the TTL has lapsed, so the refresh is at most\n // one per hot segment per TTL, and at most one per point read. With no timed refresh (`genTtlMs: 0`) it is none.\n const refreshes =\n genTtlMs > 0\n ? Math.min((readerProcesses * hotSegments * S * 1000) / genTtlMs, readsPerSec * S)\n : 0;\n\n const storageUSD = storedGiB * storage.storagePerGiBMonth;\n const readsUSD = readMisses * storageGetUSD;\n const intersectGets =\n chunksPerIntersect + SIZED_READS_PER_COLD_OPERAND * operandsPerIntersect * sizedRead;\n const intersectsUSD = intersects * intersectGets * storageGetUSD;\n const loadGets = STORE_LOAD_SIZED_READS * sizedRead;\n const loadsUSD =\n loadsPerMonth * ((requestsPerLoad + STORE_LOAD_PUT_CLASS) * putUSD + loadGets * storageGetUSD);\n const refreshUSD = refreshes * sizedRead * storageGetUSD;\n const total = readsUSD + intersectsUSD + storageUSD + loadsUSD + refreshUSD;\n\n // Crossover: the sustained read rate (other axes 0) where request cost alone passes the baseline less the fixed\n // monthly costs (storage and the pointer refresh), evaluated at this report's cache posture (misses).\n const headroom = Math.max(0, baselineUSD - storageUSD - refreshUSD);\n const readsCross =\n storageGetUSD > 0 && missFraction > 0\n ? headroom / (S * storageGetUSD * missFraction)\n : Infinity;\n\n const verdict: CostReport['verdict'] =\n total <= baselineUSD * 0.1 ? 'win-big' : total < baselineUSD ? 'win' : 'lose-zone';\n\n const dominant = Math.max(readsUSD, intersectsUSD, storageUSD, loadsUSD, refreshUSD);\n let driver = 'storage';\n if (dominant === readsUSD && readsUSD > 0) driver = 'point reads (object GETs)';\n else if (dominant === intersectsUSD && intersectsUSD > 0) {\n driver = 'intersections (pointer, index and chunk GETs)';\n } else if (dominant === loadsUSD && loadsUSD > 0)\n driver = 'loads (object, listing and pointer requests)';\n else if (dominant === refreshUSD && refreshUSD > 0) driver = 'the pointer refresh (object GETs)';\n // A fixed baseline keeps the words it always had \u2014 \"flat baseline\" in a lose-zone, \"baseline\" otherwise \u2014 and a\n // sized one says what it priced.\n const sized = baseline.sized;\n // No bytes counted \u2014 nothing stored, or a source that cannot measure \u2014 sizes Redis to nothing: the rationale says\n // so, rather than naming a cluster that \"would hold 0 bytes\" as if that were a measurement.\n const noBytes = Math.round(storageBytes) === 0;\n const redisWords =\n sized === undefined\n ? undefined\n : noBytes\n ? `$${baselineUSD.toFixed(2)}/mo cheapest Redis cluster in the catalogue, as no bytes are counted ` +\n `(${clusterWords(sized.cluster)})`\n : `$${baselineUSD.toFixed(2)}/mo Redis that would hold ${bytesWords(storageBytes)} ` +\n `(${clusterWords(sized.cluster)})`;\n const rationale =\n verdict === 'lose-zone'\n ? `pay-per-use total $${total.toFixed(2)}/mo exceeds the ${redisWords ?? `$${baselineUSD}/mo flat baseline`}; dominated by ${driver}`\n : verdict === 'win-big'\n ? `$${total.toFixed(2)}/mo \u2014 \u226410% of the ${redisWords ?? `$${baselineUSD}/mo baseline`}; dominated by ${driver}`\n : `$${total.toFixed(2)}/mo, under the ${redisWords ?? `$${baselineUSD}/mo baseline`}; dominated by ${driver}`;\n\n const dataTiering = sized !== undefined && (sized.cluster.nodeType.ssdGiB ?? 0) > 0;\n const redisNote =\n sized === undefined\n ? `Redis priced at the fixed $${baselineUSD}/mo given, whatever the data size.`\n : (noBytes\n ? 'Redis priced at the cheapest cluster in the catalogue, as no bytes are counted, because nothing ' +\n 'is stored or nothing was measured'\n : `Redis priced at the cheapest cluster in the catalogue that holds the ${bytesWords(storageBytes)} stored`) +\n `: ${clusterWords(sized.cluster)}, each shard a primary and ${sized.sizing.replicasPerShard} ` +\n `replica(s), ${Number((sized.sizing.reservedMemoryFraction * 100).toFixed(1))}% of memory reserved ` +\n `(${sized.sizing.source}).` +\n (noBytes\n ? ' Pass pricing.redis: { monthlyUSD } to compare against a Redis of your own.'\n : ' Compressed bytes are a floor on the memory Redis needs: a native Redis bitmap is sized ' +\n 'by its highest id, so sparse ids need more.') +\n ' Redis bought another way \u2014 fewer replicas, another engine, reserved nodes \u2014 can cost less than the ' +\n 'catalogue prices it.' +\n (dataTiering\n ? ` ${sized.cluster.nodeType.name} is a data-tiering node, which keeps the values read least recently ` +\n 'on its SSD. It is priced as if the data read often fits in its memory: AWS recommends data tiering ' +\n 'for workloads that regularly read up to 20% of their data.'\n : '') +\n (input.sizesFromCardinality\n ? ' Some segment sizes came from cardinality, at 2 bytes an id, which is more than a dense bitmap ' +\n 'needs, so this Redis may be larger than the data calls for: pass sizeBytes to size it to the ' +\n 'compressed bytes.'\n : '');\n\n const notes = [\n 'Same-region egress treated as free; internet egress is not modeled.',\n 'Request cost is from the supplied workload rates (live-metrics-derived request cost is a later phase).',\n loadsPerMonth > 0\n ? `Loads modeled: ${loadsPerMonth}/mo, each ${requestsPerLoad} PUT-class request(s) for the object plus ` +\n `${STORE_LOAD_PUT_CLASS} PUT-class and ${loadGets} GETs that store.load() adds (listings, pointer, index).`\n : 'Loads are NOT modeled \u2014 set workload.loadsPerMonth (+ requestsPerLoad for multipart) to include them.',\n ...(intersects > 0\n ? [\n `Intersections priced cold: ${intersectGets} GETs each, ` +\n `${SIZED_READS_PER_COLD_OPERAND * sizedRead} for each of ${operandsPerIntersect} operand(s) plus ` +\n `${chunksPerIntersect} chunk read(s); cacheHitRate does not apply.`,\n ]\n : []),\n hotSegments > 0\n ? genTtlMs > 0\n ? `Pointer refresh modeled: ${hotSegments} hot segment(s) in each of ${readerProcesses} reader ` +\n `process(es), each re-reading its pointer at most every ${genTtlMs} ms, and at most once a point read.`\n : 'Pointer refresh: none \u2014 genTtlMs 0 turns the timed refresh off. A reader still re-reads a pointer on an ' +\n 'eviction, a swept generation or an invalidation, which this does not price.'\n : readsPerSec > 0\n ? 'The pointer refresh is NOT modeled \u2014 set workload.hotSegments to the segments each long-lived reader ' +\n 'keeps reading, and workload.readerProcesses to how many readers there are.'\n : null,\n hotSegments > DEFAULT_MAX_OPEN_SEGMENTS\n ? `More hot segments in a reader than a store keeps open by default (${DEFAULT_MAX_OPEN_SEGMENTS}): a read of a segment ` +\n 'the reader evicted opens it again, a pointer and a tail read, which this does not price. Raise ' +\n 'cache.readerMax, and cache.readerMaxBytes, to keep them open.'\n : null,\n ...(input.extraNotes ?? []),\n // Last, so the notes a report already carried keep their places.\n redisNote,\n ].filter((n): n is string => n !== null);\n\n return {\n monthlyUSD: {\n byOp: {\n reads: readsUSD,\n intersects: intersectsUSD,\n storage: storageUSD,\n loads: loadsUSD,\n pointerRefresh: refreshUSD,\n },\n total,\n },\n redisBaseline:\n sized === undefined\n ? { basis: 'fixed', monthlyUSD: baselineUSD }\n : {\n basis: 'sized-to-data',\n monthlyUSD: baselineUSD,\n cluster: {\n nodeType: sized.cluster.nodeType.name,\n shards: sized.cluster.shards,\n nodes: sized.cluster.nodes,\n dataTiering,\n },\n },\n redisCrossover: { readsPerSec: readsCross },\n verdict,\n rationale,\n assumptions: { cacheHitRate, pricingName: pricing.name, grounded, notes },\n };\n}\n\n/**\n * **Planning** cost estimate \u2014 pure, no instance or live data needed (sizing, sales, what-if). Segment sizes\n * are taken as given (or roughly derived from cardinality); use the grounded `segment.costReport()` for\n * exact, real sizes. See {@link CostReport}.\n */\nexport function estimateCost(input: EstimateInput): CostReport {\n const pricing = input.pricing ?? AWS_US_EAST_1_ONDEMAND;\n const workload = input.workload ?? {};\n let storageBytes = 0;\n let sizesFromCardinality = false;\n for (const spec of input.segments) {\n const count = Math.floor(requireFiniteNonNeg(spec.count ?? 1, 'segment.count'));\n storageBytes += sizingBytes(spec) * count;\n if (\n spec.sizeBytes === undefined &&\n spec.cardinality !== undefined &&\n sizingBytes(spec) * count > 0\n ) {\n sizesFromCardinality = true;\n }\n }\n return buildReport({ storageBytes, workload, pricing, grounded: false, sizesFromCardinality });\n}\n\n/**\n * **Grounded** report from a real segment byte total (from the `.crbm` index) + a supplied workload. Used by\n * `Segment.costReport()` in the facade. `grounded` defaults to true (the size is exact, not estimated); the\n * caller passes `grounded: false` + a note when the Storage source can't measure size.\n */\nexport function groundedReport(input: {\n readonly storageBytes: number;\n readonly grounded?: boolean;\n readonly workload?: Workload;\n readonly pricing?: PricingProfile;\n readonly extraNotes?: readonly string[];\n}): CostReport {\n return buildReport({\n storageBytes: input.storageBytes,\n workload: input.workload ?? {},\n pricing: input.pricing ?? AWS_US_EAST_1_ONDEMAND,\n grounded: input.grounded ?? true,\n extraNotes: input.extraNotes,\n });\n}\n", "/**\n * The **due index** \u2014 a time-bucketed set of the segments that carry an expiry, so a retention cycle costs what\n * is *expiring* rather than what the fleet *holds*.\n *\n * Without it, every sweep drains `registry.list()` and filters: `O(fleet)` reads every cycle, forever, even when\n * nothing expires. At 500 segments that is free; at 100,000 on a 15-minute cadence it is a standing bill for\n * finding nothing. Redis solves the same problem with a separate `expires` dict, and Elasticsearch ILM with a\n * scheduler; this is that structure, built out of registry rows so it needs **no driver change** and works on\n * every backend.\n *\n * ## Why a namespace per bucket\n *\n * `IRegistryDriver.list()` filters by **namespace and nothing else** \u2014 there is no cursor, no `startAfter`, no\n * key range. That single fact drives the whole design: the only way to read a *subset* of rows is to make the\n * subset a namespace. So the day a segment expires becomes the namespace, and listing one due day yields exactly\n * the segments due that day.\n *\n * ```\n * cbm.due.20356 \u2500\u25BA the segments whose expiry falls on day 20,356\n * cbm.due.20357 \u2500\u25BA \u2026 day 20,357\n * ```\n *\n * The prefix `cbm.due.` is declared once, as `RESERVED_NAMESPACE_PREFIX` in `validate.ts`: the validator refuses it in\n * any name an application passes in, and this module writes and recognises its rows by it.\n *\n * A day index rather than a formatted date, because `core/` reads no ambient time and formatting a date would\n * drag in a calendar (and a timezone question) for no benefit. `Math.floor(expiresAt / 86_400_000)` is total,\n * reversible and has no edge cases.\n *\n * ## The index is a FAST PATH, never the source of truth\n *\n * The segment's own registry row holds the policy; an index row is a pointer that may be stale, missing, or\n * left behind. That is deliberate, and it is what makes a second index safe here \u2014 the failure family this\n * project has hit fourteen times is *a check that cannot fire*, and the answer is that **nothing is only known\n * by the index**:\n *\n * - **A stale pointer cannot retire anything.** The sweep re-reads the live segment row before acting (it\n * already does this \u2014 the `policy-changed` skip), so an index row whose policy has since been cleared or\n * moved is a wasted read and nothing worse.\n * - **A missing pointer cannot lose data.** The full `registry.list()` scan runs as a periodic **repair**\n * pass. Anything the index never learned about \u2014 a policy whose pointer write failed, or a segment whose\n * name is too long to encode (see {@link canIndex}) \u2014 is retired by that pass instead. Slower, never wrong.\n *\n * So the index can only make the sweep *cheaper*, never *wronger*, and both drift directions are bounded by\n * machinery that already exists.\n */\nimport { ValidationError } from './errors';\nimport {\n MAX_NAME_LENGTH,\n RESERVED_NAMESPACE_PREFIX,\n encodedNameLength,\n isReservedNamespace,\n} from './validate';\nimport type { RegistryRecord, SegmentRef } from './ports';\n\n/** Bucket width. One day: small enough that a cycle reads little, coarse enough that the index stays tiny. */\nexport const DUE_BUCKET_MS = 86_400_000;\n\n/**\n * The name-length cap, and an index row encodes **both** of the original ref's parts into one name \u2014 so a ref\n * whose encoding would exceed it is simply not indexed (see {@link canIndex}).\n *\n * Re-exported from the validator rather than restated. The two were separate constants with the same value\n * until the validator started measuring the **encoded** length; `canIndex` went on counting raw characters and\n * began answering `true` for refs the registry then refused, turning a documented graceful degradation into a\n * guaranteed throw on the retention fast path.\n */\nexport { MAX_NAME_LENGTH };\n\n/** Which bucket an expiry falls in. Total and reversible; no calendar, no timezone, no ambient time. */\nexport function dueBucket(expiresAt: number): number {\n return Math.floor(expiresAt / DUE_BUCKET_MS);\n}\n\n/** The namespace holding one bucket's pointers. */\nexport function dueNamespace(bucket: number): string {\n return `${RESERVED_NAMESPACE_PREFIX}${bucket}`;\n}\n\n/** Is this record a due-index pointer rather than a segment? */\nexport function isDueIndexRow(record: Pick<RegistryRecord, 'namespace'>): boolean {\n return isReservedNamespace(record.namespace);\n}\n\n/**\n * Encode a ref into one index-row name, unambiguously.\n *\n * `${namespaceLength}.${namespace}${segment}` \u2014 a decimal length, a dot, then the two parts concatenated. The\n * length prefix is what makes it reversible: a name may contain any character, the dot included, so no\n * separator character could ever be unambiguous on its own. Reading the digits\n * up to the first dot tells the parser exactly where the namespace ends.\n */\nexport function encodeDueName(ref: SegmentRef): string {\n const ns = ref.namespace ?? '';\n return `${ns.length}.${ns}${ref.segment}`;\n}\n\n/**\n * Can this ref be indexed at all? False when the encoded name would exceed the 256-character name cap \u2014\n * possible only for a ref whose namespace and segment are together near the limit.\n *\n * **Not indexable is not \"not retired\".** The repair scan still sees the segment's own row, so the consequence\n * is that it expires on the repair cadence instead of the fast one. Callers must not treat `false` as an error.\n */\nexport function canIndex(ref: SegmentRef): boolean {\n // Measured the way the registry will measure it. Counting raw characters here made this guard lie: a ref of\n // 80 `#`s in each part encodes to 483 and was reported indexable, then threw on the write.\n return encodedNameLength(encodeDueName(ref)) <= MAX_NAME_LENGTH;\n}\n\n/** The registry ref of the pointer for `ref` in `bucket`. Throws if the ref cannot be encoded \u2014 check first. */\nexport function dueIndexRef(bucket: number, ref: SegmentRef): SegmentRef {\n const segment = encodeDueName(ref);\n if (segment.length > MAX_NAME_LENGTH) {\n throw new ValidationError(\n `due index: ${JSON.stringify(ref)} encodes to ${segment.length} characters, over the ` +\n `${MAX_NAME_LENGTH}-character name limit \u2014 check canIndex() first; such a segment is retired by the ` +\n `repair scan instead`,\n );\n }\n return { namespace: dueNamespace(bucket), segment };\n}\n\n/**\n * Recover the original ref from an index row's name. `null` for anything we did not write \u2014 a foreign row in\n * the reserved namespace is ignored rather than acted upon.\n */\nexport function decodeDueName(name: string): SegmentRef | null {\n const dot = name.indexOf('.');\n if (dot <= 0) return null;\n const lengthPart = name.slice(0, dot);\n if (!/^(0|[1-9][0-9]*)$/.test(lengthPart)) return null;\n const nsLength = Number(lengthPart);\n const rest = name.slice(dot + 1);\n const namespace = rest.slice(0, nsLength);\n const segment = rest.slice(nsLength);\n // A pointer to a segment with no name is meaningless \u2014 and would round-trip to a ref the validator rejects.\n // This also covers a length prefix larger than the remainder: the slice then consumes everything and leaves\n // the segment empty, so a separate overflow guard would be unreachable.\n if (segment.length === 0) return null;\n return namespace.length === 0 ? { segment } : { namespace, segment };\n}\n\n/**\n * Which buckets a cycle must read at `now`: everything from `since` up to and including the current one.\n *\n * Past buckets are included because a sweep that did not run \u2014 a scaled-to-zero worker, a failed deploy, a\n * paused schedule \u2014 must not leave its buckets stranded. `lookbackBuckets` bounds how far back a first run\n * reaches, so a long outage costs a bounded number of list calls per cycle rather than one per day since the\n * epoch; anything older is the repair scan's job.\n */\nexport function dueBucketsAt(now: number, lookbackBuckets: number): number[] {\n if (!Number.isSafeInteger(lookbackBuckets) || lookbackBuckets < 0) {\n throw new ValidationError(\n `due index: lookbackBuckets must be a non-negative integer; got ${lookbackBuckets}`,\n );\n }\n const current = dueBucket(now);\n const buckets: number[] = [];\n for (let b = current - lookbackBuckets; b <= current; b++) buckets.push(b);\n return buckets;\n}\n", "/**\n * The one bounded drain of `registry.list()`.\n *\n * `runConsistencyCheck`, `retireExpired` and `eraseNamespace` drain through it.\n * One copy of the loop, the ceiling constant and the option validation keeps them from drifting apart, and a\n * fleet-wide enumeration is exactly the place where drift between callers costs memory rather than tidiness.\n *\n * **Why drain at all rather than stream.** Every caller mutates rows as it goes (a CAS, a delete), and iterating a\n * live listing while doing so is driver-dependent \u2014 a paged LIST may or may not observe its own writes. A\n * snapshot also makes per-cycle counters and limits mean something. The cost is resident memory proportional to\n * the fleet, which is why the ceiling is not optional: at the default of 250,000 rows the snapshot is tens of MB,\n * comfortably more than the 128\u2013256 MB Lambda the guide suggests starting with, so it fails loudly instead.\n */\nimport { BudgetExceededError, ValidationError } from './errors';\nimport { isDueIndexRow } from './due-index';\nimport type { IRegistryDriver, RegistryRecord } from './ports';\n\n/**\n * Ceiling on registry records one fleet-wide scan holds resident. Raise it via the caller's `maxScanSegments` when\n * the fleet really is that large *and* the memory is there; narrow the scan with a `namespace` otherwise.\n */\nexport const DEFAULT_MAX_SCAN_SEGMENTS = 250_000;\n\n/** Fail fast on a bad ceiling BEFORE the (possibly huge) scan, not after. */\nexport function validateMaxScanSegments(value: number, op: string): void {\n if (!Number.isFinite(value) || value < 1) {\n throw new ValidationError(`${op}: maxScanSegments must be a finite number >= 1; got ${value}`);\n }\n}\n\n/**\n * Drain every registry record (optionally one namespace) into an array, refusing past `maxScanSegments`.\n *\n * The bound is checked **before** each push, so the array never exceeds the ceiling the caller agreed to. Pushing\n * first and then throwing would leave the very row that broke the budget resident, and a caller sizing a container\n * against `maxScanSegments` off by one row at the worst possible moment.\n */\nexport async function drainRegistry(\n registry: IRegistryDriver,\n options: {\n namespace?: string;\n maxScanSegments: number;\n op: string;\n /**\n * How a caller of `op` raises the ceiling, completing \"Narrow it with `namespace`, or \u2026\" in the refusal.\n * Defaults to raising `maxScanSegments`, which is right wherever `op` takes that option itself.\n */\n raise?: string;\n /**\n * Whether the caller could scope the scan down to a `namespace`. Defaults to true; `eraseNamespace` is already\n * one namespace, so it passes false and the refusal does not tell it to narrow what it cannot.\n */\n narrowable?: boolean;\n },\n): Promise<RegistryRecord[]> {\n const { maxScanSegments, op } = options;\n validateMaxScanSegments(maxScanSegments, op);\n const raise = options.raise ?? 'raise `maxScanSegments`';\n const advice =\n options.narrowable === false\n ? `${raise[0]!.toUpperCase()}${raise.slice(1)} if the namespace really is that large and the memory is `\n : `Narrow it with \\`namespace\\`, or ${raise} if the fleet really is that large and the memory is `;\n const rows: RegistryRecord[] = [];\n for await (const rec of registry.list(options.namespace)) {\n // A due-index pointer is bookkeeping, not a segment. It lives in a reserved namespace, so an unscoped fleet\n // scan would otherwise pay a strong `get` per pointer row in `checkConsistency`, hand it to a retention\n // sweep, and inflate every fleet-wide count. A scope that names the reserved namespace never gets here: the\n // callers refuse it before they scan.\n if (options.namespace === undefined && isReservedRow(rec)) continue;\n if (rows.length >= maxScanSegments) {\n throw new BudgetExceededError(\n `${op} would enumerate more than ${maxScanSegments} segments \u2014 the scan was abandoned there rather than ` +\n `completed. ${advice}available (a record is a few hundred bytes resident).`,\n );\n }\n rows.push(rec);\n }\n return rows;\n}\n\n/**\n * Is this record **bookkeeping** rather than a segment? Covers every reserved family \u2014 today the due-index\n * pointers. **Every unscoped fleet-wide enumeration must skip these**: the bounded drain,\n * the export/eject scan, and the all-namespaces GDPR paths.\n *\n * This is the ONE place a reserved family is declared, and a new one belongs here rather than at the call\n * sites. A comparison inlined per site is easy to miss at one of them \u2014 at `subjectReport`, the rows would consume\n * an Art. 15 request's per-op budget. A filter you have to remember at each site is a check that cannot fire.\n */\nexport function isReservedRow(record: Pick<RegistryRecord, 'namespace'>): boolean {\n return isDueIndexRow(record);\n}\n\n/**\n * Filter bookkeeping rows out of a registry listing **before** anything downstream pays for them. Needed where\n * the enumeration is consumed inside a budgeted collector rather than a plain loop: charging a GDPR Art. 15\n * request's per-op budget for pointer rows can refuse a subject report for a reason that has nothing to do with\n * the subject.\n */\nexport async function* excludingReservedRows(\n source: AsyncIterable<RegistryRecord>,\n): AsyncIterable<RegistryRecord> {\n for await (const record of source) {\n if (!isReservedRow(record)) yield record;\n }\n}\n", "/**\n * The **bitmap-codec seam**.\n *\n * `core/` is **codec-agnostic**: `SegmentEngine`, the erasure rewrite, and the `.crbm` read/write helpers only ever\n * construct and combine bitmaps through the {@link CodecInterface} factory + the {@link CodecBitmap} value type\n * defined here \u2014 never a concrete implementation. The flagship codec is roaring (in\n * `@cloudbitmaps/roaring`), and another codec would plug in behind the same seam with zero engine or driver\n * changes. That is the seam's value even with one codec shipped: a plain-bitset flavor was measured and\n * **decided against** (above ~6% density a Roaring chunk already *is* an uncompressed bitset), so the seam is\n * what let that question be answered by measurement rather than by a rewrite.\n *\n * **Homogeneity contract:** a single store uses a single codec, so every {@link CodecBitmap} an operation sees\n * was produced by the same {@link CodecInterface}. The binary set ops ({@link CodecBitmap.orInPlace} etc.) may\n * therefore assume `other` is the same concrete type and are not required to interoperate across codecs.\n * (Corollary: a decoded-chunk cache is codec-specific \u2014 never share one `cache` across engines built with\n * different codecs, or a cached bitmap from codec A could reach codec B's in-place op. The `CloudRoaring` facade\n * mints the cache per store, so this cannot arise in normal use.)\n *\n * **Interface surface = exactly what the engine needs**, kept general enough for the known fast-follow codecs\n * (positional/rank-select access + raw-bitset interop that `@cloudbitmaps/bitset` will add live on that\n * package's own extended value type, not here \u2014 the engine never calls them). The one codec-linked concern\n * this seam intentionally leaves in the format layer is the `.crbm` **payload codec id**\n * (`PAYLOAD_CODEC_ROARING_PORTABLE` / `KNOWN_PAYLOAD_CODEC_IDS` in `crbm/format.ts`): it is stamped in the footer\n * and validated on read, as membership of a registry rather than equality with one constant, so a codec-specific\n * name is not frozen into the format at `1.0` and a future codec is a one-line registration rather than a major\n * format version. Still deliberately outside this interface: a codec declares\n * its bytes, the seam does not.\n */\n\nimport { ValidationError } from './errors';\n\n/**\n * The value type a codec produces \u2014 a mutable set of `u32` with set algebra and portable (de)serialization.\n * This is the shape the roaring flavor's bitmap already has; the engine holds these, caches them, and merges tiers with them.\n */\nexport interface CodecBitmap {\n /** Serialize with the codec's **stable, portable** format (never a frozen/unsafe variant). */\n serialize(): Uint8Array;\n add(value: number): void;\n addMany(values: Iterable<number>): void;\n remove(value: number): void;\n removeMany(values: Iterable<number>): void;\n has(value: number): boolean;\n /** Cardinality. */\n readonly size: number;\n readonly isEmpty: boolean;\n /** A deep copy \u2014 mutating the clone must not touch the original (the cache relies on this). */\n clone(): CodecBitmap;\n /** In-place union `this = this \u222A other`. `other` is from the same codec (see the homogeneity contract). */\n orInPlace(other: CodecBitmap): void;\n /** In-place difference `this = this \\ other`. */\n andNotInPlace(other: CodecBitmap): void;\n /** In-place intersection `this = this \u2229 other`. */\n andInPlace(other: CodecBitmap): void;\n /** Ascending iterator over the set values. */\n [Symbol.iterator](): IterableIterator<number>;\n toArray(): number[];\n /**\n * The largest value in the set, or `undefined` when empty.\n *\n * **Optional, and deliberately so.** The engine uses it for one thing: asserting that a payload it is about\n * to interpret **as a chunk** holds only 16-bit remainders (see {@link assertChunkPayload}). A codec that\n * cannot answer this in better than O(n) should simply omit it \u2014 the engine then skips the check rather\n * than walking every value on the read path, which is the one thing this must never cost.\n *\n * Roaring answers it in O(1) from its container index, so the flagship codec implements it. The check is only\n * as good as the answer: a codec that implements this must return the true largest value of whatever\n * {@link CodecInterface.safeDeserialize} decoded, which for roaring holds only because that call refuses bytes\n * whose containers or values are out of order.\n */\n maximum?(): number | undefined;\n /**\n * Re-encode for size, in place, immediately before a **storage** write. Representation only \u2014 this must never\n * change membership, and `serialize()` afterwards must decode back to exactly the same set.\n *\n * **Optional, like {@link maximum}.** A codec whose encoding has no size decision to make (a plain bitset has\n * one representation and nothing to choose) simply omits it, and the engine skips the call.\n *\n * WHY THIS EXISTS. Roaring picks per container between an array, a bitset and a RUN \u2014 but the run choice is\n * not automatic in any implementation: it is a `runOptimize()` pass you have to ask for, and nothing here was\n * asking. So two of the three container types were ever used, and run-shaped data paid list or bitmap prices\n * for a run. Measured on the shipped codec: a contiguous 1,000,000-id range serialized to 128.1 KiB where\n * run-encoding needs **0.2 KiB** (570\u00D7), and a 2,000-run shape 536.5 KiB against **8.5 KiB** (63\u00D7). Sparse\n * data is unchanged, because there are no runs to find \u2014 the pass is not a gamble.\n *\n * WHY ONLY AT WRITE TIME. This is called where a whole immutable generation is written, so its cost amortizes\n * over a write that is already serializing and checksumming every chunk \u2014 and a generation is read many times\n * after. It is deliberately NOT called on the read path (a clone made for a combine is transient): the hot path\n * must not pay for a win that only the stored bytes collect, per KISS/YAGNI.\n */\n optimize?(): void;\n}\n\n/**\n * A pluggable bitmap codec \u2014 the factory the codec-agnostic engine constructs {@link CodecBitmap}s through.\n * Implementations **must** size-cap before handing untrusted bytes to any native decoder and use a safe\n * (never a trusting/frozen) deserializer \u2014 hard invariant 5.\n */\nexport interface CodecInterface {\n /** An empty set. */\n empty(): CodecBitmap;\n /** A set seeded from an iterable of `u32` values. */\n fromValues(values: Iterable<number>): CodecBitmap;\n /**\n * Size-cap (`bytes.length <= maxBytes`) then portable-deserialize. Throws `IntegrityError` when the input\n * exceeds the cap or fails to decode \u2014 the native decoder is never handed unbounded or unsafe-format input.\n *\n * \"Fails to decode\" includes bytes that are the right length and the wrong shape. A bitmap this returns must\n * satisfy every invariant its own operations assume (for roaring: containers and values in order, runs\n * disjoint, cardinalities matching the bits), because the engine trusts `has`, `size`, iteration order and\n * {@link CodecBitmap.maximum} on what comes back, and a decoder that only bounds its reads guarantees none of\n * them.\n */\n safeDeserialize(bytes: Uint8Array, maxBytes: number): CodecBitmap;\n}\n\n/**\n * Resolve a codec that a **public core entry point** was given, failing fast when it is missing.\n *\n * Why these entry points take `codec?` rather than a required field: `loadSegment` / `eraseIdFromSegment` /\n * `runExport` are call-compatible public API, and core cannot supply a default (the concrete\n * codec lives in a *flavor* package that depends on core \u2014 a default here would invert that arrow). A **flavor**\n * package binds the codec for its users (`@cloudbitmaps/roaring`'s store passes its codec to each of these), so an\n * application never reaches this throw; only someone calling `@cloudbitmaps/core` directly \u2014 i.e. a flavor or\n * driver author \u2014 can, and for them the typed error names exactly what to pass.\n */\nexport function requireCodec(codec: CodecInterface | undefined, api: string): CodecInterface {\n if (codec === undefined) {\n throw new ValidationError(\n `${api} needs a bitmap codec: pass \\`codec\\`. ` +\n `@cloudbitmaps/core is codec-agnostic and has no default. A flavor's store passes its own codec, ` +\n `so an application calls the store's method instead of this function.`,\n );\n }\n return codec;\n}\n", "/**\n * Segment **export (\"eject\")** \u2014 dump every registered segment's current *effective* set to a portable file,\n * using only public read APIs, so your data is readable **without CloudRoaring** (a first-class exit path;\n * see the README \"Your data stays yours\" section).\n *\n * Two formats:\n * - `'roaring'` (default) \u2014 one **portable RoaringBitmap32** per segment (the cross-language CRoaring format,\n * loadable by any roaring library in Java/Go/Python/Rust/C++/\u2026). Compact; builds one bitmap in memory per\n * segment.\n * - `'ndjson'` \u2014 newline-delimited ids, streamed (constant memory, any segment size); zero dependencies to read.\n *\n * The engine is **storage-agnostic and I/O-free**: it enumerates via an {@link IRegistryDriver}, reads each\n * segment's current generation via a store's `segment(...).iterate()`, and writes bytes through an injected\n * {@link ExportSink} \u2014 so it never imports `node:fs` (the `export-segments` CLI supplies a filesystem sink; a\n * test supplies an in-memory one; you could supply an S3 uploader). Crypto-shredded (`destroyed`) segments are\n * skipped (their bytes are unrecoverable); an encrypted segment is decrypted transparently **iff** the store was\n * wired with its keystore \u2014 so the export is **cleartext**, and protecting it is on you.\n *\n * **Enumeration** = the registry's known set. Every loaded segment has a registry row (the publish writes it),\n * so the registry is complete by construction; a segment loaded without a registry is not exportable here \u2014 wire\n * the registry the store was loaded with.\n *\n * **Per-segment fault isolation:** a segment that can't be read (a corrupt storage object, or an encrypted segment\n * when the store has no keystore) is recorded in the manifest's {@link ExportManifest.failed} list and the export\n * **continues** \u2014 one bad segment never blocks the rest. Its partial output is aborted, so a truncated file never\n * masquerades as complete. Therefore \"a manifest exists\" means *the run finished*, not that every segment\n * succeeded \u2014 check `failed` (the CLI also exits non-zero when it's non-empty).\n */\nimport { isReservedRow } from '../core/registry-scan';\nimport type { CodecInterface } from '../core/codec';\nimport { requireCodec } from '../core/codec';\nimport type { IRegistryDriver, SegmentRef } from '../core/ports';\nimport { validateUserNamespace, validateSegmentRef } from '../core/validate';\n\n/** Output format for {@link runExport} / `CloudRoaring.exportSegments`. */\nexport type ExportFormat = 'roaring' | 'ndjson';\n\n/** A writer for one segment's output \u2014 inject a filesystem file, an S3 upload, stdout, or a test buffer. */\nexport interface ExportWriter {\n /** Append bytes (called one or more times; for `ndjson`, batched line chunks; for `roaring`, once). */\n write(bytes: Uint8Array): Promise<void> | void;\n /** **Commit** the output \u2014 called once, only on success (all bytes written). Finalize/flush/rename here. */\n close(): Promise<void> | void;\n /**\n * **Discard** a partial output \u2014 called instead of `close()` if a fault occurs mid-segment, so a truncated file\n * never masquerades as complete (e.g. delete the temp file). Optional; sinks with nothing to undo can omit it.\n */\n abort?(): Promise<void> | void;\n}\n\n/** A destination the export writes each segment to. `ext` is `.roaring` or `.ndjson`. */\nexport interface ExportSink {\n open(ref: SegmentRef, ext: string): Promise<ExportWriter> | ExportWriter;\n}\n\nexport interface ExportOptions {\n /** Output format; defaults to `'roaring'`. */\n readonly format?: ExportFormat;\n /**\n * Bitmap codec that builds the exported bitmap for the `'roaring'` format. Optional in the type; a **flavor**'s\n * store passes its own (see `requireCodec`). Not needed for `'ndjson'`, which streams plain ids.\n */\n readonly codec?: CodecInterface;\n /** Scope the export to one namespace; omit to export every registered segment. */\n readonly namespace?: string;\n /** `ndjson` flush threshold in ~bytes (ids are ASCII, so \u2248 chars); defaults to 64 KiB. */\n readonly ndjsonBatchBytes?: number;\n}\n\n/** Per-segment result recorded in the {@link ExportManifest}. */\nexport interface ExportedSegment {\n readonly segment: string;\n readonly namespace?: string;\n /** Ids written (the segment's effective cardinality at export time). */\n readonly count: number;\n /** Bytes written to the sink for this segment. */\n readonly bytes: number;\n}\n\n/**\n * A segment that could **not** be exported \u2014 recorded rather than aborting the whole run (fault isolation), so a\n * single corrupt/undecryptable segment never blocks the rest of the dump.\n */\nexport interface ExportFailure {\n readonly segment: string;\n readonly namespace?: string;\n /** Why it failed (an error message) \u2014 e.g. a corrupt storage object, or an encrypted segment with no keystore. */\n readonly error: string;\n}\n\n/** What {@link runExport} produced \u2014 a self-describing summary the CLI persists as `manifest.json`. */\nexport interface ExportManifest {\n /** Schema version of this manifest shape (currently `1`) \u2014 insurance for evolving the format post-1.0. */\n readonly version: number;\n readonly format: ExportFormat;\n readonly totalSegments: number;\n readonly totalIds: number;\n readonly segments: readonly ExportedSegment[];\n /**\n * Segments that could not be read (empty when everything succeeded). Its presence in the manifest is the point:\n * \"a manifest exists\" \u21D2 the run finished, **not** that every segment succeeded \u2014 always check this list.\n */\n readonly failed: readonly ExportFailure[];\n}\n\n/** The minimal read surface the export needs from a store \u2014 a `CloudRoaring` satisfies this structurally. */\nexport interface SegmentReader {\n segment(name: string, options?: { namespace?: string }): { iterate(): AsyncIterable<number> };\n}\n\nconst DEFAULT_NDJSON_BATCH_BYTES = 64 * 1024;\n\nconst errMessage = (err: unknown): string => (err instanceof Error ? err.message : String(err));\n\n/**\n * Export every registered segment's current generation through `sink`. Enumerates via `registry`, reads via\n * `reader` (the store). Returns a manifest. `CloudRoaring.exportSegments` is the\n * public entry \u2014 it calls this with its own store + registry (so enumeration and the read path provably share one\n * registry).\n *\n * **Fault isolation:** a per-segment read/write fault aborts that segment's partial (so a truncated file never\n * looks complete), records it in `failed`, and the run continues \u2014 one bad segment never blocks the rest. A\n * `close()` fault is likewise recorded (never triggering `abort()` \u2014 they're mutually exclusive).\n *\n * Re-running overwrites the segments it re-exports but does **not** prune files for segments that have since\n * disappeared \u2014 export to a fresh directory for a clean dump. For a *current* image, run against a\n * freshly-constructed store: a store's storage source re-resolves each segment's generation on a short TTL, or, with\n * no registry, with `cache.genTtlMs: 0`, or built without a clock, only when an eviction, a sweep or an invalidation\n * makes it, so a long-lived store may export a view one load behind, or more. Each segment is read live, not pinned:\n * a publish while a long segment exports can re-resolve it part-way, and its file then holds chunks of two\n * generations, each whole and verified. For one generation per file, export from a quiet window.\n */\nexport async function runExport(\n reader: SegmentReader,\n registry: IRegistryDriver,\n sink: ExportSink,\n options: ExportOptions = {},\n): Promise<ExportManifest> {\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const format = options.format ?? 'roaring';\n const ext = format === 'roaring' ? '.roaring' : '.ndjson';\n const batchCap = options.ndjsonBatchBytes ?? DEFAULT_NDJSON_BATCH_BYTES;\n const segments: ExportedSegment[] = [];\n const failed: ExportFailure[] = [];\n let totalIds = 0;\n\n const exportRef = async (ref: SegmentRef): Promise<void> => {\n let writer: ExportWriter | undefined;\n let count = 0;\n let bytes = 0;\n try {\n validateSegmentRef(ref); // defense-in-depth: registry rows are untrusted bytes here\n const ids = reader.segment(ref.segment, { namespace: ref.namespace }).iterate();\n writer = await sink.open(ref, ext);\n if (format === 'roaring') {\n const bm = requireCodec(options.codec, \"runExport({ format: 'roaring' })\").empty();\n for await (const id of ids) {\n bm.add(id);\n count += 1;\n }\n const out = bm.serialize(); // portable RoaringBitmap32 \u2014 the cross-language CRoaring format\n await writer.write(out);\n bytes = out.length;\n } else {\n let buf = '';\n for await (const id of ids) {\n buf += `${id}\\n`;\n count += 1;\n if (buf.length >= batchCap) {\n const b = Buffer.from(buf, 'utf8');\n await writer.write(b);\n bytes += b.length;\n buf = '';\n }\n }\n if (buf.length > 0) {\n const b = Buffer.from(buf, 'utf8');\n await writer.write(b);\n bytes += b.length;\n }\n }\n } catch (err) {\n // Read/write fault: discard the partial (best-effort, so a failing cleanup can't mask the real fault),\n // record it, and move on \u2014 one bad segment never blocks the rest.\n if (writer !== undefined) {\n try {\n await writer.abort?.();\n } catch {\n /* best-effort cleanup */\n }\n }\n failed.push({ segment: ref.segment, namespace: ref.namespace, error: errMessage(err) });\n return;\n }\n // Commit outside the write try: a `close()` failure must NOT also trigger `abort()` (they're mutually\n // exclusive \u2014 abort on a write fault, close on success \u2014 so a custom sink never double-finalizes). A close\n // fault is still isolated: recorded, not thrown.\n try {\n await writer.close();\n } catch (err) {\n failed.push({ segment: ref.segment, namespace: ref.namespace, error: errMessage(err) });\n return;\n }\n segments.push({ segment: ref.segment, namespace: ref.namespace, count, bytes });\n totalIds += count;\n };\n\n for await (const rec of registry.list(options.namespace)) {\n // A due-index pointer row is not a segment: an unscoped eject would otherwise write one empty file per pointer\n // into the portability dump \u2014 the artifact whose whole value is being a faithful copy of the user's data.\n if (options.namespace === undefined && isReservedRow(rec)) continue;\n if (rec.status === 'destroyed') continue; // crypto-shredded \u2192 bytes unrecoverable; nothing to export\n await exportRef({ segment: rec.segment, namespace: rec.namespace });\n }\n return { version: 1, format, totalSegments: segments.length, totalIds, segments, failed };\n}\n", "/**\n * `segmentExists` and `listSegments` \u2014 ask the registry what is there.\n *\n * The registry has always known which segments exist; it is what `checkConsistency`, the retention sweep and\n * `exportSegments` each enumerate. It was simply never exposed, so a user asking \"do I already have this?\" had\n * to infer it: `count()` returns `0` for a segment that was never loaded AND for one loaded with no ids, and\n * `generations()` costs a bucket listing to answer a question the pointer alone settles.\n *\n * Inferring it is the expensive kind of wrong. The alternative a user reaches for is keeping their own list of\n * segment names alongside the store \u2014 a second source of truth, updated by hand, drifting from the first the\n * moment a load fails halfway. The registry is already the list; these two functions read it.\n */\nimport type { IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\nimport { excludingReservedRows } from './registry-scan';\nimport { validateUserNamespace, validateUserRef } from './validate';\n\n/** One segment the registry knows about. */\nexport interface SegmentInfo extends SegmentRef {\n /**\n * The generation the **pointer names** \u2014 not what a read would resolve. On a `destroyed` tombstone this is\n * still a number while reads answer empty, so read it together with {@link SegmentInfo.status} rather than\n * alone. `null` means the row exists with no Storage generation yet.\n */\n readonly currentGen: number | null;\n /**\n * `destroyed` is a tombstone: the row stays so sweeps can still find it, and the generations are gone \u2014 on an\n * encrypted segment the DEK wrappings went with them, which is what makes the bytes unrecoverable rather than\n * merely deleted.\n */\n readonly status: RegistryRecord['status'];\n}\n\n/**\n * Whether this segment's pointer resolves a generation \u2014 the question behind \"does it already exist?\".\n *\n * One registry point read, on every backend. Deliberately narrower than \"is there a row\": it is **false** for a\n * row that exists with `currentGen: null` (a retention policy recorded ahead of the first load) and for a\n * `destroyed` tombstone, because a read answers empty in both. The predicate mirrors generation resolution\n * clause for clause, which is what keeps the two from disagreeing.\n *\n * It is NOT the same question as `count() > 0`. A segment loaded with no ids exists and counts zero \u2014 the\n * distinction between \"never loaded\" and \"loaded, and genuinely empty\" is the one `count()` cannot make and\n * the main reason this function is worth having.\n *\n * Two states answer `true` where a read still gives you nothing, and neither is this call's job to detect: a\n * **torn restore** (`missing-storage-generation` \u2014 a live pointer whose object was deleted) makes reads *throw*\n * `NotFoundError` rather than answer empty, and `runConsistencyCheck` is the call that looks for it; and a\n * handle carrying an expired `expiresAt` reads as empty by a rule that lives on the handle, not on the row.\n *\n * Not a lock. A segment can be created or dropped between this call and whatever you do next; if the answer\n * has to hold, use the fences that exist for it \u2014 `load`'s guard, or `expectFrom`/`expectToken` on a publish.\n */\nexport async function segmentExists(ref: SegmentRef, registry: IRegistryDriver): Promise<boolean> {\n validateUserRef(ref);\n const record = await registry.get(ref);\n return record !== null && record.status !== 'destroyed' && record.currentGen !== null;\n}\n\n/**\n * Every segment the registry holds, streamed \u2014 optionally scoped to one namespace.\n *\n * **This is an admin/discovery call, not a request-path one.** It is the registry's own enumeration \u2014 a paged\n * LIST over the `registry/` prefix \u2014 so cost grows with the fleet, not with what you are looking for. The\n * fleet-wide sweeps that *drain* it bound it (`maxScanSegments`); this one streams, so the bound is yours.\n *\n * **Scoping to a namespace narrows the LIST prefix**, so it really is the difference between reading one\n * tenant and reading all of them.\n *\n * Streams, so a large fleet need not be held at once, and stopping the iteration stops the scan \u2014 as long as the\n * registry driver's own `list` streams. One that collects its whole enumeration before yielding defeats both\n * properties, since the scan is then paid for and resident before the first row reaches you, so a custom driver's\n * `list` should yield each row as it reads it. The registry drivers this library ships all stream; on S3, GCS and\n * Azure Blob the granularity is a page, so one LIST page and its in-flight row reads complete regardless. Nothing\n * here retries the scan: a transient fault part-way through ends the iteration with that error, and calling\n * `listSegments` again scans from the start, since the iterable one call returns is used up.\n *\n * Yields `destroyed` tombstones, and rows whose `currentGen` is `null`, because hiding either would make this\n * disagree with the registry it reports on \u2014 a filtered enumeration that looks complete is how a retention\n * sweep ends up permanently skipping rows nobody can see. Filter on `status`/`currentGen` yourself, or use\n * {@link segmentExists} for the \"does the pointer resolve\" question. Internal bookkeeping rows are the one\n * exclusion, and only on an unscoped scan: they are not segments, and a caller naming their namespace still\n * sees them.\n */\nexport async function* listSegments(\n registry: IRegistryDriver,\n options: { namespace?: string } = {},\n): AsyncIterable<SegmentInfo> {\n // Validated here rather than only at the facade: this is a public export of `@cloudbitmaps/core`, and its\n // sibling `segmentExists` validates. Without it a typo'd or externally-supplied tenant id reads as \"this\n // tenant has no segments\" \u2014 an empty result is the most dangerous possible answer to a malformed question.\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const raw = registry.list(options.namespace);\n const source = options.namespace === undefined ? excludingReservedRows(raw) : raw;\n for await (const record of source) {\n yield {\n ...(record.namespace === undefined ? {} : { namespace: record.namespace }),\n segment: record.segment,\n currentGen: record.currentGen,\n status: record.status,\n };\n }\n}\n", "/**\n * The two SDK-free backends, each as one object: `MemoryStorage` and `LocalFsStorage`.\n *\n * These live in the main entry because neither needs a cloud SDK \u2014 there is nothing to keep out of the\n * install, and it keeps the first five minutes to a single import. Their cloud siblings (`S3Storage`,\n * `GcsStorage`, `AzureBlobStorage`) ship in `@cloudbitmaps/s3`, `/gcs` and `/azure-blob` for the opposite\n * reason: each carries an SDK nobody should install for a service they do not use.\n *\n * Each one exists to state a location **once**. See {@link StorageBackend} for why that is worth a class.\n */\nimport { join } from 'node:path';\nimport { brandAsBackend, STORAGE_BACKEND } from '@/core/ports';\nimport type { IRegistryDriver, IStorageDriver, StorageBackend } from '@/core/ports';\nimport { MemoryRegistryDriver, MemoryStorageDriver } from './memory';\nimport { LocalFsStorageDriver } from './localfs/storage';\nimport { LocalFsRegistryDriver } from './localfs/registry';\n\nexport interface MemoryStorageOptions {\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n}\n\n/**\n * An in-process backend \u2014 nothing to install, nothing to clean up, and it disappears with the process.\n *\n * This is the one to reach for in tests and in the first five minutes. It is **not** a cache in front of\n * anything: it is the whole store, held in RAM, so a restart is a data loss.\n */\nexport class MemoryStorage implements StorageBackend {\n /** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */\n declare readonly [STORAGE_BACKEND]: true;\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n\n constructor(options: MemoryStorageOptions = {}) {\n this.storage = new MemoryStorageDriver();\n this.registry = new MemoryRegistryDriver(options.now === undefined ? {} : { now: options.now });\n brandAsBackend(this);\n }\n}\n\nexport interface LocalFsStorageOptions {\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n}\n\n/**\n * A local-filesystem backend rooted at one directory: generations under `<root>/storage`, pointers under\n * `<root>/registry`.\n *\n * The layout is stated here rather than by the caller, which is the point \u2014 two paths a caller wrote out\n * separately could name different roots, and nothing would stop them. It is also the layout\n * the `export-segments` CLI expects, so a store built this way can be ejected without being told where to look.\n *\n * A root is for **one process**. Every instance in a process that names the same root shares one lock per\n * registry row, however the root is spelled (relative path, symlink), so they cannot both advance a row from\n * one token. Two processes on one root are **not** fenced from each other.\n */\nexport class LocalFsStorage implements StorageBackend {\n /** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */\n declare readonly [STORAGE_BACKEND]: true;\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n\n constructor(\n readonly root: string,\n options: LocalFsStorageOptions = {},\n ) {\n this.storage = new LocalFsStorageDriver(join(root, 'storage'));\n this.registry = new LocalFsRegistryDriver(\n join(root, 'registry'),\n options.now === undefined ? {} : { now: options.now },\n );\n brandAsBackend(this);\n }\n}\n", "/**\n * In-memory drivers \u2014 the reference backing for tests and quick experiments.\n * They store **serialized bytes + an OCC token** (not live objects), so the engine's\n * safe-deserialize / size-cap paths and the registry's OCC paths are genuinely exercised.\n *\n * Not in `core/` (this is a driver), so it may hold concrete state.\n */\nimport { createHash } from 'node:crypto';\nimport { BufferSink } from '../core/blob';\nimport type { BlobSink } from '../core/blob';\nimport { NotFoundError, ValidationError, WriteConflictError } from '../core/errors';\nimport { segmentKey } from '../core/keys';\nimport { validateSegmentRef } from '../core/validate';\nimport type {\n StorageCaps,\n GenKey,\n IStorageDriver,\n IRegistryDriver,\n NewRegistryRecord,\n RegCaps,\n RegistryPatch,\n RegistryRecord,\n SegmentRef,\n Token,\n} from '../core/ports';\nimport {\n applyRegistryPatch,\n recordFromNew,\n validateNewRegistryRecord,\n validateRegistryPatch,\n} from './_shared/registry';\n\nexport interface MemoryRegistryDriverOptions {\n /** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now` (drivers may use ambient time). */\n readonly now?: () => number;\n}\n\n/**\n * In-memory {@link IRegistryDriver} \u2014 one record per segment under OCC. The token is a single global,\n * monotonic, never-reused counter (so a record recreated after `delete` always gets a strictly-greater\n * token \u2192 ABA-safe even though `delete` removes the row physically).\n */\nexport class MemoryRegistryDriver implements IRegistryDriver {\n private readonly rows = new Map<string, RegistryRecord>();\n private readonly now: () => number;\n private seq = 0;\n\n constructor(options: MemoryRegistryDriverOptions = {}) {\n this.now = options.now ?? (() => Date.now());\n }\n\n private nextToken(): Token {\n this.seq += 1;\n return String(this.seq);\n }\n\n capabilities(): RegCaps {\n return { strongRead: true };\n }\n\n async get(ref: SegmentRef): Promise<RegistryRecord | null> {\n validateSegmentRef(ref);\n const row = this.rows.get(segmentKey(ref));\n return row ? structuredClone(row) : null; // clone out: callers can't mutate stored (nested) state\n }\n\n async create(ref: SegmentRef, record: NewRegistryRecord): Promise<{ token: Token }> {\n validateSegmentRef(ref);\n validateNewRegistryRecord(record);\n const key = segmentKey(ref);\n if (this.rows.has(key)) {\n throw new WriteConflictError(`registry row already exists for segment ${ref.segment}`);\n }\n const token = this.nextToken();\n // clone in: the caller's (nested) retention/residency can't alias stored state (value semantics, parity\n // with the serialize-based persistent drivers).\n this.rows.set(key, structuredClone(recordFromNew(ref, record, this.now(), token)));\n return { token };\n }\n\n async compareAndSwap(\n ref: SegmentRef,\n expected: Token,\n patch: RegistryPatch,\n ): Promise<{ token: Token }> {\n validateSegmentRef(ref);\n validateRegistryPatch(patch);\n const key = segmentKey(ref);\n const existing = this.rows.get(key);\n if (!existing || existing.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n const token = this.nextToken();\n this.rows.set(key, structuredClone(applyRegistryPatch(existing, patch, this.now(), token)));\n return { token };\n }\n\n async *list(namespace?: string): AsyncIterable<RegistryRecord> {\n for (const row of this.rows.values()) {\n if (namespace === undefined || row.namespace === namespace) yield structuredClone(row);\n }\n }\n\n async delete(ref: SegmentRef, expected?: Token): Promise<void> {\n validateSegmentRef(ref);\n const key = segmentKey(ref);\n if (expected !== undefined && this.rows.get(key)?.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n this.rows.delete(key); // idempotent; token uniqueness is global so a recreate is ABA-safe\n }\n}\n\n/**\n * In-memory {@link IStorageDriver} \u2014 write-once immutable generation objects as opaque bytes. A \"dumb byte\n * mover\" (understands neither roaring nor `.crbm`), so it's a faithful storage backend for tests and a zero-setup\n * local storage tier. Mirrors the LocalFs/S3 contract (write-once, range/tail reads).\n */\nexport class MemoryStorageDriver implements IStorageDriver {\n private readonly objects = new Map<string, Uint8Array>();\n\n capabilities(): StorageCaps {\n return { rangeRead: true, maxObjectBytes: Number.MAX_SAFE_INTEGER, conditionalPut: true };\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const k = genObjectKey(key); // validates ref + generation\n const sink = new BufferSink();\n await write(sink);\n const body = sink.bytes();\n if (this.objects.has(k)) {\n throw new WriteConflictError(\n `generation already exists (write-once): ${key.segment}.${key.generation}`,\n );\n }\n this.objects.set(k, body);\n return { size: body.length, sha256: createHash('sha256').update(body).digest('hex') };\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n const body = this.require(key);\n if (offset + length > body.length) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${body.length}B`,\n );\n }\n return body.slice(offset, offset + length);\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const body = this.require(key);\n const take = Math.min(Math.max(maxBytes, 0), body.length);\n return { bytes: body.slice(body.length - take), size: body.length };\n }\n\n async delete(key: GenKey): Promise<void> {\n this.objects.delete(genObjectKey(key)); // idempotent\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n validateSegmentRef(ref);\n const prefix = `${segmentKey(ref)} `;\n for (const k of this.objects.keys()) {\n if (k.startsWith(prefix)) {\n yield {\n namespace: ref.namespace,\n segment: ref.segment,\n generation: Number(k.slice(prefix.length)),\n };\n }\n }\n }\n\n private require(key: GenKey): Uint8Array {\n const body = this.objects.get(genObjectKey(key));\n if (body === undefined) {\n throw new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);\n }\n return body;\n }\n}\n\n/** Collision-proof object key for a generation: `<segmentKey> <generation>` (the name parts are encoded, so a space cannot appear inside one). */\nfunction genObjectKey(key: GenKey): string {\n validateSegmentRef(key);\n if (!Number.isInteger(key.generation) || key.generation < 0) {\n throw new ValidationError(`generation must be a non-negative integer; got ${key.generation}`);\n }\n return `${segmentKey(key)} ${key.generation}`;\n}\n", "/**\n * The byte-IO seam the `.crbm` codec reads/writes through.\n *\n * Deliberately tiny and driver-independent: a `BlobSink` is a streaming append target (the writer needs\n * no random access), a `BlobReader` supports the speculative tail read + bounded range reads. An\n * `IStorageDriver` adapts a `GenKey` to these; the in-memory impls below let the format be\n * golden-tested and let the in-memory storage tier hold a real `.crbm` with no filesystem.\n */\nimport { ValidationError } from './errors';\n\n/** Streaming append target for a single object being written. */\nexport interface BlobSink {\n write(bytes: Uint8Array): Promise<void>;\n}\n\n/**\n * Random/tail read access to a single finished object. The total size comes back from `getTail` (the\n * codec's entry point), so there's no separate `size()` \u2014 keeping the seam minimal for driver authors.\n */\nexport interface BlobReader {\n /** Exactly `length` bytes starting at `offset`. Out-of-range is a `ValidationError`, never a short read. */\n getRange(offset: number, length: number): Promise<Uint8Array>;\n /** The last `min(maxBytes, size)` bytes plus the total size \u2014 the one-GET footer+index path. */\n getTail(maxBytes: number): Promise<{ bytes: Uint8Array; size: number }>;\n}\n\n/** In-memory `BlobSink` that accumulates written bytes; `bytes()` returns the concatenation. */\nexport class BufferSink implements BlobSink {\n private readonly chunks: Uint8Array[] = [];\n private length = 0;\n\n write(bytes: Uint8Array): Promise<void> {\n // Copy: callers may reuse their buffer after write() resolves.\n this.chunks.push(bytes.slice());\n this.length += bytes.length;\n return Promise.resolve();\n }\n\n bytes(): Uint8Array {\n const out = new Uint8Array(this.length);\n let offset = 0;\n for (const chunk of this.chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n }\n}\n\n/** In-memory `BlobReader` over a fixed byte buffer. */\nexport class BufferReader implements BlobReader {\n constructor(private readonly buffer: Uint8Array) {}\n\n getRange(offset: number, length: number): Promise<Uint8Array> {\n if (\n !Number.isInteger(offset) ||\n !Number.isInteger(length) ||\n offset < 0 ||\n length < 0 ||\n offset + length > this.buffer.length\n ) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${this.buffer.length}-byte blob`,\n );\n }\n return Promise.resolve(this.buffer.subarray(offset, offset + length));\n }\n\n getTail(maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const size = this.buffer.length;\n const take = Math.min(Math.max(maxBytes, 0), size);\n return Promise.resolve({ bytes: this.buffer.subarray(size - take), size });\n }\n}\n", "/**\n * `LocalFsStorageDriver` \u2014 a zero-cloud {@link IStorageDriver} backed by the local filesystem.\n *\n * Generations are write-once immutable files. A new object is streamed to a temp file (with its content\n * hashed in-flight), `fsync`-ed, then published with an atomic `link` that fails if the destination\n * already exists \u2014 so a generation can never be silently overwritten (hard invariant 2), and a crash\n * leaves only an orphan temp file, never a torn object. Drivers do filesystem I/O and may use `node:crypto`; only `core/`\n * is bound by the determinism lint.\n */\nimport { constants as FS } from 'node:fs';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { link, mkdir, open, readdir, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { NotFoundError, ValidationError, WriteConflictError } from '@/core/errors';\nimport type { BlobSink } from '@/core/blob';\nimport type { StorageCaps, GenKey, IStorageDriver, SegmentRef } from '@/core/ports';\nimport { storageObjectPath, parseGeneration, segmentsDir } from './paths';\nimport { O_NOFOLLOW, fsyncDir, isCode, mapFsError } from './fs-util';\n\nexport class LocalFsStorageDriver implements IStorageDriver {\n constructor(private readonly root: string) {}\n\n capabilities(): StorageCaps {\n return { rangeRead: true, maxObjectBytes: Number.MAX_SAFE_INTEGER, conditionalPut: false };\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const finalPath = storageObjectPath(this.root, key);\n await mkdir(dirname(finalPath), { recursive: true });\n\n const tmpPath = `${finalPath}.${randomUUID()}.tmp`;\n // 'wx' = O_CREAT|O_EXCL|O_WRONLY; add O_NOFOLLOW so a pre-planted symlink can't redirect the write.\n const handle = await open(tmpPath, FS.O_CREAT | FS.O_EXCL | FS.O_WRONLY | O_NOFOLLOW);\n const hash = createHash('sha256');\n let size = 0;\n const sink: BlobSink = {\n async write(bytes) {\n await handle.write(bytes);\n hash.update(bytes);\n size += bytes.length;\n },\n };\n\n try {\n await write(sink);\n await handle.sync();\n } catch (err) {\n await handle.close().catch(() => {});\n await unlink(tmpPath).catch(() => {});\n throw mapFsError(err);\n }\n await handle.close();\n\n // Atomic write-once publish: link fails with EEXIST if the generation already exists.\n try {\n await link(tmpPath, finalPath);\n } catch (err) {\n await unlink(tmpPath).catch(() => {});\n if (isCode(err, 'EEXIST')) {\n // A generation is write-once; a collision is a concurrency/state condition, not bad input.\n // Identify it by the logical key only \u2014 never leak the absolute filesystem path.\n throw new WriteConflictError(\n `generation already exists (write-once): ${key.segment}.${key.generation}`,\n );\n }\n throw mapFsError(err);\n }\n await unlink(tmpPath).catch(() => {});\n await fsyncDir(dirname(finalPath));\n return { size, sha256: hash.digest('hex') };\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n const handle = await this.openRead(key);\n try {\n const { size } = await handle.stat();\n if (offset + length > size) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${size}B`,\n );\n }\n const buf = Buffer.alloc(length);\n if (length > 0) await handle.read(buf, 0, length, offset);\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n } finally {\n await handle.close();\n }\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const handle = await this.openRead(key);\n try {\n const { size } = await handle.stat();\n const take = Math.min(Math.max(maxBytes, 0), size);\n const buf = Buffer.alloc(take);\n if (take > 0) await handle.read(buf, 0, take, size - take);\n return { bytes: new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength), size };\n } finally {\n await handle.close();\n }\n }\n\n async delete(key: GenKey): Promise<void> {\n // Idempotent: deleting an absent generation is a no-op (GC may race / retry).\n await unlink(storageObjectPath(this.root, key)).catch((err) => {\n if (!isCode(err, 'ENOENT')) throw mapFsError(err);\n });\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n const dir = segmentsDir(this.root, ref);\n let names: string[];\n try {\n names = await readdir(dir);\n } catch (err) {\n if (isCode(err, 'ENOENT')) return; // no generations yet\n throw mapFsError(err);\n }\n for (const name of names) {\n const generation = parseGeneration(ref.segment, name);\n if (generation !== null) {\n yield { namespace: ref.namespace, segment: ref.segment, generation };\n }\n }\n }\n\n private async openRead(key: GenKey): Promise<Awaited<ReturnType<typeof open>>> {\n try {\n return await open(storageObjectPath(this.root, key), FS.O_RDONLY | O_NOFOLLOW);\n } catch (err) {\n if (isCode(err, 'ENOENT')) {\n throw new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);\n }\n // A symlink at the object path is rejected (ELOOP) \u2014 treat as not-found, don't follow it.\n if (isCode(err, 'ELOOP')) {\n throw new NotFoundError(\n `generation path is a symlink, refusing: ${key.segment}.${key.generation}`,\n );\n }\n throw mapFsError(err);\n }\n }\n}\n", "/**\n * Logical-ref \u2192 filesystem-path mapping for the LocalFs drivers.\n *\n * Names are re-validated here at the driver boundary \u2014 defense in depth, because a driver can be\n * constructed and driven directly rather than through the engine that would otherwise have validated for\n * it, and a path built from an unvalidated name is a traversal or injection. The default\n * (absent) namespace maps to `_default`, which cannot collide with a real namespace because a\n * caller's `_default` encodes to `%5Fdefault` while this sentinel is emitted literally.\n */\nimport { join } from 'node:path';\nimport { ValidationError } from '@/core/errors';\nimport { validateSegmentRef } from '@/core/validate';\nimport type { GenKey, SegmentRef } from '@/core/ports';\nimport {\n DEFAULT_NAMESPACE,\n decodeNameFromPath,\n encodeNameForPath,\n namespacePathPart,\n} from '../_shared/keys';\n\n/** Directory holding all of a namespace's segment objects. */\nexport function segmentsDir(root: string, ref: SegmentRef): string {\n validateSegmentRef(ref);\n return join(root, namespacePathPart(ref.namespace), 'segments');\n}\n\n/** Absolute path of one `.crbm` generation object. */\nexport function storageObjectPath(root: string, key: GenKey): string {\n validateSegmentRef(key);\n if (!Number.isInteger(key.generation) || key.generation < 0) {\n throw new ValidationError(`generation must be a non-negative integer; got ${key.generation}`);\n }\n return join(segmentsDir(root, key), storageObjectFilename(key.segment, key.generation));\n}\n\n/** Filename pattern for a segment's generations: `<segment>.<gen>.crbm`. */\nexport function storageObjectFilename(segment: string, generation: number): string {\n return `${encodeNameForPath(segment)}.${generation}.crbm`;\n}\n\n/** Parse a generation number out of a `<segment>.<gen>.crbm` filename, or `null` if it doesn't match. */\nexport function parseGeneration(segment: string, filename: string): number | null {\n const prefix = `${encodeNameForPath(segment)}.`;\n const suffix = '.crbm';\n if (!filename.startsWith(prefix) || !filename.endsWith(suffix)) return null;\n const middle = filename.slice(prefix.length, filename.length - suffix.length);\n // Canonical decimal only: no leading zeros (so `s.07.crbm` can't alias `s.7.crbm`), and within\n // safe-integer range. The writer only ever emits the canonical form.\n if (!/^(0|[1-9]\\d*)$/.test(middle)) return null;\n const gen = Number(middle);\n return Number.isSafeInteger(gen) ? gen : null;\n}\n\nconst REGISTRY_SUFFIX = '.reg';\n\n/** Directory holding a namespace's registry rows (one file per segment). */\nexport function registryDir(root: string, namespace: string | undefined): string {\n // Validated like every sibling builder here. It is the caller-facing entry for a namespace-scoped\n // `list()`, so without this a namespace of `../..` reaches `readdir` outside the storage root \u2014 no content\n // escapes (iteration aborts before yielding) but it answers \"does this directory exist?\", and the whole\n // point of re-validating at the driver boundary is that a driver driven directly must be safe on its own.\n if (namespace !== undefined) validateSegmentRef({ segment: 'x', namespace });\n return join(root, namespacePathPart(namespace), 'registry');\n}\n\n/** Absolute path of one segment's registry row file: `<ns>/registry/<segment>.reg`. */\nexport function registryRowPath(root: string, ref: SegmentRef): string {\n validateSegmentRef(ref);\n return join(\n registryDir(root, ref.namespace),\n `${encodeNameForPath(ref.segment)}${REGISTRY_SUFFIX}`,\n );\n}\n\n/**\n * Parse a segment name out of a `<segment>.reg` filename, or `null` if it doesn't match \u2014 including any\n * file whose stem isn't a valid segment name. A strict parser means a stray or planted `.reg` file makes\n * `list()` *skip* it, never abort the whole enumeration on a boundary throw.\n */\nexport function parseRegistryRow(filename: string): string | null {\n if (!filename.endsWith(REGISTRY_SUFFIX)) return null;\n const stem = filename.slice(0, filename.length - REGISTRY_SUFFIX.length);\n const segment = decodeNameFromPath(stem);\n // The encoding must ROUND-TRIP, not merely decode. POSIX will happily hold a literal `dedup:foo.reg`\n // alongside the driver's own `dedup%3Afoo.reg`; both decode to `dedup:foo`, and reporting that name twice\n // would hand `list()` a segment whose row path resolves to only one of them. The driver never writes the\n // literal form, so anything that does not re-encode to exactly this filename was not written by us.\n if (encodeNameForPath(segment) !== stem) return null;\n try {\n validateSegmentRef({ segment });\n } catch {\n return null;\n }\n return segment;\n}\n\n/**\n * Parse a namespace out of a directory name under the storage root, or `null` if it was not written by us.\n *\n * The inverse of the `namespacePathPart(ns)` that {@link segmentsDir} and {@link registryDir}\n * write. A fleet-wide scan enumerates these directories, so getting it wrong does not fail one segment \u2014 it\n * aborts the whole enumeration, taking the consistency check, the retention sweep and subject erasure with it.\n *\n * Like {@link parseRegistryRow} this requires the encoding to **round-trip**, so a planted literal `tenant:acme`\n * directory sitting next to the driver's own `tenant%3Aacme` is skipped rather than reported as a namespace\n * whose rows then resolve to the other directory. `_default` maps back to \"no namespace\".\n */\nexport function parseNamespaceDir(entry: string): { namespace: string | undefined } | null {\n if (entry === DEFAULT_NAMESPACE) return { namespace: undefined };\n const namespace = decodeNameFromPath(entry);\n if (encodeNameForPath(namespace) !== entry) return null;\n try {\n validateSegmentRef({ segment: 'x', namespace });\n } catch {\n return null;\n }\n return { namespace };\n}\n", "/**\n * Shared filesystem helpers for the LocalFs drivers (storage and registry) \u2014 one source\n * of truth for error-code matching, the symlink-refusal flag, and directory durability.\n */\nimport { constants as FS } from 'node:fs';\nimport { open } from 'node:fs/promises';\nimport { TransientError } from '@/core/errors';\n\ninterface NodeError extends Error {\n code?: string;\n}\n\n/** True when `err` is a Node system error with the given `code` (e.g. `'ENOENT'`, `'EEXIST'`). */\nexport const isCode = (err: unknown, code: string): boolean =>\n err instanceof Error && (err as NodeError).code === code;\n\n/**\n * Filesystem error codes that are transient \u2014 a retry after a brief backoff often succeeds: the resource is\n * busy (`EBUSY`), the OS asked us to retry (`EAGAIN`), or we hit the open-file-descriptor ceiling\n * (`EMFILE`/`ENFILE`, common under heavy concurrency once earlier handles close). A networked FS may also\n * surface `ETIMEDOUT`.\n */\nconst TRANSIENT_FS_CODES = new Set(['EBUSY', 'EAGAIN', 'EMFILE', 'ENFILE', 'ETIMEDOUT']);\n\nexport const isFsTransient = (err: unknown): boolean =>\n err instanceof Error && TRANSIENT_FS_CODES.has((err as NodeError).code ?? '');\n\n/**\n * Reclassify a transient filesystem fault as a retryable {@link TransientError}, so the store's read retry can\n * ride it out and a write's caller can tell it from a deterministic failure; everything else \u2014 including the\n * driver's own typed errors \u2014 propagates unchanged.\n */\nexport const mapFsError = (err: unknown): unknown =>\n isFsTransient(err)\n ? new TransientError(`transient filesystem fault: ${(err as NodeError).code}`, { cause: err })\n : err;\n\n/**\n * `O_NOFOLLOW` where the platform supports it (absent on Windows \u2192 0). OR it into an `open` flag set so a\n * symlink **at the final path component** is refused rather than followed outside the storage root.\n * Containment of symlinked *directory* components assumes the root itself is not attacker-writable.\n */\nexport const O_NOFOLLOW = FS.O_NOFOLLOW ?? 0;\n\n/**\n * Best-effort parent-directory fsync, so a just-published `rename`/`link` survives a crash. Without it the\n * directory entry can still be in the page cache when power goes: the generation's bytes are durable and\n * the name pointing at them is not, which is the one way a published generation can come back missing.\n */\nexport async function fsyncDir(dir: string): Promise<void> {\n try {\n const handle = await open(dir, 'r');\n try {\n await handle.sync();\n } finally {\n await handle.close();\n }\n } catch {\n // Some platforms reject directory fsync; durability of the entry is then best-effort.\n }\n}\n", "/**\n * `LocalFsRegistryDriver` \u2014 a zero-cloud, persistent {@link IRegistryDriver}.\n *\n * One JSON file per segment at `<root>/<namespace>/registry/<segment>.reg`, holding `{ deleted, record }`.\n * OCC: the token is a monotonic counter (stringified), advanced on every mutation and\n * even across a `delete` (which **tombstones** rather than unlinks) so a deleted-then-recreated row never\n * re-issues an old token (ABA-safe). Every write is temp \u2192 fsync(file) \u2192 atomic rename \u2192 fsync(dir), and\n * read-modify-write is serialized per row across the whole process (the lock is keyed by the row's resolved\n * path, so every instance on one root shares it). A root is for one process: two processes on one root are not\n * fenced. Drivers do I/O; only `core/` is bound by determinism.\n */\nimport { constants as FS } from 'node:fs';\nimport { randomUUID } from 'node:crypto';\nimport { mkdir, open, readdir, realpath, rename, unlink } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { IntegrityError, ValidationError, WriteConflictError } from '@/core/errors';\nimport type {\n IRegistryDriver,\n NewRegistryRecord,\n RegCaps,\n RegistryPatch,\n RegistryRecord,\n SegmentRef,\n Token,\n} from '@/core/ports';\nimport {\n applyRegistryPatch,\n parseRegistryEnvelope,\n recordFromNew,\n registryCounterOf,\n serializeRegistryEnvelope,\n validateNewRegistryRecord,\n validateRegistryPatch,\n type RegistryEnvelope,\n} from '../_shared/registry';\nimport { registryDir, registryRowPath, parseNamespaceDir, parseRegistryRow } from './paths';\nimport { O_NOFOLLOW, fsyncDir, isCode, mapFsError } from './fs-util';\n\n/** Defensive cap on a single registry file read from storage, before allocation. */\nconst DEFAULT_MAX_ROW_BYTES = 1 * 1024 * 1024;\n\n/**\n * Per-row promise chains, shared by every driver instance in the process and keyed by the row's resolved path,\n * so two instances on one root (or one root reached through a symlink or a relative path) take one lock. An\n * entry is deleted when its chain drains, so the map holds only rows with an operation in flight.\n */\nconst rowChains = new Map<string, Promise<unknown>>();\n\n/** Entries in the process-wide row-lock map; the map is empty whenever no registry operation is in flight. */\nexport function localFsRowLockCount(): number {\n return rowChains.size;\n}\n\n/**\n * The row's identity for locking: the real path of the nearest directory that exists, plus the not-yet-created\n * tail. Resolving symlinks and relative roots makes every spelling of one row the same key.\n */\nasync function rowLockKey(path: string): Promise<string> {\n const abs = resolve(path);\n const tail: string[] = [basename(abs)];\n let dir = dirname(abs);\n for (;;) {\n try {\n return join(await realpath(dir), ...tail);\n } catch (err) {\n const parent = dirname(dir);\n if (!isCode(err, 'ENOENT') || parent === dir) throw mapFsError(err);\n tail.unshift(basename(dir));\n dir = parent;\n }\n }\n}\n\nexport interface LocalFsRegistryDriverOptions {\n /** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n}\n\nexport class LocalFsRegistryDriver implements IRegistryDriver {\n private readonly now: () => number;\n\n constructor(\n private readonly root: string,\n options: LocalFsRegistryDriverOptions = {},\n ) {\n this.now = options.now ?? (() => Date.now());\n }\n\n capabilities(): RegCaps {\n return { strongRead: true };\n }\n\n async get(ref: SegmentRef): Promise<RegistryRecord | null> {\n const env = await this.readRow(registryRowPath(this.root, ref));\n return env && !env.deleted ? env.record : null;\n }\n\n async create(ref: SegmentRef, record: NewRegistryRecord): Promise<{ token: Token }> {\n validateNewRegistryRecord(record);\n const path = registryRowPath(this.root, ref);\n return this.withRowLock(path, async () => {\n const current = await this.readRow(path);\n if (current !== null && !current.deleted) {\n throw new WriteConflictError(`registry row already exists for segment ${ref.segment}`);\n }\n const counter = current ? registryCounterOf(current.record) + 1 : 0; // advance across a tombstone (ABA-safe)\n const token = String(counter);\n await this.writeRow(path, false, recordFromNew(ref, record, this.now(), token));\n return { token };\n });\n }\n\n async compareAndSwap(\n ref: SegmentRef,\n expected: Token,\n patch: RegistryPatch,\n ): Promise<{ token: Token }> {\n validateRegistryPatch(patch);\n const path = registryRowPath(this.root, ref);\n return this.withRowLock(path, async () => {\n const current = await this.readRow(path);\n if (current === null || current.deleted || current.record.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n const token = String(registryCounterOf(current.record) + 1);\n await this.writeRow(\n path,\n false,\n applyRegistryPatch(current.record, patch, this.now(), token),\n );\n return { token };\n });\n }\n\n async *list(namespace?: string): AsyncIterable<RegistryRecord> {\n for (const ns of await this.namespaceDirs(namespace)) {\n const dir = registryDir(this.root, ns);\n let names: string[];\n try {\n names = await readdir(dir);\n } catch (err) {\n if (isCode(err, 'ENOENT')) continue; // no registry rows in this namespace yet\n throw mapFsError(err);\n }\n for (const name of names) {\n const segment = parseRegistryRow(name);\n if (segment === null) continue;\n const env = await this.readRow(registryRowPath(this.root, { namespace: ns, segment }));\n if (env && !env.deleted) yield env.record;\n }\n }\n }\n\n async delete(ref: SegmentRef, expected?: Token): Promise<void> {\n const path = registryRowPath(this.root, ref);\n return this.withRowLock(path, async () => {\n const current = await this.readRow(path);\n if (expected !== undefined) {\n if (current === null || current.deleted || current.record.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n } else if (current === null || current.deleted) {\n return; // idempotent\n }\n // Tombstone (advance the counter) rather than unlink \u2014 keeps the token monotonic for ABA-safety.\n const token = String(registryCounterOf(current.record) + 1);\n await this.writeRow(path, true, { ...current.record, token, updatedAt: this.now() });\n });\n }\n\n /** Namespaces to scan: just the one requested, or every namespace dir under the root. */\n private async namespaceDirs(namespace: string | undefined): Promise<Array<string | undefined>> {\n if (namespace !== undefined) return [namespace];\n let entries: string[];\n try {\n entries = await readdir(this.root);\n } catch (err) {\n if (isCode(err, 'ENOENT')) return [];\n throw mapFsError(err);\n }\n // A namespace dir name is the namespace ENCODED for a path, so it has to be parsed back rather than used\n // as-is \u2014 `tenant:acme` lives in `tenant%3Aacme`, and `%` only ever appears in an escape we wrote, so an un-decoded directory name fails the round-trip check. Anything that does\n // not parse is skipped, never thrown on: this enumeration is fleet-wide, so one unrecognised directory\n // must not take the consistency check, the retention sweep and subject erasure down with it.\n const namespaces: Array<string | undefined> = [];\n for (const entry of entries) {\n const parsed = parseNamespaceDir(entry);\n if (parsed !== null) namespaces.push(parsed.namespace);\n }\n return namespaces;\n }\n\n private async readRow(path: string): Promise<RegistryEnvelope | null> {\n let handle;\n try {\n handle = await open(path, FS.O_RDONLY | O_NOFOLLOW);\n } catch (err) {\n if (isCode(err, 'ENOENT') || isCode(err, 'ELOOP')) return null;\n throw mapFsError(err);\n }\n try {\n const { size } = await handle.stat();\n if (size > DEFAULT_MAX_ROW_BYTES) {\n throw new IntegrityError(`registry row ${size}B exceeds cap ${DEFAULT_MAX_ROW_BYTES}B`);\n }\n const text = (await handle.readFile()).toString('utf8');\n return parseRegistryEnvelope(text, path);\n } finally {\n await handle.close();\n }\n }\n\n private async writeRow(path: string, deleted: boolean, record: RegistryRecord): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const out = Buffer.from(serializeRegistryEnvelope({ deleted, record }), 'utf8');\n // Cap on the write path too (the read path caps at the same size): never produce a row that would later\n // be unreadable. The governance fields are already capped by validate*, so this is a belt-and-braces guard.\n if (out.length > DEFAULT_MAX_ROW_BYTES) {\n throw new ValidationError(\n `registry row ${out.length}B exceeds cap ${DEFAULT_MAX_ROW_BYTES}B`,\n );\n }\n\n const tmp = `${path}.${randomUUID()}.tmp`;\n let handle;\n try {\n handle = await open(tmp, FS.O_CREAT | FS.O_EXCL | FS.O_WRONLY | O_NOFOLLOW);\n } catch (err) {\n throw mapFsError(err);\n }\n try {\n await handle.write(out);\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(tmp, path).catch(async (err) => {\n await unlink(tmp).catch(() => {});\n throw mapFsError(err);\n });\n await fsyncDir(dirname(path));\n }\n\n /** Serialize callbacks for a row so read-modify-write is atomic across every instance in the process. */\n private async withRowLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const key = await rowLockKey(path);\n const prev = rowChains.get(key) ?? Promise.resolve();\n const result = prev.then(fn, fn);\n const tail = result.then(\n () => undefined,\n () => undefined,\n );\n rowChains.set(key, tail);\n void tail.then(() => {\n if (rowChains.get(key) === tail) rowChains.delete(key);\n });\n return result;\n }\n}\n", "/**\n * Audit sink \u2014 an injected, no-op-by-default seam for **security/compliance** events, distinct\n * from the metrics sink (`IMetricsSink`). Different audience (an audit log / SIEM, not a dashboard), different\n * retention, and only the compliance-relevant *state changes* \u2014 never routine reads/writes (that's the metrics\n * sink). It doubles as the GDPR Art. 30 \"record of processing\" surface: publishes, refused loads, rollbacks,\n * rewrites, erasures and disposals.\n *\n * Like `Clock`/`Rng`/`IMetricsSink`, it's injected (into the operations that emit \u2014 a load, the `*Into`\n * verbs, a rollback, the erasure rewrite, crypto-shred, disposal and the retention sweep) and wrapped\n * exception-safe, so a buggy sink can never break the operation it observes.\n * Events are vendor-neutral and carry no timestamp/actor \u2014 the sink runs synchronously at the event, so it\n * stamps its own time / attaches the caller identity (keeps `core/` free of ambient time). As with metrics,\n * `segment`/`namespace` are caller-controlled strings that may be PII \u2014 treat them accordingly when routing.\n *\n * Not (yet) emitted: **KEK rotation** \u2014 rotation here is operator-side keystore reconfiguration (key-id-tagged\n * wrappings, no data re-encryption), so there is no library call to hook. A future `rewrapSegment()` op would\n * add a `kek.rotate` variant; until then, audit key changes at your keystore-config layer.\n */\n\n/** A security/compliance-relevant state change. Vendor-neutral; the sink adds its own timestamp/actor. */\nexport type AuditEvent =\n | {\n /** A new immutable Storage generation *became the segment's current generation*, by a load's publish. */\n readonly kind: 'segment.publish';\n readonly namespace?: string;\n readonly segment: string;\n readonly generation: number;\n }\n | {\n /**\n * A segment's pointer was moved by a rollback to a generation still in the bucket that the caller named:\n * **backwards**, or forward with `allowForward`, which undoes an earlier rollback.\n *\n * The only non-forward-only pointer move in the library, and the only one no automatic path can perform \u2014\n * a human decided the current generation was wrong and named the one they wanted. Every other pointer move\n * can be reconstructed from \"a load happened\"; this one is someone overriding the ordering rule the rest of\n * the system relies on, which is precisely what an incident review or an Art. 30 record wants to find.\n *\n * `fromGeneration` is `null` when the segment had a row but no current generation.\n */\n readonly kind: 'segment.rollback';\n readonly namespace?: string;\n readonly segment: string;\n readonly fromGeneration: number | null;\n readonly generation: number;\n }\n | {\n /**\n * A load was **refused**: it did not publish, because its result failed a guard or another writer got there\n * first. The security-relevant fact is that a replacement the caller asked for did NOT happen, which a\n * downstream system reconciling \"the segment should now contain X\" needs as much as it needs the publish.\n *\n * `reason` is `'empty'` (an empty result over a non-empty segment, with no `allowEmpty`),\n * `'min-cardinality'`, `'min-retained'`, or `'superseded'` (another load took the generation number, or the\n * segment's registry row changed while the load was writing). `cardinality` is what the refused generation\n * would have contained, and `0` for a load that lost its generation number and wrote nothing.\n */\n readonly kind: 'segment.load-refused';\n readonly namespace?: string;\n readonly segment: string;\n readonly generation: number;\n readonly reason: 'empty' | 'min-cardinality' | 'min-retained' | 'superseded';\n readonly cardinality: number;\n }\n | {\n /**\n * A generation was **rewritten**: a new generation derived from `fromGeneration` became current in its\n * place. Today the one emitter is `eraseIdFromSegment` (a subject erasure clearing one id), and the event is\n * emitted at the publish \u2014 before the superseded generation is collected \u2014 so the record exists the moment\n * the generation without the id is authoritative. A `segment.publish` is NOT also emitted for a rewrite: a\n * rewrite derives its content from the segment itself, a publish brings content in from outside.\n */\n readonly kind: 'segment.rewrite';\n readonly namespace?: string;\n readonly segment: string;\n readonly fromGeneration: number;\n readonly generation: number;\n }\n | {\n /**\n * A segment was **crypto-shredded** \u2014 its wrapped DEK(s) are gone, so its at-rest Storage bytes are now\n * permanently unreadable. Emitted only for a genuine key shred, never for a cleartext tombstone (which\n * leaves the Storage bytes readable) or an idempotent re-run.\n */\n readonly kind: 'segment.erase';\n readonly namespace?: string;\n readonly segment: string;\n }\n | {\n /**\n * A segment was **disposed of** \u2014 tombstoned and its storage reclaimed (its Storage generations deleted) by\n * `dropSegment`, *without* a key shred.\n *\n * Deliberately a separate kind from {@link AuditEvent} `segment.erase`, and the distinction is the point.\n * `segment.erase` attests that bytes are unreadable **everywhere, backups included** \u2014 the only claim that\n * survives WORM. Deleting an object is strictly weaker: a noncurrent version, a cross-region replica or a\n * PITR snapshot can still hold the cleartext. Emitting one kind for both would make a compliance dashboard\n * over-attest, so a cleartext drop gets this instead.\n *\n * An **encrypted** segment dropped via `dropSegment` emits **both** \u2014 `segment.erase` for the key shred and\n * this for the storage reclamation \u2014 because both things genuinely happened.\n *\n * `generationsDeleted` is how many Storage generations went. It can be 0 (a segment whose bytes were already\n * gone), and it does not promise the storage is now fully reclaimed: check `DropResult.generationsRemaining`\n * for that.\n */\n readonly kind: 'segment.dispose';\n readonly namespace?: string;\n readonly segment: string;\n readonly generationsDeleted: number;\n }\n | {\n /** A namespace erasure was executed. `segmentsShredded` is the count actually crypto-shredded (may be 0). */\n readonly kind: 'namespace.erase';\n readonly namespace: string;\n readonly segmentsShredded: number;\n };\n\n/** Sink for {@link AuditEvent}s. Injected via the lifecycle options; omit it and nothing is recorded. */\nexport interface IAuditSink {\n onEvent(event: AuditEvent): void;\n}\n\n/** The default sink: records nothing. */\nexport const NOOP_AUDIT: IAuditSink = {\n onEvent(): void {\n /* discard */\n },\n};\n\n/**\n * Wrap a sink so a throwing/buggy `onEvent` can never break the lifecycle operation it observes \u2014 audit is\n * strictly observation. Returns {@link NOOP_AUDIT} unchanged (so the no-op case skips even the try/catch alloc).\n */\nexport function safeAudit(sink: IAuditSink): IAuditSink {\n if (sink === NOOP_AUDIT) return sink;\n return {\n onEvent(event: AuditEvent): void {\n try {\n sink.onEvent(event);\n } catch {\n /* swallow \u2014 an audit sink must never break the operation it observes */\n }\n },\n };\n}\n\n/** A ready-made sink that records events into an in-memory list \u2014 handy for tests + simple audit trails. */\nexport class RecordingAuditSink implements IAuditSink {\n private readonly recorded: AuditEvent[] = [];\n\n onEvent(event: AuditEvent): void {\n this.recorded.push(event);\n }\n\n /** An independent copy of the recorded events, in emission order. */\n snapshot(): AuditEvent[] {\n return this.recorded.map((e) => ({ ...e }));\n }\n\n reset(): void {\n this.recorded.length = 0;\n }\n}\n", "/**\n * Cooperative yielding for the long CPU-bound loops.\n *\n * Everything this library does on the hot read path is small. Bulk-load is the exception: it is the one place\n * where a single call legitimately occupies the CPU for hundreds of milliseconds, and Node has exactly one thread\n * to occupy. Measured on a 1M-id load of 65,536 chunks onto in-memory storage, on a loaded Apple M3 Pro: **518 ms\n * wall, and 519 ms during which the event loop did not turn at all** (medians of 20 fresh-process runs). Wire that to a request handler and every other in-flight request on the instance \u2014 health\n * checks included \u2014 waits out the whole load.\n *\n * The fix is not to make the work asynchronous. That was measured too, and it is a 7x *regression*: handing each\n * chunk's insert to the threadpool costs ~9 \u00B5s of dispatch against ~1.5 \u00B5s of actual work once ids are spread\n * across ~62,000 chunks (636 ms versus 92 ms). The work is genuinely CPU-bound and genuinely small per chunk;\n * the only thing wrong with it is that it never stops to let anyone else go. So the fix is to keep the work\n * synchronous and periodically hand the loop back.\n *\n * TWO TRAPS, both of which make a plausible implementation measure as no change at all:\n *\n * 1. **`await Promise.resolve()` \u2014 and equally `Clock.sleep(0)` \u2014 do nothing.** Both resolve on a microtask, and\n * the microtask queue drains to empty before the event loop advances a single phase. A loop awaiting them\n * yields to other *microtasks* and to nothing else; no pending I/O callback runs. Measured: 555 ms of\n * starvation against a 568 ms unyielded baseline **on the synthetic yield-primitive benchmark below** \u2014\n * a different, smaller workload than the 442 ms end-to-end load above, which is why the two baselines\n * differ. The yield must be a **macrotask** \u2014 hence\n * `Clock.yieldNow`, backed by `setImmediate` in production wiring.\n * 2. **Yielding per unit of work costs more than the work.** The yield has to be periodic, and `everyN` has to be\n * large enough that its cost disappears into the batch it interrupts.\n *\n * At `everyN = 1024` over that same synthetic workload the trade is close to free: 569 ms wall against a 568 ms\n * baseline, with the worst event-loop gap down from 568 ms to 13.8 ms.\n *\n * **Three experiments, three sets of numbers \u2014 do not mix them.** (a) The end-to-end 1M-id bulk load:\n * 518 ms wall / 519 ms blocked unyielded, 526 ms / 24 ms yielded. (b) This synthetic yield-primitive comparison:\n * 568 ms / 568 ms unyielded, 569 ms / 13.8 ms with `setImmediate`. (c) A per-chunk insert microbenchmark:\n * 92 ms sync versus 636 ms via the threadpool. Quoting a baseline from one against a result from another describes\n * work none of them did, such as a 92 ms operation with 450 ms of starvation inside it.\n */\n/**\n * The only part of a `Clock` that yielding actually needs.\n *\n * Deliberately looser than `Clock` so a write path can opt in with whatever it has: a bulk load or a rewrite\n * yields when its caller supplies a clock that can, and simply runs unbroken when handed nothing (or a bare\n * `{ now }`). Requiring a full `Clock` would make every out-of-process caller wire one just to write a\n * generation.\n */\nexport interface Yielder {\n yieldNow?(): Promise<void>;\n sleep?(ms: number): Promise<void>;\n}\n\n/**\n * How many units of work pass between yields. Sized so the yield is unmeasurable against the batch it interrupts\n * (~60 yields across a 1M-id load) while still capping any single blocking stretch in the low tens of ms.\n */\nexport const YIELD_EVERY = 1024;\n\n/**\n * Build a periodic yield.\n *\n * Call the returned function **once per unit of work**. It returns `null` on the vast majority of calls \u2014 nothing\n * to await, nothing allocated \u2014 and a promise only on the `everyN`th, so the caller pays for a yield only when\n * one actually happens:\n *\n * ```ts\n * const tick = yieldEvery(clock);\n * for (const item of millions) {\n * work(item);\n * const pause = tick();\n * if (pause !== null) await pause;\n * }\n * ```\n *\n * The two-line shape is deliberate. `await tick()` would read better and would force a microtask turn on every\n * single iteration \u2014 an unconditional cost on the very loop this exists to keep fast.\n *\n * With no `clock` the result always returns `null`: the loop runs unbroken, so adding a yield point to a code\n * path can never change behaviour for a caller who did not opt in. `core/` cannot supply a clock itself \u2014 it is\n * timer-free by lint, which is the whole reason scheduling goes through the seam \u2014 so `@cloudbitmaps/roaring`\n * pre-binds a real one and flavor users get this with no wiring at all.\n */\nexport function yieldEvery(\n clock: Yielder | undefined,\n everyN: number = YIELD_EVERY,\n): () => Promise<void> | null {\n if (clock === undefined) return () => null;\n // Resolved once here rather than re-tested every yield.\n //\n // A clock without `yieldNow` falls back to `sleep(1)`, which yields on a *timer-backed* clock at ~1 ms of\n // dead wall-clock apiece (measured: +10% on the load above). It does NOT yield on a clock whose `sleep`\n // ignores its argument and resolves on a microtask \u2014 `InstantClock` and `SimClock` both do, deliberately.\n // Those are non-production wirings where yielding is meaningless, and both say so by implementing\n // `yieldNow` as an explicit no-op rather than arriving here by accident.\n const sleep = clock.sleep;\n const pause =\n clock.yieldNow !== undefined\n ? (): Promise<void> => clock.yieldNow!()\n : sleep !== undefined\n ? (): Promise<void> => sleep.call(clock, 1)\n : undefined;\n if (pause === undefined) return () => null; // a clock that can neither yield nor sleep: nothing to do\n let n = 0;\n return () => {\n if (++n < everyN) return null;\n n = 0;\n return pause();\n };\n}\n", "/**\n * Encryption seams \u2014 the **pure** interfaces `core/` depends on so the codec can encrypt/decrypt without\n * importing `node:crypto` (lint-banned here, like `Clock`/`Rng`/drivers). Concrete AES-256-GCM lives outside\n * `core/` (`src/drivers/crypto.ts`) and is injected. The envelope scheme: a per-segment DEK wrapped under an\n * operator-held KEK, so discarding the DEK crypto-shreds the segment. Per-chunk AEAD over the `.crbm` payloads + index; a per-segment **DEK** wrapped (envelope\n * encryption) under one or more **KEK**s by an {@link IKeystore}; the wrapped DEK(s) live in the registry, so\n * crypto-shred = delete them.\n */\n\n/** One AEAD result: a fresh per-message nonce, the ciphertext, and the authentication tag. */\nexport interface AeadSealed {\n readonly nonce: Uint8Array;\n readonly ciphertext: Uint8Array;\n readonly tag: Uint8Array;\n}\n\n/**\n * Authenticated encryption bound to a single key (a segment's DEK). `aad` (associated data) is authenticated\n * but not encrypted \u2014 the codec passes each chunk's `(namespace, segment, generation, chunkKey)` identity\n * (the library derives it; an `Aead` implementation receives it), so a chunk's ciphertext can't be silently\n * relocated to another segment/generation/chunk or\n * the index, even by someone who can rewrite Storage objects. The seam is symmetric and self-framing-agnostic:\n * the caller decides where the nonce/tag go (inline with a chunk, or in the footer for the index).\n */\nexport interface Aead {\n /** Encrypt `plaintext`, authenticating `aad`. Returns a fresh nonce + ciphertext + tag. */\n seal(plaintext: Uint8Array, aad: Uint8Array): AeadSealed;\n /**\n * Decrypt + verify. Throws {@link IntegrityError} on **any** mismatch (wrong key, tampered ciphertext/tag,\n * or `aad` that doesn't match what was sealed) \u2014 never returns wrong-but-plausible plaintext.\n */\n open(sealed: AeadSealed, aad: Uint8Array): Uint8Array;\n}\n\n/**\n * A DEK wrapped under one KEK. A segment stores a **list** of these (one per KEK it was wrapped under \u2014 e.g.\n * an active KEK plus an offline recovery KEK), so losing one KEK isn't fatal and KEK rotation needs no\n * data re-encryption. `keyId` names which KEK; `wrapped` is the opaque wrapped-key blob (base64). Carries no\n * plaintext key material \u2014 safe to store in the registry next to the (encrypted) data.\n */\nexport interface WrappedDek {\n readonly keyId: string;\n readonly wrapped: string;\n}\n\n/**\n * Envelope key management \u2014 the injected seam between the codec and however the operator holds key material\n * (in-process BYOK by default; KMS/Vault as optional adapters later). Async so a remote keystore (KMS) fits\n * the same interface. Never exposes raw key bytes to `core/`: it returns an {@link Aead} bound to the DEK.\n */\nexport interface IKeystore {\n /**\n * Mint a fresh per-segment DEK, wrapped under the active KEK (and any recovery KEK). Returns the wrapping(s)\n * to persist in the registry plus an {@link Aead} to encrypt this segment's chunks with.\n */\n createDek(): Promise<{ wrapped: WrappedDek[]; aead: Aead }>;\n /**\n * Reconstruct the {@link Aead} from a segment's stored wrappings by unwrapping with any KEK currently held.\n * Throws {@link KeyUnavailableError} if it holds none of the referenced KEKs (lost key, or a shredded\n * segment whose wrappings were deleted).\n */\n openDek(wrapped: readonly WrappedDek[]): Promise<Aead>;\n}\n\n/**\n * What the encrypting codec needs: the {@link Aead} for this segment's DEK plus a way to build the AAD for a\n * given scope (`'index'` or a chunkKey). Built per (segment, generation) by the storage-source bridge so the\n * codec itself stays segment-agnostic.\n */\nexport interface CrbmCrypto {\n readonly aead: Aead;\n aadFor(scope: number | 'index'): Uint8Array;\n}\n\nconst AAD_VERSION = 1;\nconst SCOPE_CHUNK = 0;\nconst SCOPE_INDEX = 1;\n\n/**\n * Build the AEAD associated-data binding a chunk (or the index) to its exact location:\n * `v1 \u2016 len(namespace) \u2016 namespace \u2016 len(segment) \u2016 segment \u2016 generation \u2016 scope \u2016 chunkKey`. Length-prefixed\n * so distinct `(namespace, segment)` pairs can never collide. Pure (no crypto) \u2014 just the authenticated label.\n */\nexport function aadFor(\n ref: { readonly namespace?: string; readonly segment: string },\n generation: number,\n scope: number | 'index',\n): Uint8Array {\n const enc = new TextEncoder();\n const ns = enc.encode(ref.namespace ?? '');\n const seg = enc.encode(ref.segment);\n const out = new Uint8Array(1 + 2 + ns.length + 2 + seg.length + 8 + 1 + 4);\n const view = new DataView(out.buffer);\n let o = 0;\n out[o] = AAD_VERSION;\n o += 1;\n view.setUint16(o, ns.length, true);\n o += 2;\n out.set(ns, o);\n o += ns.length;\n view.setUint16(o, seg.length, true);\n o += 2;\n out.set(seg, o);\n o += seg.length;\n view.setBigUint64(o, BigInt(generation), true);\n o += 8;\n out[o] = scope === 'index' ? SCOPE_INDEX : SCOPE_CHUNK;\n o += 1;\n view.setUint32(o, scope === 'index' ? 0 : scope, true);\n return out;\n}\n", "/**\n * CRC32C (Castagnoli, polynomial 0x1EDC6F41) \u2014 the pre-deserialize integrity screen for `.crbm`\n * payloads, index, and footer.\n *\n * Pure TypeScript, table-driven: `core/` may not import `crypto`/`node:crypto` (the determinism lint\n * enforces it), and a from-scratch implementation is what the cross-language golden corpus will be pinned\n * against. CRC32C (not CRC32) is the locked choice \u2014 it matches the hardware-accelerated checksum used\n * by SSE/iSCSI/Btrfs, and it is the algorithm the `.crbm` format locks in.\n */\n\n/** Reflected CRC32C lookup table (256 entries), built once from the reflected polynomial. */\n/**\n * Slicing-by-8 tables. `TABLES[0]` is the classic byte-at-a-time table; each subsequent table is the previous\n * one advanced by another byte position, which is what lets the loop below consume 8 bytes per iteration\n * instead of one.\n */\nconst TABLES = ((): Uint32Array[] => {\n const POLY = 0x82f63b78; // bit-reflected 0x1EDC6F41\n const t0 = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? POLY ^ (c >>> 1) : c >>> 1;\n t0[n] = c >>> 0;\n }\n const tables = [t0];\n for (let k = 1; k < 8; k++) {\n const prev = tables[k - 1] as Uint32Array;\n const next = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n const v = prev[n] as number;\n next[n] = ((v >>> 8) ^ (t0[v & 0xff] as number)) >>> 0;\n }\n tables.push(next);\n }\n return tables;\n})();\nconst T0 = TABLES[0] as Uint32Array;\nconst T1 = TABLES[1] as Uint32Array;\nconst T2 = TABLES[2] as Uint32Array;\nconst T3 = TABLES[3] as Uint32Array;\nconst T4 = TABLES[4] as Uint32Array;\nconst T5 = TABLES[5] as Uint32Array;\nconst T6 = TABLES[6] as Uint32Array;\nconst T7 = TABLES[7] as Uint32Array;\n\nexport function crc32c(bytes: Uint8Array, seed = 0): number {\n let crc = (seed ^ 0xffffffff) >>> 0;\n let i = 0;\n // Eight bytes per iteration. Identical output to the byte-at-a-time form \u2014 this is the standard\n // slicing-by-8 arrangement of the same polynomial, not a different checksum \u2014 but it amortises the table\n // lookups and the loop overhead across eight bytes. That matters because this runs over the whole `.crbm`\n // index on every reader open, where the byte-at-a-time version measured ~3.3 ms/MiB and up to ~27 ms for a\n // maximally-sized index, all of it a synchronous stall on Node's only thread.\n const n8 = bytes.length - (bytes.length % 8);\n for (; i < n8; i += 8) {\n crc ^=\n (bytes[i] as number) |\n ((bytes[i + 1] as number) << 8) |\n ((bytes[i + 2] as number) << 16) |\n ((bytes[i + 3] as number) << 24);\n crc =\n ((T7[crc & 0xff] as number) ^\n (T6[(crc >>> 8) & 0xff] as number) ^\n (T5[(crc >>> 16) & 0xff] as number) ^\n (T4[(crc >>> 24) & 0xff] as number) ^\n (T3[bytes[i + 4] as number] as number) ^\n (T2[bytes[i + 5] as number] as number) ^\n (T1[bytes[i + 6] as number] as number) ^\n (T0[bytes[i + 7] as number] as number)) >>>\n 0;\n }\n // Tail: whatever did not divide by 8, byte-at-a-time.\n for (; i < bytes.length; i++) {\n crc = ((T0[(crc ^ (bytes[i] as number)) & 0xff] as number) ^ (crc >>> 8)) >>> 0;\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n", "/**\n * LEB128 unsigned varint codec for the `.crbm` chunk index.\n *\n * The index is delta+varint encoded so a full 16-bit span stays ~400\u2013650 KB instead of ~900 KB of\n * fixed triplets. All index varint values are small (chunk-key deltas \u2264 65535,\n * cardinalities \u2264 65536, payload lengths bounded by a size cap) \u2014 well within 32 bits \u2014 so this codec\n * accepts unsigned integers in `[0, 0xffffffff]` and rejects anything wider as a programming error.\n */\nimport { IntegrityError } from '../errors';\n\nconst U32_CEIL = 0x1_0000_0000; // 2^32\n\n/** Append the LEB128 encoding of a u32 `value` to `out`. */\nexport function writeVarint(out: number[], value: number): void {\n if (!Number.isInteger(value) || value < 0 || value >= U32_CEIL) {\n throw new RangeError(`varint value must be a u32; got ${value}`);\n }\n let v = value;\n while (v > 0x7f) {\n out.push((v & 0x7f) | 0x80);\n v >>>= 7;\n }\n out.push(v);\n}\n\nexport interface VarintRead {\n readonly value: number;\n /** Offset just past the bytes consumed. */\n readonly next: number;\n}\n\n/**\n * Decode a LEB128 varint from `bytes` starting at `offset`. Throws `IntegrityError` on a truncated or\n * over-long (> 5-byte / > u32) encoding \u2014 the bytes are untrusted, so a malformed index must fail\n * cleanly, never loop or overflow.\n */\nexport function readVarint(bytes: Uint8Array, offset: number): VarintRead {\n let result = 0;\n let shift = 0;\n let pos = offset;\n for (let i = 0; i < 5; i++) {\n if (pos >= bytes.length) {\n throw new IntegrityError('varint truncated past end of buffer');\n }\n const byte = bytes[pos]!;\n pos += 1;\n // The 5th byte may contribute at most 4 bits (32 total); reject wider.\n if (i === 4 && byte > 0x0f) {\n throw new IntegrityError('varint exceeds u32 range');\n }\n // Integer arithmetic: the value stays a small integer where it fits, rather than a heap-boxed double.\n result = (result | ((byte & 0x7f) << shift)) >>> 0;\n if ((byte & 0x80) === 0) {\n return { value: result, next: pos };\n }\n shift += 7;\n }\n throw new IntegrityError('varint exceeds u32 range');\n}\n", "/**\n * Frozen `.crbm` v1.0 layout constants.\n *\n * These byte widths/offsets are pinned by the golden corpus and must never change for v1 \u2014\n * a new layout is a new format version. All multi-byte integers are little-endian (v1 fixes LE).\n */\n\n/** 4-byte magic at both file ends: \"CRBM\". */\nexport const MAGIC = Uint8Array.of(0x43, 0x52, 0x42, 0x4d);\n\nexport const VERSION_MAJOR = 1;\nexport const VERSION_MINOR = 0;\n\n/** Front preamble: magic(4) + version_major(1) + version_minor(1) + reserved(2). */\nexport const PREAMBLE_BYTES = 8;\n\n/** Payloads begin immediately after the preamble. */\nexport const PAYLOAD_START = PREAMBLE_BYTES;\n\n/** Fixed footer size (v1.0). */\nexport const FOOTER_BYTES = 104;\n\n/** Byte offsets of each field within the 104-byte footer. */\nexport const FOOTER = {\n indexOffset: 0, // u64\n indexLength: 8, // u64\n indexCrc32c: 16, // u32\n flags: 20, // u32\n payloadCodecId: 24, // u16 \u2014 which codec wrote the chunk payloads\n elementWidth: 26, // u8\n containerCodec: 27, // u8\n versionMajor: 28, // u8\n versionMinor: 29, // u8\n reserved2: 30, // u16\n generation: 32, // u64\n indexNonce: 40, // 12 B\n indexTag: 52, // 16 B\n keyId: 68, // 16 B\n chunkCount: 84, // u32\n totalCardinality: 88, // u64\n footerCrc32c: 96, // u32 \u2014 covers footer bytes [0, 96)\n endMagic: 100, // char[4]\n} as const;\n\n/** Footer bytes covered by `footer_crc32c` (everything before the CRC field). */\nexport const FOOTER_CRC_COVERAGE = FOOTER.footerCrc32c; // 96\n\n/** `flags` bit positions. */\nexport const FLAG_ENCRYPTED = 1 << 0;\nexport const FLAG_INDEX_COMPRESSED = 1 << 1; // reserved\nexport const FLAG_LITTLE_ENDIAN = 1 << 2; // =1 in v1\n\n/** Flag bits a v1 reader understands; any bit outside this mask is an unsupported feature. */\nexport const KNOWN_FLAGS = FLAG_ENCRYPTED | FLAG_LITTLE_ENDIAN;\n\n/**\n * Default element width: 32-bit ids (the u32 member space). `64` is the *reserved* escape above the\n * ~4.29 B-ids/segment u32 ceiling; a v1 (32-bit) reader must **reject** a 64-bit generation rather than feed\n * it to the 32-bit deserializer (which would mis-count fleet-wide). A future 64-bit generation is therefore a\n * **major**-version bump (auto-rejected by old readers), never a silent minor one \u2014 see\n * {@link CrbmReader.open} validates this.\n */\nexport const ELEMENT_WIDTH_32 = 32;\n\n/**\n * **Which codec produced the chunk payloads.** Recorded in the footer, validated on read.\n *\n * `.crbm` is a shared container: the index, the CRC32Cs, the AEAD framing and the generation model are all\n * codec-independent, and **only the chunk payload bytes belong to a flavor** (hence *Chunked Remote BitMap* \u2014\n * see the `.crbm` format section of the API reference). This field is what lets one container hold either, the same way ZIP tags each member\n * with a compression method.\n *\n * **A codec id, not a roaring-specific one.** A second codec is genuinely expected \u2014 `soaring` is a planned\n * Roaring *variant*, so its serialized bytes are unlikely to be roaring-portable, and it lands *after* the `1.0`\n * format freeze. A field frozen under a codec-specific meaning could not be reinterpreted later without a major\n * format version.\n *\n * **Ids are permanent once published.** Add to {@link KNOWN_PAYLOAD_CODEC_IDS} when a codec ships; never\n * reuse or renumber. Ids are deliberately *not* pre-allocated for codecs that do not exist \u2014 a reserved number\n * for an unbuilt codec is a guess about a format nobody has designed.\n */\nexport const PAYLOAD_CODEC_ROARING_PORTABLE = 1;\n\n/**\n * Every payload codec id this reader can decode.\n *\n * The reader validates membership rather than equality with a single constant. That is the whole point of an\n * id registry: an unknown id is rejected with a typed error naming it, so an old reader meeting a\n * future-codec generation **fails closed** \u2014 the correct direction, and the reason a store built on one codec\n * can never silently misread another's bytes as its own. (The homogeneity contract means one store is one\n * codec, so meeting a foreign generation implies misconfiguration, and a loud rejection is exactly what you\n * want there.)\n */\nexport const KNOWN_PAYLOAD_CODEC_IDS: ReadonlySet<number> = new Set([\n PAYLOAD_CODEC_ROARING_PORTABLE,\n]);\n\n/**\n * The only container codec v1 defines: `0` = none (payloads are stored as the portable roaring serialization\n * with no extra container-level transform). A non-zero codec is a future format feature; a v1 reader rejects\n * it ({@link UnsupportedError}) rather than mis-decode. Stamped by the writer, validated by the reader.\n */\nexport const CONTAINER_CODEC_NONE = 0;\n\n/**\n * Default speculative-tail-read size. The spec's worst-case index (a full 16-bit span) is ~400\u2013650 KB,\n * so 256 KB collapses the read to a single GET for the vast majority of real (sparse/medium) segments;\n * only near-full-span segments take the documented second GET.\n */\nexport const DEFAULT_TAIL_BYTES = 256 * 1024;\n\n/**\n * Hard cap on the index region a reader will fetch/parse from one (untrusted) object. The index is read\n * whole on the cheap `open()`/`count()` path, so it must be bounded before allocation \u2014 generously\n * above the ~650 KB full-span worst case, well below a denial-of-wallet allocation.\n */\nexport const DEFAULT_MAX_INDEX_BYTES = 8 * 1024 * 1024;\n\n/** Hard cap on a single chunk payload, defending the native deserializer against oversized input. */\nexport const DEFAULT_MAX_PAYLOAD_BYTES = 16 * 1024 * 1024;\n\n/** A chunk's cardinality is in `[1, 65536]` (empty chunks are never written). */\nexport const MAX_CHUNK_CARDINALITY = 0x1_0000;\n\n/** Fixed width of a per-chunk CRC32C field in the index (high-entropy \u2192 not varint). */\nexport const CRC32C_BYTES = 4;\n\n/**\n * v1 AEAD framing sizes (AES-256-GCM), fixed by the format exactly like the payload codec id \u2014 a\n * different cipher is a new format version. An encrypted chunk payload is stored as `nonce \u2016 ciphertext \u2016 tag`;\n * the encrypted **index**'s nonce/tag live in the footer's reserved `indexNonce`/`indexTag` slots. Matches\n * {@link FOOTER.indexNonce} (12 B) and {@link FOOTER.indexTag} (16 B).\n */\nexport const AEAD_NONCE_BYTES = 12;\nexport const AEAD_TAG_BYTES = 16;\n", "/**\n * `CrbmReader` \u2014 speculative-tail-read reader for one `.crbm` generation.\n *\n * `open()` fetches the object's tail in **one GET** (footer + usually the whole index), verifies the\n * footer/index CRCs, and parses the delta+varint index into parallel typed arrays. `getChunk()` then range-GETs a single\n * payload and verifies its CRC32C **before** any native deserialize \u2014 every byte from storage is\n * untrusted.\n *\n * **Encryption.** When the footer's `FLAG_ENCRYPTED` is set, a {@link CrbmCrypto} must be supplied:\n * the index is AES-256-GCM-decrypted (nonce/tag from the footer) and each chunk payload is decrypted after its\n * on-disk CRC passes. The footer's `chunkCount`/`totalCardinality` are zero on an encrypted object (metadata is\n * hidden), so both are derived from the decrypted index; AEAD authentication (incl. the per-location AAD)\n * replaces the cleartext-count cross-check.\n */\nimport { IntegrityError, UnsupportedError, ValidationError } from '../errors';\nimport type { BlobReader } from '../blob';\nimport type { CrbmCrypto } from '../crypto';\nimport { crc32c } from './crc32c';\nimport { readVarint } from './varint';\nimport {\n AEAD_NONCE_BYTES,\n AEAD_TAG_BYTES,\n CONTAINER_CODEC_NONE,\n CRC32C_BYTES,\n DEFAULT_MAX_INDEX_BYTES,\n DEFAULT_MAX_PAYLOAD_BYTES,\n DEFAULT_TAIL_BYTES,\n ELEMENT_WIDTH_32,\n FLAG_ENCRYPTED,\n FLAG_LITTLE_ENDIAN,\n FOOTER,\n FOOTER_BYTES,\n FOOTER_CRC_COVERAGE,\n KNOWN_FLAGS,\n MAGIC,\n MAX_CHUNK_CARDINALITY,\n PAYLOAD_START,\n PREAMBLE_BYTES,\n KNOWN_PAYLOAD_CODEC_IDS,\n PAYLOAD_CODEC_ROARING_PORTABLE,\n VERSION_MAJOR,\n} from './format';\n\n/**\n * A parsed index as parallel typed arrays, one slot per entry, ascending by key. Typed arrays hold each field at\n * its own width with no per-entry object, so the retained heap is the arrays' byte length and nothing else.\n * `cardinalityMinusOne` stores `cardinality - 1` because a chunk's cardinality runs `[1, 65536]` and 65,536 does\n * not fit a `u16`; an offset is a `f64` because an object past 4 GiB puts a payload beyond a `u32`.\n */\nexport interface ParsedIndex {\n readonly keys: Uint16Array;\n readonly cardinalityMinusOne: Uint16Array;\n readonly lengths: Uint32Array;\n readonly crcs: Uint32Array;\n readonly offsets: Float64Array;\n /** \u03A3 cardinality over every entry. */\n readonly cardinalitySum: number;\n}\n\n/**\n * Retained JS heap for one parsed index entry, in bytes: the `u16` key, the `u16` cardinality-minus-one, the `u32`\n * length, the `u32` CRC and the `f64` offset of {@link ParsedIndex}. It is the exact byte length of the arrays\n * (checked against the measured heap by a test), and weights the reader-cache byte bound\n * ({@link CrbmReader.retainedIndexBytes}).\n */\nexport const RETAINED_BYTES_PER_INDEX_ENTRY = 20;\n\n/** The fewest bytes one index record takes: four one-byte varints and the four-byte payload CRC. */\nconst MIN_INDEX_RECORD_BYTES = 4 + CRC32C_BYTES;\n\n/**\n * How many entries `parseIndex` makes room for, from the index's length alone: what its bytes could hold at the\n * smallest record, and never more than one entry per 16-bit key. A hostile index cannot make the reader allocate more.\n */\nexport function indexCapacity(indexLength: number): number {\n return Math.min(0x1_0000, Math.floor(indexLength / MIN_INDEX_RECORD_BYTES));\n}\n\nexport interface CrbmReaderOptions {\n /** Speculative tail size in bytes (default 256 KB; clamped up to at least the footer size). */\n readonly tailBytes?: number;\n /** Hard cap on a single chunk payload length (default 16 MB). */\n readonly maxPayloadBytes?: number;\n /** Hard cap on the whole index region fetched/parsed from one object (default 8 MB). */\n readonly maxIndexBytes?: number;\n /** Decryption context for an encrypted object (its DEK's AEAD + AAD builder). Required iff `FLAG_ENCRYPTED`. */\n readonly crypto?: CrbmCrypto;\n /**\n * Opaque marker for the *incarnation of the name* this object belongs to \u2014 the caller's own identity for the\n * registry row it resolved. The reader never interprets it; it carries it so a caller holding an open reader\n * can tell \"the same generation of the same segment\" from \"the same generation NUMBER of a segment that was\n * deleted and re-created\", which the number alone cannot express.\n */\n readonly lineage?: unknown;\n}\n\nfunction magicMatches(bytes: Uint8Array, offset: number): boolean {\n return (\n bytes[offset] === MAGIC[0] &&\n bytes[offset + 1] === MAGIC[1] &&\n bytes[offset + 2] === MAGIC[2] &&\n bytes[offset + 3] === MAGIC[3]\n );\n}\n\n/** What names an object: its size, then its footer's CRC. One spelling, for an open reader and a footer read alone. */\nconst sizePart = (size: number): string => `${size}:`;\nconst fingerprintFor = (size: number, footerCrc: number): string => `${sizePart(size)}${footerCrc}`;\n\n/**\n * Refuses a `size` that is not a whole byte count its own tail fits in. The size is the tier's word too: one that\n * is not would turn off every bounds check an open makes against it.\n */\nfunction checkSize(tail: Uint8Array, size: number): void {\n if (!Number.isSafeInteger(size) || size < tail.length) {\n throw new IntegrityError(`.crbm size is not a byte count its tail fits in: ${size}`);\n }\n}\n\n/** The footer at the end of `tail`, once its size, magic and CRC hold, with its view and its CRC. */\nfunction checkedFooter(\n tail: Uint8Array,\n size: number,\n): { footer: Uint8Array; fview: DataView; storedFooterCrc: number } {\n checkSize(tail, size);\n if (size < PREAMBLE_BYTES + FOOTER_BYTES || tail.length < FOOTER_BYTES) {\n throw new IntegrityError(`.crbm too small: ${size}B`);\n }\n const footer = tail.subarray(tail.length - FOOTER_BYTES);\n if (!magicMatches(footer, FOOTER.endMagic)) {\n throw new IntegrityError('.crbm end magic mismatch');\n }\n const fview = new DataView(footer.buffer, footer.byteOffset, footer.byteLength);\n const storedFooterCrc = fview.getUint32(FOOTER.footerCrc32c, true);\n if (crc32c(footer.subarray(0, FOOTER_CRC_COVERAGE)) !== storedFooterCrc) {\n throw new IntegrityError('.crbm footer CRC mismatch');\n }\n return { footer, fview, storedFooterCrc };\n}\n\n/** Read a u64 footer field, rejecting values past JS safe-integer range (precision would be lost). */\nfunction readU64(view: DataView, offset: number, field: string): number {\n const big = view.getBigUint64(offset, true);\n if (big > BigInt(Number.MAX_SAFE_INTEGER)) {\n throw new IntegrityError(`.crbm ${field} ${big} exceeds safe-integer range`);\n }\n return Number(big);\n}\n\nexport class CrbmReader {\n private constructor(\n private readonly blob: BlobReader,\n private readonly objectSize: number,\n readonly generation: number,\n /** See {@link CrbmReaderOptions.lineage} \u2014 carried, never interpreted. */\n readonly lineage: unknown,\n readonly totalCardinality: number,\n private readonly index: ParsedIndex,\n /** True when `open()` satisfied the index from the tail GET alone (no second range read). */\n readonly servedFromTail: boolean,\n /** Set iff the object is encrypted \u2014 used to decrypt each chunk payload in {@link getChunk}. */\n private readonly crypto: CrbmCrypto | undefined,\n /** The footer's own CRC, as verified at open: it covers the index's CRC, the chunk count and the generation. */\n private readonly footerCrc: number,\n ) {}\n\n /**\n * Which object this reader opened, as its size and its footer's CRC. The footer covers the index's CRC, so two\n * objects with the same fingerprint hold the same chunks, as far as a CRC can tell; a pin compares it to know that\n * the object it reopens is the one it pinned, since a purged and reloaded name reuses the generation number.\n */\n get fingerprint(): string {\n return fingerprintFor(this.objectSize, this.footerCrc);\n }\n\n /** Total object bytes (from the one-GET tail read) \u2014 for grounded storage cost. */\n get sizeBytes(): number {\n return this.objectSize;\n }\n\n /**\n * Retained JS heap of this reader's parsed index \u2014 the weight the storage reader cache bounds on (a wide\n * segment's parsed index, not its payloads, dominates the reader's footprint): the exact byte length of the\n * arrays that hold it, {@link RETAINED_BYTES_PER_INDEX_ENTRY} per entry.\n */\n get retainedIndexBytes(): number {\n const { keys, cardinalityMinusOne, lengths, crcs, offsets } = this.index;\n return (\n keys.byteLength +\n cardinalityMinusOne.byteLength +\n lengths.byteLength +\n crcs.byteLength +\n offsets.byteLength\n );\n }\n\n /** Per-chunk cardinality (`chunkKey \u2192 count`) from the parsed index \u2014 no payload reads. */\n cardinalities(): Map<number, number> {\n const { keys, cardinalityMinusOne } = this.index;\n const out = new Map<number, number>();\n for (let i = 0; i < keys.length; i++) out.set(keys[i]!, cardinalityMinusOne[i]! + 1);\n return out;\n }\n\n /**\n * Whether the object behind `blob` is the one `fingerprint` names ({@link fingerprint}), from one tail read of a\n * footer's worth, with no key and no index, since the footer is stored in the clear and checked by its own CRC.\n * Another size is another object, whatever its bytes hold: a short one that is no `.crbm` at all is not the one\n * pinned. At the same size the footer's CRC says, and a footer that fails its own checks says nothing about which\n * object is there, so this throws, as an open would. What a pin asks, to tell the object it opened from one that\n * has since been stored under its key, whoever wrote it.\n */\n static async sameObject(blob: BlobReader, fingerprint: string): Promise<boolean> {\n const { bytes: tail, size } = await blob.getTail(FOOTER_BYTES);\n checkSize(tail, size);\n if (!fingerprint.startsWith(sizePart(size))) return false;\n return fingerprintFor(size, checkedFooter(tail, size).storedFooterCrc) === fingerprint;\n }\n\n static async open(blob: BlobReader, options: CrbmReaderOptions = {}): Promise<CrbmReader> {\n // Always fetch at least a footer's worth, regardless of a smaller caller request.\n const tailBytes = Math.max(options.tailBytes ?? DEFAULT_TAIL_BYTES, FOOTER_BYTES);\n const maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_PAYLOAD_BYTES;\n const maxIndexBytes = options.maxIndexBytes ?? DEFAULT_MAX_INDEX_BYTES;\n const { bytes: tail, size } = await blob.getTail(tailBytes);\n const { footer, fview, storedFooterCrc } = checkedFooter(tail, size);\n const versionMajor = footer[FOOTER.versionMajor]!;\n if (versionMajor !== VERSION_MAJOR) {\n throw new UnsupportedError(`.crbm major version ${versionMajor} not supported`);\n }\n const flags = fview.getUint32(FOOTER.flags, true);\n const encrypted = (flags & FLAG_ENCRYPTED) !== 0;\n if (encrypted && options.crypto === undefined) {\n throw new ValidationError('.crbm is encrypted but no decryption key (crypto) was provided');\n }\n if ((flags & FLAG_LITTLE_ENDIAN) === 0) {\n throw new UnsupportedError('.crbm big-endian layout not supported (v1 is little-endian)');\n }\n if ((flags & ~KNOWN_FLAGS) !== 0) {\n throw new UnsupportedError(`.crbm has unknown flag bits set: 0x${flags.toString(16)}`);\n }\n\n // Payload-decoding contract: the\n // reader must decode with the *same* element width, roaring serialization, and container codec the writer\n // stamped \u2014 a mismatch means the payloads are a format this v1 (32-bit, portable, uncompressed) reader can't\n // safely deserialize, so reject up front rather than feed them to the 32-bit deserializer and mis-count\n // fleet-wide. A 64-bit generation is the reserved >4.29 B-ids/segment escape and is a **major**-version bump\n // (auto-rejected here), never a silent minor one. All three fields are inside FOOTER_CRC_COVERAGE (verified).\n const elementWidth = footer[FOOTER.elementWidth]!;\n if (elementWidth !== ELEMENT_WIDTH_32) {\n throw new UnsupportedError(\n `.crbm element_width ${elementWidth} not supported (v1 reads 32-bit ids; 64-bit is a future major version)`,\n );\n }\n // Membership, not equality: the field says which codec wrote the payloads, and this reader accepts every\n // id it can actually decode. An unknown one fails closed rather than being handed to a decoder that would\n // misread it \u2014 see KNOWN_PAYLOAD_CODEC_IDS for why that direction is the safe one.\n const payloadCodecId = fview.getUint16(FOOTER.payloadCodecId, true);\n if (!KNOWN_PAYLOAD_CODEC_IDS.has(payloadCodecId)) {\n throw new UnsupportedError(\n `.crbm payload_codec_id ${payloadCodecId} not supported by this build ` +\n `(known: ${[...KNOWN_PAYLOAD_CODEC_IDS].join(', ')}; ` +\n `${PAYLOAD_CODEC_ROARING_PORTABLE}=roaring portable). A generation written by a different codec ` +\n `is rejected rather than decoded \u2014 a store uses one codec throughout.`,\n );\n }\n const containerCodec = footer[FOOTER.containerCodec]!;\n if (containerCodec !== CONTAINER_CODEC_NONE) {\n throw new UnsupportedError(\n `.crbm container_codec ${containerCodec} not supported (v1 defines only ${CONTAINER_CODEC_NONE}=none)`,\n );\n }\n\n const indexOffset = readU64(fview, FOOTER.indexOffset, 'index_offset');\n const indexLength = readU64(fview, FOOTER.indexLength, 'index_length');\n const indexCrc = fview.getUint32(FOOTER.indexCrc32c, true);\n const chunkCount = fview.getUint32(FOOTER.chunkCount, true);\n const totalCardinality = readU64(fview, FOOTER.totalCardinality, 'total_cardinality');\n const generation = readU64(fview, FOOTER.generation, 'generation');\n\n // Bounds + size cap on the index region before trusting/fetching it.\n if (indexOffset < PAYLOAD_START || indexOffset + indexLength > size - FOOTER_BYTES) {\n throw new IntegrityError(\n `.crbm index region [${indexOffset}, +${indexLength}) out of bounds`,\n );\n }\n if (indexLength > maxIndexBytes) {\n throw new IntegrityError(`.crbm index ${indexLength}B exceeds cap ${maxIndexBytes}B`);\n }\n\n // Validate the front preamble too, but only when this GET already covers it (no extra request).\n const tailCoversFrom = size - tail.length;\n if (tailCoversFrom === 0) {\n if (!magicMatches(tail, 0) || tail[4] !== versionMajor) {\n throw new IntegrityError('.crbm preamble magic/version mismatch with footer');\n }\n }\n\n // --- Index: already in the tail, or one more GET ---\n let indexBytes: Uint8Array;\n let servedFromTail: boolean;\n if (indexOffset >= tailCoversFrom) {\n const start = indexOffset - tailCoversFrom;\n indexBytes = tail.subarray(start, start + indexLength);\n servedFromTail = true;\n } else {\n indexBytes = await blob.getRange(indexOffset, indexLength);\n servedFromTail = false;\n }\n if (crc32c(indexBytes) !== indexCrc) {\n throw new IntegrityError('.crbm index CRC mismatch');\n }\n\n // Decrypt the index (nonce/tag from the footer) before parsing; AAD binds it to this (segment, generation).\n // A wrong key / tampered index / wrong context fails here as an IntegrityError \u2014 never a wrong parse.\n const indexForParse = encrypted\n ? options.crypto!.aead.open(\n {\n nonce: footer.subarray(FOOTER.indexNonce, FOOTER.indexNonce + AEAD_NONCE_BYTES),\n ciphertext: indexBytes,\n tag: footer.subarray(FOOTER.indexTag, FOOTER.indexTag + AEAD_TAG_BYTES),\n },\n options.crypto!.aadFor('index'),\n )\n : indexBytes;\n\n // Payloads live in [PAYLOAD_START, indexOffset); an encrypted one is at least its nonce and tag.\n const index = parseIndex(\n indexForParse,\n indexOffset,\n maxPayloadBytes,\n encrypted ? AEAD_NONCE_BYTES + AEAD_TAG_BYTES : 1,\n );\n if (encrypted) {\n // Footer count/cardinality are zeroed on an encrypted object; the decrypted index is authoritative (and\n // already AEAD-authenticated, AAD-bound to this object), so derive the total from it.\n if (chunkCount !== 0 || totalCardinality !== 0) {\n throw new IntegrityError(\n '.crbm encrypted footer must zero chunk_count + total_cardinality',\n );\n }\n } else {\n // Cleartext: the footer total + count must match the index \u2014 don't trust the footer blindly.\n if (index.keys.length !== chunkCount) {\n throw new IntegrityError(\n `.crbm chunk_count ${chunkCount} != ${index.keys.length} index entries`,\n );\n }\n if (index.cardinalitySum !== totalCardinality) {\n throw new IntegrityError(\n `.crbm total_cardinality ${totalCardinality} != \u03A3 index cardinality ${index.cardinalitySum}`,\n );\n }\n }\n\n return new CrbmReader(\n blob,\n size,\n generation,\n options.lineage,\n index.cardinalitySum,\n index,\n servedFromTail,\n options.crypto,\n storedFooterCrc,\n );\n }\n\n /** Chunk keys present in this generation, ascending. */\n chunkKeys(): number[] {\n return Array.from(this.index.keys);\n }\n\n /** Segment cardinality from the footer (no payload reads) \u2014 the cheap `count()` path. */\n count(): number {\n return this.totalCardinality;\n }\n\n /** The slot holding `chunkKey` (binary search over the ascending keys), or -1 when this generation has none. */\n private slotOf(chunkKey: number): number {\n const keys = this.index.keys;\n let lo = 0;\n let hi = keys.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const k = keys[mid]!;\n if (k === chunkKey) return mid;\n if (k < chunkKey) lo = mid + 1;\n else hi = mid - 1;\n }\n return -1;\n }\n\n has(chunkKey: number): boolean {\n return this.slotOf(chunkKey) >= 0;\n }\n\n /**\n * Range-GET one chunk's payload, verifying its CRC32C before returning. Returns `null` if the chunk is\n * absent. Throws `IntegrityError` on a CRC mismatch (the bytes must never reach the native\n * deserializer). The returned buffer is a **read-only view** owned by the reader/driver \u2014 callers must\n * not mutate it.\n */\n async getChunk(chunkKey: number): Promise<Uint8Array | null> {\n const slot = this.slotOf(chunkKey);\n if (slot < 0) return null;\n const offset = this.index.offsets[slot]!;\n const length = this.index.lengths[slot]!;\n // Re-validate bounds at read time (defense in depth \u2014 a buggy caller or future mutable index path).\n if (offset < PAYLOAD_START || offset + length > this.objectSize - FOOTER_BYTES) {\n throw new IntegrityError(`chunk ${chunkKey} payload out of bounds`);\n }\n const bytes = await this.blob.getRange(offset, length);\n if (crc32c(bytes) !== this.index.crcs[slot]) {\n throw new IntegrityError(`chunk ${chunkKey} payload CRC mismatch`);\n }\n if (this.crypto === undefined) return bytes;\n // Encrypted: on-disk frame is nonce \u2016 ciphertext \u2016 tag. Decrypt (AAD-bound to this chunkKey) after the CRC\n // passes \u2014 a wrong key / tamper / relocated chunk fails as an IntegrityError, never a wrong payload.\n if (bytes.length < AEAD_NONCE_BYTES + AEAD_TAG_BYTES) {\n throw new IntegrityError(`chunk ${chunkKey} encrypted payload too short`);\n }\n return this.crypto.aead.open(\n {\n nonce: bytes.subarray(0, AEAD_NONCE_BYTES),\n ciphertext: bytes.subarray(AEAD_NONCE_BYTES, bytes.length - AEAD_TAG_BYTES),\n tag: bytes.subarray(bytes.length - AEAD_TAG_BYTES),\n },\n this.crypto.aadFor(chunkKey),\n );\n }\n}\n\n// Exported for the coverage-guided fuzz harness, which fuzzes the hand-written index parser\n// directly on raw bytes \u2014 bypassing the CRC wall that mutational fuzzing can't cross. NOT part of the public\n// API surface (`src/index.ts`); reached only via `src/testing/fuzz-core.ts` \u2192 the gitignored `fuzz/build/`.\n//\n// This is where an index is checked for internal consistency, once per open: `count()` and every other read that\n// answers from the index alone trust what it records without decoding a payload. Every entry's key is in range\n// and strictly above the last; its cardinality is in `[1, 65536]`; its payload is non-empty (at least\n// `minPayloadBytes`), within `maxPayloadBytes`, and lies inside the payload region `[PAYLOAD_START, payloadEnd)`.\n// Offsets are stored as unsigned gaps after the previous payload, so payloads ascend and never overlap by\n// construction. `open` then holds the sum and the count to the footer's, where the footer records them.\n//\n// Not checked here, because the format does not record enough: an encrypted footer hides the count and the total\n// (the index's AEAD tag stands in for them), and a cardinality is not compared with its payload, since only the\n// codec can read a payload, and no read compares them afterwards: a payload whose bits disagree with its entry's\n// cardinality decodes to what it holds, and `count()` still reports the entry's.\nexport function parseIndex(\n indexBytes: Uint8Array,\n payloadEnd: number,\n maxPayloadBytes: number,\n minPayloadBytes = 1,\n): ParsedIndex {\n // The arrays are sized from what the index could hold, never from what it claims: every record takes at least\n // MIN_INDEX_RECORD_BYTES and keys are unique within 16 bits, so a hostile index cannot make this allocate more\n // than its own bytes (and 65,536 slots) could fill.\n const capacity = indexCapacity(indexBytes.length);\n const keys = new Uint16Array(capacity);\n const cardinalityMinusOne = new Uint16Array(capacity);\n const lengths = new Uint32Array(capacity);\n const crcs = new Uint32Array(capacity);\n const offsets = new Float64Array(capacity);\n let count = 0;\n let pos = 0;\n let prevKey = 0;\n let prevEnd = PAYLOAD_START;\n let cardinalitySum = 0;\n\n // Parse entries until the index region is exactly consumed (chunk_count is hidden on encrypted objects, so\n // the byte length is the bound \u2014 readVarint throws on any overrun, so a malformed index fails cleanly).\n for (let i = 0; pos < indexBytes.length; i++) {\n const keyDeltaRead = readVarint(indexBytes, pos);\n const offDeltaRead = readVarint(indexBytes, keyDeltaRead.next);\n const lenRead = readVarint(indexBytes, offDeltaRead.next);\n const cardRead = readVarint(indexBytes, lenRead.next);\n const keyDelta = keyDeltaRead.value;\n const offDelta = offDeltaRead.value;\n const len = lenRead.value;\n const card = cardRead.value;\n pos = cardRead.next;\n if (pos + CRC32C_BYTES > indexBytes.length) {\n throw new IntegrityError('.crbm index truncated (missing payload CRC)');\n }\n const crc =\n (indexBytes[pos]! |\n (indexBytes[pos + 1]! << 8) |\n (indexBytes[pos + 2]! << 16) |\n (indexBytes[pos + 3]! << 24)) >>>\n 0;\n pos += CRC32C_BYTES;\n\n const chunkKey = prevKey + keyDelta;\n const offset = prevEnd + offDelta;\n // Unsigned deltas mean keys can only stay flat or rise; a 0 delta after the first is a duplicate.\n if (i > 0 && keyDelta === 0) {\n throw new IntegrityError(`.crbm index has a duplicate chunkKey ${chunkKey}`);\n }\n if (chunkKey > 0xffff) throw new IntegrityError(`.crbm chunkKey ${chunkKey} out of range`);\n if (len < minPayloadBytes || len > maxPayloadBytes) {\n throw new IntegrityError(`.crbm chunk ${chunkKey} length ${len} invalid`);\n }\n if (card < 1 || card > MAX_CHUNK_CARDINALITY) {\n throw new IntegrityError(`.crbm chunk ${chunkKey} cardinality ${card} invalid`);\n }\n // `offset` cannot start below the preamble: it is the previous payload's end plus an unsigned gap.\n if (offset + len > payloadEnd) {\n throw new IntegrityError(`.crbm chunk ${chunkKey} payload out of bounds`);\n }\n\n keys[count] = chunkKey;\n cardinalityMinusOne[count] = card - 1;\n lengths[count] = len;\n crcs[count] = crc;\n offsets[count] = offset;\n count++;\n cardinalitySum += card;\n prevKey = chunkKey;\n prevEnd = offset + len;\n }\n // The loop ends only when pos === indexBytes.length exactly (each entry consumes a whole record; a partial\n // trailing record makes readVarint or the CRC bound throw), so there are never unconsumed trailing bytes.\n // Trim to the entries parsed, so the arrays' byte length is exactly what the reader retains.\n if (count === capacity) {\n return { keys, cardinalityMinusOne, lengths, crcs, offsets, cardinalitySum };\n }\n return {\n keys: keys.slice(0, count),\n cardinalityMinusOne: cardinalityMinusOne.slice(0, count),\n lengths: lengths.slice(0, count),\n crcs: crcs.slice(0, count),\n offsets: offsets.slice(0, count),\n cardinalitySum,\n };\n}\n", "/**\n * `CrbmWriter` \u2014 streaming, constant-memory writer for one immutable `.crbm` generation.\n *\n * Chunks are appended in **ascending chunkKey order**; the writer holds only the small index in memory\n * (offsets aren't known until payloads are written \u2014 which is exactly why the index is a footer).\n *\n * **Encryption (opt-in via `crypto`).** When a {@link CrbmCrypto} is supplied, each chunk payload is\n * AES-256-GCM-sealed (`nonce \u2016 ciphertext \u2016 tag`, AAD = its chunkKey) and the whole index is sealed too (its\n * nonce/tag go in the footer). The footer then sets `FLAG_ENCRYPTED` and **zeroes `chunkCount` +\n * `totalCardinality`** so a leaked object reveals neither how many chunks nor how many ids it holds \u2014 the\n * reader derives both from the decrypted index. The per-chunk CRC covers the *encrypted* on-disk bytes.\n */\nimport { ValidationError } from '../errors';\nimport type { CrbmCrypto } from '../crypto';\nimport { crc32c } from './crc32c';\nimport { writeVarint } from './varint';\nimport type { BlobSink } from '../blob';\nimport {\n CONTAINER_CODEC_NONE,\n ELEMENT_WIDTH_32,\n FLAG_ENCRYPTED,\n FLAG_LITTLE_ENDIAN,\n FOOTER,\n FOOTER_BYTES,\n FOOTER_CRC_COVERAGE,\n MAGIC,\n MAX_CHUNK_CARDINALITY,\n PAYLOAD_START,\n PREAMBLE_BYTES,\n PAYLOAD_CODEC_ROARING_PORTABLE,\n VERSION_MAJOR,\n VERSION_MINOR,\n} from './format';\n\nexport interface CrbmWriterOptions {\n /** Self-describing generation number (also encoded in the object key). */\n readonly generation: number;\n /**\n * Which codec produced the chunk payloads. Defaults to {@link PAYLOAD_CODEC_ROARING_PORTABLE}.\n *\n * A codec id rather than a roaring-specific one, because `.crbm` is a shared container and a second codec is\n * expected before the format freezes.\n */\n readonly payloadCodecId?: number;\n readonly elementWidth?: number;\n /** When set, payloads + index are AES-256-GCM-encrypted, bound to `(segment, generation, scope)` via AAD. */\n readonly crypto?: CrbmCrypto;\n}\n\ninterface IndexEntry {\n readonly chunkKey: number;\n readonly offset: number;\n readonly length: number;\n readonly cardinality: number;\n readonly crc32c: number;\n}\n\nexport class CrbmWriter {\n private readonly entries: IndexEntry[] = [];\n private offset = PAYLOAD_START;\n private lastKey = -1;\n private totalCardinality = 0;\n private preambleWritten = false;\n private finished = false;\n\n constructor(\n private readonly sink: BlobSink,\n private readonly options: CrbmWriterOptions,\n ) {\n if (!Number.isInteger(options.generation) || options.generation < 0) {\n throw new ValidationError(\n `generation must be a non-negative integer; got ${options.generation}`,\n );\n }\n }\n\n /**\n * Append one chunk's payload (the roaring-serialized bytes) with its cardinality. `chunkKey` must be\n * strictly greater than the previous one (ascending, no duplicates); `cardinality` is `[1, 65536]`.\n */\n async addChunk(chunkKey: number, payload: Uint8Array, cardinality: number): Promise<void> {\n if (this.finished) throw new ValidationError('CrbmWriter already finished');\n if (!Number.isInteger(chunkKey) || chunkKey < 0 || chunkKey > 0xffff) {\n throw new ValidationError(`chunkKey must be a u16; got ${chunkKey}`);\n }\n if (chunkKey <= this.lastKey) {\n throw new ValidationError(\n `chunkKey ${chunkKey} not strictly ascending (last ${this.lastKey})`,\n );\n }\n if (!Number.isInteger(cardinality) || cardinality < 1 || cardinality > MAX_CHUNK_CARDINALITY) {\n throw new ValidationError(\n `cardinality must be in [1, ${MAX_CHUNK_CARDINALITY}]; got ${cardinality}`,\n );\n }\n if (payload.length === 0) throw new ValidationError('chunk payload must be non-empty');\n\n await this.ensurePreamble();\n // Encrypt (if configured) to the on-disk bytes; the CRC + index length cover what actually lands on disk.\n const stored = this.options.crypto ? this.sealChunk(chunkKey, payload) : payload;\n await this.sink.write(stored);\n this.entries.push({\n chunkKey,\n offset: this.offset,\n length: stored.length,\n cardinality,\n crc32c: crc32c(stored),\n });\n this.offset += stored.length;\n this.totalCardinality += cardinality;\n this.lastKey = chunkKey;\n }\n\n /** Seal one chunk payload into the on-disk frame `nonce \u2016 ciphertext \u2016 tag`, bound to its chunkKey. */\n private sealChunk(chunkKey: number, payload: Uint8Array): Uint8Array {\n const crypto = this.options.crypto!;\n const sealed = crypto.aead.seal(payload, crypto.aadFor(chunkKey));\n const out = new Uint8Array(sealed.nonce.length + sealed.ciphertext.length + sealed.tag.length);\n out.set(sealed.nonce, 0);\n out.set(sealed.ciphertext, sealed.nonce.length);\n out.set(sealed.tag, sealed.nonce.length + sealed.ciphertext.length);\n return out;\n }\n\n /** Write the index region + the fixed footer. After this the object is complete and immutable. */\n async finish(): Promise<void> {\n if (this.finished) throw new ValidationError('CrbmWriter already finished');\n await this.ensurePreamble();\n\n const indexPlain = this.encodeIndex();\n // Encrypt the index too (its nonce/tag go in the footer); a leaked object then reveals no chunk metadata.\n let indexRegion = indexPlain;\n let indexNonce: Uint8Array | undefined;\n let indexTag: Uint8Array | undefined;\n if (this.options.crypto !== undefined) {\n const sealed = this.options.crypto.aead.seal(indexPlain, this.options.crypto.aadFor('index'));\n indexRegion = sealed.ciphertext;\n indexNonce = sealed.nonce;\n indexTag = sealed.tag;\n }\n const indexOffset = this.offset;\n await this.sink.write(indexRegion);\n this.offset += indexRegion.length;\n\n const footer = this.buildFooter(indexOffset, indexRegion, indexNonce, indexTag);\n await this.sink.write(footer);\n this.finished = true;\n }\n\n private async ensurePreamble(): Promise<void> {\n if (this.preambleWritten) return;\n const preamble = new Uint8Array(PREAMBLE_BYTES);\n preamble.set(MAGIC, 0);\n preamble[4] = VERSION_MAJOR;\n preamble[5] = VERSION_MINOR;\n // bytes 6-7 reserved = 0\n await this.sink.write(preamble);\n this.preambleWritten = true;\n }\n\n private encodeIndex(): Uint8Array {\n const out: number[] = [];\n let prevKey = 0;\n let prevEnd = PAYLOAD_START;\n for (const e of this.entries) {\n writeVarint(out, e.chunkKey - prevKey);\n writeVarint(out, e.offset - prevEnd);\n writeVarint(out, e.length);\n writeVarint(out, e.cardinality);\n // crc32c is high-entropy \u2192 fixed 4-byte LE, not varint.\n out.push(\n e.crc32c & 0xff,\n (e.crc32c >>> 8) & 0xff,\n (e.crc32c >>> 16) & 0xff,\n (e.crc32c >>> 24) & 0xff,\n );\n prevKey = e.chunkKey;\n prevEnd = e.offset + e.length;\n }\n return Uint8Array.from(out);\n }\n\n private buildFooter(\n indexOffset: number,\n indexRegion: Uint8Array,\n indexNonce: Uint8Array | undefined,\n indexTag: Uint8Array | undefined,\n ): Uint8Array {\n const encrypted = this.options.crypto !== undefined;\n const footer = new Uint8Array(FOOTER_BYTES);\n const view = new DataView(footer.buffer);\n view.setBigUint64(FOOTER.indexOffset, BigInt(indexOffset), true);\n view.setBigUint64(FOOTER.indexLength, BigInt(indexRegion.length), true);\n view.setUint32(FOOTER.indexCrc32c, crc32c(indexRegion), true);\n view.setUint32(FOOTER.flags, FLAG_LITTLE_ENDIAN | (encrypted ? FLAG_ENCRYPTED : 0), true);\n view.setUint16(\n FOOTER.payloadCodecId,\n this.options.payloadCodecId ?? PAYLOAD_CODEC_ROARING_PORTABLE,\n true,\n );\n footer[FOOTER.elementWidth] = this.options.elementWidth ?? ELEMENT_WIDTH_32;\n footer[FOOTER.containerCodec] = CONTAINER_CODEC_NONE;\n footer[FOOTER.versionMajor] = VERSION_MAJOR;\n footer[FOOTER.versionMinor] = VERSION_MINOR;\n // When encrypted, the index's AEAD nonce/tag live in these reserved slots; key_id stays zero (the wrapped\n // DEKs live in the registry, not the object). Unencrypted: all crypto fields stay zero.\n if (indexNonce !== undefined) footer.set(indexNonce, FOOTER.indexNonce);\n if (indexTag !== undefined) footer.set(indexTag, FOOTER.indexTag);\n view.setBigUint64(FOOTER.generation, BigInt(this.options.generation), true);\n // Hide chunk count + cardinality on an encrypted object (a leak reveals neither); the reader derives both\n // from the decrypted index. Cleartext objects keep them for the O(1) count()/validation path.\n view.setUint32(FOOTER.chunkCount, encrypted ? 0 : this.entries.length, true);\n view.setBigUint64(FOOTER.totalCardinality, BigInt(encrypted ? 0 : this.totalCardinality), true);\n view.setUint32(FOOTER.footerCrc32c, crc32c(footer.subarray(0, FOOTER_CRC_COVERAGE)), true);\n footer.set(MAGIC, FOOTER.endMagic);\n return footer;\n }\n}\n", "/**\n * Bridges the `.crbm` archive format to the engine's Storage seam.\n *\n * `CrbmStorageChunkSource` implements the {@link StorageChunkSource} the engine reads through, over an\n * {@link IStorageDriver}: it resolves a segment's current generation, opens its {@link CrbmReader} once, and serves\n * per-chunk payloads. `writeCrbmGeneration` / `writeCrbmGenerationStream` are the write primitives (a generation\n * built from bitmaps, in memory or streamed), `bulkLoadCrbmGeneration` is the loader `load()` is built on, and\n * `publishGeneration` is the forward-only pointer advance every write ends with.\n */\nimport {\n DEFAULT_CURRENT_GEN_TTL_MS,\n DEFAULT_MAX_OPEN_INDEX_BYTES,\n DEFAULT_MAX_OPEN_SEGMENTS,\n REFRESH_RETRY_MS,\n} from './reader-defaults';\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport {\n IntegrityError,\n CapabilityError,\n KeyUnavailableError,\n NotFoundError,\n ValidationError,\n WriteConflictError,\n isIntegrityError,\n isNotFoundError,\n isTransientError,\n isValidationError,\n isWriteConflictError,\n} from './errors';\nimport type { BlobReader } from './blob';\nimport { yieldEvery } from './cooperative';\nimport type { Yielder } from './cooperative';\nimport type { Clock } from './determinism';\nimport { BoundedLru } from './lru';\nimport { splitId } from './bit-route';\nimport { segmentKey } from './keys';\nimport { aadFor } from './crypto';\nimport type { CrbmCrypto, IKeystore, WrappedDek } from './crypto';\nimport { validateChunkRef, validateUserRef } from './validate';\nimport type {\n ChunkRef,\n StorageChunkSource,\n GenKey,\n IStorageDriver,\n IRegistryDriver,\n SegmentRef,\n SegmentSize,\n Token,\n} from './ports';\nimport { CrbmReader } from './crbm/reader';\nimport type { CrbmReaderOptions } from './crbm/reader';\nimport { CrbmWriter } from './crbm/writer';\nimport type { CodecBitmap, CodecInterface } from './codec';\nimport { requireCodec } from './codec';\n\nexport interface CrbmStorageChunkSourceOptions extends CrbmReaderOptions {\n /**\n * Optional {@link IRegistryDriver}. When provided, the current generation is resolved via the registry's\n * authoritative `currentGen` (one cheap strong read) instead of a `list` scan of every generation \u2014 the\n * retirement of that scan. When absent, the source falls back to the `list`-scan (so the\n * in-memory / simple setups keep working with no registry). The resolved generation is cached and\n * **re-resolved on a short TTL** ({@link currentGenTtlMs}, needs a {@link clock}) so a long-lived source\n * observes another process's load within the TTL, not only when something else happens to re-resolve it.\n */\n readonly registry?: IRegistryDriver;\n /**\n * Optional {@link IKeystore} for reading **encrypted** segments. When a segment's registry record\n * carries wrapped DEK(s), the source unwraps the DEK via this keystore and decrypts each chunk/index. Reading\n * an encrypted segment without a keystore throws {@link KeyUnavailableError}; cleartext segments ignore it.\n * Requires a `registry` (that's where the wrapped DEKs live).\n */\n readonly keystore?: IKeystore;\n /**\n * Enforce that every segment read is encrypted. When true, resolving a **cleartext** segment (no\n * wrapped DEKs) throws {@link KeyUnavailableError} \u2014 a guard against silently reading a segment that should\n * have been encrypted. Off by default (encryption is opt-in).\n */\n readonly requireEncryption?: boolean;\n /**\n * Time source for the current-generation TTL refresh \u2014 the determinism seam; `core/` never reads\n * ambient time. Refresh needs **both** a clock and a `registry`. Without either there is no timed refresh, and\n * that is the only difference: a segment is still re-resolved when its reader is evicted, when a fetch finds its\n * generation swept, and on {@link CrbmStorageChunkSource.invalidate}. What goes is the bound on how long another\n * process's publish takes to arrive. The `CloudRoaring` facade passes its clock automatically, so wiring a\n * `registry` is enough to get the bounded refresh.\n */\n readonly clock?: Pick<Clock, 'now'>;\n /**\n * How long (ms) a resolved `currentGen` is trusted before the next read re-resolves it (default 2000) \u2014 the\n * bound on read staleness after a load publishes: a reader may serve the prior generation for up to this long,\n * then converges. Lazy (checked on read \u2014 no timer); \u2264 one cheap registry read per segment per window, and a\n * new {@link CrbmReader} is opened only when the generation actually changed. `0` turns the refresh off, as a\n * missing clock or registry does: {@link clock} says what that changes, and what it does not.\n */\n readonly currentGenTtlMs?: number;\n /**\n * Hard ceiling on how many segments' readers (each holding a fully-parsed `.crbm` index) are cached at once\n * (default 1024) \u2014 a memory bound for a long-running server. Past it, the least-recently-used\n * segment's reader is evicted; the next read of an evicted segment re-opens it (one cheap tail GET, since\n * generations are immutable). Raise it for a big cache working set of small segments.\n */\n readonly maxOpenSegments?: number;\n /**\n * Aggregate byte ceiling on the parsed `.crbm` indices resident in the reader cache (default 64 MiB) \u2014 the\n * **second half of that memory bound**. `maxOpenSegments` alone bounds by *count*, but a wide/dense segment's\n * parsed index can reach about 1.3 MB, so 1024 wide indices could pin over a GB and blow a small heap (e.g. a 128 MB\n * Lambda) while the count is nominally \"in bounds\". This caps the summed {@link CrbmReader.retainedIndexBytes}\n * across cached readers; the least-recently-used reader is evicted once the total would exceed it \u2014 whichever\n * of the count/byte bounds binds first. Lower it for memory-tight deployments with wide segments; a single\n * segment whose index alone exceeds the budget is still cached (it can't be shrunk) but nothing else alongside.\n */\n readonly maxOpenIndexBytes?: number;\n}\n\n/** Adapt one `(driver, key)` pair to the codec's `BlobReader` seam. */\nfunction storageBlobReader(driver: IStorageDriver, key: GenKey): BlobReader {\n return {\n getRange(offset, length) {\n return driver.getRange(key, offset, length);\n },\n getTail(maxBytes) {\n return driver.getTail(key, maxBytes);\n },\n };\n}\n\n/**\n * A resolved read target, as `resolveTarget` produces it: which generation is current, and its DEK wrappings if it\n * is encrypted. `lineage` is the registry row's OCC token \u2014 the identity that survives a delete,\n * because `IRegistryDriver.delete` tombstones rather than unlinks (\"a later `create` still gets a fresh,\n * greater token\"). It is what separates two *incarnations* of one name, which a generation number cannot:\n * `nextGeneration` restarts at 0 once the row is purged and the bucket emptied, so a retired-and-re-created\n * segment presents different data at the same `currentGen`. Undefined for a registry-less source, which has no\n * row: there, only the object itself tells two incarnations apart, by the fingerprint a pin records.\n */\ntype Target = { generation: number; lineage?: Token; wrappedDeks?: readonly WrappedDek[] };\n\n/**\n * What a pin holds of the object it pinned: the version its cache entries are keyed by, and the object's\n * fingerprint, which a pinned read checks each time it opens the generation. `pinGeneration` always records one.\n * A pin built by hand without one is not checked: it reads whatever object is under its generation's key.\n */\nexport interface PinnedObject {\n readonly version: string;\n readonly fingerprint?: string;\n}\n\n/** A pinned read of a generation that is no longer the object the pin opened: stated as a fact, not a cause. */\nconst notThePinned = (ref: SegmentRef, generation: number): NotFoundError =>\n new NotFoundError(\n `segment \"${ref.segment}\" generation ${generation} is no longer the object this handle pinned`,\n );\n\n/**\n * The version of one generation of one incarnation: `<generation>`, or `<generation>:<row token>` where there is\n * a row. One spelling, for the live lookup and for what a pin holds.\n */\nfunction versionOf(generation: number, lineage: unknown): string {\n return lineage === undefined ? String(generation) : `${generation}:${String(lineage)}`;\n}\n\n/** A memoized per-segment reader plus the time it was installed, for the current-generation TTL refresh. */\ninterface Snapshot {\n readonly reader: Promise<CrbmReader | null>;\n /** When the snapshot's refresh clock started. A refresh that failed transiently moves it back, so it lapses sooner. */\n installedAtMs: number;\n}\n\n/**\n * How many buffered remainders bulk-load holds before flushing them into their chunk bitmaps. Bounds the\n * transient JS-side buffer to **~28 MB measured** irrespective of input size, while keeping batches large\n * enough that the per-id JS\u2194native crossing is amortised away.\n */\nconst BULK_FLUSH_IDS = 1 << 20;\n/**\n * Ids between ingest-loop yields. 16x coarser than the per-chunk cadence because the per-id work is ~40 ns\n * (a `splitId` and an array push) against ~1.3 \u00B5s per chunk \u2014 matching cadences would put the yield cost on the\n * wrong side of the work it interrupts. Sized against the *slowest* ingest, an in-memory async generator at\n * ~220 ns/id, so the resulting stretch stays a few ms there and well under 1 ms on a plain array.\n */\nconst YIELD_EVERY_IDS = 1 << 14;\n\nexport class CrbmStorageChunkSource implements StorageChunkSource {\n /**\n * One resolved reader per segment, re-resolved on a short TTL ({@link CrbmStorageChunkSourceOptions.currentGenTtlMs},\n * needs a clock). Within the TTL a segment's Storage bytes are treated as an immutable snapshot; when the TTL\n * elapses the next read cheaply re-resolves `currentGen` and, only if it advanced (a load published),\n * opens the new generation \u2014 so a long-lived source observes new generations within the TTL. The engine pairs\n * this with a **generation-keyed** cache so a bump never serves a stale decoded chunk. Without a clock or a\n * registry there is no timed refresh, and a segment's reader is replaced only when it is evicted, when a read\n * has to fetch from a generation a sweep deleted, or when {@link invalidate} drops it. A segment with no generation yet is not memoized, so\n * it's re-checked until one exists. **Bounded** by a {@link BoundedLru} ({@link CrbmStorageChunkSourceOptions.maxOpenSegments}, default\n * 1024): past the ceiling the least-recently-used segment's reader (and its parsed index) is evicted \u2014 the\n * steady-state memory bound; re-opening an evicted segment is one cheap tail GET.\n */\n private readonly snapshots: BoundedLru<string, Snapshot>;\n /**\n * Pins ({@link heldKey}) whose object has been found replaced: another object is under its generation's key. Kept,\n * and bounded as the readers are, so that a pin's later chunk reads and any reopen fail at once rather than pay for\n * the check again. A replacement can be undone, by a restore that puts the pinned object back, so\n * {@link invalidate} forgets the segment's, and a pin of the same version that opens the object forgets the one\n * against it. An object found gone is not kept: that is the open's own `NotFoundError`, and a 404 can pass. A\n * reader still memoised keeps answering from the index it holds, so the pin's `count()` does, until a later pin of\n * that version opens the object now under the key, which takes its place.\n */\n private readonly replacedPins: BoundedLru<string, true>;\n /**\n * A check of a pinned object still under way, by {@link heldKey}, so the reads that ask at once share one footer\n * read. One forgotten before it ends, by {@link invalidate} or a pin, keeps nothing it finds.\n */\n private readonly checking = new Map<string, Promise<boolean>>();\n /**\n * How many times {@link invalidate} has run, so a read that waited can tell whether the store was told something\n * meanwhile. An eviction from the reader cache is not an invalidation, and what a read found stands through one. It\n * counts every segment's invalidations: a read that waited through another segment's only costs a later read the\n * check again.\n */\n private invalidations = 0;\n private readonly registry: IRegistryDriver | undefined;\n private readonly keystore: IKeystore | undefined;\n private readonly requireEncryption: boolean;\n private readonly readerOptions: CrbmReaderOptions;\n private readonly clock: Pick<Clock, 'now'> | undefined;\n private readonly currentGenTtlMs: number;\n\n constructor(\n private readonly driver: IStorageDriver,\n options: CrbmStorageChunkSourceOptions = {},\n ) {\n if (!driver.capabilities().rangeRead) {\n throw new CapabilityError('Storage driver must support range reads (rangeRead)');\n }\n const {\n registry,\n keystore,\n requireEncryption,\n clock,\n currentGenTtlMs,\n maxOpenSegments,\n maxOpenIndexBytes,\n ...readerOptions\n } = options;\n if (keystore !== undefined && registry === undefined) {\n throw new CapabilityError(\n 'a keystore needs a storage backend \u2014 that is where the wrapped DEKs live. A bare IStorageDriver has no registry to hold them',\n );\n }\n if (requireEncryption === true && registry === undefined) {\n // Without a registry the source can't see wrapped DEKs or `destroyed` tombstones (it list-scans storage),\n // so encryption can't be enforced or even observed here \u2014 fail fast rather than mislead.\n throw new CapabilityError('requireEncryption needs a registry');\n }\n this.registry = registry;\n this.keystore = keystore;\n this.requireEncryption = requireEncryption ?? false;\n this.readerOptions = readerOptions;\n this.clock = clock;\n this.currentGenTtlMs = currentGenTtlMs ?? DEFAULT_CURRENT_GEN_TTL_MS;\n // Bound the reader cache by BOTH count and aggregate parsed-index bytes. No TTL on the LRU itself \u2014\n // the currentGen TTL is handled separately via each snapshot's `installedAtMs`; these ceilings only bound how\n // many segment readers/indices stay resident. Each reader's byte weight is reported once it resolves (below).\n // The cache never compares against wall-clock, so a zero clock is fine when none is injected.\n this.snapshots = new BoundedLru<string, Snapshot>({\n maxEntries: maxOpenSegments ?? DEFAULT_MAX_OPEN_SEGMENTS,\n maxBytes: maxOpenIndexBytes ?? DEFAULT_MAX_OPEN_INDEX_BYTES,\n clock: clock ?? { now: () => 0 },\n });\n this.replacedPins = new BoundedLru<string, true>({\n maxEntries: maxOpenSegments ?? DEFAULT_MAX_OPEN_SEGMENTS,\n clock: { now: () => 0 },\n });\n }\n\n /** The current resolved reader for a segment, refreshing on the TTL. Cheap within the TTL window. */\n private resolvedReader(ref: SegmentRef): Promise<CrbmReader | null> {\n const key = segmentKey(ref);\n const existing = this.snapshots.get(key);\n if (existing === undefined) return this.install(key, this.resolveLatest(ref)).reader;\n if (!this.expired(existing.installedAtMs)) return existing.reader;\n // Expired: install the in-flight refresh **synchronously** (before any await) so concurrent readers in this\n // window coalesce onto the one re-resolve \u2014 \u2264 one registry read + at most one reopen per segment per window\n // (no boundary thundering-herd). computeRefreshedReader reuses the prior reader unless the generation moved.\n const snap: Snapshot = this.install(\n key,\n this.computeRefreshedReader(ref, existing, () => this.retrySoon(snap)),\n );\n return snap.reader;\n }\n\n /**\n * A refresh failed transiently and the prior reader keeps serving: make this snapshot lapse after\n * {@link REFRESH_RETRY_MS} (never longer than the TTL) rather than after a whole TTL, so the next read after\n * that asks the registry again. It costs no read of its own; it only changes when the next refresh happens.\n */\n private retrySoon(snap: Snapshot): void {\n snap.installedAtMs =\n this.now() - (this.currentGenTtlMs - Math.min(this.currentGenTtlMs, REFRESH_RETRY_MS));\n }\n\n /** TTL elapsed: cheaply re-resolve `currentGen`; reopen only if it actually changed, else reuse the prior reader. */\n private async computeRefreshedReader(\n ref: SegmentRef,\n existing: Snapshot,\n onTransientFault: () => void,\n ): Promise<CrbmReader | null> {\n const current = await existing.reader.catch(() => null);\n let target: Target | null | undefined;\n try {\n target = await this.resolveTarget(ref);\n } catch (err) {\n // Only a transient fault is ridden out. Anything else \u2014 an access denial, a corrupt row, a registry that\n // answers NotFound \u2014 fails this read exactly as a cold resolve of the segment would, and the snapshot is\n // forgotten, so the reader and the key it unwrapped do not outlive a refresh that could not be trusted.\n if (!isTransientError(err)) throw err;\n target = undefined;\n }\n // Transient resolve fault: keep serving the prior reader if it's alive, but ask again soon rather than a whole\n // TTL later, so an outage ends the stale serving shortly after the registry answers. If it's dead (null/failed open),\n // reopen rather than re-arm a dead snapshot (else the segment reads empty for a whole TTL window).\n if (target === undefined) {\n if (current === null) return this.resolveLatest(ref);\n onTransientFault();\n return current;\n }\n if (target === null) return null; // segment gone / destroyed\n // Reuse only when BOTH the generation and the row match. Comparing the number alone treated a\n // retired-and-re-created name as unchanged \u2014 `nextGeneration` restarts at 0, so incarnation 2's generation 0\n // is indistinguishable from the reader already open \u2014 and the snapshot was never refreshed, so the store\n // kept serving a deleted segment's ids. A token that moved for an unrelated row write costs one reopen.\n if (\n current !== null &&\n target.generation === current.generation &&\n target.lineage === current.lineage\n ) {\n return current; // unchanged \u2014 reuse\n }\n return this.openForTarget(ref, target); // advanced / appeared / prior open was dead \u2014 reopen fresh\n }\n\n /**\n * Memoize a reader-promise as the segment's snapshot at `now()`, forgetting it later if it resolves to\n * null / throws (identity-guarded, so a stale cleanup never clobbers a fresher snapshot). Installing the\n * (pending) promise **synchronously** is what lets concurrent callers coalesce onto one in-flight resolve.\n */\n private install(key: string, reader: Promise<CrbmReader | null>): Snapshot {\n const snap: Snapshot = { reader, installedAtMs: this.now() };\n this.snapshots.set(key, snap);\n const forgetIfStale = (): void => {\n if (this.snapshots.get(key) === snap) this.snapshots.delete(key);\n };\n reader.then((r) => {\n if (r === null) {\n forgetIfStale();\n return;\n }\n // Report the parsed index's footprint so the cache can bound aggregate resident bytes. Identity-\n // guarded via `peek` (no recency change) so a since-replaced snapshot doesn't mis-weight the fresh entry.\n if (this.snapshots.peek(key) === snap) this.snapshots.setWeight(key, r.retainedIndexBytes);\n }, forgetIfStale);\n return snap;\n }\n\n private now(): number {\n return this.clock ? this.clock.now() : 0;\n }\n\n /**\n * How often this source re-reads a segment's pointer while the segment is being read, in ms: its TTL, or 0 when\n * it has no timed refresh (no clock, no registry, or a TTL of 0). The grounded cost report prices the refresh at\n * this.\n */\n get pointerRefreshMs(): number {\n return this.clock !== undefined && this.registry !== undefined ? this.currentGenTtlMs : 0;\n }\n\n private expired(installedAtMs: number): boolean {\n // Refresh needs a clock (the TTL) AND a registry (the *cheap* `currentGen` read the design assumes \u2014\n // without one, re-resolution is a full storage `list`-scan, and a registry-less setup is single-process\n // local, not the shared bucket that separate loaders publish into). Otherwise there is no timed refresh, and\n // only an eviction, a sweep's heal or `invalidate` re-resolves the segment.\n return (\n this.clock !== undefined &&\n this.registry !== undefined &&\n this.currentGenTtlMs > 0 &&\n this.clock.now() - installedAtMs >= this.currentGenTtlMs\n );\n }\n\n /**\n * The segment's current generation number \u2014 the engine keys its chunk cache by this so a generation bump\n * is observed instead of serving a stale decoded chunk. Served from the (TTL-refreshed)\n * snapshot, so no extra backend read within the TTL window. `null` if the segment has no committed generation.\n *\n * Heals a swept generation exactly like {@link withFreshSnapshot}: a caller that resolves the generation once\n * per op, before any chunk fetch, would otherwise fail the whole operation rather than one chunk. The engine\n * keys by {@link currentVersion} instead, which heals the same way, and falls back to this for a source that\n * cannot report a version. It is spelled out rather than delegated because a lookup like this runs once per\n * operand of every `has`/`count`/`iterate`/`intersect`, almost always served from the cached snapshot with\n * no backend call at all. Routing it through the generic helper cost ~115 ns/op on that path (a second async\n * frame, a per-call closure, and an `await` on a plain number) for a race that fires only during a\n * concurrent sweep. One retry, then propagate \u2014 same contract, same eviction rule.\n */\n async currentGeneration(ref: SegmentRef): Promise<number | null> {\n const pending = this.resolvedReader(ref);\n try {\n return (await pending)?.generation ?? null;\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n this.dropStale(segmentKey(ref), pending);\n return (await this.resolvedReader(ref))?.generation ?? null; // a second miss propagates\n }\n }\n\n /**\n * Forget everything derived from `ref`: the resolved snapshot and the open reader behind it (and with the\n * reader, the DEK it unwrapped at open time), and what its pins' objects were found to be. The next read\n * resolves the segment again.\n *\n * Needed because the TTL refresh only ever answers \"has the pointer moved?\", and every *destructive* verb \u2014\n * an erasure that deletes the generation holding the bit, a `dropSegment`, a crypto-shred, a retirement \u2014\n * leaves the pointer's answer unchanged from this source's point of view while making the snapshot wrong.\n * Until this is called the source keeps serving that snapshot with no backend read at all, so nothing on the\n * storage side can close the window; with no registry, no clock or `cache.genTtlMs: 0`, nothing bounds it at all.\n */\n invalidate(ref: SegmentRef): void {\n this.invalidations += 1;\n const key = segmentKey(ref);\n this.snapshots.delete(key);\n // \u2026and every PINNED reader of the same segment, which is memoized under `<key>@<generation>`. Dropping\n // only the live entry left a pinned handle reading a crypto-shredded segment: the pin is exactly the\n // reader that does not re-resolve on its own, so it is the one that most needs to be told.\n const pinnedPrefix = `${key}@`;\n this.snapshots.deleteWhere((k) => k.startsWith(pinnedPrefix));\n // \u2026and what its pins' objects were found to be, and any check still finding out: an object restored under its\n // key is its pin's again, and a restore is what an operator invalidates for.\n this.replacedPins.deleteWhere((k) => k.startsWith(pinnedPrefix));\n for (const k of this.checking.keys()) if (k.startsWith(pinnedPrefix)) this.checking.delete(k);\n }\n\n /**\n * `<generation>` for a registry-less source, or `<generation>:<row token>` with one. The token moves on every row\n * write, so an unrelated write (a `setRetention`) costs the segment's decoded chunks once \u2014 bounded, and on an\n * admin path. Exactness in the direction that matters: with a registry, two incarnations can never share a version\n * string. Without one they can: a name purged and loaded again out of band restarts at the same bare number, and\n * a live read that reopens it after an eviction reads the new object's chunks beside the old one's cached ones. A\n * store with no registry cannot write, so that takes a change made from outside it; a pin is kept apart from it by\n * the fingerprint it records.\n *\n * This is the call the engine makes **once per operand of every read**, before any chunk fetch, to key its\n * chunk cache, so it heals a swept generation exactly as {@link currentGeneration} does: unhealed, a cold read\n * racing a publish and a `keep: 0` sweep failed the whole operation with `NotFoundError`, where the same race\n * on a chunk fetch heals. Spelled out rather than delegated for the reason `currentGeneration` gives: it is\n * almost always served from the cached snapshot with no backend call at all. One retry, then propagate.\n */\n async currentVersion(ref: SegmentRef): Promise<string | null> {\n const pending = this.resolvedReader(ref);\n let reader: CrbmReader | null;\n try {\n reader = await pending;\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n this.dropStale(segmentKey(ref), pending);\n reader = await this.resolvedReader(ref); // a second miss propagates\n }\n return reader === null ? null : versionOf(reader.generation, reader.lineage);\n }\n\n /**\n * Resolve the segment **once** and report what a pin should hold: the generation, and the version that\n * identifies those exact bytes. `null` when the segment resolves to no generation \u2014 there is nothing to pin,\n * and a caller must treat that as \"this handle reads empty\", not \"pinning is unsupported here\".\n *\n * Heals a generation swept between the registry read and the open, as {@link currentVersion} does: a publish\n * and a `keep: 0` sweep can land in that gap, and nothing has been handed out yet, so the generation current\n * once the swept one is gone is exactly what a pin taken now should hold. One retry, then propagate.\n */\n async pinGeneration(\n ref: SegmentRef,\n ): Promise<({ generation: number } & Required<PinnedObject>) | null> {\n try {\n return await this.pinOnce(ref);\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n return this.pinOnce(ref); // a second miss propagates\n }\n }\n\n private async pinOnce(\n ref: SegmentRef,\n ): Promise<({ generation: number } & Required<PinnedObject>) | null> {\n // Resolved FRESH, not through the snapshot memo. \"The generation current right now\" is the whole promise\n // of a pin, and the memo is allowed to be up to `cache.genTtlMs` behind \u2014 or, on a store with no timed refresh,\n // arbitrarily far behind. Pinning through it made a pin on such a store\n // return whatever generation the store happened to hold, however old.\n const target = await this.resolveTarget(ref);\n if (target === null) return null;\n const version = versionOf(target.generation, target.lineage);\n // Opened now, not at the first pinned read, so the pin knows which object it holds: a name purged and loaded\n // again starts again at generation 0, and only the object itself tells the two apart. With a registry the\n // version already does, since the row a name is loaded into again is a new row with a new token, so every pin of\n // this version shares one open. It is installed before it resolves, so pins taken at the same moment wait on it\n // rather than each make their own: N pins of one generation make one tail read and one key unwrap between them,\n // while the store keeps the reader. Without a registry the version is the bare generation number, which two\n // incarnations share, so every pin opens the object afresh and reads its fingerprint.\n let reader: CrbmReader | null;\n let opened = true;\n if (target.lineage === undefined) {\n reader = await this.openForTarget(ref, target);\n this.install(this.pinnedKey(ref, version, reader.fingerprint), Promise.resolve(reader));\n } else {\n const epoch = this.invalidations;\n const key = this.pinnedKey(ref, version);\n let entry = this.snapshots.get(key);\n opened = entry === undefined;\n entry ??= this.install(key, this.openForTarget(ref, target));\n reader = await entry.reader;\n // A pinned read's reopen of this version, already under way, found the row gone or destroyed: the segment\n // resolves no generation now, so this pin pins nothing, as one taken a moment later would.\n if (reader === null) return null;\n const replaced = (r: CrbmReader) =>\n this.replacedPins.get(this.heldKey(ref, version, r.fingerprint)) !== undefined;\n // A memoised reader is of the object that was under the key when it was opened. One the store has since found\n // replaced is not what is there now, so this pin opens the object afresh rather than pin a replaced one, and\n // pins taken at the same moment share that one open. The replaced pin's reader goes from the memo with it, so\n // that pin's index answers, its `count()` among them, end here: what is under its key is another object now.\n const known = !opened && replaced(reader);\n // The open this pin shared can be gone by now: a replaced pin's reopen removes the reader it opened, and the\n // reader cache can evict it. With no invalidation in between, and no replacement found, that reader is still of\n // the object under the key, and this pin keeps it memoised.\n if (!known && this.invalidations === epoch && this.snapshots.peek(key) === undefined) {\n this.install(key, Promise.resolve(reader));\n }\n if (known) {\n const current = this.snapshots.peek(key);\n let fresh =\n current !== undefined && current !== entry\n ? current\n : this.install(key, this.openForTarget(ref, target));\n reader = await fresh.reader;\n // A reopen of this version, already under way, found the row gone or destroyed.\n if (reader === null) return null;\n // What another pin put there can be the replaced reader too, put back before its verdict was known.\n if (fresh === current && replaced(reader)) {\n fresh = this.install(key, this.openForTarget(ref, target));\n reader = await fresh.reader;\n if (reader === null) return null;\n }\n opened = fresh !== current;\n }\n }\n // Only an open this call made has read what is under the key. The object there is that one now, whatever was\n // found before: a restore puts back what a replacement took, and a verdict kept against it would fail this pin.\n if (opened) {\n const held = this.heldKey(ref, version, reader.fingerprint);\n this.replacedPins.delete(held);\n this.checking.delete(held);\n }\n return { generation: target.generation, version, fingerprint: reader.fingerprint };\n }\n\n /**\n * Where a pinned reader of one object is memoised. With a registry that is its version, which names the object:\n * a name purged and loaded again gets a new row, and with it a new token. Without one the version is the bare\n * generation number, which two incarnations share, so the object's fingerprint is added to tell them apart.\n */\n private pinnedKey(ref: SegmentRef, version: string, fingerprint?: string): string {\n return this.registry === undefined && fingerprint !== undefined\n ? this.heldKey(ref, version, fingerprint)\n : `${segmentKey(ref)}@${version}`;\n }\n\n /**\n * One pin's hold on one segment: its version, and the fingerprint of the object it pinned. A check of that object\n * is shared under it, and what the check finds is kept under it, with a registry or without one: pins of one\n * version can hold different objects, since a pin built by hand can name any, and an object replaced from outside\n * the store leaves the row, and so the version, as it was.\n */\n private heldKey(ref: SegmentRef, version: string, fingerprint: string): string {\n return `${segmentKey(ref)}@${version}#${fingerprint}`;\n }\n\n /**\n * A reader for one **specific** generation, memoized in the same bounded LRU as the live snapshots under a\n * generation-qualified key.\n *\n * Sharing the LRU is the point, not an implementation detail. A pinned reader held in a private field is\n * outside the memory ceiling the library advertises \u2014 measured at 10.34 MiB per live pin, 32 pins holding\n * 111.7 MiB against an 8 MiB configured bound. A pinned *generation number* has no such problem: an object is\n * immutable, so eviction is harmless, and re-opening at the same number reopens the same bytes unless the name\n * was purged and loaded again, which the pin's fingerprint then says. It also removes a memoized-rejection bug by\n * construction \u2014 `install` forgets a promise that rejects,\n * where a hand-rolled `this.reader ??= open()` cached the rejection for the life of the handle and made a pin\n * the one read path with no resilience.\n *\n * **`status` is re-checked here**, on every open rather than once at pin time. A pin taken before a\n * crypto-shred must not keep unwrapping a DEK the shred destroyed; a destroyed segment resolves no generation\n * for anyone, and a pin's read of one fails. That holds from the pin's next open: a reader already open keeps\n * the key it unwrapped until it is evicted or invalidated.\n *\n * **And the object is checked**, when the caller says which one it pinned. A generation number is not an\n * identity (invariant 1): once a name is purged and loaded again, its new segment starts again at generation 0,\n * so a pin of the old one would open the new one's object as its own, and read it beside the chunks it had\n * already cached from the old one. The row's token cannot tell them apart, since it moves on every write, a\n * publish included; the object's fingerprint can, and a pin whose object has been replaced fails, as one whose\n * generation has been swept does. The memo is keyed so that two pins of one generation number in two\n * incarnations never share a reader, with a registry or without one ({@link pinnedKey}).\n */\n private async readerAt(\n ref: SegmentRef,\n generation: number,\n version?: string,\n fingerprint?: string,\n ): Promise<CrbmReader | null> {\n const at = version ?? String(generation);\n const key = this.pinnedKey(ref, at, fingerprint);\n const epoch = this.invalidations;\n let entry = this.snapshots.get(key);\n const opening = entry === undefined;\n if (entry === undefined) {\n // An object already found replaced is not opened again: the open would pay a row read and a key unwrap for\n // an object that is not the pin's.\n if (\n fingerprint !== undefined &&\n this.replacedPins.get(this.heldKey(ref, at, fingerprint)) !== undefined\n )\n throw notThePinned(ref, generation);\n entry = this.install(key, this.openAt(ref, generation));\n }\n let reader: CrbmReader | null;\n try {\n reader = await entry.reader;\n } catch (err) {\n // An open can fail on an object that is not the pinned one: written under a key this store lacks, or stored in\n // the clear where encryption is required. Its footer says which, with no key and no row. One that is gone needs\n // no footer read to say so: that is this NotFoundError.\n if (fingerprint === undefined || isTransientError(err) || isNotFoundError(err)) throw err;\n await this.throwIfReplaced(ref, generation, at, fingerprint);\n throw err;\n }\n // A pin's read fails where a bare read of a generation reads nothing: a pin describes one instant, and one that\n // goes empty partway through a call, because its row was dropped or destroyed, has torn it.\n if (reader === null) {\n if (version === undefined) return null;\n throw new NotFoundError(\n `segment \"${ref.segment}\" generation ${generation}, which this handle pinned, can no longer be read: its ` +\n 'registry row is gone or destroyed',\n );\n }\n if (fingerprint !== undefined && reader.fingerprint !== fingerprint) {\n // The verdict is kept only if nothing was invalidated while this read waited: an invalidation forgets the\n // segment's verdicts, as after a restore, and this one is from before it. An eviction from the reader cache is\n // no reason to forget it. What this read opened is the object now under the key, which is not the pin's: left\n // memoised under the pin's key, it would hold a place in the reader cache, and the key it unwrapped, for reads\n // that can only fail, so it goes whatever was invalidated.\n if (this.invalidations === epoch)\n this.replacedPins.set(this.heldKey(ref, at, fingerprint), true);\n if (opening && this.snapshots.peek(key) === entry) this.snapshots.delete(key);\n throw notThePinned(ref, generation);\n }\n return reader;\n }\n\n private async openAt(ref: SegmentRef, generation: number): Promise<CrbmReader | null> {\n if (this.registry === undefined) return this.openForTarget(ref, { generation });\n // One read of the row, for its status, its incarnation and its key wrappings together.\n const record = await this.registry.get(ref);\n // Re-checked at open, not captured at pin: a shred between the two must be observed. A row that has gone\n // takes its key material with it, so there is nothing left to read under any generation.\n if (record === null || record.status === 'destroyed') return null;\n return this.openForTarget(ref, {\n generation,\n lineage: record.token,\n wrappedDeks: record.wrappedDeks,\n });\n }\n\n /**\n * Read one chunk of a specific generation \u2014 the pinned read path. `held`, when given, is what a pin holds of the\n * object, and the read fails if the generation is now another object.\n */\n async getChunkAt(\n ref: ChunkRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<Uint8Array | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n if (reader === null) return null;\n const fingerprint = held?.fingerprint;\n if (\n held !== undefined &&\n fingerprint !== undefined &&\n this.replacedPins.get(this.heldKey(ref, held.version, fingerprint)) !== undefined\n )\n throw notThePinned(ref, generation);\n try {\n return await reader.getChunk(ref.chunkKey);\n } catch (err) {\n // A pin's memoised reader outlives its object when the name is purged and loaded again: its index then points\n // into bytes that are not its own, so the chunk fails its checksum or, when the new object is smaller, asks\n // for a range past its end, which every driver refuses with a ValidationError. The object's footer says which\n // it was: replaced, which is a pin's NotFoundError, or not, and the error stands. Only these two errors pay\n // for the footer read. Told apart by their brands, not `instanceof`: the range error is a driver package's,\n // which may carry a copy of core of its own.\n if (held === undefined || fingerprint === undefined) throw err;\n if (!(isIntegrityError(err) || isValidationError(err))) throw err;\n await this.throwIfReplaced(ref, generation, held.version, fingerprint);\n throw err;\n }\n }\n\n /**\n * Throws the pin's NotFoundError if the object under `generation`'s key is no longer the one the pin holds. Its\n * footer says, read with no key and no row ({@link CrbmReader.sameObject}), so an object written under a key this\n * store lacks answers the same way, and so does one gone from its key. A replacement is kept\n * ({@link replacedPins}), and a check under way is shared by the reads that ask at once; neither \"gone\" nor \"the\n * same\" is kept, since either can change. A transient fault is rethrown, for the store's retries to see for what it\n * is. A footer that cannot be read otherwise says nothing about which object is there, so this returns, and the\n * caller's own error stands.\n */\n private async throwIfReplaced(\n ref: SegmentRef,\n generation: number,\n version: string,\n fingerprint: string,\n ): Promise<void> {\n const held = this.heldKey(ref, version, fingerprint);\n if (this.replacedPins.get(held) !== undefined) throw notThePinned(ref, generation);\n let check = this.checking.get(held);\n if (check === undefined) {\n const at: GenKey = { namespace: ref.namespace, segment: ref.segment, generation };\n // Whether this check is still the one asked for as it ends, which it then stops being. One forgotten before it\n // ends (see checking) keeps nothing it finds, and leaves in place the one asked for since.\n const ended = (): boolean =>\n this.checking.get(held) === started && this.checking.delete(held);\n // True when the object under the key is not the pin's: another one, or none.\n const started: Promise<boolean> = CrbmReader.sameObject(\n storageBlobReader(this.driver, at),\n fingerprint,\n ).then(\n (same) => {\n if (ended() && !same) this.replacedPins.set(held, true);\n return !same;\n },\n (err: unknown) => {\n ended();\n if (isNotFoundError(err)) return true;\n throw err;\n },\n );\n this.checking.set(held, started);\n check = started;\n }\n let replaced: boolean;\n try {\n replaced = await check;\n } catch (err) {\n if (isTransientError(err)) throw err;\n return;\n }\n if (replaced) throw notThePinned(ref, generation);\n }\n\n /** Chunk keys of a specific generation \u2014 the pinned shape read. `held` as for {@link getChunkAt}. */\n async listChunkKeysAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<number[]> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? [] : reader.chunkKeys();\n }\n\n /**\n * Per-chunk cardinalities of a specific generation \u2014 powers a pinned `count()` with no payload reads. `held` as\n * for {@link getChunkAt}.\n */\n async cardinalitiesAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<ReadonlyMap<number, number> | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? null : reader.cardinalities();\n }\n\n /** Grounded size of a specific generation. `held` as for {@link getChunkAt}. */\n async sizeOfAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<SegmentSize | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? null : { sizeBytes: reader.sizeBytes };\n }\n\n /**\n * Whether the segment is a registered name at all. With a registry that is exactly \"a row exists\" \u2014 including\n * a row with no generation yet (minted by `setRetention` before the first load) and a `destroyed` tombstone,\n * both of which are names somebody deliberately created. Without a registry there is no registration to\n * consult, so the honest answer is whether the bucket holds any generation of it.\n *\n * Deliberately NOT served from the snapshot memo: the memo resolves to `null` for both states this exists to\n * tell apart.\n */\n async exists(ref: SegmentRef): Promise<boolean> {\n if (this.registry !== undefined) return (await this.registry.get(ref)) !== null;\n for await (const key of this.driver.list(ref)) {\n void key;\n return true;\n }\n return false;\n }\n\n /**\n * Evict a snapshot we just failed to read from \u2014 matched by its reader *promise*, so a concurrent call's\n * fresher snapshot is never clobbered. The identity guard is the whole point: `install` replaces the entry\n * wholesale, so comparing anything else would drop a good snapshot on the floor.\n */\n private dropStale(key: string, pending: Promise<CrbmReader | null>): void {\n const cur = this.snapshots.get(key);\n if (cur !== undefined && cur.reader === pending) this.snapshots.delete(key);\n }\n\n private async resolveLatest(ref: SegmentRef): Promise<CrbmReader | null> {\n const target = await this.resolveTarget(ref);\n if (target === null) return null;\n return this.openForTarget(ref, target);\n }\n\n /** Open a {@link CrbmReader} for an already-resolved generation target (decrypting if the segment is encrypted). */\n private async openForTarget(ref: SegmentRef, target: Target): Promise<CrbmReader> {\n const genKey: GenKey = {\n namespace: ref.namespace,\n segment: ref.segment,\n generation: target.generation,\n };\n const crypto = await this.cryptoForRead(ref, target.generation, target.wrappedDeks);\n return openChecked(this.driver, genKey, {\n ...this.readerOptions,\n crypto,\n lineage: target.lineage,\n });\n }\n\n /**\n * The current generation + its DEK wrappings: the registry's authoritative record (one strong read), or a\n * `list` scan for the max generation when there's no registry (a registry-less source can only read cleartext\n * \u2014 there's nowhere a wrapped DEK could live).\n */\n private async resolveTarget(ref: SegmentRef): Promise<Target | null> {\n if (this.registry !== undefined) {\n const record = await this.registry.get(ref);\n if (record === null) return null;\n // A crypto-shredded segment reads as empty \u2014 its DEK is gone, so its Storage bytes are unrecoverable.\n if (record.status === 'destroyed') return null;\n // A row with no Storage generation yet (minted by `setSegmentRetention` ahead of the first load, so admin tools\n // can see the segment) resolves exactly like a segment with NO row: every read answers empty. Returning\n // `null` here rather than a generation is the whole reason such a row is safe to create \u2014 the alternative,\n // pointing at a generation that does not exist, is the `missing-storage-generation` state.\n if (record.currentGen === null) return null;\n return {\n generation: record.currentGen,\n lineage: record.token,\n wrappedDeks: record.wrappedDeks,\n };\n }\n let maxGen = -1;\n for await (const key of this.driver.list(ref)) {\n if (key.generation > maxGen) maxGen = key.generation;\n }\n return maxGen < 0 ? null : { generation: maxGen };\n }\n\n /** Build the per-generation decryption context for an encrypted segment (undefined for cleartext). */\n private async cryptoForRead(\n ref: SegmentRef,\n generation: number,\n wrappedDeks: readonly WrappedDek[] | undefined,\n ): Promise<CrbmCrypto | undefined> {\n if (wrappedDeks === undefined || wrappedDeks.length === 0) {\n if (this.requireEncryption) {\n throw new KeyUnavailableError(\n `requireEncryption: segment \"${ref.segment}\" is cleartext but encryption is required`,\n );\n }\n return undefined; // cleartext segment\n }\n if (this.keystore === undefined) {\n throw new KeyUnavailableError(\n `segment \"${ref.segment}\" is encrypted but this CrbmStorageChunkSource has no keystore`,\n );\n }\n const aead = await this.keystore.openDek(wrappedDeks);\n return { aead, aadFor: (scope) => aadFor(ref, generation, scope) };\n }\n\n async getChunk(ref: ChunkRef): Promise<Uint8Array | null> {\n validateChunkRef(ref);\n return this.withFreshSnapshot(ref, (reader) => reader.getChunk(ref.chunkKey), null);\n }\n\n async listChunkKeys(ref: SegmentRef): Promise<number[]> {\n validateUserRef(ref);\n return this.withFreshSnapshot(ref, (reader) => reader.chunkKeys(), []);\n }\n\n async sizeOf(ref: SegmentRef): Promise<SegmentSize | null> {\n validateUserRef(ref);\n return this.withFreshSnapshot<SegmentSize | null>(\n ref,\n (reader) => ({ sizeBytes: reader.sizeBytes }),\n null,\n );\n }\n\n async cardinalities(ref: SegmentRef): Promise<ReadonlyMap<number, number> | null> {\n validateUserRef(ref);\n return this.withFreshSnapshot<ReadonlyMap<number, number> | null>(\n ref,\n (reader) => reader.cardinalities(),\n null,\n );\n }\n\n /**\n * Run `read` against the pinned snapshot, healing the torn-read window generation GC can open: if the\n * generation we resolved was superseded *and* swept (the grace window elapsed), the Storage driver throws\n * {@link NotFoundError}. Surfacing that as a query failure would turn a benign, recoverable race into a\n * caller-visible error, so instead we drop the stale snapshot,\n * re-resolve `currentGen`, and retry once \u2014 the read then serves the newer (committed, immutable) generation,\n * a monotonic move forward \u2014 within one incarnation of the row. A name that was retired and re-created is a\n * different segment and can resolve to a LOWER generation, which is why the snapshot carries the row's token\n * as its lineage rather than trusting the generation number (invariant 1).\n *\n * **Resolution and open are inside the retry, not before it.** Resolving `currentGen` and opening that\n * generation's object are two backend round trips with a gap between them, so the object can be swept after\n * the registry named it and before its tail is read \u2014 and that miss surfaces from awaiting the snapshot, not\n * from `read`. Awaiting outside the retry left the *first* attempt unhealed on the one path GC actually\n * races: `keep: 0`, which every id erasure passes, sweeps microseconds after the publish.\n *\n * A *second* NotFound is pathological (GC outrunning resolution) and propagates rather than fabricating an\n * absent answer \u2014 never return a wrong result. `ifGone` is returned when the segment has no committed\n * generation to serve: storage is empty, or the row was dropped or crypto-shredded, in which case reading empty\n * is the documented outcome rather than a failure.\n *\n * **Bounded to exactly two resolve-and-open round trips.** That is a cost contract, not a detail: the retry\n * now re-reads the *registry*, so an unbounded one would hammer the shared, throttle-prone resource in a\n * tight loop on a segment whose pointer names an object that is permanently absent \u2014 and an N-way\n * `intersect` would do it N times. Gated in `tests/core/storage-source-heal-open.test.ts` by counting calls.\n */\n private async withFreshSnapshot<T>(\n ref: SegmentRef,\n read: (reader: CrbmReader) => T | Promise<T>,\n ifGone: T,\n ): Promise<T> {\n for (let attempt = 0; attempt < 2; attempt++) {\n const pending = this.resolvedReader(ref);\n try {\n const reader = await pending;\n if (reader === null) return ifGone;\n return await read(reader);\n } catch (err) {\n // Only a vanished pinned generation is recoverable here, and only on the first try; anything else\n // (corruption, a real second miss) propagates / falls through.\n if (!isNotFoundError(err) || attempt === 1) throw err;\n this.dropStale(segmentKey(ref), pending); // lazily: the happy path never needs the key\n }\n }\n return ifGone;\n }\n}\n\n/**\n * Write one immutable generation from in-memory bitmaps. Chunks are\n * sorted ascending and empty bitmaps skipped (empty chunks are never stored). Returns the driver's\n * `{ size, sha256 }` for the written object. Pass `options.crypto` to AES-256-GCM-encrypt the generation\n * (built from the segment's DEK, with associated data bound to `(segment, generation)`).\n */\nexport function writeCrbmGeneration(\n driver: IStorageDriver,\n key: GenKey,\n chunks: Iterable<{ chunkKey: number; bitmap: CodecBitmap }>,\n options: { crypto?: CrbmCrypto; clock?: Yielder } = {},\n): Promise<{ size: number; sha256: string }> {\n const sorted = [...chunks].sort((a, b) => a.chunkKey - b.chunkKey);\n // The single longest blocking stretch in a bulk load: serialize + CRC32C + frame, once per chunk, ~62,000\n // times. `await writer.addChunk(...)` looks like it yields and does not \u2014 the sink buffers in memory, so the\n // promise is already resolved and awaiting it is a microtask. See {@link yieldEvery}.\n const tick = yieldEvery(options.clock);\n return driver.putImmutable(key, async (sink) => {\n const writer = new CrbmWriter(sink, { generation: key.generation, crypto: options.crypto });\n for (const { chunkKey, bitmap } of sorted) {\n if (bitmap.isEmpty) continue;\n // Run-encode before serializing. Storage generations are immutable and read many times, so the one-off cost\n // here buys every later read a smaller fetch \u2014 see CodecBitmap.optimize for the measured factors.\n bitmap.optimize?.();\n await writer.addChunk(chunkKey, bitmap.serialize(), bitmap.size);\n const pause = tick();\n if (pause !== null) await pause;\n }\n await writer.finish();\n });\n}\n\n/** What {@link writeCrbmGenerationStream} wrote: the driver's `{ size, sha256 }` + a tally of the generation. */\nexport interface StreamWriteResult {\n readonly size: number;\n readonly sha256: string;\n /** Non-empty chunk keys written, in ascending order (for the post-write verify, since the stream is consumed). */\n readonly chunkKeys: number[];\n /** Total ids written. */\n readonly cardinality: number;\n}\n\n/**\n * Streaming variant of {@link writeCrbmGeneration} for a **constant-memory** rewrite (the erasure rewrite, a\n * sorted load): consumes an **already-ascending** async stream of `{ chunkKey, bitmap }`, feeding each to the\n * codec and freeing it, instead of materializing the whole generation. Paired with a streaming storage sink (S3\n * multipart / LocalFs temp file), peak memory is ~one chunk + one part. Returns a tally so the caller can verify\n * the re-opened object without re-iterating the (now-consumed) stream. Input **must** be ascending by `chunkKey`\n * \u2014 the codec rejects an out-of-order chunk; empty bitmaps are skipped.\n */\nexport async function writeCrbmGenerationStream(\n driver: IStorageDriver,\n key: GenKey,\n chunks: AsyncIterable<{ chunkKey: number; bitmap: CodecBitmap }>,\n options: { crypto?: CrbmCrypto; clock?: Yielder } = {},\n): Promise<StreamWriteResult> {\n const chunkKeys: number[] = [];\n let cardinality = 0;\n // A rewrite runs this over a whole segment. The `for await` is not itself a yield \u2014 a stream backed by\n // already-resident chunks resolves on a microtask \u2014 so it needs the same periodic macrotask as the\n // non-streaming writer above.\n const tick = yieldEvery(options.clock);\n const { size, sha256 } = await driver.putImmutable(key, async (sink) => {\n const writer = new CrbmWriter(sink, { generation: key.generation, crypto: options.crypto });\n for await (const { chunkKey, bitmap } of chunks) {\n if (bitmap.isEmpty) continue;\n bitmap.optimize?.(); // same storage-write rationale as the non-streaming writer above\n await writer.addChunk(chunkKey, bitmap.serialize(), bitmap.size);\n chunkKeys.push(chunkKey);\n cardinality += bitmap.size;\n const pause = tick();\n if (pause !== null) await pause;\n }\n await writer.finish();\n });\n return { size, sha256, chunkKeys, cardinality };\n}\n\n/**\n * Point a segment's registry `currentGen` at `key.generation` \u2014 the publish step that makes a freshly-written\n * generation the authoritative latest (so registry-aware readers see it). **Forward-only and idempotent:**\n * if the registry has no row it creates one; if it's already at/ahead of `key.generation` it's a no-op (an\n * out-of-order/duplicate publish never regresses the pointer); otherwise it advances via compare-and-swap,\n * retrying a few times under contention. Separated from the Storage write so callers can publish atomically\n * after the immutable object is durable (write-then-publish).\n *\n * **Throws** rather than returning, in two cases, because each would otherwise create a state no reader can\n * recover from: a `destroyed` (crypto-shredded) row, where advancing the pointer would name an object encrypted\n * under a shredded key; and an advance carrying **new** `wrappedDeks`, which would either strand that key or make\n * the row advertise encryption over cleartext generations. Contention that never clears throws\n * {@link WriteConflictError} after the retries.\n *\n * **`expectFrom` turns forward-only into read-modify-write.** Forward-only is the right rule for a writer whose\n * content does not depend on what was current \u2014 a load computes its ids upstream, so publishing over a newer\n * generation loses nothing the loader knew about. It is the WRONG rule for a writer that *derived* its content\n * from a particular generation: the erasure rewrite streams generation `from` and clears one bit, so publishing\n * it over a newer generation silently discards whatever that generation added. Passing `expectFrom` makes the\n * publish land only while the pointer is still exactly there, and return `false` otherwise, so the caller can\n * report `superseded` and re-derive. The compare-and-swap itself carries the token read in the same iteration,\n * so the check and the write see the same row.\n *\n * **`expectToken` fences the LINEAGE, and a derived writer needs both.** A generation number identifies a\n * generation only within one incarnation of a name: `nextGeneration` restarts at `0` once the row is purged and\n * the bucket empty, so a name that is retired and re-created has a *different* segment wearing the *same*\n * `currentGen`. `expectFrom` alone matched it \u2014 and an erasure rewrite then published one incarnation's content\n * over another's, deleted the live objects with its `keep: 0` collection, and returned `erased: true`. The row's\n * OCC token is the identity that survives this: {@link IRegistryDriver.delete} tombstones rather than unlinks,\n * so \"a later `create` still gets a fresh, greater token\" and no token is ever reused across incarnations.\n *\n * Pass the token read alongside `expectFrom` and the publish lands only on the same row it was derived from.\n * The check is deliberately **conservative**: the token also changes on writes that are not supersessions at\n * all (a `setRetention`, a due-index reindex), so such a write makes a derived publish report `superseded` and\n * the caller re-derive. That costs a re-run on a rare, unrelated write; the alternative costs a segment.\n */\nexport async function publishGeneration(\n registry: IRegistryDriver,\n key: GenKey,\n options: {\n wrappedDeks?: readonly WrappedDek[];\n expectFrom?: number;\n expectToken?: Token;\n /**\n * Publish only while the segment still has **no registry row**.\n *\n * The fence for a caller whose decision rests on the row being ABSENT. `expectFrom` and `expectToken`\n * cannot express it: both compare against a value read from a row, so when there was no row there is\n * nothing to compare and both are simply omitted \u2014 leaving the publish a bare forward-only advance that\n * happily lands over whatever appeared in the meantime.\n *\n * That gap was a silent wipe, not a theoretical one. A guarded load into a segment that did not exist yet\n * read \"no row\", so its empty/`minRetained` bounds had nothing to judge and passed vacuously; a\n * concurrent writer then created the row and published a thousand ids; and the guarded load published an\n * EMPTY generation over them, reporting `published: true` with no reason. Reproduced through both\n * `loadSegment` and the `*Into` verbs, which hit it far more often because materialising into a\n * destination that does not exist yet is the ordinary first run of a pipeline.\n */\n expectAbsent?: boolean;\n } = {},\n): Promise<boolean> {\n for (let attempt = 0; attempt < 5; attempt++) {\n const record = await registry.get(key);\n try {\n if (options.expectFrom !== undefined && record?.currentGen !== options.expectFrom) {\n // The pointer is no longer where the caller derived its content from \u2014 including the cases where the row\n // has vanished or has no generation at all. Not an error: the caller re-reads and re-derives.\n return false;\n }\n if (options.expectAbsent === true && record !== null) {\n // A row appeared since the caller looked. Whatever it decided from \"this segment does not exist\" no\n // longer holds \u2014 most importantly \"there is nothing here to overwrite\".\n return false;\n }\n if (options.expectToken !== undefined && record?.token !== options.expectToken) {\n // Same pointer VALUE, different row. Either the row was written since (harmless, and we re-derive\n // anyway) or the name was retired and re-created, in which case `currentGen` matching means nothing:\n // it is a different segment that restarted its generation counter at the same number.\n return false;\n }\n if (record === null) {\n // First publish for the segment \u2014 carry the wrapped DEK(s) so encrypted reads can resolve the key.\n await registry.create(key, {\n currentGen: key.generation,\n wrappedDeks: options.wrappedDeks,\n });\n } else if (record.status === 'destroyed') {\n // Closes the window between a writer's own destroyed-check and its publish.\n //\n // `bulkLoadCrbmGeneration` reads the registry record once, refuses if it is already destroyed, and then\n // spends a KMS call plus a whole object write before getting here \u2014 seconds to minutes on a large load.\n // A check of `currentGen` alone cannot see a `destroySegment` landing inside that window: it would\n // advance the pointer on a destroyed record, leaving an object encrypted with the DEK that destroy had\n // just shredded \u2014 permanently unreadable, still paid for, and attached to a segment the registry says\n // was erased.\n //\n // Checking here rather than at the caller is what makes it a fence instead of a second guess: this\n // record was re-read moments ago inside the retry loop, so the check and the CAS that follows it see the\n // same state. This is the publish step's half of coupling the write path to destruction.\n throw new ValidationError(\n `segment \"${key.segment}\" was destroyed (crypto-shredded) while generation ${key.generation} was ` +\n `being written \u2014 refusing to publish it; the written object is unreadable. Use a new segment.`,\n );\n } else if (record.currentGen === null) {\n // The row exists with no Storage generation yet (a retention policy recorded ahead of the first load). There\n // is no pointer to regress past, so this publish advances it \u2014 and carries the wrapped\n // DEK(s) exactly like a first publish onto no row, since no generation is encrypted under the row's\n // current wrappings (there is no generation at all).\n //\n // `wrappedDeks` is spread in ONLY when there is something to store. A registry patch clears an optional\n // field by *mentioning* it (`'wrappedDeks' in patch`), so passing `wrappedDeks: undefined` unconditionally\n // would wipe key material off the row whenever a cleartext generation is published onto it \u2014 a divergence\n // from the branch below, which never touches the field.\n await registry.compareAndSwap(key, record.token, {\n currentGen: key.generation,\n ...(options.wrappedDeks === undefined ? {} : { wrappedDeks: options.wrappedDeks }),\n });\n } else if (record.currentGen > key.generation) {\n return false; // a newer generation is already current \u2014 forward-only, never regress\n } else if (record.currentGen === key.generation) {\n return true; // already exactly current (an idempotent re-publish) \u2014 nothing to advance\n } else {\n // Advancing over an existing generation. `wrappedDeks` is deliberately NOT carried here, and a caller\n // that supplies it is refused rather than served.\n //\n // Reaching this branch with a freshly minted DEK means an encrypted generation is being published onto a\n // lineage whose current generation the row already describes \u2014 i.e. onto a segment whose existing\n // generations are cleartext. Both ways of resolving that silently are damaging: dropping the wrapping\n // advances the pointer to an object encrypted under a key that exists\n // nowhere, so the data is unrecoverable the moment the call returns; carrying it makes the row advertise\n // encryption over a lineage that still holds readable cleartext objects, which is what makes\n // `destroySegment` emit `segment.erase` \u2014 \"unreadable everywhere, backups included\" \u2014 over plaintext.\n // Two entry points reach it with no race: a re-load with a keystore newly wired, and an `*Into` on a\n // keystore-wired store whose destination is cleartext. So the state is refused before the pointer moves;\n // the object stays an orphan.\n if (options.wrappedDeks !== undefined && options.wrappedDeks.length > 0) {\n throw new ValidationError(\n `publishGeneration: refusing to publish generation ${key.generation} of \"${key.segment}\" with new ` +\n `key material onto a segment that already has generation ${record.currentGen} \u2014 its existing ` +\n `generations are not encrypted under this key. Encrypt a new segment and load into that instead.`,\n );\n }\n await registry.compareAndSwap(key, record.token, { currentGen: key.generation });\n }\n return true; // created or advanced the pointer to key.generation \u2192 it is now current\n } catch (err) {\n if (isWriteConflictError(err)) continue; // lost the race \u2014 re-read and retry\n throw err;\n }\n }\n // Exhausted: only possible if competing writers kept advancing currentGen \u2014 which likely already satisfies\n // the forward-only goal. Confirm before reporting failure, so a lost-every-race-but-already-current case\n // isn't a spurious error. Whether *this* generation is the current one decides the returned flag.\n const final = await registry.get(key);\n // `currentGen === null` after exhausting the retries is a genuine failure, not an already-current case: nothing\n // is published, so it falls through to the conflict below rather than being read as \"a newer gen won\".\n if (final !== null && final.currentGen !== null && final.currentGen >= key.generation)\n return final.currentGen === key.generation;\n throw new WriteConflictError(\n `publishGeneration: contention setting currentGen for ${key.segment}`,\n );\n}\n\n/** What a bulk-load wrote \u2014 the driver's `{ size, sha256 }` plus a summary of the built generation. */\nexport interface BulkLoadResult {\n /** Bytes written to the Storage object. */\n readonly size: number;\n /** The driver's content hash of the object. */\n readonly sha256: string;\n /** Distinct non-empty chunks written (\u2264 65536). */\n readonly chunkCount: number;\n /**\n * The DEK freshly minted for this generation, wrapped \u2014 present only when the segment is encrypted AND this\n * call minted one (an existing segment reuses its key, which is already on the row).\n *\n * Returned so a caller that deliberately does **not** pass `registry` here \u2014 writing now and publishing later,\n * which is how a load applies a guard while the old generation is still authoritative \u2014 can carry the key onto\n * its own `publishGeneration`. Without it that publish stores no DEK and the generation it makes current is\n * unreadable: the bytes are encrypted with a key nothing recorded.\n */\n readonly wrappedDeks?: readonly WrappedDek[];\n /** Total distinct ids in the generation (post-dedup). */\n readonly cardinality: number;\n /**\n * Whether this generation is now the segment's **current** one \u2014 `undefined` when no `registry` was wired, so\n * there is no pointer and no publish step to report on.\n *\n * `false` means the object is durable but a **concurrent writer published a higher generation first**, so this\n * one is an orphan that no reader will ever resolve. The write succeeded and the *load* did not: whatever this\n * call was asked to make the segment contain, the segment does not contain. That is not a detail a caller can\n * be left to infer \u2014 a `*Into` materialisation that reported the generation it wrote as \"the destination's new\n * current generation\" on this path would state a plain untruth \u2014 so the flag is on the result and the verbs that\n * promise a published generation check it.\n */\n readonly becameCurrent?: boolean;\n}\n\n/**\n * **Bulk-load** a whole immutable generation from a flat id source \u2014 the batch \"seed/rebuild\" entry point.\n * Unlike {@link writeCrbmGeneration} (which takes pre-grouped bitmaps), this\n * consumes an arbitrary, **unsorted** stream of u32 ids (sync or async) and routes each into its chunk's\n * Roaring bitmap as it goes. The input is consumed lazily and ids dedup on insert, so memory is bounded by\n * the **built generation** \u2014 the in-memory Roaring representation of the *distinct* set (\u2264 one bitmap per\n * non-empty chunk) \u2014 not by the input length: you can stream a billion duplicate-heavy ids and hold only the\n * distinct result. Note this is `O(distinct set)`, **not** window-bounded like {@link SegmentEngine.intersect}:\n * bulk-load holds the whole generation in RAM, which suits batch seed/rebuild jobs but not an\n * unbounded-cardinality stream (a segment larger than RAM needs external-merge / pre-sorted input \u2014 later phase).\n *\n * Each id must be an integer in `[0, 2\u00B3\u00B2)` ({@link splitId} throws {@link ValidationError} otherwise) \u2014 the\n * source is consumed lazily, so a bad id aborts mid-stream without writing a partial object (the driver\n * commits only after the callback resolves). An empty source writes a valid empty generation.\n *\n * Writes a fresh full snapshot of a segment. The caller picks the generation number in `key` \u2014 `nextGeneration`\n * computes the right one from the registry and the bucket \u2014 and re-using an existing generation throws\n * {@link WriteConflictError} (write-once). Without a registry a `StorageChunkSource` serves the **highest**\n * generation present, so a too-high number silently shadows real data; with one, `publishGeneration` decides.\n */\nexport async function bulkLoadCrbmGeneration(\n driver: IStorageDriver,\n key: GenKey,\n ids: Iterable<number> | AsyncIterable<number>,\n options: {\n registry?: IRegistryDriver;\n keystore?: IKeystore;\n requireEncryption?: boolean;\n audit?: IAuditSink;\n /** Bitmap codec. Optional in the type; a **flavor** package binds it ({@link requireCodec}). */\n codec?: CodecInterface;\n /**\n * Injected clock. Supplying one makes bulk-load **cooperative**: it yields the event loop periodically so a\n * long load does not stall everything else on the process. Without it the load still completes, just\n * without yielding \u2014 which is the pre-existing behaviour, kept so this is purely additive.\n *\n * `@cloudbitmaps/roaring` supplies a real clock by default, so flavor users get cooperative behaviour with\n * no wiring. `core/` cannot default it: it is timer-free by lint, which is exactly why waiting goes through\n * this seam rather than `setTimeout`.\n */\n clock?: Clock;\n /**\n * Write the object but do **not** advance the pointer (default: publish when a `registry` is wired).\n *\n * For a caller that has to inspect what it wrote before deciding whether it should become current \u2014 a\n * guarded {@link loadSegment} is the one in-tree case \u2014 because the check is only meaningful while the old\n * generation is still authoritative. The `registry` is still required for an encrypted segment and still\n * consulted: it is where an existing segment's DEK lives, and reusing that key is not optional. The wrapped\n * DEK comes back on the result so the deferred publish can store it.\n *\n * A caller that defers the publish owns what it wrote: an unpublished object sits ABOVE `currentGen`, where\n * generation collection deliberately never looks, so nothing reclaims it until a later generation above it is\n * current.\n */\n publish?: boolean;\n } = {},\n): Promise<BulkLoadResult> {\n if (options.keystore === undefined && options.requireEncryption === true) {\n throw new ValidationError('requireEncryption: a load needs a keystore to write encrypted');\n }\n const codec = requireCodec(options.codec, 'bulkLoadCrbmGeneration');\n const byChunk = new Map<number, CodecBitmap>();\n // Batched per chunk, not one native `add()` per id.\n //\n // The obvious loop \u2014 `bitmap.add(remainder)` for every id \u2014 crosses the JS\u2194native boundary once per id, and\n // measured **1,679 ms for 1M ids** with no yield point anywhere in it. Since this is a synchronous stretch on\n // Node's only thread, a caller who wires it to a request handler stalls every other request on that instance\n // for over a second (measured separately: a 0.7 ms health check took 275 ms).\n //\n // Buffering the remainders and inserting them per chunk in one `fromValues`/`addMany` call amortises that\n // boundary crossing across the whole batch.\n //\n // WHY THE BUFFER IS CAPPED. Bucketing *everything* first and inserting once per chunk at the end is faster\n // still, but it holds every remainder as a JS number before any bitmap compression happens \u2014 and with up to\n // 65,536 chunks in play that is unbounded in exactly the way this library refuses to be. So the buffer is\n // flushed whenever the total pending count crosses `BULK_FLUSH_IDS`, bounding the extra memory regardless of\n // input size or key distribution while still getting the batching win. That bound is **~28 MB measured** at\n // 1M staged ids across ~65,000 chunks \u2014 not the ~8 MB a naive 8-bytes-per-number estimate gives, because the\n // cost is dominated by per-array and Map overhead across tens of thousands of small arrays.\n const pendingByChunk = new Map<number, number[]>();\n let pending = 0;\n // Yield periodically, NOT per chunk \u2014 see {@link yieldEvery} for why per-unit async is a 7x regression here.\n const tickChunk = yieldEvery(options.clock);\n const tickId = yieldEvery(options.clock, YIELD_EVERY_IDS);\n // Yield every N chunks, NOT per chunk. Measured: handing each chunk's insert to the threadpool\n // (`fromArrayAsync`) costs ~9 \u00B5s of dispatch against ~1.5 \u00B5s of actual work once ids are spread across\n // ~61,000 chunks \u2014 636 ms versus 92 ms, a 7x regression that would have undone the per-chunk batching this\n // function already does. Keeping the inserts synchronous and interrupting them periodically gets the\n // starvation fix without the cost \u2014 measured on the per-chunk insert microbenchmark: 88 ms wall against a\n // 92 ms unyielded baseline. The whole-load end-to-end figures are a DIFFERENT experiment and live in\n // `cooperative.ts`. Quoting this 92 ms baseline beside that experiment's 450 ms stall would describe a 92 ms\n // operation with 450 ms of starvation inside it, which is impossible on its face.\n //\n // The yield must be a REAL macrotask. `await Promise.resolve()` is a microtask and never lets I/O run, which\n // is the trap that makes naive \"just await something\" fixes measure as no change at all.\n const flushPending = async (): Promise<void> => {\n for (const [chunkKey, rems] of pendingByChunk) {\n if (rems.length === 0) continue;\n const existingBitmap = byChunk.get(chunkKey);\n if (existingBitmap === undefined) byChunk.set(chunkKey, codec.fromValues(rems));\n else existingBitmap.addMany(rems);\n rems.length = 0;\n const pause = tickChunk();\n if (pause !== null) await pause;\n }\n pending = 0;\n };\n /** Bucket one id. Returns true when the pending buffer is full and must be flushed. */\n const ingest = (id: number): boolean => {\n const { chunkKey, remainder } = splitId(id); // validates the u32 range\n let bucket = pendingByChunk.get(chunkKey);\n if (bucket === undefined) {\n bucket = [];\n pendingByChunk.set(chunkKey, bucket);\n }\n bucket.push(remainder);\n return ++pending >= BULK_FLUSH_IDS;\n };\n // The two ingest loops are deliberately NOT collapsed into one `for await` over a normalising wrapper.\n //\n // That is the tidier code and it was measured at **20x** the cost: routing a sync source through an async\n // generator forces a microtask per id, and over 1M ids that is 224 ms against 11 ms for a plain `for..of` \u2014\n // 55% of a whole bulk load spent on iteration protocol rather than on work. Since an array, a Set and a\n // generator are what callers actually hand this function most of the time, the sync path is the common one.\n //\n // Note that `for await` yields nothing to the event loop either way: a microtask per id still drains before\n // the loop turns a phase. Both paths therefore need the same explicit yield.\n if (Symbol.asyncIterator in ids) {\n for await (const id of ids as AsyncIterable<number>) {\n if (ingest(id)) await flushPending();\n const pause = tickId();\n if (pause !== null) await pause;\n }\n } else {\n for (const id of ids as Iterable<number>) {\n if (ingest(id)) await flushPending();\n const pause = tickId();\n if (pause !== null) await pause;\n }\n }\n await flushPending();\n\n let cardinality = 0;\n const chunks: Array<{ chunkKey: number; bitmap: CodecBitmap }> = [];\n for (const [chunkKey, bitmap] of byChunk) {\n // Every chunk in the map received at least one id, so no bitmap here is empty.\n cardinality += bitmap.size; // a native call per chunk \u2014 5 ms across 62,000 of them, so it yields too\n chunks.push({ chunkKey, bitmap });\n const pause = tickChunk();\n if (pause !== null) await pause;\n }\n\n if (options.keystore !== undefined && options.registry === undefined) {\n throw new ValidationError('an encrypted load requires a registry to store the wrapped DEK');\n }\n // Read the segment's record once (when a registry is wired): to refuse writing to a crypto-shredded segment\n // (which would create unreadable/unreachable bytes), and to reuse its DEK if it's encrypted.\n const existing = options.registry !== undefined ? await options.registry.get(key) : null;\n if (existing?.status === 'destroyed') {\n throw new ValidationError(\n `segment \"${key.segment}\" is destroyed (crypto-shredded) \u2014 refusing to write; use a new segment`,\n );\n }\n\n // An encrypted row with no keystore is a lost key, not a cleartext segment \u2014 the same fail-fast the erasure\n // rewrite does. Without it this wrote a CLEARTEXT generation onto a row that still advertises `wrappedDeks`, and the\n // damage is not just a confusing state: `destroySegment` keys `cryptoShredded` off the *presence* of wrappings,\n // so shredding that segment emits `segment.erase` \u2014 the audit event defined as \"these bytes are unreadable\n // everywhere, backups included\" \u2014 over bytes that are plaintext and stay readable from any copy. An audit trail\n // that over-attests is the one failure it exists to prevent, so this refuses to create the state.\n if (\n existing?.wrappedDeks !== undefined &&\n existing.wrappedDeks.length > 0 &&\n options.keystore === undefined\n ) {\n throw new KeyUnavailableError(\n `segment \"${key.segment}\" is encrypted but this load has no keystore \u2014 refusing to write a cleartext ` +\n `generation onto an encrypted segment. Pass the keystore holding its DEK.`,\n );\n }\n\n // Encryption (opt-in): reuse the segment's existing DEK, or mint a fresh one on its FIRST generation.\n //\n // The segment's own posture decides, not the presence of a keystore. A keystore is wired on the store, so it is\n // in scope for every segment that store touches \u2014 including ones deliberately left cleartext \u2014 and minting on\n // that basis alone is what made \"load it again with a keystore wired\" destroy the data: the minted wrapping has\n // nowhere to live on a row whose pointer is already set (see `publishGeneration`'s advance branch), so the\n // generation ends up encrypted under a key that exists in no persistent store. `requireEncryption` is the way\n // to *demand* encryption, and on an already-cleartext lineage it fails fast rather than silently downgrading.\n let crypto: CrbmCrypto | undefined;\n let newWrapped: readonly WrappedDek[] | undefined;\n if (options.keystore !== undefined) {\n if (existing?.wrappedDeks !== undefined && existing.wrappedDeks.length > 0) {\n const aead = await options.keystore.openDek(existing.wrappedDeks); // reuse the segment's DEK\n crypto = { aead, aadFor: (scope) => aadFor(key, key.generation, scope) };\n } else if (existing !== null && existing.currentGen !== null) {\n // An existing lineage with no key material on the row: the segment is cleartext, and one segment cannot be\n // half-encrypted \u2014 a pin of a superseded generation, once its reader is reopened, would find bytes its key\n // cannot open, and a later `destroySegment` would attest that shredding one DEK made every copy unreadable\n // while the older cleartext objects stay readable from any of them.\n if (options.requireEncryption === true) {\n throw new ValidationError(\n `requireEncryption: segment \"${key.segment}\" already has generation ${existing.currentGen} in ` +\n `cleartext, so this load cannot be encrypted \u2014 encryption is chosen when a segment is first ` +\n `loaded. Load into a new segment with the keystore wired, then drop this one.`,\n );\n }\n // Otherwise the segment stays what it is: cleartext.\n } else {\n const minted = await options.keystore.createDek();\n newWrapped = minted.wrapped;\n crypto = { aead: minted.aead, aadFor: (scope) => aadFor(key, key.generation, scope) };\n }\n }\n\n const { size, sha256 } = await writeCrbmGeneration(driver, key, chunks, {\n crypto,\n clock: options.clock,\n });\n // Publish only after the immutable object is durable (write-then-publish): a registry-aware reader should\n // never point at a generation that isn't fully written. A freshly minted DEK is stored on this publish.\n if (options.registry !== undefined && options.publish !== false) {\n const becameCurrent = await publishGeneration(options.registry, key, {\n wrappedDeks: newWrapped,\n });\n // Audit the publish only when this generation actually *became* the current one \u2014 not when a\n // forward-only publish no-oped because a newer generation was already current (the event's contract is\n // \"became the segment's current generation\"). Needs a registry to have a \"current generation\" at all.\n if (becameCurrent) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.publish',\n namespace: key.namespace,\n segment: key.segment,\n generation: key.generation,\n });\n }\n return {\n size,\n sha256,\n chunkCount: chunks.length,\n cardinality,\n becameCurrent,\n wrappedDeks: newWrapped,\n };\n }\n return { size, sha256, chunkCount: chunks.length, cardinality, wrappedDeks: newWrapped };\n}\n\n/**\n * Open a {@link CrbmReader} on one generation over the storage driver's range/tail reads (decrypting if `crypto`).\n * The reader the write paths use to re-read what they wrote, and the erasure rewrite uses to stream the old\n * generation; the engine's read path goes through {@link CrbmStorageChunkSource} instead, which caches these.\n */\nexport function openGenerationReader(\n storage: IStorageDriver,\n key: GenKey,\n crypto: CrbmCrypto | undefined,\n options: Omit<CrbmReaderOptions, 'crypto'> = {},\n): Promise<CrbmReader> {\n return openChecked(storage, key, { ...options, crypto });\n}\n\n/**\n * Open a reader on `key`, refusing an object whose footer names another generation. Every writer stamps the key's\n * number, and the chunk cache, the load guard and the erasure rewrite all trust it, so an object that disagrees\n * was written under another key or altered. Every open, the live read's, a pin's and the write paths', comes here.\n */\nasync function openChecked(\n storage: IStorageDriver,\n key: GenKey,\n options: CrbmReaderOptions,\n): Promise<CrbmReader> {\n const reader = await CrbmReader.open(storageBlobReader(storage, key), options);\n if (reader.generation !== key.generation) {\n throw new IntegrityError(\n `segment \"${key.segment}\" generation ${key.generation}: its footer says generation ${reader.generation}`,\n );\n }\n return reader;\n}\n\n/**\n * Re-open a freshly written generation and assert it round-trips exactly what was streamed into it: the same\n * per-chunk key set *and* the same total cardinality (on top of the codec's own per-chunk CRC + footer checks).\n * `expected` is the streaming writer's tally (the stream is consumed, so it can't be re-iterated) \u2014 the key-set\n * comparison catches a dropped/extra chunk that a cardinality-only check could miss when two errors cancel out.\n * Throws {@link IntegrityError}: the object is on disk but must not be published.\n */\nexport async function verifyGeneration(\n storage: IStorageDriver,\n key: GenKey,\n expected: { readonly chunkKeys: readonly number[]; readonly cardinality: number },\n crypto: CrbmCrypto | undefined,\n): Promise<void> {\n const expectedKeys = [...expected.chunkKeys].sort((a, b) => a - b);\n const reader = await openGenerationReader(storage, key, crypto);\n const actualKeys = [...reader.chunkKeys()].sort((a, b) => a - b);\n const keysMatch =\n actualKeys.length === expectedKeys.length && actualKeys.every((k, i) => k === expectedKeys[i]);\n if (!keysMatch) {\n throw new IntegrityError(\n `verify failed for ${key.segment}.${key.generation}: chunk-key set mismatch (${actualKeys.length} vs ${expectedKeys.length})`,\n );\n }\n if (reader.count() !== expected.cardinality) {\n throw new IntegrityError(\n `verify failed for ${key.segment}.${key.generation}: cardinality ${reader.count()} != ${expected.cardinality}`,\n );\n }\n}\n", "/**\n * A {@link StorageChunkSource} view that holds **one** segment at **one** generation, and passes everything else\n * through to the live source.\n *\n * A pin exists so a long job describes a single instant. An ordinary handle re-resolves on `cache.genTtlMs`, so a\n * publish part-way through an export, a send or a reconciliation means the second half of the job describes a\n * different instant than the first \u2014 and nothing in the result says so.\n *\n * **A pin is a generation number and the object it named, not a captured reader.** The reader behind it can live\n * in the shared bounded LRU and be evicted freely: re-opening at the same number reopens the same object, or, if\n * the name was purged and loaded again since, finds another one, which the pin's fingerprint refuses. Holding the\n * reader instead put every live pin outside the library's memory ceiling.\n *\n * **Other segments are not pinned, and that is the contract, not an omission.** `snap.intersect([other])` reads\n * `snap` at its pinned generation and `other` at whatever is current. Pinning the whole query means pinning\n * each segment. The alternative shapes are both worse: refusing the combine outright makes a pin useless to the\n * export and reconciliation jobs it exists for, and silently pinning the operands would quietly change what\n * `other` means to a caller who did not ask.\n *\n * What this must never do is the inverse \u2014 read a pinned handle's segment **live** because the call was made\n * on some other handle. That is why the routing is by segment identity here rather than by which object the\n * caller happened to start from.\n */\nimport type { ChunkRef, StorageChunkSource, SegmentRef, SegmentSize } from './ports';\nimport type { CrbmStorageChunkSource, PinnedObject } from './crbm-storage-source';\nimport { segmentKey } from './keys';\n\n/**\n * What a pin holds for one segment: the generation, the version identifying those exact bytes, and the pinned\n * object's fingerprint, so a read can tell that the generation it opens is still that object.\n *\n * `fingerprint` is optional so that a pin built by hand, before there was one, still compiles. Nothing checks the\n * object such a pin reads, so it cannot tell a name purged and loaded again from the segment it pinned.\n * `seg.pin()` always records it.\n */\nexport interface PinnedAt {\n readonly generation: number | null;\n readonly version: string | null;\n readonly fingerprint?: string | null;\n}\n\n/** The part of a pin a pinned read checks the object against; none for a pin of no generation. */\nconst heldBy = (pin: PinnedAt): PinnedObject | undefined => {\n if (pin.version === null) return undefined;\n return pin.fingerprint == null\n ? { version: pin.version }\n : { version: pin.version, fingerprint: pin.fingerprint };\n};\n\nexport class PinnedStorageChunkSource implements StorageChunkSource {\n constructor(\n private readonly inner: CrbmStorageChunkSource,\n /**\n * The pinned segments, keyed by {@link segmentKey}. A **set** rather than one entry because a combine may\n * involve several pinned handles, and each must be read at its own pin \u2014 including when the call was made\n * on a different handle. Reading a pinned handle live because the caller started elsewhere is the silent\n * wrong answer this exists to prevent.\n */\n private readonly pins: ReadonlyMap<string, PinnedAt>,\n ) {}\n\n /** The unpinned segments refresh as the store's own source does; a pinned one never does. */\n get pointerRefreshMs(): number {\n return this.inner.pointerRefreshMs;\n }\n\n /**\n * The pin for this segment, or `undefined` if it is not pinned here.\n *\n * A pin whose `generation` is `null` is a segment that had none when it was pinned: that handle reads empty\n * for its lifetime, which is exactly what an unpinned read of it does. That is **not** the same as \"this\n * source cannot pin\" \u2014 a source that genuinely cannot is a capability gap and fails fast at `pin()`.\n */\n private pinFor(ref: SegmentRef): PinnedAt | undefined {\n return this.pins.get(segmentKey(ref));\n }\n\n getChunk(ref: ChunkRef): Promise<Uint8Array | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.getChunk(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.getChunkAt(ref, pin.generation, heldBy(pin));\n }\n\n listChunkKeys(ref: SegmentRef): Promise<number[]> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.listChunkKeys(ref);\n return pin.generation === null\n ? Promise.resolve([])\n : this.inner.listChunkKeysAt(ref, pin.generation, heldBy(pin));\n }\n\n sizeOf(ref: SegmentRef): Promise<SegmentSize | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.sizeOf(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.sizeOfAt(ref, pin.generation, heldBy(pin));\n }\n\n cardinalities(ref: SegmentRef): Promise<ReadonlyMap<number, number> | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.cardinalities(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.cardinalitiesAt(ref, pin.generation, heldBy(pin));\n }\n\n currentGeneration(ref: SegmentRef): Promise<number | null> {\n const pin = this.pinFor(ref);\n return pin === undefined ? this.inner.currentGeneration(ref) : Promise.resolve(pin.generation);\n }\n\n /**\n * A pinned segment reports the version captured when it was pinned, marked as a pin's, so its decoded chunks\n * are cached under keys that no live read writes. A live read keys its fetches by the version it resolved when\n * it began, but is served whatever generation the live source holds when each fetch lands \u2014 after a publish\n * and a lapsed `cache.genTtlMs`, a reader-cache eviction, a sweep that heals the read forward, or an\n * invalidation, a different one \u2014 so an entry under a live version can hold another generation's chunk.\n * Invariant 3 lets that live call see it, and later live reads resolve the current version and never look the\n * entry up; a pin sharing the key would be handed it, and return a read that mixed two generations. A pinned\n * read fetches exactly its own generation, so the entries it fills are always that generation's.\n *\n * The pin still shares the store's chunk cache, and its memory ceiling; what it gives up is a hit on a chunk a\n * live read of the same version cached, which costs a pin one GET per such chunk. Live reads make no call for it,\n * though a pin's entries share the cache's bound with theirs, so under a small `cache.maxChunks` each can evict\n * the other. The version, not the bare generation, is still what the key carries: sharing on a generation-only\n * key was how a pinned read could resurrect an id that `eraseIdFromSegment` had reported physically gone.\n */\n currentVersion(ref: SegmentRef): Promise<string | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.currentVersion(ref);\n // A live version starts with its generation number, so no live version can equal a pin's. The fingerprint keeps\n // two incarnations' pins apart where the version cannot: on a store with no registry it is the bare number.\n if (pin.version === null) return Promise.resolve(null);\n return Promise.resolve(\n `pin ${pin.version}${pin.fingerprint == null ? '' : `#${pin.fingerprint}`}`,\n );\n }\n\n exists(ref: SegmentRef): Promise<boolean> {\n return this.inner.exists(ref);\n }\n\n invalidate(ref: SegmentRef): void {\n this.inner.invalidate(ref);\n }\n}\n", "/**\n * Generation bookkeeping for the loaded store: which generation a writer should take next, and which\n * superseded generations may be collected.\n *\n * Storage generations are write-once, generation-keyed objects (`<segment>.<gen>.crbm`) behind one registry pointer\n * (`currentGen`). Every write path in the library \u2014 a load, an `*Into` materialisation, a subject-erasure rewrite \u2014\n * writes a **new** object and then advances the pointer. Every load that finds a row fences its publish on the\n * row's token; a guarded load (the default, since the empty refusal reads the current generation) also fences on\n * the pointer it judged (`expectFrom`), and one that found no row fences on that absence instead. Only an unguarded\n * load (`allowEmpty: true` and no `guard.minRetained`) onto a segment with no row publishes bare forward-only. An\n * `*Into` materialisation is a load, and publishes the same way. The rewrite is fenced on its source generation and\n * the row's token (see invariant 1). That leaves the superseded object in the bucket, still billed, so something has\n * to collect it: {@link gcOrphanGenerations}. Pure orchestration over the driver ports \u2014 no I/O, time or randomness\n * of its own.\n */\nimport { ValidationError, WriteConflictError } from './errors';\nimport type { IStorageDriver, IRegistryDriver, SegmentRef } from './ports';\n\n/** The two ports generation bookkeeping needs: the objects, and the pointer that says which one is current. */\nexport interface GenerationDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n}\n\n/**\n * The generation number a writer should use for the segment's **next** object: one above the highest generation\n * the registry points at *or* that is present in Storage \u2014 whichever is higher.\n *\n * Both are consulted because they can disagree. A load that wrote its object and crashed before publishing leaves\n * an object *above* `currentGen`; a writer that consulted only the pointer would pick that same number and hit the\n * write-once conflict on every retry. Skipping past it costs one `list` and keeps the retry trivial \u2014 the crashed\n * object is an orphan {@link gcOrphanGenerations} collects once the pointer moves on. Reusing such an object\n * instead of skipping it (verify-and-publish) is `load()`'s job, not a numbering helper's.\n *\n * A segment with no row and no objects starts at 0. An admin/write-path helper, never on the read path.\n */\nexport async function nextGeneration(ref: SegmentRef, deps: GenerationDeps): Promise<number> {\n const record = await deps.registry.get(ref);\n let highest = record?.currentGen ?? -1;\n for await (const key of deps.storage.list(ref)) {\n if (key.generation > highest) highest = key.generation;\n }\n return highest + 1;\n}\n\n/**\n * Garbage-collect superseded Storage generations for a segment: everything strictly below `currentGen`, keeping\n * the most recent `keep` of them as a grace window, so a read still fetching from a just-superseded\n * generation need not re-resolve mid-call (**invariant 4**). It is a window, not a lock: a read whose\n * generation is swept anyway re-resolves and retries once rather than failing (see `withFreshSnapshot`), so\n * `keep` trades storage for round trips. Generations \u2265 `currentGen` are never touched. Returns the generations\n * deleted.\n *\n * **Except on a `destroyed` segment, where EVERY generation is garbage** and the grace window is meaningless.\n * A tombstoned segment resolves no generation for any reader, pinned or not \u2014 a pin cannot be taken on one, and\n * a pin taken while it was still active re-checks the status on every open \u2014 and nothing else in the library\n * would ever collect them, so without this those objects are billed forever.\n *\n * **Throws {@link WriteConflictError} when it cannot act on the row it read**: the row is gone when it is re-read,\n * after the listing or before a delete; on an active segment, the pointer has fallen below the cutoff before a\n * delete; on a `destroyed` segment, the row's token has changed. Not to be confused with the empty array it still\n * returns when there was genuinely nothing to collect (no row at all, or no pointer yet), which is why an empty\n * array is not a receipt. The row is read before the listing and acted on after it, so a name that was purged and\n * re-created in that window is a *different* segment wearing the same name, and its live object must not be\n * collected on the strength of the old row: on an active segment the lower of the two pointers is what keeps it\n * safe, and on a `destroyed` one the token. Refusing is safe \u2014 the objects are not going anywhere and the next\n * pass reads a consistent row \u2014 but it has to be **distinguishable** from \"there was nothing to collect\", which\n * an empty array is not: `eraseIdFromSegment` reads the returned list as the physical half of its erasure\n * receipt, and would otherwise report `erased: true` over bytes still in the bucket. Re-run it.\n *\n * That state is reachable in practice: a `dropSegment` whose Storage sweep threw part-way, or a load that was\n * already writing its object when the tombstone landed and finished the write afterwards. `dropSegment` re-sweeps\n * and reports whatever it could not reclaim in `generationsRemaining`, but a drop that was never re-run leaves a\n * residual, and this is what eventually collects it from the retention sweep.\n *\n * `keep: 0` is how a subject-erasure rewrite makes a bit **physically** gone on return: the generation that held\n * it is collected the moment the rewrite is current. A live read still fetching from it heals forward to the\n * rewrite, and a pin of it (`seg.pin()`) fails with `NotFoundError` for any chunk it has yet to read.\n */\nexport async function gcOrphanGenerations(\n ref: SegmentRef,\n deps: GenerationDeps,\n options: { keep?: number } = {},\n): Promise<number[]> {\n const keep = options.keep ?? 1;\n // Refused, not clamped: `NaN` slices nothing off the end and would collect the whole grace window, and a\n // negative count that clamps to 0 collects it too, for a caller who wrote a typo.\n if (!Number.isInteger(keep) || keep < 0) {\n throw new ValidationError(`keep must be a non-negative integer; got ${String(keep)}`);\n }\n const record = await deps.registry.get(ref);\n if (record === null) return []; // no authoritative pointer \u2192 don't delete anything\n const current = record.currentGen;\n // A set, not an array: a listing that spans a purge-and-recreate can yield the same generation number\n // twice (the objects are re-created under the numbers just swept), and the grace window below keeps the\n // newest `keep` ENTRIES \u2014 so a duplicate would silently consume a keep slot and evict a live generation.\n const seen = new Set<number>();\n for await (const key of deps.storage.list(ref)) seen.add(key.generation);\n const gens = [...seen];\n // Everything above was read BEFORE the listing and is acted on after it, and the listing is paginated \u2014\n // seconds wide on a real object store. So re-read the row and reconcile, because BOTH branches can otherwise\n // delete an object the live pointer names, which is the forbidden `missing-storage-generation` state.\n //\n // A purged row refuses outright, on either branch: purged-and-idle is indistinguishable from\n // purged-and-being-recreated, and the top of this function already declines to act without an authoritative\n // pointer.\n const after = await deps.registry.get(ref);\n if (after === null)\n throw new WriteConflictError(`registry row for segment ${ref.segment} was purged mid-pass`);\n\n // On a tombstone, require the *same* row. A token is never reused (ABA-safe), so an unchanged one proves the\n // segment was not purged and re-created underneath this pass \u2014 which matters here because this branch deletes\n // every object it enumerated, `currentGen` included, so a re-created segment would lose the generation its\n // new pointer names.\n if (record.status === 'destroyed' && after.token !== record.token) {\n throw new WriteConflictError(\n `segment ${ref.segment} changed incarnation while its generations were being listed`,\n );\n }\n\n // On the ordinary branch, take the LOWER of the two pointers. Within one incarnation the pointer only moves\n // forward, so a publish landing mid-listing leaves the cutoff exactly where it was and routine GC still\n // collects \u2014 refusing on any token change would make GC useless on a busy segment. But the pointer is only\n // monotonic *within* an incarnation: `nextGeneration` restarts at 0 once a row is purged and the bucket\n // emptied, so a name that was retired and re-created wears a LOWER `currentGen` than the one read before the\n // listing \u2014 and `rollbackSegment` lets an operator lower it deliberately \u2014 so `g < current` would then select\n // a live object. `Math.min` is what makes a\n // regressed pointer narrow the cutoff instead of widening it.\n const cutoff =\n current === null || after.currentGen === null ? null : Math.min(current, after.currentGen);\n\n /**\n * Re-prove that everything still queued for deletion is still collectable. Two different questions, because\n * the two branches delete under different licences:\n *\n * - a **tombstone** deletes at and above `currentGen`, so it needs incarnation IDENTITY \u2014 the same row,\n * by token;\n * - the **ordinary** branch deletes strictly below `cutoff`, so it needs only that the live pointer has not\n * fallen below `cutoff`. A forward publish moves it up and changes nothing, which is what keeps routine\n * collection working on a busy segment. A purge-and-recreate or a `rollbackSegment` can move it down, which\n * is why the pointer is re-proved before every delete (below).\n */\n const stillCollectable = async (): Promise<void> => {\n const still = await deps.registry.get(ref);\n if (still === null) {\n throw new WriteConflictError(`registry row for segment ${ref.segment} was purged mid-pass`);\n }\n const ok =\n record.status === 'destroyed'\n ? still.token === after.token\n : cutoff === null || (still.currentGen !== null && still.currentGen >= cutoff);\n if (!ok) {\n throw new WriteConflictError(\n `segment ${ref.segment} changed incarnation while its generations were being collected`,\n );\n }\n };\n\n const toDelete =\n record.status === 'destroyed'\n ? gens.sort((a, b) => a - b) // all of it: no reader can resolve a generation of a tombstoned segment\n : cutoff === null\n ? // No Storage pointer yet, so \"below current\" selects nothing and there is nothing safe to infer: an object\n // here is either a load about to publish or an orphan we cannot tell apart from it. Deleting would race\n // that publish into a dangling pointer. It is collected once a pointer exists.\n []\n : // Delete generations below the cutoff, except the newest `keep` of them (the grace window).\n gens\n .filter((g) => g < cutoff)\n .sort((a, b) => b - a) // newest-first\n .slice(keep);\n // The re-read above proves the segment was intact at ONE instant; the deletes below are one round trip each,\n // so the exposure is the whole loop, not that instant. The ordinary branch deletes newest-first, which puts a\n // restarted incarnation's generation 0 LAST \u2014 the worst ordering.\n //\n // Re-proved before EVERY delete, the first included. The re-read above does not cover the first: a rising\n // pointer only makes more things collectable, but `rollbackSegment` can move the pointer *down*, onto a\n // generation this pass has already queued, and a rollback landing between the re-read and the first delete\n // would leave the pass deleting the live generation before its second iteration noticed anything.\n //\n // Cost is one registry read per object actually deleted, on a path that is already one round trip per object\n // and is never on the read path.\n for (const generation of toDelete) {\n await stillCollectable();\n await deps.storage.delete({ namespace: ref.namespace, segment: ref.segment, generation });\n }\n return toDelete;\n}\n", "/**\n * `loadSegment` \u2014 the loaded store's primary write path, as one call.\n *\n * A load is always the same four steps: take the next generation number, write one immutable object, move the\n * pointer, collect what the move superseded. Inside the library each is its own internal function, and the\n * step a hand-composed load leaves out is the last one, so a store that composed it would accumulate superseded\n * generations it keeps paying for.\n * Composing it here, once, is the point.\n *\n * The other reason it is one call is the **guard**. A load REPLACES a segment: whatever the stream contains is\n * what the segment contains afterwards. That makes an upstream query returning fewer rows than usual \u2014 or none \u2014\n * a silent, successful-looking wipe, and nothing downstream can tell it from a legitimate shrink. The guard is\n * the place to say what a plausible result looks like, and it has to sit **between the write and the publish**,\n * because that is the only moment where the new content is known and the old one is still authoritative.\n *\n * Refusing therefore has to clean up after itself. The object is already durable at that point, and it sits\n * ABOVE `currentGen`, where generation collection deliberately never looks (it deletes strictly below the\n * pointer), so a refused load deletes its own object before returning, while the segment's row is unchanged or\n * gone. Once another write has changed the row, the object's number may name a re-created segment's live object,\n * so it stays, and collection takes it like any other generation once one above it is current.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { type CodecInterface, requireCodec } from './codec';\nimport {\n bulkLoadCrbmGeneration,\n openGenerationReader,\n publishGeneration,\n} from './crbm-storage-source';\nimport type { Clock } from './determinism';\nimport { aadFor } from './crypto';\nimport type { CrbmCrypto, IKeystore } from './crypto';\nimport {\n KeyUnavailableError,\n ValidationError,\n isNotFoundError,\n isWriteConflictError,\n} from './errors';\nimport { gcOrphanGenerations, nextGeneration } from './generation-gc';\nimport type { IStorageDriver, IRegistryDriver, RegistryRecord, SegmentRef, Token } from './ports';\nimport { validateUserRef } from './validate';\n\n/** What {@link loadSegment} needs: the objects, the pointer, the codec, and key material if encrypted. */\nexport interface LoadDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n readonly codec?: CodecInterface;\n readonly keystore?: IKeystore;\n readonly requireEncryption?: boolean;\n readonly clock?: Clock;\n}\n\n/**\n * What a plausible result looks like, so an implausible one is refused instead of published.\n *\n * Both bounds are compared against the **current** generation's cardinality, read from the `.crbm` index rather\n * than its payload \u2014 one object-header read, and only when a bound is actually set.\n */\nexport interface LoadGuard {\n /** Refuse a generation with fewer than this many ids. Use for \"this segment is never legitimately tiny\". */\n readonly minCardinality?: number;\n /**\n * Refuse a generation that retains less than this fraction of the current one \u2014 `0.5` means \"at least half the\n * ids must survive\". A *growing* load is never refused by this bound, and neither is the first load of a\n * segment, which has nothing to shrink from.\n *\n * Phrased as a floor rather than a ceiling on the loss so that `0` means the same thing here as it does on\n * `minCardinality` above: **no bound**. The inverse spelling (`maxShrink`) would make `0` mean \"refuse any\n * shrink at all\" \u2014 maximally strict \u2014 sitting in the same object literal as a field where `0` is maximally\n * permissive, which is where an inversion would eventually be written and not noticed.\n */\n readonly minRetained?: number;\n}\n\nexport interface LoadOptions {\n /**\n * Publish an empty generation over a non-empty one. Off by default: an empty result is far more often an\n * upstream failure than an intent, and the failure is indistinguishable from success once it lands.\n */\n readonly allowEmpty?: boolean;\n readonly guard?: LoadGuard;\n /**\n * Generations to keep below the new pointer as a grace window (default 1). A non-negative integer: anything\n * else (negative, fractional, `NaN`, infinite) throws `ValidationError` before anything is written. Keep at\n * least one: a read that is\n * still fetching chunks from the just-superseded generation would otherwise have it deleted out from under it\n * and pay a re-resolve. A wider window costs storage, and it is what a pinned handle needs: a pin is never\n * re-resolved, so a chunk a pinned read has not fetched fails with `NotFoundError` once its generation is\n * collected. Keep at least one generation for every one that can be written above the pinned one while your\n * longest pinned job runs, on every writer that loads the segment \u2014 see \"Generations and `keep`\" in the loading\n * guide.\n */\n readonly keep?: number;\n readonly audit?: IAuditSink;\n}\n\n/** Why a load did not become current. */\nexport type LoadRefusal =\n | 'empty'\n | 'min-cardinality'\n | 'min-retained'\n /**\n * Another writer got there first. Either another load wrote the same generation number first, so the write-once\n * put refused this one and it wrote nothing (`size: 0`); or the segment's registry row changed while the load was\n * writing: another load published, a retention change, a rollback or an erasure wrote the row, or the row was\n * deleted. Once another write has changed the row, the object stays in the bucket, because its generation number\n * may by then name another incarnation's live object; once the row is gone, the object is deleted.\n */\n | 'superseded';\n\nexport interface LoadResult {\n /**\n * The generation written. Present even when refused: a refusal deletes that object while the segment's row is\n * unchanged or gone, and leaves it in the bucket once another write has changed the row. A load that lost its\n * generation number to another wrote nothing, and reports `size: 0`.\n */\n readonly generation: number;\n /** Whether this generation is now the segment's current one. */\n readonly published: boolean;\n /** Set only when `published` is false. */\n readonly reason?: LoadRefusal;\n readonly size: number;\n readonly sha256: string;\n readonly chunkCount: number;\n /** Distinct ids in the generation that was written, post-dedup. */\n readonly cardinality: number;\n /**\n * What the segment held when the guard judged it \u2014 `null` when there was no current generation, or when no\n * bound needed it. Returned because an alert on `reason: 'min-retained'` is useless without it: \"refused\" is a\n * page, \"refused: 4 \u2192 1\" is a diagnosis, and re-reading the segment to find out costs a round trip and races\n * whatever happens next.\n */\n readonly cardinalityBefore: number | null;\n /** The superseded generations collected after publishing. Empty when nothing was published. */\n readonly collected: readonly number[];\n}\n\n/**\n * Cardinality of the segment's current generation, read from the `.crbm` index rather than its payload.\n * `null` when there is no current generation to compare against.\n *\n * The crypto is derived **here** rather than taken from the caller, and that is not a convenience. AAD is bound\n * to a specific generation, so a `CrbmCrypto` passed in from outside would have to be pre-bound to whichever\n * generation turns out to be current \u2014 which only this function learns, by reading the row. A caller cannot\n * satisfy that seam even in principle, so offering it would be offering a parameter nobody can fill correctly.\n */\nasync function currentCardinality(\n ref: SegmentRef,\n deps: LoadDeps,\n record: RegistryRecord | null,\n): Promise<number | null> {\n if (record === null || record.currentGen === null || record.status === 'destroyed') return null;\n const generation = record.currentGen;\n let crypto: CrbmCrypto | undefined;\n const wrapped = record.wrappedDeks;\n if (wrapped !== undefined && wrapped.length > 0) {\n if (deps.keystore === undefined) {\n throw new KeyUnavailableError(\n `segment \"${ref.segment}\" is encrypted but load was given no keystore`,\n );\n }\n const aead = await deps.keystore.openDek(wrapped);\n crypto = { aead, aadFor: (scope) => aadFor(ref, generation, scope) };\n }\n let reader;\n try {\n reader = await openGenerationReader(deps.storage, { ...ref, generation }, crypto);\n } catch (err) {\n // The row names a generation whose OBJECT is gone \u2014 the `missing-storage-generation` state a consistency\n // check reports, produced by a partial `dropSegment`, a bucket lifecycle rule, or a restore that brought\n // the registry back without the bucket.\n //\n // `null`, not a throw. The guard exists to protect ids that are still there, and these are already gone:\n // refusing to write would leave the segment unreadable AND unrepairable, which is the opposite of what a\n // guard is for. Writing over it is precisely the repair, and it is what this path did before the guard\n // reached it. A caller who wants to be told instead can run `checkConsistency()`, whose job that is.\n if (!isNotFoundError(err)) throw err;\n return null;\n }\n let total = 0;\n for (const n of reader.cardinalities().values()) total += n;\n return total;\n}\n\n/**\n * Replace a segment's contents with `ids`, as one immutable generation, and make it current.\n *\n * Returns what was written and whether it became current. A refusal is **not** an exception: `published: false`\n * with a `reason` is a normal outcome a caller branches on, in the same shape as a successful load, because the\n * interesting cases (a guard tripped, a racing writer won) are operational facts rather than faults.\n */\nexport async function loadSegment(\n ref: SegmentRef,\n ids: Iterable<number> | AsyncIterable<number>,\n deps: LoadDeps,\n options: LoadOptions = {},\n): Promise<LoadResult> {\n validateUserRef(ref);\n // Before any round trip: a core caller that forgot the codec learns it from this call's name, not the loader's.\n requireCodec(deps.codec, 'loadSegment');\n const keep = options.keep ?? 1;\n if (!Number.isInteger(keep) || keep < 0) {\n throw new ValidationError(`keep must be a non-negative integer; got ${String(keep)}`);\n }\n const guard = options.guard;\n if (guard?.minCardinality !== undefined) {\n const m = guard.minCardinality;\n if (!Number.isInteger(m) || m < 0) {\n throw new ValidationError(\n `guard.minCardinality must be a non-negative integer; got ${String(m)}`,\n );\n }\n }\n if (guard?.minRetained !== undefined) {\n const s = guard.minRetained;\n if (!Number.isFinite(s) || s < 0 || s > 1) {\n throw new ValidationError(`guard.minRetained must be a fraction in 0..1; got ${String(s)}`);\n }\n }\n const audit = safeAudit(options.audit ?? NOOP_AUDIT);\n\n // One row read, used for three things: the guard's \"before\", the incarnation this call is acting on, and the\n // pointer it derived its decision from.\n const row = await deps.registry.get(ref);\n const fromToken: Token | undefined = row?.token;\n const fromGeneration = row?.currentGen ?? undefined;\n\n // Read the \"before\" cardinality ONLY when a bound needs it. The empty guard needs to know whether the current\n // generation is non-empty; `minRetained` needs its size. `minCardinality` compares against the new generation\n // alone, so it costs nothing extra.\n const needsBefore = guard?.minRetained !== undefined || options.allowEmpty !== true;\n const before = needsBefore ? await currentCardinality(ref, deps, row) : null;\n\n const generation = await nextGeneration(ref, deps);\n const key = { namespace: ref.namespace, segment: ref.segment, generation };\n\n // `publish: false`, deliberately: the guard has to run while the old generation is still authoritative, so the\n // pointer moves below rather than here. The registry is still passed \u2014 it is where an existing encrypted\n // segment's DEK lives, and reusing that key is not optional \u2014 and the freshly minted one comes back on the\n // result so the deferred publish can store it.\n let written;\n try {\n written = await bulkLoadCrbmGeneration(deps.storage, key, ids, {\n registry: deps.registry,\n publish: false,\n keystore: deps.keystore,\n requireEncryption: deps.requireEncryption,\n codec: deps.codec,\n clock: deps.clock,\n });\n } catch (err) {\n // Another loader took this generation number first \u2014 write-once refused the second put. That is a lost race,\n // and `LoadRefusal` documents a lost race as `'superseded'`; letting a `WriteConflictError` escape here would\n // make a caller who branches on `published` (as the doc-comment tells them to) miss the one outcome they were\n // told to expect. Nothing was written, so there is nothing to clean up; the refusal is still audited, as every\n // other one is, since a downstream reconciliation needs to know the replacement it asked for did not happen.\n if (!isWriteConflictError(err)) throw err;\n audit.onEvent({\n kind: 'segment.load-refused',\n segment: ref.segment,\n namespace: ref.namespace,\n generation,\n reason: 'superseded',\n cardinality: 0,\n });\n return {\n generation,\n published: false,\n reason: 'superseded',\n size: 0,\n sha256: '',\n chunkCount: 0,\n cardinality: 0,\n cardinalityBefore: before,\n collected: [],\n };\n }\n\n const refuse = async (reason: LoadRefusal): Promise<LoadResult> => {\n // The object is durable and sits above `currentGen`, where collection never looks until a later load numbers\n // above it, so the refusal reclaims it here \u2014 but ONLY while this is still the same segment.\n //\n // A generation number identifies a generation within one incarnation of a row, and nothing more (invariant\n // 1). If the row was purged and the name re-created while this load was in flight, `nextGeneration` restarts\n // from 0 and the number this call is holding can name the NEW incarnation's live object. Deleting it would\n // put an active row over a missing generation \u2014 the forbidden `missing-storage-generation` state, produced by\n // the one code path whose whole purpose is to prevent data loss. Leaving an orphan behind is strictly the\n // better failure: it costs storage until something collects it, rather than costing a live segment.\n const now = await deps.registry.get(ref);\n if (now === null || now.token === fromToken) await deps.storage.delete(key);\n audit.onEvent({\n kind: 'segment.load-refused',\n segment: ref.segment,\n namespace: ref.namespace,\n generation,\n reason,\n cardinality: written.cardinality,\n });\n // Constructed field by field, never spread from the write result. That result now carries `wrappedDeks` \u2014\n // wrapped key material \u2014 and a spread would put it on a public, JSON-serialisable object that a load job\n // will reasonably `logger.info({ result })`. Declared shapes are not a filter at runtime.\n return {\n generation,\n published: false,\n reason,\n size: written.size,\n sha256: written.sha256,\n chunkCount: written.chunkCount,\n cardinality: written.cardinality,\n cardinalityBefore: before,\n collected: [],\n };\n };\n\n if (written.cardinality === 0 && options.allowEmpty !== true && (before ?? 0) > 0) {\n return refuse('empty');\n }\n if (guard?.minCardinality !== undefined && written.cardinality < guard.minCardinality) {\n return refuse('min-cardinality');\n }\n if (guard?.minRetained !== undefined && before !== null) {\n if (written.cardinality < before * guard.minRetained) return refuse('min-retained');\n }\n\n // Fence the publish on the row the guard judged.\n //\n // Forward-only is right for an UNGUARDED load: its ids come from upstream, so losing a race costs nothing that\n // the winner did not also bring. A guarded load is a different animal \u2014 it derived its decision from a\n // particular generation's cardinality, which makes it exactly the writer invariant 1 says must publish with\n // `expectFrom`. Without that, anything published between the \"before\" read and this publish voids the guard's\n // premise while the guard still reports success: reproduced, two loaders on a fresh segment let an EMPTY\n // generation land over a thousand ids, under default options, because `before` was read as \"no row\".\n //\n // `expectToken` goes on regardless. It is incarnation identity rather than a derivation fence, it costs\n // nothing legitimate \u2014 a token only changes when the row does \u2014 and it is what stops this call publishing\n // into a segment that merely reuses the name it started with.\n const published = await publishGeneration(deps.registry, key, {\n wrappedDeks: written.wrappedDeks,\n ...(fromToken === undefined ? {} : { expectToken: fromToken }),\n ...(needsBefore && fromGeneration !== undefined ? { expectFrom: fromGeneration } : {}),\n // The third case, and the one the two fences above structurally cannot cover: the guard judged a segment\n // that had NO ROW. Both `expectFrom` and `expectToken` compare against a value read from a row, so with no\n // row both are omitted and the publish becomes a bare forward-only advance \u2014 which lands over anything\n // that appeared in between. `before` was `null`, so the empty and `minRetained` bounds had nothing to\n // judge and passed vacuously. Verified: an empty generation published over a thousand ids that a\n // concurrent writer had created meanwhile, reporting success. A guarded write therefore has to fence on\n // the ABSENCE it relied on, exactly as it fences on the pointer it relied on.\n ...(needsBefore && row === null ? { expectAbsent: true } : {}),\n });\n if (!published) return refuse('superseded');\n\n audit.onEvent({\n kind: 'segment.publish',\n segment: ref.segment,\n namespace: ref.namespace,\n generation,\n });\n\n const collected = await gcOrphanGenerations(ref, deps, { keep });\n return {\n generation,\n published: true,\n size: written.size,\n sha256: written.sha256,\n chunkCount: written.chunkCount,\n cardinality: written.cardinality,\n cardinalityBefore: before,\n collected,\n };\n}\n", "/**\n * `listGenerations` and `rollbackSegment` \u2014 see what a segment has been, and put it back.\n *\n * Immutable generations mean the previous version of a segment is usually still sitting in the bucket: a load\n * that replaced it did not overwrite anything, it wrote a new object and moved a pointer. So recovering from a\n * bad load is, in principle, moving the pointer back, which no load, publish or sweep can do, because every other\n * write path in the library only ever advances the pointer, and refuses a move that would lower it.\n *\n * That refusal is right for a *writer*: a load whose ids came from upstream loses nothing by being out-raced, and\n * letting it regress the pointer would let a slow loader silently undo a fast one. It is wrong for an *operator*,\n * who has looked at the segment, decided the current generation is wrong, and knows which one they want. So\n * rollback is the one call that moves the pointer backwards, and it is deliberately not reachable from any\n * automatic path \u2014 no sweep, no retry, no reconciliation calls it.\n *\n * It is audited for the same reason. Every other pointer move can be reconstructed from \"a load happened\"; this\n * one is a human overriding the ordering rule the rest of the system relies on, which is exactly the event an\n * Art. 30 record or an incident review wants to find.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { NotFoundError, ValidationError } from './errors';\nimport type { IStorageDriver, IRegistryDriver, SegmentRef } from './ports';\nimport { validateUserRef } from './validate';\n\n/** What the generation helpers need: the objects, and the pointer that says which one is current. */\nexport interface GenerationListDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n}\n\n/** One generation present in the bucket. */\nexport interface GenerationEntry {\n readonly generation: number;\n /** Whether the registry pointer currently names this generation. */\n readonly current: boolean;\n}\n\n/**\n * Every generation still present for a segment, ascending, with the current one marked.\n *\n * This is what the bucket holds, not what the segment has ever been: generation collection deletes superseded\n * objects, so the list is the grace window plus whatever has not been collected yet. It is the set a\n * {@link rollbackSegment} can choose from, which is the reason to look at it.\n *\n * One registry read and one `list` call, whether or not the segment has a registry row, so it also finds the\n * objects a purged row left behind. It does not open the objects, so it costs nothing per generation and tells you\n * nothing about their contents \u2014 read the sizes through the store's own report if you need them.\n */\nexport async function listGenerations(\n ref: SegmentRef,\n deps: GenerationListDeps,\n): Promise<GenerationEntry[]> {\n validateUserRef(ref);\n const record = await deps.registry.get(ref);\n const current = record?.currentGen ?? null;\n const seen = new Set<number>();\n for await (const key of deps.storage.list(ref)) seen.add(key.generation);\n return [...seen]\n .sort((a, b) => a - b)\n .map((generation) => ({ generation, current: generation === current }));\n}\n\nexport interface RollbackResult {\n /** The generation the pointer named before the rollback. `null` when the segment had no current generation. */\n readonly fromGeneration: number | null;\n /** The generation the pointer names now. */\n readonly generation: number;\n}\n\n/**\n * Move a segment's pointer **back** to a generation that is still in the bucket.\n *\n * The only non-forward-only publish in the library, and the only one an operator has to ask for by name. It\n * refuses rather than guesses:\n *\n * - a generation whose object is **not in the bucket** \u2014 collected, or never written \u2014 is refused with\n * `NotFoundError` rather than pointed at, because a pointer naming a missing object is the one state the whole\n * design exists to avoid;\n * - a **crypto-shredded** segment is refused: every generation of it is unreadable, so a rollback would produce a\n * segment that resolves and then fails;\n * - rolling to the generation **already current** is a no-op that reports itself, not an error.\n *\n * What it does **not** do is delete anything. The generations above the new pointer stay in the bucket, which is\n * what makes the rollback reversible \u2014 roll forward again by naming one of them. They are also then *above*\n * `currentGen`, where generation collection never looks. They stay until a load numbers above them (collection\n * then keeps the newest `keep` of what is below its pointer), `dropSegment` deletes them, or a subject erasure\n * deletes them: all of those present when it rewrites, and only those holding the id when the current generation does not.\n * Not collecting them is a deliberate trade: an operator who has just undone a bad load should not have the\n * evidence collected out from under them, while a rollback target that still holds erased data would make the\n * erasure undoable by an ordinary operator action.\n */\nexport async function rollbackSegment(\n ref: SegmentRef,\n toGeneration: number,\n deps: GenerationListDeps,\n options: { audit?: IAuditSink; allowForward?: boolean } = {},\n): Promise<RollbackResult> {\n validateUserRef(ref);\n if (!Number.isInteger(toGeneration) || toGeneration < 0) {\n throw new ValidationError(\n `rollback: generation must be a non-negative integer; got ${String(toGeneration)}`,\n );\n }\n\n const record = await deps.registry.get(ref);\n if (record === null) {\n throw new NotFoundError(`rollback: no registry row for segment \"${ref.segment}\"`);\n }\n if (record.status === 'destroyed') {\n throw new ValidationError(\n `rollback: segment \"${ref.segment}\" is destroyed (crypto-shredded) \u2014 every generation of it is unreadable`,\n );\n }\n if (record.currentGen === toGeneration) {\n return { fromGeneration: record.currentGen, generation: toGeneration };\n }\n\n // A first look, for the affordance rather than the safety: an operator who named a collected generation needs\n // to be told what IS available, and that is much nicer to produce before anything has moved.\n const present = new Set<number>();\n for await (const key of deps.storage.list(ref)) present.add(key.generation);\n if (!present.has(toGeneration)) {\n const available = [...present].sort((a, b) => a - b);\n throw new NotFoundError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" is not in the bucket` +\n (available.length === 0\n ? ' (no generations remain)'\n : ` \u2014 present: ${available.join(', ')}`),\n );\n }\n if (\n record.currentGen !== null &&\n toGeneration > record.currentGen &&\n options.allowForward !== true\n ) {\n // Above the pointer is not \"a later version of this segment\". It is where objects live that were never\n // authoritative: a load that wrote its object and died before publishing, and a guard-refused load whose\n // cleanup was skipped because the row had changed. Rolling onto one of those makes current the very\n // generation a guard refused \u2014 an empty one, typically. The legitimate above-pointer case is undoing a\n // rollback, which is what the opt-in is for.\n throw new ValidationError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" is above the current pointer ` +\n `(${record.currentGen}) \u2014 it may never have been published. Pass { allowForward: true } if you are ` +\n `undoing an earlier rollback.`,\n );\n }\n\n // Fenced on the row this decision was made against. A rollback is the most derived write there is \u2014 an\n // operator looked at a particular state and chose \u2014 so publishing it into a row that has moved since would\n // undo whatever moved it, which is the opposite of what they asked for.\n const { token } = await deps.registry.compareAndSwap(ref, record.token, {\n currentGen: toGeneration,\n });\n\n // THE check, and it has to be here rather than above. Every target but an `allowForward` one is below the old\n // pointer, which is precisely generation collection's range \u2014 and a collector never writes the row, so the\n // token fence above cannot see it coming. A listing taken before the swap therefore proves nothing: the object\n // can be collected between that listing and this swap, leaving the pointer naming a missing object.\n //\n // Once the swap lands, the target is safe \u2014 collection only ever takes what is strictly below `currentGen`,\n // and the target now IS `currentGen`. So the ordering is: move first, then verify, and put the pointer back if\n // the object went. Re-pointing can itself be raced, which is why it is fenced on the token the swap returned\n // and why failing to undo is reported rather than swallowed.\n let stillThere = false;\n for await (const key of deps.storage.list(ref)) {\n if (key.generation === toGeneration) {\n stillThere = true;\n break;\n }\n }\n if (!stillThere) {\n let undone = false;\n try {\n await deps.registry.compareAndSwap(ref, token, { currentGen: record.currentGen });\n undone = true;\n } catch {\n // Not proof the undo did not land: a swap can apply and still throw, as when its response is lost. Only a\n // read of the row says where the pointer is, so the message below says \"may\", not \"does\".\n }\n throw new NotFoundError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" was collected while the pointer was moving` +\n (undone\n ? ' \u2014 the pointer was put back'\n : `, and the move back failed, so the pointer may still name ${toGeneration} (a write that landed and lost ` +\n 'its response reads as a failure here). Check which generation is current, then re-run a rollback to a ' +\n 'generation that exists, or restore the object.'),\n );\n }\n\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.rollback',\n segment: ref.segment,\n namespace: ref.namespace,\n fromGeneration: record.currentGen,\n generation: toGeneration,\n });\n\n return { fromGeneration: record.currentGen, generation: toGeneration };\n}\n", "/**\n * Subject erasure on a loaded segment (GDPR Art. 17): remove **one id** from a segment by rewriting its current\n * generation without that id.\n *\n * There is no per-id delete on an immutable object, and there is no mutable tier to hold a tombstone, so erasure\n * is what every other write in this library is: a new generation. The current generation is streamed chunk by\n * chunk through the ascending writer \u2014 every chunk decoded, range-checked and re-encoded, the one holding the id\n * with that bit cleared \u2014 then published fenced on the generation it streamed, and that generation is collected immediately\n * (`keep: 0`), so the bit is **physically gone from the bucket when this returns**. Constant memory: one chunk in\n * flight, never the whole segment.\n *\n * **`erased: true` is a claim about every generation of the segment, not only the one it replaced.** A rollback\n * leaves generations above the pointer that were once current and can be made current again, so a holder can sit\n * above the pointer as well as below it, and there can be several. Before it reports `erased: true` the call\n * therefore lists the bucket once more and reads every generation it has not already seen to be clean; any that\n * still holds the id makes it report or throw instead. In the ordinary case that list returns one generation, the\n * current one, and costs one `list`.\n *\n * What it does NOT reach: backups, replicas and noncurrent object versions hold the old object until their own\n * lifecycle removes it. For an at-rest guarantee that survives those, use encryption plus crypto-shred\n * (`destroySegment`), which is per segment \u2014 a per-id shred is infeasible because one DEK covers the whole segment.\n *\n * Concurrency. This is a **read-modify-write**, not a plain forward-only publish, and that distinction is where\n * its correctness lives: the new generation is `from` minus one bit, so it is a valid successor to `from` and to\n * nothing else. The publish is therefore fenced on `from` **and on the row it was read from**\n * (`publishGeneration`'s `expectFrom` + `expectToken`) and lands only while the pointer is still exactly there,\n * on that same row. Both halves are needed: a generation number identifies a generation only within one\n * incarnation of a name, and a name that is retired and re-created restarts at `0`, so the number alone matched\n * a different segment entirely. Anything else \u2014 a load that published in the meantime, or another\n * erasure that got there first \u2014 is reported as `reason: 'superseded'`, `erased: false`, and the caller re-runs\n * against the new generation. A writer that claims the same generation *number* first surfaces as a\n * `WriteConflictError` (write-once).\n *\n * The same answer covers the read half, and for the same reason. An erasure collects with `keep: 0`, which takes\n * every generation below its new pointer \u2014 so a racing erasure can delete the generation this call is streaming,\n * and (once it is below the winner's pointer) the object this call just wrote. All three exposed round trips (the\n * reader open, any chunk of the whole-segment rewrite, the verify) therefore report a *reason* rather than a bare\n * `NotFoundError`, which was the same event wearing an unactionable face: through `eraseSubject` it became a note\n * the facade documents as ambiguous about which side of the publish it landed on, telling an operator to triage\n * where the truth was to re-run.\n *\n * Which reason comes from **re-reading the row**, not from assuming a supersession: a moved pointer is\n * `'superseded'`, a tombstoned row `'destroyed'`, a purged row `'absent'`, a row with no pointer\n * `'no-generation'`. A pointer still on `from` means the object it names is genuinely gone \u2014 the forbidden\n * `missing-storage-generation` state \u2014 and that throws, because no re-run fixes it. That `NotFoundError` is the only\n * signal of that state, so a faulting re-read rethrows it rather than replacing it with a transient-looking\n * registry error.\n *\n * **A refused rewrite deletes its own object when it would outlive the winner above the pointer.** `putImmutable`\n * commits atomically, so a rewrite whose stream throws leaves no object at all. One that completed is either\n * below the winner's pointer, where the winner's collection takes it, or above it: the case where this call took\n * its number after the winner's object was already in the bucket. Up there nothing collects it, and it was derived\n * from a generation the winner has replaced, so when the winner was another erasure it still holds the id that\n * erasure has just reported gone. That object is deleted before this call returns \u2014 see `discardRefused`.\n *\n * Without that fence the failure was silent and severe, and both halves were reproduced: `nextGeneration` picks a\n * number above everything in the bucket, so a forward-only publish here always won \u2014 discarding a concurrent\n * load's whole set, and letting two concurrent erasures each return `erased: true` while the second one's\n * generation put the first one's id back and collected the generation that had evidenced its removal. A false\n * Art. 17 receipt is the worst output this module can produce.\n *\n * A live read still fetching from the collected generation heals forward to the rewrite, and a pin of it fails with\n * `NotFoundError` for any chunk it has yet to read: that is the documented cost of physical deletion on return. **Do not re-load the id while erasing it**: a load that lands after this rewrite\n * carries whatever its source held, and the library cannot know that source was meant to exclude the id.\n * A load already in flight is the same case: it writes its object above the pointer before it publishes, and an\n * erasure that finds the id in that object deletes it as it does any holder there, so a load that then publishes\n * leaves the pointer naming a missing object.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { MAX_REMAINDER, splitId } from './bit-route';\nimport type { CodecBitmap, CodecInterface } from './codec';\nimport { requireCodec } from './codec';\nimport type { Yielder } from './cooperative';\nimport {\n openGenerationReader,\n publishGeneration,\n verifyGeneration,\n writeCrbmGenerationStream,\n} from './crbm-storage-source';\nimport type { CrbmReader } from './crbm/reader';\nimport { aadFor } from './crypto';\nimport type { Aead, CrbmCrypto, IKeystore } from './crypto';\nimport {\n IntegrityError,\n KeyUnavailableError,\n ValidationError,\n WriteConflictError,\n isNotFoundError,\n} from './errors';\nimport { gcOrphanGenerations, nextGeneration } from './generation-gc';\nimport type { GenKey, IStorageDriver, IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\nimport { validateUserRef } from './validate';\n\nconst DEFAULT_MAX_BITMAP_BYTES = 1 << 20;\n\n/** What {@link eraseIdFromSegment} needs: the objects, the pointer, the codec, and the key material if encrypted. */\nexport interface EraseIdDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n /**\n * Bitmap codec \u2014 the rewrite decodes and re-encodes every chunk through it. Optional in the type so this stays\n * call-compatible public API; a **flavor** package binds it (see {@link requireCodec}).\n */\n readonly codec?: CodecInterface;\n /**\n * Keystore for an **encrypted** segment: its registry record carries wrapped DEK(s), and the rewrite reuses\n * that DEK \u2014 unwrapping it to decrypt the old generation and encrypt the new one under a `(segment, generation)`\n * -bound AAD. Rewriting an encrypted segment without a keystore throws {@link KeyUnavailableError}.\n */\n readonly keystore?: IKeystore;\n /** When true, refuse to rewrite a **cleartext** segment (the same guard the read path and a load offer). */\n readonly requireEncryption?: boolean;\n /** Supplying a clock that can yield makes the rewrite cooperative, as it makes a load. */\n readonly clock?: Yielder;\n /** Per-chunk decode ceiling (invariant 5); defaults to 1 MiB. */\n readonly maxBitmapBytes?: number;\n}\n\nexport interface EraseIdResult {\n readonly segment: string;\n readonly namespace?: string;\n /**\n * True iff the id **was** in the segment \u2014 in its current generation, or in any other generation still in the\n * bucket, above the pointer or below it \u2014 and **no generation of the segment holds it now**. The current\n * generation is one without it, every generation that held it has been deleted (see `collected`), and the call\n * listed the bucket and read what was left before saying so. This is the field an erasure ledger records.\n */\n readonly erased: boolean;\n /**\n * Why the id was not erased, when `erased` is false. `'absent'` (no registry row), `'destroyed'` (a crypto-shred\n * tombstone \u2014 already unreadable), `'no-generation'` (a row with no Storage data yet), `'not-member'` (no\n * generation in the bucket holds the id \u2014 the common case across a fleet scan), or `'superseded'`.\n *\n * **`'superseded'` means this call did not erase the id, not that the id is still there.** Another writer \u2014 a\n * load, another erasure, or a rollback \u2014 moved the pointer off the generation this call read, so what it was\n * doing no longer follows from what is current: a rewrite derived from that generation is not a valid successor\n * to the new one, and a generation it meant to delete above the pointer may be the one the pointer now names.\n * Re-run against the new generation: if the id is still present it is erased then; if the racing writer was\n * another erasure of the *same* id, the re-run reports `'not-member'` because it is already gone. Either way the\n * re-run settles it, which is why it is the documented action for this reason and for no other.\n *\n * The first three reasons are also returned when the row changes *underneath* a call already in flight, not\n * only when it is read up front \u2014 a concurrent `dropSegment` gives `'destroyed'`, a retention sweep that purges\n * the row gives `'absent'`. A caller branching on `reason` never has to care which it was.\n */\n readonly reason?: 'absent' | 'destroyed' | 'no-generation' | 'not-member' | 'superseded';\n /**\n * The generation the id was found in (present whenever one was read) \u2014 the newest of them, when the current\n * generation did not hold it and several others did.\n */\n readonly fromGeneration?: number;\n /**\n * The generation written without the id (present whenever one was written, even if `superseded`). A refused\n * rewrite whose object sits above the winner's pointer deletes that object again before it returns.\n */\n readonly generation?: number;\n /**\n * Generations this call deleted. On a rewrite that is normally `[fromGeneration]`, plus any older orphans. When\n * the current generation did not hold the id, it is every generation below the pointer (`keep: 0` takes them\n * together) and each one above the pointer that held the id; the ones above it that did not hold the id stay,\n * because they are an operator's rollback targets. Empty when nothing was deleted.\n *\n * **It is what THIS call deleted, not the proof that the id is gone** \u2014 those differ: a concurrent collector can\n * take a generation holding the id first, and then `erased: true` is returned without it in `collected`, because\n * the claim is about the bucket rather than about who emptied it. Treat a non-empty `collected` as evidence and\n * a missing holder as \"someone else got there\", never as a failure.\n *\n * A call that cannot establish the claim **throws** `WriteConflictError` or the underlying fault rather than\n * report `erased: true` over bytes that are still there. Four ways that happens: a Storage `delete` fault; a\n * collection pass that could not prove the segment was still the same one (which an ordinary retirement landing\n * mid-call is enough to cause); the same refusal on the path where the current generation did not hold the id\n * and nothing was published at all; and a generation still holding the id when the bucket is listed at the end\n * \u2014 one an operator rolled the pointer onto while a rewrite was collecting, say.\n *\n * **Re-run it**, and read what the re-run says rather than assuming it finished the job. The re-run looks for\n * the id in every generation in the bucket, not only the current one, and there are three outcomes:\n *\n * - `erased: true` naming the generation it found the id in \u2014 the ordinary case, and the one that gives you\n * the receipt the failed call could not;\n * - `'not-member'` \u2014 a racing collector took that generation first. The bit is gone, but **no run reports a\n * receipt for it**, so keep the failed call's error alongside your ledger if you need the audit trail;\n * - `'absent'` \u2014 the segment's registry row is gone, so it is no longer a segment at all and\n * `store.eraseSubject` will not even scan it. Anything left in the bucket is an **orphan**: list it with\n * `listGenerations` (`store.generations`) and delete it with `dropSegment` (`store.dropSegment`), which both\n * read the bucket as well as the row. `checkConsistency()` and the generation collection start from the row, so\n * neither reaches a segment that has none.\n */\n readonly collected: readonly number[];\n}\n\n/**\n * Remove `id` from `ref`: rewrite its current generation without it, and delete every other generation that holds\n * it. See the module note for the contract.\n *\n * Emits one `segment.rewrite` audit event at the publish (before the superseded generation is collected), so the\n * compliance record exists the moment the generation without the id is authoritative.\n */\nexport async function eraseIdFromSegment(\n ref: SegmentRef,\n id: number,\n deps: EraseIdDeps,\n options: { audit?: IAuditSink } = {},\n): Promise<EraseIdResult> {\n validateUserRef(ref);\n const { chunkKey, remainder } = splitId(id); // validates the u32 range\n const codec = requireCodec(deps.codec, 'eraseIdFromSegment');\n const maxBytes = deps.maxBitmapBytes ?? DEFAULT_MAX_BITMAP_BYTES;\n const base = { segment: ref.segment, namespace: ref.namespace };\n\n const record = await deps.registry.get(ref);\n if (record === null) return { ...base, erased: false, reason: 'absent', collected: [] };\n if (record.status === 'destroyed')\n return { ...base, erased: false, reason: 'destroyed', collected: [] };\n if (record.currentGen === null)\n return { ...base, erased: false, reason: 'no-generation', collected: [] };\n const from = record.currentGen;\n /**\n * The row this rewrite derives its content from, not just the number it points at. `nextGeneration` restarts\n * at `0` once a row is purged and the bucket emptied, so a retired-and-re-created name presents a *different*\n * segment at the *same* `currentGen` \u2014 which `expectFrom` alone matched, republishing one incarnation's\n * content over another's and reporting `erased: true`. The token is never reused across incarnations.\n */\n const fromToken = record.token;\n\n // The segment's DEK, reused across generations. Resolved before any object I/O so a slow keystore/KMS call\n // sits outside the read-write window; an encrypted row with no keystore is a lost key, not a cleartext segment.\n let aead: Aead | undefined;\n if (record.wrappedDeks !== undefined && record.wrappedDeks.length > 0) {\n if (deps.keystore === undefined) {\n throw new KeyUnavailableError(\n `segment \"${ref.segment}\" is encrypted but eraseIdFromSegment has no keystore`,\n );\n }\n aead = await deps.keystore.openDek(record.wrappedDeks);\n } else if (deps.requireEncryption === true) {\n throw new ValidationError(`requireEncryption: segment \"${ref.segment}\" is cleartext`);\n }\n // The AAD binds each chunk/index to (segment, generation), so reading gen g and writing gen g' use distinct\n // contexts over the same DEK.\n const cryptoAt = (generation: number): CrbmCrypto | undefined =>\n aead === undefined ? undefined : { aead, aadFor: (scope) => aadFor(ref, generation, scope) };\n\n const fromKey: GenKey = { ...base, generation: from };\n const refused = (\n reason: 'superseded' | 'absent' | 'no-generation' | 'destroyed',\n generation?: number,\n ): EraseIdResult => ({\n ...base,\n erased: false,\n reason,\n fromGeneration: from,\n generation,\n collected: [],\n });\n\n /**\n * What the row says about the premise this call was working from \u2014 `null` when the premise still holds\n * (the pointer is exactly `from`, so nothing raced us), otherwise the reason to report.\n *\n * Each state gets the answer this function already gives when it reads that state *up front*, so a caller\n * branching on `reason` never has to care at which point in the call it was discovered: a tombstoned row is\n * `'destroyed'` (a concurrent `dropSegment` leaves `currentGen` where it was, so testing the pointer alone\n * would miss it and report an error for a segment the operator deliberately dropped), a vanished row is\n * `'absent'` (the retention sweep purged a tombstone while we worked), a row with no pointer is\n * `'no-generation'`, and a pointer that moved is `'superseded'`.\n */\n const rowVerdict = (\n row: RegistryRecord | null,\n ): 'superseded' | 'absent' | 'no-generation' | 'destroyed' | null => {\n if (row === null) return 'absent';\n if (row.status === 'destroyed') return 'destroyed';\n if (row.currentGen === null) return 'no-generation';\n // A different row is a different lineage even at the same pointer value \u2014 see `fromToken`.\n return row.currentGen === from && row.token === fromToken ? null : 'superseded';\n };\n\n /**\n * Whether `generation` still holds the id: its index is opened, and the id's chunk is fetched only if the index\n * lists it. `null` when the object is gone \u2014 a concurrent collector took it, which is not a failure of this call\n * but the outcome it wants. Any other fault propagates: it must never be swallowed into a clean receipt.\n */\n const holds = async (generation: number): Promise<boolean | null> => {\n try {\n const reader = await openGenerationReader(\n deps.storage,\n { ...base, generation },\n cryptoAt(generation),\n );\n const bytes = await reader.getChunk(chunkKey);\n return bytes !== null && codec.safeDeserialize(bytes, maxBytes).has(remainder);\n } catch (err) {\n if (isNotFoundError(err)) return null;\n throw err;\n }\n };\n\n /**\n * The receipt check, and the one place `erased: true` is decided: the newest generation still in the bucket that\n * holds the id, or `undefined` when none does. `clean` names the generations this call has already read or\n * written without the id; every other generation present is read now.\n *\n * It verifies the **claim** \u2014 no generation of the segment holds the id \u2014 rather than what this call deleted,\n * in both directions. A holder missing from `collected` does not mean it survived: a concurrent collector\n * (the generation collection with `keep: 0`, which this call and a retirement run) may simply have\n * taken it first, and then the claim is true no matter who made it true. And a collection pass that declined\n * does not mean nothing was left: it returns an empty list when the row was gone as it started, the same value\n * it returns when there was genuinely nothing to collect, and a retirement only has to land between this call's\n * publish and its collect for that to happen.\n *\n * Nor is the generation this call replaced the only one to look at. A rollback can move the pointer onto a\n * holder while the collection runs, and the collection then takes the lower pointer as its bound and leaves\n * that holder current; and a concurrent writer that derived its object from the generation this call replaced\n * can leave one above the pointer. So the list is of everything, and in the ordinary case it returns only the\n * current generation, which is in `clean`: one `list`, and no read.\n */\n const holderLeft = async (clean: ReadonlySet<number>): Promise<number | undefined> => {\n const present = new Set<number>();\n for await (const key of deps.storage.list(ref)) present.add(key.generation);\n for (const generation of [...present].sort((a, b) => b - a)) {\n if (!clean.has(generation) && (await holds(generation)) === true) return generation;\n }\n return undefined;\n };\n\n const cannotRemove = (generation: number): WriteConflictError =>\n new WriteConflictError(\n `erasure of segment ${ref.segment} could not remove generation ${generation}, which still holds the id and is still in the bucket; re-run`,\n );\n\n /**\n * The id is not in the current generation \u2014 which is **not** the same as not being in the bucket.\n *\n * A re-seed that simply stops including someone leaves their bit in the generation it dropped them from, and\n * the collection a load runs after it publishes (default `keep: 1`) *retains* exactly that\n * generation as the reader grace window. So the ordinary lifecycle of a rotating audience leaves an ex-member's\n * bit sitting in a retained object. Answering `'not-member'` there would filter the segment out of the\n * `eraseSubject` ledger entirely: a clean Art. 17 receipt over bytes still in the bucket, for the one\n * population most likely to be asking, people who already left.\n *\n * And the generations **above** the pointer count too. After a rollback they include the ones it rolled back\n * from, which `rollbackSegment` with `allowForward` makes current again, so a holder there is one ordinary\n * operator action from being served. There can be several: every generation loaded after the one the pointer\n * was rolled back to.\n *\n * So look, at all of them. A holder cannot be rewritten \u2014 a rewrite of a non-current generation would regress\n * the pointer \u2014 so the only remedy is to delete it:\n *\n * - **Below the pointer**, `gcOrphanGenerations` with `keep: 0` takes every generation at once, re-proving the\n * row before each delete. It costs the segment its grace window and its older rollback targets, which is\n * proportionate: only a segment that genuinely held the subject pays it.\n * - **Above the pointer**, collection never looks, so each holder is deleted here, and only the holders: a\n * generation up there without the id is still an operator's rollback target and stays. This is the one place\n * the library deletes an above-pointer object that it did not write, and it costs `rollbackSegment` a\n * target, deliberately: a rollback point that still contains data we were required to erase is not a\n * rollback point, and keeping it would make the erasure undoable by an ordinary operator action. Each delete\n * is re-proved against the row first, as collection's are, because the danger is the same one: a rollback\n * that lands on a generation this call has queued, which would leave the pointer naming a missing object.\n * If the pointer has moved at all, the deletes stop. One round trip remains between that read and the delete,\n * exactly as in collection's own loop: a rollback that lands inside it onto the generation being deleted\n * leaves the pointer naming a missing object. The rollback's own move-then-verify catches every such landing\n * except one whose check runs before the delete, and no storage port offers a conditional delete to close it.\n *\n * Then {@link holderLeft} decides. If nothing in the bucket holds the id, the claim is true however the pointer\n * moved meanwhile: a load that published mid-call puts every holder below its pointer, where the collection\n * takes them. If something still holds it and the pointer moved, the row says why, as it does everywhere else in\n * this function. If something holds it and the pointer did not move, the delete that should have taken it did\n * not, and that throws.\n *\n * **The cost is paid only where it is owed.** The cheap filter comes first: one `list`, and if the segment has no\n * other generations at all \u2014 true of any store that collects with `keep: 0`, and of a segment loaded once \u2014\n * nothing else is read. Then per generation, the index is opened and the chunk is fetched only if the index\n * says that chunk exists: every generation above the pointer, since each holder there must be found; and below\n * it only until the first holder, since `keep: 0` takes the rest regardless \u2014 and not at all once a holder was\n * found above. `eraseSubject` fans this out across every registered segment, so the filter is what keeps a\n * fleet-wide subject scan from doubling its reads on segments that never held the id.\n */\n const notInCurrent = async (): Promise<EraseIdResult> => {\n const notMember: EraseIdResult = {\n ...base,\n erased: false,\n reason: 'not-member',\n fromGeneration: from,\n collected: [],\n };\n const others = new Set<number>();\n for await (const key of deps.storage.list(ref)) {\n if (key.generation !== from) others.add(key.generation);\n }\n if (others.size === 0) return notMember;\n const newestFirst = [...others].sort((a, b) => b - a);\n\n const clean = new Set<number>([from]);\n const holdersAbove: number[] = []; // newest first\n for (const generation of newestFirst.filter((g) => g > from)) {\n const held = await holds(generation);\n if (held === true) holdersAbove.push(generation);\n else if (held === false) clean.add(generation);\n }\n let holderBelow: number | undefined;\n if (holdersAbove.length === 0) {\n for (const generation of newestFirst.filter((g) => g < from)) {\n const held = await holds(generation);\n if (held === true) {\n holderBelow = generation;\n break;\n }\n if (held === false) clean.add(generation);\n }\n }\n const newest = holdersAbove[0] ?? holderBelow;\n if (newest === undefined) return notMember;\n\n const collected = [...(await gcOrphanGenerations(ref, deps, { keep: 0 }))];\n let moved: ReturnType<typeof rowVerdict> = null;\n for (const generation of holdersAbove) {\n moved = rowVerdict(await deps.registry.get(ref));\n if (moved !== null) break;\n await deps.storage.delete({ ...base, generation });\n collected.push(generation);\n }\n\n const left = await holderLeft(clean);\n if (left === undefined) return { ...base, erased: true, fromGeneration: newest, collected };\n if (moved !== null) {\n return { ...base, erased: false, reason: moved, fromGeneration: newest, collected };\n }\n throw cannotRemove(left);\n };\n\n /**\n * Read `from`, write its successor, and verify it \u2014 the read-modify-write whose premise is that the pointer is\n * still at `from`. Returns a staged generation, or the result to report if the premise stopped holding.\n *\n * **Every object this phase touches can be deleted underneath it, by a concurrent call of this very\n * function.** An erasure collects with `keep: 0`, which takes every generation below its new pointer \u2014 so a\n * racing erasure deletes `from` while we are still streaming it, and deletes *the object we just wrote* too,\n * since ours is below its pointer and can never become current. There are three separate round trips exposed:\n * the reader open, every chunk of the rewrite (a whole-segment read \u2014 by far the longest window), and the\n * verify.\n *\n * The documented answer to that race is `reason: 'superseded'` \u2014 *this call did not erase it; re-run against\n * the new generation* \u2014 and it is what the publish already reports when its `expectFrom` fence fails. A bare\n * `NotFoundError` instead is the same event wearing a different, unactionable face; through `eraseSubject` it\n * becomes a note the facade documents as ambiguous about which side of the publish it landed on, so an\n * Art. 17 operator is told *triage this* where the truth is *re-run*.\n *\n * **A `NotFoundError` is translated only after re-reading the row, and the row decides which answer.** The\n * pointer still at exactly `from` means the object it names is genuinely absent \u2014 the forbidden\n * `missing-storage-generation` state a failed publish leaves behind \u2014 and that **throws**, because it is an\n * integrity problem rather than a race and no re-run fixes it. A pointer that moved is `'superseded'`. The\n * other two states are not supersessions and are not reported as one: the row **gone** (the retention sweep\n * purged a tombstone while we worked) is `'absent'`, and a row whose `currentGen` is `null` is\n * `'no-generation'` \u2014 the same answers this function gives when it reads either state up front, so a caller\n * branching on `reason` never has to special-case where in the call it was discovered.\n *\n * `generation` is set only once the object is **durable**. Reporting it from `nextGeneration` was wrong in a\n * way worth naming: if the row is gone by then, `nextGeneration` finds no row and no objects and restarts at\n * `0`, so the ledger read `fromGeneration: 0 \u2192 generation: 0` \u2014 the generation that *held* the bit named as\n * the one written *without* it, for an object that was never written.\n */\n const stage = async (): Promise<EraseIdResult | { generation: number; key: GenKey }> => {\n /** Set only once the object exists in the bucket \u2014 see the note above. */\n let written: number | undefined;\n try {\n const reader = await openGenerationReader(deps.storage, fromKey, cryptoAt(from));\n const bytes = await reader.getChunk(chunkKey);\n // `return await`, not `return`: the sweep's rejection must land in the `catch` below, which is where a\n // `NotFoundError` is translated by re-reading the row. A bare `return` of the promise hands it past the `try`.\n if (bytes === null) return await notInCurrent();\n const target = codec.safeDeserialize(bytes, maxBytes);\n assertRemaindersInRange(target, chunkKey); // invariant 5, on the chunk we are about to re-encode\n if (!target.has(remainder)) return await notInCurrent();\n target.remove(remainder);\n\n const generation = await nextGeneration(ref, deps);\n const key: GenKey = { ...base, generation };\n const tally = await writeCrbmGenerationStream(\n deps.storage,\n key,\n rewrite(reader, chunkKey, target, codec, maxBytes),\n { crypto: cryptoAt(generation), clock: deps.clock },\n );\n written = generation; // `putImmutable` commits atomically, so the object exists exactly now\n // Re-check the pointer before spending the verification read. The fence below is what makes the publish\n // correct; this only saves a round trip on the common race, which the catch reports either way.\n const beforeVerify = await deps.registry.get(ref);\n const early = rowVerdict(beforeVerify);\n if (early !== null) return refused(early, written);\n await verifyGeneration(deps.storage, key, tally, cryptoAt(generation));\n return { generation, key };\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n // The row decides which answer this is \u2014 but only if it can be read. A re-read that faults must not\n // replace the `NotFoundError`: that error is the ONLY signal of the integrity state below, and swapping\n // it for a transient-looking registry fault loses the one thing an operator needs to see.\n let now: RegistryRecord | null;\n try {\n now = await deps.registry.get(ref);\n } catch {\n throw err;\n }\n const verdict = rowVerdict(now);\n if (verdict !== null) return refused(verdict, written);\n throw err; // the pointer still names the missing object: genuinely absent, not a race\n }\n };\n\n /**\n * Delete the object a refused rewrite wrote, when it would otherwise outlive the winner above its pointer.\n *\n * Only one position needs it: the pointer moved forward past `from` and stopped **below** `written`. That is\n * where this call took its number after the winner's object was already in the bucket \u2014 `nextGeneration`\n * numbers above everything present \u2014 and it is above the winner's pointer, where no collection ever looks. The\n * object was derived from `from`, so when the winner was another erasure it still holds the id that erasure\n * has just reported gone, one `rollback({ allowForward: true })` from being served, and the newest superseded\n * generation once a later load raises the pointer past it, which is exactly what that load's `keep` retains.\n *\n * Everywhere else the object is left alone, as a refused load leaves its own once the row has changed. At or\n * above `written` the pointer either names it or belongs to a writer that numbered after it and whose\n * collection takes it; at or below `from` nothing replaced `from`; and a row that is gone, tombstoned or\n * without a pointer is not one this call can reason about. The position is read from a fresh row, just before\n * the delete, and the number still names this call's own object: a name re-created since would have had to\n * lose this object and then write that many generations of its own, and only its pointer landing on `written`\n * could make the delete unsafe, which the bound excludes.\n */\n const discardRefused = async (written: number): Promise<void> => {\n const row = await deps.registry.get(ref);\n if (row === null || row.status !== 'active' || row.currentGen === null) return;\n if (row.currentGen <= from || row.currentGen >= written) return;\n await deps.storage.delete({ ...base, generation: written });\n };\n\n const staged = await stage();\n if ('erased' in staged) {\n if (!staged.erased && staged.generation !== undefined) await discardRefused(staged.generation);\n return staged;\n }\n const { generation, key } = staged;\n\n // Read-modify-write, not merely forward-only, and the distinction is the whole correctness of this function.\n //\n // The new generation's content is `from` minus one bit, so it is only a valid successor to `from`. Publishing\n // it forward-only would land it over ANY newer generation, because `nextGeneration` deliberately picks a number\n // above everything in the bucket \u2014 including an object a concurrent writer just published. Two ways that goes\n // wrong: a load that published between our read and our publish would have its whole set discarded and its\n // object collected; and of two concurrent erasures, the second one's generation would put the FIRST one's id\n // back, deleting the generation that evidenced its removal, while both returned `erased: true` with a\n // `segment.rewrite` event. A false Art. 17 receipt is the worst output this module can produce.\n //\n // `expectFrom` makes the publish land only while the pointer is still exactly `from`.\n //\n // Reported, not thrown \u2014 the caller re-runs against the new generation, which may or may not still hold the id.\n const published = await publishGeneration(deps.registry, key, {\n expectFrom: from,\n expectToken: fromToken,\n });\n if (!published) {\n await discardRefused(generation);\n return {\n ...base,\n erased: false,\n reason: 'superseded',\n fromGeneration: from,\n generation,\n collected: [],\n };\n }\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.rewrite',\n namespace: ref.namespace,\n segment: ref.segment,\n fromGeneration: from,\n generation,\n });\n // `keep: 0`: the whole point is that the generation holding the bit does not survive this call. Every\n // generation below the new pointer is safe to take: the CAS succeeded while the pointer was still at `from`, so\n // nothing in `(from, generation)` had been published before us, and no read that resolves the pointer can reach\n // any of them. Deleting them is the strongest form of the \"physically gone on return\" claim rather than a risk to\n // a load still in flight. The pointer can still move DOWN onto one of them \u2014 an operator's `rollbackSegment`,\n // or a purge-and-recreate (invariant 1) \u2014 which is why the collector re-proves the row before every delete\n // rather than trusting the one this call read, and why the receipt below reads what is left rather than\n // assuming.\n const collected = await gcOrphanGenerations(ref, deps, { keep: 0 });\n const left = await holderLeft(new Set([generation]));\n if (left !== undefined) throw cannotRemove(left);\n return { ...base, erased: true, fromGeneration: from, generation, collected };\n}\n\n/**\n * The new generation's chunks, ascending: every chunk of the old generation decoded and passed through, except\n * `chunkKey`, which is replaced by `replacement` (already missing the id). Decoding rather than copying bytes is\n * deliberate \u2014 it puts every chunk through the **safe** deserializer and the size cap on the one path that\n * rewrites a whole segment, so a chunk that is not decodable, or is larger than the cap, stops the rewrite\n * instead of being copied forward \u2014 and the writer skips a chunk the removal emptied. One chunk is live at a\n * time.\n *\n * Both halves of **invariant 5** apply, including the remainder range: a chunk of a 16-bit-keyed segment cannot\n * hold a value above `MAX_REMAINDER`, and one that does was not written by this codec. Carrying it forward would\n * re-encode pre-existing corruption into a brand-new object and then report `erased: true` over a segment that\n * still cannot be read \u2014 so the rewrite stops instead, naming the chunk. An `IntegrityError` here says \"this\n * segment is corrupt\", which is what the operator needs to know; a successful-looking erasure says nothing. The\n * check is one `maximum()` call per chunk against a bitmap this function has already decoded, so honouring the\n * invariant costs nothing measurable on a path that is re-encoding every chunk anyway.\n */\nasync function* rewrite(\n reader: CrbmReader,\n chunkKey: number,\n replacement: CodecBitmap,\n codec: CodecInterface,\n maxBytes: number,\n): AsyncGenerator<{ chunkKey: number; bitmap: CodecBitmap }> {\n const keys = [...reader.chunkKeys()].sort((a, b) => a - b);\n for (const k of keys) {\n if (k === chunkKey) {\n yield { chunkKey: k, bitmap: replacement };\n continue;\n }\n const bytes = await reader.getChunk(k);\n if (bytes === null) continue; // listed but absent: nothing to carry forward\n const bitmap = codec.safeDeserialize(bytes, maxBytes);\n assertRemaindersInRange(bitmap, k);\n yield { chunkKey: k, bitmap };\n }\n}\n\n/**\n * Invariant 5's range half, for one decoded chunk: every value in a chunk is a 16-bit remainder, so a value above\n * `MAX_REMAINDER` means the object was not written by this codec. `maximum()` is optional on the codec seam (a\n * codec with no size decision to make may omit it); when it is absent the check is skipped rather than faked.\n */\nfunction assertRemaindersInRange(bitmap: CodecBitmap, chunkKey: number): void {\n const max = bitmap.maximum?.();\n if (max !== undefined && max > MAX_REMAINDER) {\n throw new IntegrityError(\n `chunk ${chunkKey} payload holds value ${max}, outside the 16-bit remainder range [0, ${MAX_REMAINDER}] \u2014 ` +\n `the stored object is corrupt or was not written by this codec; refusing to carry it into a new generation`,\n );\n }\n}\n", "/**\n * Concrete AES-256-GCM crypto (lives outside `core/`, which lint-bans `node:crypto`). Implements the injected\n * {@link Aead}/{@link IKeystore} seams from `core/crypto`: a `NodeAead` over `aes-256-gcm`, and an in-process\n * **BYOK** keystore that envelope-wraps a per-segment DEK under one or more operator-held KEKs.\n *\n * Default, dependency-free key management: you bring 32-byte KEK(s); we never call a cloud service. KMS/Vault\n * are future optional adapters against the same {@link IKeystore} interface. **The KEK is the\n * one secret to back up** \u2014 lose it and that segment's at-rest bytes are unrecoverable by design (re-seed the\n * segment from your source of truth); see the encryption guide.\n */\nimport { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport type { Aead, AeadSealed, IKeystore, WrappedDek } from '../core/crypto';\nimport { IntegrityError, KeyUnavailableError, ValidationError } from '../core/errors';\n\nconst KEY_BYTES = 32; // AES-256\nconst NONCE_BYTES = 12; // GCM standard nonce\nconst TAG_BYTES = 16; // GCM tag\n\n/** AES-256-GCM AEAD bound to one 32-byte key. A fresh CSPRNG 96-bit nonce per `seal`; `aad` is authenticated,\n * never stored. Nonce-reuse safety: the random-96-bit birthday bound is ~q\u00B2/2\u2079\u2077, negligible below ~2\u00B3\u00B2 seals\n * under one key. A per-segment DEK is **reused across all generations** (every load and rewrite encrypts under it), so\n * seal count is cumulative over the segment's life \u2014 a segment approaching ~2\u00B3\u00B2 lifetime chunk-seals should be\n * re-seeded (a fresh segment \u21D2 fresh DEK). Far beyond any realistic workload; noted for completeness. */\nexport class NodeAead implements Aead {\n private readonly key: Buffer;\n\n constructor(key: Uint8Array) {\n if (key.length !== KEY_BYTES)\n throw new ValidationError(`AEAD key must be ${KEY_BYTES} bytes, got ${key.length}`);\n this.key = Buffer.from(key);\n }\n\n seal(plaintext: Uint8Array, aad: Uint8Array): AeadSealed {\n const nonce = randomBytes(NONCE_BYTES);\n const cipher = createCipheriv('aes-256-gcm', this.key, nonce);\n cipher.setAAD(aad);\n const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n return { nonce, ciphertext, tag: cipher.getAuthTag() };\n }\n\n open(sealed: AeadSealed, aad: Uint8Array): Uint8Array {\n if (sealed.nonce.length !== NONCE_BYTES || sealed.tag.length !== TAG_BYTES)\n throw new IntegrityError('AEAD nonce/tag have the wrong length');\n const decipher = createDecipheriv('aes-256-gcm', this.key, sealed.nonce);\n decipher.setAAD(aad);\n decipher.setAuthTag(Buffer.from(sealed.tag));\n try {\n return Buffer.concat([decipher.update(sealed.ciphertext), decipher.final()]);\n } catch {\n // Wrong key, tampered ciphertext/tag, or mismatched AAD \u2014 never leak which, never return plaintext.\n throw new IntegrityError(\n 'AEAD authentication failed (wrong key, tampered data, or wrong context)',\n );\n }\n }\n}\n\n/** Wrap/unwrap a DEK under a KEK with AES-256-GCM, binding the wrapping to its `keyId` via AAD so a wrapping\n * can't be relabelled to another KEK. Stored blob = nonce \u2016 ciphertext \u2016 tag, base64. */\nfunction wrapKey(dek: Uint8Array, kek: Uint8Array, keyId: string): string {\n const sealed = new NodeAead(kek).seal(dek, new TextEncoder().encode(keyId));\n return Buffer.concat([sealed.nonce, sealed.ciphertext, sealed.tag]).toString('base64');\n}\n\nfunction unwrapKey(wrapped: string, kek: Uint8Array, keyId: string): Uint8Array {\n const blob = Buffer.from(wrapped, 'base64');\n if (blob.length < NONCE_BYTES + TAG_BYTES)\n throw new IntegrityError('wrapped DEK is too short to be valid');\n const nonce = blob.subarray(0, NONCE_BYTES);\n const tag = blob.subarray(blob.length - TAG_BYTES);\n const ciphertext = blob.subarray(NONCE_BYTES, blob.length - TAG_BYTES);\n return new NodeAead(kek).open({ nonce, ciphertext, tag }, new TextEncoder().encode(keyId));\n}\n\n/**\n * Options for {@link InProcessKeystore}. `keys` maps a `keyId` \u2192 its 32-byte KEK; `activeKeyId` names the KEK\n * new DEKs are wrapped under; an optional `recoveryKeyId` additionally wraps each new DEK under a second\n * (e.g. offline) KEK, so losing the active KEK isn't fatal. Rotating the active KEK needs no data\n * re-encryption: add the new KEK, point `activeKeyId` at it, keep the old one to unwrap existing segments.\n */\nexport interface InProcessKeystoreOptions {\n readonly keys: Readonly<Record<string, Uint8Array>>;\n readonly activeKeyId: string;\n readonly recoveryKeyId?: string;\n}\n\n/**\n * Dependency-free BYOK keystore: envelope-wraps a fresh random per-segment DEK under the active (and optional\n * recovery) KEK using `node:crypto`. No external service. The raw DEK never leaves a returned {@link NodeAead}.\n */\nexport class InProcessKeystore implements IKeystore {\n private readonly keys: Map<string, Uint8Array>;\n private readonly activeKeyId: string;\n private readonly recoveryKeyId: string | undefined;\n\n constructor(options: InProcessKeystoreOptions) {\n const entries = Object.entries(options.keys);\n if (entries.length === 0) throw new ValidationError('keystore needs at least one KEK');\n for (const [id, k] of entries) {\n if (k.length !== KEY_BYTES)\n throw new ValidationError(`KEK \"${id}\" must be ${KEY_BYTES} bytes, got ${k.length}`);\n }\n if (!(options.activeKeyId in options.keys))\n throw new ValidationError(`activeKeyId \"${options.activeKeyId}\" is not in keys`);\n if (options.recoveryKeyId !== undefined && !(options.recoveryKeyId in options.keys))\n throw new ValidationError(`recoveryKeyId \"${options.recoveryKeyId}\" is not in keys`);\n this.keys = new Map(entries.map(([id, k]) => [id, Buffer.from(k)]));\n this.activeKeyId = options.activeKeyId;\n this.recoveryKeyId =\n options.recoveryKeyId === options.activeKeyId ? undefined : options.recoveryKeyId;\n }\n\n // async to satisfy the IKeystore seam (a KMS adapter awaits a network call here); the in-process path is\n // synchronous, but staying async keeps unwrap failures as rejections, matching a remote keystore.\n async createDek(): Promise<{ wrapped: WrappedDek[]; aead: Aead }> {\n const dek = randomBytes(KEY_BYTES);\n const ids = this.recoveryKeyId ? [this.activeKeyId, this.recoveryKeyId] : [this.activeKeyId];\n const wrapped = ids.map((keyId) => ({\n keyId,\n wrapped: wrapKey(dek, this.keys.get(keyId)!, keyId),\n }));\n return { wrapped, aead: new NodeAead(dek) };\n }\n\n // async for the same reason as createDek \u2014 a synchronous unwrap failure surfaces as a promise rejection.\n async openDek(wrapped: readonly WrappedDek[]): Promise<Aead> {\n // Try EVERY held wrapping, not just the first: a DEK is wrapped under both the active KEK and (if\n // configured) the offline recovery KEK precisely so a damaged/tampered active-KEK wrapping can be\n // recovered from the other. Returning on the first held keyId and letting a corrupt unwrap throw would\n // defeat that insurance \u2014 the recovery wrapping would never be attempted. So catch a failed unwrap and\n // fall through to the next held wrapping; only give up once none succeeds.\n let anyKekHeld = false;\n let lastUnwrapError: unknown;\n for (const w of wrapped) {\n const kek = this.keys.get(w.keyId);\n if (kek === undefined) continue;\n anyKekHeld = true;\n try {\n return new NodeAead(unwrapKey(w.wrapped, kek, w.keyId));\n } catch (err) {\n lastUnwrapError = err; // held KEK but this wrapping is corrupt/tampered \u2014 try the next one\n }\n }\n // A KEK WAS held but every held wrapping failed to unwrap \u2192 the wrapping(s) are corrupt/tampered, not\n // missing. Surface that integrity failure in its original type rather than masking it as \"no held KEK\".\n if (anyKekHeld) throw lastUnwrapError;\n const have = [...this.keys.keys()].join(', ') || '(none)';\n const need = wrapped.map((w) => w.keyId).join(', ') || '(none)';\n throw new KeyUnavailableError(\n `no held KEK can unwrap this DEK \u2014 have [${have}], wrappings reference [${need}]. ` +\n `Restore the missing KEK (or its recovery KEK), or rebuild the segment from source.`,\n );\n }\n}\n", "/**\n * Whole-segment erasure: crypto-shred (`destroySegment`, `eraseNamespace`) and disposal (`dropSegment`).\n *\n * GDPR \"right to erasure\" on immutable, backed-up storage: you can't delete a `.crbm` object from every\n * backup, but you can **delete its key**. `destroySegment` drops the segment's wrapped DEK(s) from the registry\n * (a CAS to a `destroyed` tombstone) \u2014 the encrypted Storage bytes are then permanently unreadable, everywhere,\n * forever. **Immediate + irreversible**, so it's gated behind an explicit confirmation (name the exact\n * segment/namespace). A `destroyed` segment reads as empty (its DEK is gone). Only works on an **encrypted**\n * segment; a cleartext segment has no key to shred.\n *\n * The tombstone is also the fence every writer respects: a load refuses\n * a `destroyed` row, so a load racing an erasure cannot resurrect the segment. A single id's erasure is a different\n * operation \u2014 `eraseIdFromSegment` rewrites the generation without it.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { mapWithConcurrency } from './concurrency';\nimport { ValidationError, WriteConflictError, isWriteConflictError } from './errors';\nimport type { IStorageDriver, IRegistryDriver, SegmentRef } from './ports';\nimport { validateUserNamespace, validateUserRef } from './validate';\nimport { DEFAULT_MAX_SCAN_SEGMENTS, drainRegistry } from './registry-scan';\n\nexport interface EraseDeps {\n readonly registry: IRegistryDriver;\n}\n\nexport interface DestroyResult {\n readonly segment: string;\n readonly namespace?: string;\n /** True iff the segment is now a `destroyed` tombstone (incl. the idempotent already-destroyed case). */\n readonly destroyed: boolean;\n /**\n * True iff **this call** performed a genuine crypto-shred \u2014 an encrypted segment whose wrapped DEK(s) were\n * dropped, so its Storage bytes are now permanently unreadable. False for a cleartext tombstone (bytes remain\n * readable), for the idempotent already-destroyed case, and for an absent segment. This is what the\n * `segment.erase` audit event keys off \u2014 a cleartext \"erase\" is not an irreversible destruction.\n */\n readonly cryptoShredded: boolean;\n /**\n * Why this segment was not a fresh crypto-shred. `'absent'` (no registry row), `'already'` (already\n * destroyed), `'cleartext'` (no DEK to shred) \u2014 and from `eraseNamespace` only, where per-segment faults are\n * isolated so one failure cannot discard the ledger: `'contended'` (the row was rewritten during every CAS\n * attempt) or `` `failed: <message>` `` (any other fault).\n *\n * The last two come with `destroyed: false` and mean **the segment still holds data**. A namespace erase\n * returns them rather than throwing, so its entries have to be inspected \u2014 see {@link eraseNamespace}.\n */\n readonly reason?: string;\n}\n\nconst MAX_CAS_ATTEMPTS = 8;\n/**\n * Storage deletes in flight at once. Erasure is a fan-out over *independent* keys, so serial would pay one\n * round-trip per generation for work that has no ordering between items; unbounded would be a self-inflicted\n * thundering herd against one bucket. Not configurable: an admin path called by a human or a nightly job.\n */\nconst ERASE_CONCURRENCY = 8;\n/**\n * Storage list-then-delete passes a drop will make before giving up and reporting the residual.\n *\n * Two is the honest floor and three is the working value: pass 1 clears what was there, pass 2 catches an object\n * a load was still writing when the tombstone landed (its publish is then refused, but the object write\n * completes), pass 3 covers a second such writer. It terminates regardless \u2014 the tombstone hard-fences\n * *publishing* a new generation, so the supply of late objects is whatever was already mid-write, and any\n * residual is reported rather than silently dropped.\n */\nconst MAX_STORAGE_SWEEPS = 3;\n\n/**\n * Crypto-shred one segment. **Irreversible.** `confirmSegment` must equal `ref.segment` (a guard against an\n * accidental destroy \u2014 you must name the exact segment). A non-encrypted segment is rejected unless you opt in\n * via `allowCleartext` (there's no key to shred; its Storage bytes stay readable \u2014 the tombstone only stops the\n * segment resolving).\n */\nexport async function destroySegment(\n ref: SegmentRef,\n deps: EraseDeps,\n options: { confirmSegment: string; allowCleartext?: boolean; audit?: IAuditSink },\n): Promise<DestroyResult> {\n validateUserRef(ref);\n if (options.confirmSegment !== ref.segment) {\n throw new ValidationError(\n `destroySegment: confirmSegment must equal the segment name \"${ref.segment}\" (guard against accidental crypto-shred)`,\n );\n }\n const result = await shredSegment(ref, deps, options.allowCleartext ?? false);\n // Audit only a genuine crypto-shred \u2014 not the idempotent already-destroyed/absent no-ops, and not a\n // cleartext tombstone (whose Storage bytes stay readable, so it is not an irreversible erasure).\n if (result.cryptoShredded) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.erase',\n namespace: ref.namespace,\n segment: ref.segment,\n });\n }\n return result;\n}\n\n/**\n * Crypto-shred every segment in a namespace. **Irreversible.** `confirmNamespace` must equal `namespace`.\n * Returns a per-segment result (skips cleartext segments unless `allowCleartext`).\n *\n * **Bounded.** It lists the whole namespace before it destroys anything, and holds the listing resident, so the\n * listing is capped at `maxScanSegments` (default {@link DEFAULT_MAX_SCAN_SEGMENTS}, 250,000), the ceiling every\n * other fleet scan keeps. A namespace over it throws `BudgetExceededError` **before any segment is destroyed**,\n * never part-way through; raise `maxScanSegments` when the namespace really is that large and the memory is there.\n * A `maxScanSegments` that is not a finite number >= 1 throws `ValidationError`, again before anything is destroyed.\n */\nexport async function eraseNamespace(\n namespace: string,\n deps: EraseDeps,\n options: {\n confirmNamespace: string;\n allowCleartext?: boolean;\n audit?: IAuditSink;\n maxScanSegments?: number;\n },\n): Promise<{ destroyed: DestroyResult[] }> {\n if (typeof namespace !== 'string' || namespace.length === 0) {\n throw new ValidationError('eraseNamespace: namespace must be a non-empty string');\n }\n validateUserNamespace(namespace);\n if (options.confirmNamespace !== namespace) {\n throw new ValidationError(\n `eraseNamespace: confirmNamespace must equal the namespace \"${namespace}\" (guard against accidental erasure)`,\n );\n }\n // The whole listing is drained, and bounded, before the first segment is destroyed: a namespace over the\n // ceiling is refused with nothing erased, not abandoned part-way through an irreversible loop.\n const rows = await drainRegistry(deps.registry, {\n namespace,\n maxScanSegments: options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS,\n op: 'eraseNamespace',\n narrowable: false, // already one namespace: the only remedy is a higher ceiling\n });\n const refs: SegmentRef[] = rows.map((rec) => ({\n namespace: rec.namespace,\n segment: rec.segment,\n }));\n const audit = safeAudit(options.audit ?? NOOP_AUDIT);\n const destroyed: DestroyResult[] = [];\n let segmentsShredded = 0;\n for (const ref of refs) {\n // Per-segment faults stay isolated so one failure cannot discard the ledger, mirroring `eraseSubject`'s\n // entries (\"one failure never aborts the ledger\") and for the same reason: on an erasure command the\n // caller's load-bearing question is *which segments are now destroyed*, and an exception thrown from the\n // middle of the loop answers it for none of them while having already destroyed some.\n //\n // Every error is caught, not just contention \u2014 again matching `eraseSubject`, which records any thrown\n // fault as a ledger entry rather than deciding which faults deserve a record. A `KeyUnavailableError` on\n // segment 3 is exactly as important to report as a `WriteConflictError`, and just as segment-local.\n //\n // NOTE the caller's obligation, because this is quieter than a throw: entries must be INSPECTED.\n // `destroyed: false` with a `reason` is a segment that still holds data. The `namespace.erase` audit event\n // carries the honest `segmentsShredded` count, which will be lower than the segment count, so an audit\n // trail still shows the shortfall even if the return value is ignored.\n let result: DestroyResult;\n try {\n result = await shredSegment(ref, deps, options.allowCleartext ?? false);\n } catch (err) {\n result = {\n segment: ref.segment,\n namespace: ref.namespace,\n destroyed: false,\n cryptoShredded: false,\n reason: isWriteConflictError(err)\n ? 'contended'\n : `failed: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n destroyed.push(result);\n // A per-segment record for each genuine crypto-shred, so the trail can prove *which* segments were\n // destroyed (each carries the namespace) \u2014 not just that the command ran.\n if (result.cryptoShredded) {\n segmentsShredded += 1;\n audit.onEvent({ kind: 'segment.erase', namespace: ref.namespace, segment: ref.segment });\n }\n }\n // Plus one namespace-level record of the erasure command, carrying the honest count actually shredded\n // (may be 0 \u2014 e.g. an empty namespace, or all-cleartext without `allowCleartext`): the command ran, but\n // the count keeps the record from over-attesting a destruction that did not happen.\n audit.onEvent({ kind: 'namespace.erase', namespace, segmentsShredded });\n return { destroyed };\n}\n\n/**\n * Deps for {@link dropSegment}. Adds `storage`, because unlike a crypto-shred this one deletes the objects.\n */\nexport interface DropDeps extends EraseDeps {\n readonly storage: IStorageDriver;\n}\n\nexport interface DropResult {\n readonly segment: string;\n readonly namespace?: string;\n /**\n * **True iff this segment is now empty as a result of this call \u2014 or already was.** This is the field a\n * retention job should branch on: `false` means the call found nothing to do and your ref is probably wrong.\n */\n readonly dropped: boolean;\n /** Storage generations physically deleted, ascending. Empty on a dry run \u2014 see {@link DropResult.wouldDelete}. */\n readonly generationsDeleted: readonly number[];\n /**\n * Generations still present in Storage when the sweep gave up, ascending. **Empty is the normal outcome** \u2014 a\n * non-empty value means the storage was NOT fully reclaimed and the drop should be re-run.\n *\n * Without it, a drop would return `dropped: true` with a populated `generationsDeleted` and no `reason` even\n * when an object holding the **complete set** had just been left in the bucket by a writer that was already\n * mid-write when the tombstone landed (its publish is refused, but the object survives). For a cleartext\n * segment those bytes are readable \u2014 and the collection only takes a destroyed segment's generations\n * when something runs it. That result would look like a clean drop, and an operator would get no signal to\n * re-run.\n */\n readonly generationsRemaining: readonly number[];\n /**\n * On a dry run, the generations that **would** be deleted. `undefined` on a real run.\n *\n * This exists because the confirmation guard the erasure calls use \u2014 naming the segment twice \u2014 protects a\n * hand-typed literal and nothing else. In the loop this operation is actually *for*\n * (`for (const day of expired) drop({segment: day}, {confirmSegment: day})`) the same variable appears twice\n * and the guard is pure ceremony. A dry run is the guard that still works when a machine is calling.\n *\n * It is a **snapshot**: a later real run can see a different set.\n */\n readonly wouldDelete?: readonly number[];\n /**\n * On a dry run, whether the real drop would **also** crypto-shred (i.e. the segment is encrypted).\n * `undefined` on a real run. This is the one fact in the preview that is irreversible *everywhere*, backups\n * included \u2014 a dry run that did not surface it was previewing the recoverable half only.\n */\n readonly wouldCryptoShred?: boolean;\n /**\n * True iff the segment was encrypted and its wrapped DEK(s) were dropped as part of this call \u2014 so its bytes\n * are unreadable *everywhere*, backups included, not merely deleted from the bucket. Deleting an object does\n * not reach a noncurrent version, a replica, or a PITR snapshot; discarding the key does.\n */\n readonly cryptoShredded: boolean;\n /**\n * How the segment got to its current state. Absent on a fresh, ordinary drop (tombstone written, Storage swept).\n *\n * - `'already'` \u2014 it was already a tombstone. Idempotent; pairs with `dropped: true`. A re-drop is **not** a\n * no-op \u2014 it re-sweeps Storage, so it is how a residual in `generationsRemaining` is collected.\n * - `'absent'` \u2014 **nothing existed.** No registry row and no Storage objects. Pairs with `dropped: false`, and it\n * is the one value worth alerting on: the usual cause is a mistyped name or an omitted `namespace`, both of\n * which address a *different* segment than you meant.\n */\n readonly reason?: string;\n}\n\n/**\n * **Dispose of a segment: tombstone it, then delete its Storage objects.** Irreversible.\n *\n * WHY THIS EXISTS, given {@link destroySegment} already erases. Because `destroySegment` answers a *compliance*\n * question and this one answers an *operational* question, and they are not the same:\n *\n * - `destroySegment` **crypto-shreds** \u2014 it discards the key so the bytes are unreadable everywhere including\n * immutable backups, which is the only erasure that survives WORM. But it **leaves the objects in the\n * bucket**, still billed, and it *requires* encryption because a cleartext segment has no key to discard.\n * - `dropSegment` **removes the storage**. It works on a cleartext segment, and on an encrypted one it *also*\n * drops the DEKs, so it is a strict superset there.\n *\n * It is the supported way to delete a segment and stop paying for it. The obvious workaround \u2014 an object-store\n * lifecycle rule on the key prefix \u2014 deletes the bytes while the registry still points at them. That is exactly the `missing-storage-generation` state the DR runbook says not to serve traffic\n * on, and it surfaces *intermittently*, because a read consults the cache before Storage: cached chunks answer\n * correctly and evicted ones throw. The whole value of this function is that the ordering below cannot be got\n * wrong by a caller.\n *\n * **THE ORDER IS THE CONTRACT \u2014 registry first, then Storage.**\n *\n * 1. **Registry first.** After the tombstone nothing resolves a generation for this segment, so no reader can\n * reach for bytes that are about to disappear, and no writer can publish onto it (a\n * load refuses a `destroyed` row).\n * 2. **Storage second, best-effort, and re-swept.** Once the pointer is a tombstone the segment resolves as empty,\n * so a failure part-way through leaves **orphaned bytes, not a wrong answer.** Orphans cost money and are\n * cleaned up by re-running; a torn pointer costs correctness and is not self-healing. Given the choice, leak\n * bytes \u2014 but say so: whatever survives the sweep is reported in {@link DropResult.generationsRemaining}.\n *\n * **Why step 2 sweeps more than once.** A load that was already writing its object when the tombstone landed\n * still finishes the write \u2014 its publish is then refused, but the object survives, and it holds the complete\n * set. A single list-then-delete misses it entirely. The re-sweep converges because the tombstone *is* a hard\n * fence on **publishing**, so only already-in-flight writes can appear and they are finite.\n *\n * **When \"reads as empty\" starts being true.** Not instantly, for a store that has already read this segment: a\n * resolved generation is cached and decoded chunks sit in the cache, so an in-flight reader can answer from\n * cache for a window. A fresh store, or any reader that had not touched the segment, sees empty at once.\n *\n * That window is bounded by `cache.genTtlMs` (default 2 s) **only for a reader whose storage source has both a clock\n * and a registry and a positive TTL** \u2014 expiry needs all three. A source built without a clock or a registry, or\n * with `cache.genTtlMs: 0`, has no timed refresh: it notices the drop only when a read has to fetch from a deleted\n * generation or its reader cache evicts the segment. Because a cache-LRU hit never reaches Storage, such a reader\n * can answer `true` for a dropped segment **indefinitely**, and must be invalidated or restarted. This is the *same* caching that makes the delete-bytes-first ordering fail intermittently rather\n * than loudly \u2014 it cuts both ways.\n *\n * `confirmSegment` must equal `ref.segment`, matching `destroySegment`/`eraseNamespace`. For an automated\n * caller that guard is ceremony \u2014 use `dryRun` first, which reports what would go without touching anything.\n */\nexport async function dropSegment(\n ref: SegmentRef,\n deps: DropDeps,\n options: { confirmSegment: string; dryRun?: boolean; audit?: IAuditSink },\n): Promise<DropResult> {\n validateUserRef(ref);\n if (options.confirmSegment !== ref.segment) {\n throw new ValidationError(\n `dropSegment: confirmSegment must equal the segment name \"${ref.segment}\" (guard against accidental deletion)`,\n );\n }\n const base = { segment: ref.segment, namespace: ref.namespace };\n\n if (options.dryRun === true) {\n const record = await deps.registry.get(ref);\n const wouldDelete = await listGenerations(deps.storage, ref);\n return {\n ...base,\n dropped: false,\n generationsDeleted: [],\n generationsRemaining: [],\n wouldDelete,\n wouldCryptoShred: record?.wrappedDeks !== undefined && record.wrappedDeks.length > 0,\n cryptoShredded: false,\n reason:\n record === null\n ? wouldDelete.length === 0\n ? 'absent'\n : undefined\n : record.status === 'destroyed'\n ? 'already'\n : undefined,\n };\n }\n\n // Step 1, reused wholesale. `allowCleartext` is true because deleting objects does not need a key \u2014 the\n // encryption requirement belongs to crypto-shred, not to disposal.\n let shred = await shredSegment(ref, deps, true, 'dropSegment');\n\n // \u2500\u2500 THE ABSENT CASE. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // `shredSegment` returns `absent` having written NOTHING when there is no registry row \u2014 and going on to delete\n // every Storage generation anyway would skip the one step that makes the ordering safe while still running the\n // destructive one: a drop landing between a load's object write and its publish (minutes apart on a large\n // load) would leave a published pointer with no object behind it \u2014 precisely the\n // `missing-storage-generation` state this function exists to PREVENT.\n //\n // So the identity is claimed before anything is deleted. A `destroyed` row is exactly the fence the\n // writers already respect \u2014 a load refuses one \u2014 so creating it\n // converts the race into \"the writer is refused and the bytes are collected\".\n //\n // Only when Storage actually holds something, though. A drop against a *genuinely* nonexistent segment (the\n // typo the facade docs warn about) must not leave a `destroyed` row behind: that is registry litter, and worse,\n // it would refuse a later legitimate load of that name forever. No row and no objects \u21D2 nothing existed \u21D2 say\n // `absent` and touch nothing, which is what that reason is documented to mean.\n if (shred.reason === 'absent') {\n const orphans = await listGenerations(deps.storage, ref);\n if (orphans.length === 0) {\n return {\n ...base,\n dropped: false,\n generationsDeleted: [],\n generationsRemaining: [],\n cryptoShredded: false,\n reason: 'absent',\n };\n }\n try {\n await deps.registry.create(ref, {\n // The max listed generation, so the row is consistent with what is on disk if anything reads it before\n // the sweep finishes. It is a tombstone, so no reader resolves through it either way.\n currentGen: orphans[orphans.length - 1]!,\n status: 'destroyed',\n });\n shred = { ...shred, destroyed: true, reason: undefined };\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n // A row appeared between our `get` and our `create` \u2014 that is the racing writer we were trying to fence,\n // and it won. Fall back into the normal path, which now has a row to CAS into a tombstone.\n shred = await shredSegment(ref, deps, true, 'dropSegment');\n }\n }\n\n // `segment.erase` ONLY on a genuine crypto-shred \u2014 exactly the condition `destroySegment` uses, and\n // deliberately NOT `|| generationsDeleted.length > 0`.\n //\n // Adding that disjunct would be wrong in a way that matters. Four documents \u2014 including\n // `docs/guide/dashboards.md`, which calls this event the compliance *receipt* \u2014 define `segment.erase` as\n // proof of an irreversible crypto-shred: bytes unreadable everywhere, backups included. Deleting an object is\n // a weaker guarantee, because a noncurrent version, a cross-region replica or a PITR snapshot still holds the\n // cleartext. Emitting one event for both would make a compliance dashboard **over-attest**, which is the one\n // failure an audit trail exists to prevent.\n //\n // The cleartext case is NOT silent \u2014 it emits `segment.dispose` after the sweep (below). That kind exists\n // precisely so this one does not have to lie: disposal is attested as disposal, shredding as shredding, and an\n // encrypted drop emits both because both genuinely happened.\n //\n // And it goes out BEFORE the Storage sweep, not after. A genuine crypto-shred is complete the moment\n // `shredSegment` returns \u2014 the DEK wrappings are gone and the bytes are unreadable everywhere. If the sweep\n // then throws (a Storage driver that cannot list), emitting afterwards would mean **no receipt for a destruction\n // that really happened** \u2014 the exact mirror of the over-attestation above, and just as wrong. Both directions\n // of a false audit trail are defects; only one of them was obvious.\n if (shred.cryptoShredded) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.erase',\n namespace: ref.namespace,\n segment: ref.segment,\n });\n }\n\n // Step 2, swept until Storage comes back empty. See \"Why step 2 sweeps more than once\" above. `mapWithConcurrency`\n // returns results in *input* order, so each pass contributes ascending generations and the concatenation stays\n // ascending. Failures are swallowed per item so the pool never aborts \u2014 one unreachable generation must not\n // leave the rest orphaned too.\n const generationsDeleted: number[] = [];\n for (let pass = 0; pass < MAX_STORAGE_SWEEPS; pass++) {\n const present = await listGenerations(deps.storage, ref);\n if (present.length === 0) break;\n const outcomes = await mapWithConcurrency(present, ERASE_CONCURRENCY, async (generation) => {\n try {\n await deps.storage.delete({ ...ref, generation });\n return generation;\n } catch {\n // Leave it orphaned. The segment already reads as empty, so this is a billing problem, not a\n // correctness one, and re-running the drop collects whatever was missed.\n return null;\n }\n });\n let deletedThisPass = 0;\n for (const g of outcomes) {\n if (g !== null) {\n generationsDeleted.push(g);\n deletedThisPass += 1;\n }\n }\n if (deletedThisPass === 0) break; // nothing went \u2014 another pass will not help\n }\n // Enumerate once more rather than inferring the residual from the last pass, so the reported value is exactly\n // what is still in Storage whichever way the loop ended: empty, a delete that kept failing, or the pass budget\n // exhausted while a writer kept finishing. Inferring it would report `[]` in precisely the case that matters\n // \u2014 a final pass that deleted everything it saw, after which one more object appeared.\n const generationsRemaining = await listGenerations(deps.storage, ref);\n\n // `segment.dispose` attests the *storage reclamation*, which is the weaker but still compliance-relevant fact,\n // and it is emitted AFTER the sweep because unlike a crypto-shred it is not established until the sweep runs.\n // Only when a tombstone was actually written \u2014 an absent no-op disposed of nothing.\n if (shred.destroyed) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.dispose',\n namespace: ref.namespace,\n segment: ref.segment,\n generationsDeleted: generationsDeleted.length,\n });\n }\n\n return {\n ...base,\n dropped: shred.destroyed,\n generationsDeleted,\n generationsRemaining,\n cryptoShredded: shred.cryptoShredded,\n reason: shred.reason,\n };\n}\n\n/**\n * Every generation currently present in Storage for a segment, ascending.\n *\n * No error handling on purpose: `IStorageDriver.list` is mandatory (not optional in `ports.ts`), so a driver that\n * cannot list cannot exist, and one that *throws* from `list` should propagate.\n *\n * **Be precise about what a throw means at each call site.** From the `dryRun` branch it is accurate that the\n * call \"has established nothing\". From the real run it is NOT: by then the tombstone is written, so the segment\n * IS dropped and only the bytes leaked. The throw is still the right behaviour \u2014 it is the signal to re-run, and\n * a re-run takes the `'already'` path and re-attempts the sweep \u2014 but a caller who logs \"drop failed\" is wrong\n * about the segment's state. This is also why the audit event is emitted *before* the sweep: an irreversible\n * crypto-shred that really happened must not go unrecorded because a later, weaker step threw.\n */\nasync function listGenerations(storage: IStorageDriver, ref: SegmentRef): Promise<number[]> {\n const generations: number[] = [];\n for await (const key of storage.list(ref)) generations.push(key.generation);\n return generations.sort((a, b) => a - b);\n}\n\n/**\n * The shred itself: CAS the registry row to a `destroyed` tombstone with no wrappings.\n *\n * `op` is only for error text \u2014 `destroySegment` and `dropSegment` both come through here, and a message naming\n * the wrong one is operator-facing text on the single path where the operator must act.\n */\nasync function shredSegment(\n ref: SegmentRef,\n deps: EraseDeps,\n allowCleartext: boolean,\n op: 'destroySegment' | 'dropSegment' = 'destroySegment',\n): Promise<DestroyResult> {\n const base = { segment: ref.segment, namespace: ref.namespace };\n for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {\n const record = await deps.registry.get(ref);\n if (record === null) {\n // No authoritative row \u2192 nothing to crypto-shred.\n return { ...base, destroyed: false, cryptoShredded: false, reason: 'absent' };\n }\n if (record.status === 'destroyed') {\n return { ...base, destroyed: true, cryptoShredded: false, reason: 'already' };\n }\n const encrypted = record.wrappedDeks !== undefined && record.wrappedDeks.length > 0;\n if (!encrypted && !allowCleartext) {\n return { ...base, destroyed: false, cryptoShredded: false, reason: 'cleartext' };\n }\n try {\n await deps.registry.compareAndSwap(ref, record.token, {\n status: 'destroyed',\n wrappedDeks: undefined, // \u2190 the crypto-shred: the only copy of the DEK wrappings is gone\n keyId: undefined,\n });\n // A genuine crypto-shred only when there were wrappings to drop; a cleartext opt-in tombstone leaves the\n // Storage bytes readable, so it is not an irreversible destruction (and does not emit `segment.erase`).\n return { ...base, destroyed: true, cryptoShredded: encrypted };\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n // A concurrent publish or policy write advanced the row \u2014 re-read and shred again (it always converges).\n }\n }\n throw new WriteConflictError(`${op}: contention shredding \"${ref.segment}\" \u2014 retry`);\n}\n", "/**\n * Segment retention policy \u2014 **when** a segment becomes eligible for retirement.\n *\n * This module only *records and reads the intent*. Nothing here deletes anything, and nothing here runs on a\n * timer: setting a policy is one registry write, and the sweep that acts on it (`retireExpired`) is a separate\n * call the operator schedules. That split is deliberate \u2014 see the \"who owns what\" note below.\n *\n * **The expiry is an absolute epoch-ms the writer sets, not a duration the library derives.** A relative TTL\n * anchored to anything the library knows would be wrong: every load republishes `currentGen` and touches\n * `updatedAt`, so a \"30 days since last write\" rule would keep a daily bucket alive forever precisely because\n * it keeps being reloaded. The caller who knows what the segment *means* computes the instant:\n *\n * ```ts\n * const DAY = 86_400_000;\n * await store.setRetention({ namespace: 'active-daily', segment: '2026-08-05' }, {\n * expiresAt: Date.now() + 30 * DAY,\n * });\n * ```\n *\n * **Who owns what.** The library owns the policy field, its validation, and the sweep that acts on it. **You own\n * the heartbeat** \u2014 an EventBridge rule, a Kubernetes CronJob, `cron`, a queue consumer, whatever already runs in\n * your deployment. There is no background thread here, and there will not be one: this library runs in a Lambda,\n * an edge isolate and a long-lived server alike, and a timer that only works in one of those is worse than none.\n *\n * **Storage.** The policy lives in the registry row's free-form `retention` metadata, which already round-trips\n * through every driver and is carried by `list()` \u2014 so a fleet-wide sweep reads it from the enumeration with no\n * per-segment `get()`. A segment that has no row yet (a policy recorded before the first load) gets one, with\n * `currentGen: null`: the row exists so the segment is *enumerable*, and it claims no Storage generation, so every\n * read answers empty until the first load publishes onto it.\n */\nimport { ValidationError, WriteConflictError, isWriteConflictError } from './errors';\nimport { canIndex, dueBucket, dueIndexRef } from './due-index';\nimport type { GovernanceMeta, IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\nimport { validateUserRef } from './validate';\n\n/** The key the policy is stored under inside the row's `retention` metadata. */\nconst EXPIRES_AT = 'expiresAt';\n\n/** How many times a policy write re-reads and retries under contention before reporting a conflict. */\nconst RETENTION_CAS_ATTEMPTS = 5;\n\n/**\n * The smallest accepted `expiresAt`: 2001-09-09, the point below which an epoch-**seconds** value would land.\n *\n * This is a deliberate guard against the single most damaging typo available here. `Date.now() / 1000 + 30 * 86400`\n * is a natural thing to write, and it produces a number ~1.8 billion \u2014 an instant in **1970**, which is to say\n * *already expired*. Without this check, that typo is not a validation error, it is a sweep that retires the\n * segment on its very next pass. Anything below this floor is far likelier to be seconds than a genuine intent to\n * expire a segment two decades ago, and the caller who really means \"retire it now\" can say so with `dropSegment`.\n */\nexport const MIN_EXPIRES_AT_MS = 1_000_000_000_000;\n\n/** A segment's retention policy: the instant it becomes eligible for retirement. */\nexport interface RetentionPolicy {\n /**\n * Absolute epoch-**milliseconds**. Once `now >= expiresAt`, a retention sweep may retire the segment \u2014 tombstone\n * it and delete its Storage generations. A value in the past is legal (backfilling a policy onto existing\n * buckets is a normal thing to do) and means \"eligible on the next sweep\".\n */\n readonly expiresAt: number;\n}\n\nexport interface RetentionDeps {\n readonly registry: IRegistryDriver;\n}\n\nexport interface SetRetentionResult {\n readonly segment: string;\n readonly namespace?: string;\n /** The policy now stored on the row. */\n readonly expiresAt: number;\n /**\n * True iff this call **minted the registry row** \u2014 a policy recorded before the segment's first load. The\n * segment is now enumerable by `registry.list()` and therefore by every fleet-wide operation; the row claims no\n * Storage generation (`currentGen: null`), so reads answer empty until the first load publishes onto it.\n */\n readonly createdRow: boolean;\n /**\n * True iff the **due-index pointer** for this policy was written, so a fast sweep will find this segment by\n * reading only its expiry day instead of scanning the fleet.\n *\n * `false` is a **degradation, not an error**, and the policy is committed either way: either the ref is too\n * long to encode into one index-row name, or the pointer write failed. The full-scan repair pass still sees\n * the segment's own row, so it is retired on the repair cadence rather than the fast one \u2014 slower, never\n * never. Alarm on a *sustained* run of `false`, not on one.\n */\n readonly indexed: boolean;\n}\n\n/** Fail-fast validation of a caller-supplied policy. Boundary check \u2014 untrusted-input posture. */\nfunction validateRetentionPolicy(policy: RetentionPolicy): void {\n const { expiresAt } = policy;\n if (typeof expiresAt !== 'number' || !Number.isInteger(expiresAt)) {\n throw new ValidationError(\n `retention.expiresAt must be an integer epoch-ms; got ${String(expiresAt)}`,\n );\n }\n if (expiresAt < MIN_EXPIRES_AT_MS) {\n throw new ValidationError(\n `retention.expiresAt (${expiresAt}) is before ${MIN_EXPIRES_AT_MS} \u2014 that is almost certainly epoch ` +\n `SECONDS rather than milliseconds, which would read as \"already expired\" and retire the segment on the ` +\n `next sweep. Use \\`Date.now() + days * 86_400_000\\`. To retire a segment now, call \\`dropSegment\\`.`,\n );\n }\n}\n\n/**\n * Read the policy off a registry row's `retention` metadata. Returns `null` when there is no policy, and\n * `'invalid'` when the key is present but unusable (wrong type, or below the epoch-seconds floor).\n *\n * The three-way answer exists so a fleet sweep can *report* a malformed policy instead of choosing between two\n * bad options: treating it as \"no policy\" hides an operator error on a segment someone believes is expiring, and\n * throwing aborts a ledger over one bad row. Nothing writes an invalid policy through\n * {@link setSegmentRetention} \u2014 hand-edited rows, an older/newer writer, or a restore are how it happens.\n */\nexport function readRetentionPolicy(\n meta: GovernanceMeta | undefined,\n): RetentionPolicy | null | 'invalid' {\n if (meta === undefined) return null;\n // Guard the CONTAINER before the `in` test, not just the value inside it. `in` dereferences its operand, so a\n // stored `retention` of `null` (or a string, a number, an array) threw an untyped `TypeError` from here \u2014 and a\n // fleet sweep calls this *outside* its per-segment `try`, so one malformed row aborted the whole sweep and the\n // healthy expired segment next to it was never retired. That is precisely the outcome the three-way answer\n // exists to prevent. Both registry boundaries now reject a non-object blob, so this is the third line of defence\n // rather than the only one \u2014 but it is the one that keeps a ledger alive.\n if (meta === null || typeof meta !== 'object' || Array.isArray(meta)) return 'invalid';\n if (!(EXPIRES_AT in meta)) return null;\n const raw = meta[EXPIRES_AT];\n if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < MIN_EXPIRES_AT_MS)\n return 'invalid';\n return { expiresAt: raw };\n}\n\n/** The stored policy for one segment, or `null` if it has none (and `'invalid'` for a malformed one). */\nexport async function getSegmentRetention(\n ref: SegmentRef,\n deps: RetentionDeps,\n): Promise<RetentionPolicy | null | 'invalid'> {\n validateUserRef(ref);\n const record = await deps.registry.get(ref);\n if (record === null || record.status === 'destroyed') return null;\n return readRetentionPolicy(record.retention);\n}\n\n/**\n * Record when a segment becomes eligible for retirement, creating its registry row if it has none.\n *\n * Idempotent and safe to re-run: writing the same instant twice is a no-op in effect. Other keys already in the\n * row's `retention` metadata are preserved \u2014 this owns one key, not the whole object.\n *\n * Refuses a `destroyed` segment: a tombstone has nothing left to retire, and putting a policy on one would make\n * a sweep repeatedly \"retire\" bytes that are already gone.\n */\n/** The expiry currently recorded on a row, if any \u2014 the input to deciding which old pointer to remove. */\nfunction readExpiresAt(record: { retention?: GovernanceMeta } | null): number | undefined {\n const parsed = record === null ? null : readRetentionPolicy(record.retention);\n return parsed === null || parsed === 'invalid' ? undefined : parsed.expiresAt;\n}\n\n/**\n * Move the due-index pointer to match a policy change: add the new bucket's pointer, drop the old one.\n *\n * **Best-effort by construction, and that is the design rather than a shortcut.** The policy is already\n * committed when this runs, and the index is a fast path that nothing depends on for correctness \u2014 a pointer\n * that fails to appear costs a repair-cadence delay, and one that fails to disappear costs a single wasted read\n * when its bucket comes due (the sweep re-reads the live row and skips). So a failure here must never turn a\n * successful policy write into a thrown error; it is reported through `indexed` instead.\n *\n * Order matters the other way round from intuition: the **new pointer is written first**. Interrupted between\n * the two writes, the segment is then reachable from both buckets \u2014 a duplicate the sweep handles by re-reading\n * \u2014 whereas the reverse order would leave a window in which it is reachable from neither.\n */\nasync function reindex(\n ref: SegmentRef,\n deps: RetentionDeps,\n expiresAt: number | undefined,\n previousExpiresAt: number | undefined,\n): Promise<boolean> {\n // Not independently observable: `dueIndexRef` would throw for the same refs and the catch below would swallow\n // it to the same `indexed: false`. Kept because relying on a thrown ValidationError for ordinary control flow\n // reads as an accident, and because it keeps a programming condition out of a try block whose catch exists for\n // I/O failures. No mutation kills it, and that is a property of the equivalence, not of the tests.\n if (!canIndex(ref)) return false;\n const nextBucket = expiresAt === undefined ? undefined : dueBucket(expiresAt);\n const oldBucket = previousExpiresAt === undefined ? undefined : dueBucket(previousExpiresAt);\n let indexed = expiresAt === undefined;\n if (nextBucket !== undefined) {\n try {\n await deps.registry.create(dueIndexRef(nextBucket, ref), { currentGen: null });\n indexed = true;\n } catch (err) {\n // Already there \u2014 the pointer is idempotent, and a second policy write in the same day is the common case.\n if (isWriteConflictError(err)) indexed = true;\n }\n }\n if (oldBucket !== undefined && oldBucket !== nextBucket) {\n try {\n await deps.registry.delete(dueIndexRef(oldBucket, ref));\n } catch {\n // A pointer left behind is read once, when its bucket comes due, and skipped. Nothing to recover.\n }\n }\n return indexed;\n}\n\nexport async function setSegmentRetention(\n ref: SegmentRef,\n deps: RetentionDeps,\n policy: RetentionPolicy,\n): Promise<SetRetentionResult> {\n validateUserRef(ref);\n validateRetentionPolicy(policy);\n const base = { segment: ref.segment, namespace: ref.namespace };\n /** The bucket this segment was already in, if any \u2014 its pointer has to be removed when the expiry moves. */\n let previousExpiresAt: number | undefined;\n for (let attempt = 0; attempt < RETENTION_CAS_ATTEMPTS; attempt += 1) {\n const record = await deps.registry.get(ref);\n try {\n previousExpiresAt = readExpiresAt(record);\n if (record === null) {\n // A policy ahead of the first load. `currentGen: null` is what makes this safe: the row exists purely so\n // the segment is enumerable, and it claims no Storage generation, so generation resolution takes the same\n // path it takes for a segment with no row at all \u2014 and the first publish advances it.\n await deps.registry.create(ref, {\n currentGen: null,\n retention: { [EXPIRES_AT]: policy.expiresAt },\n });\n return {\n ...base,\n expiresAt: policy.expiresAt,\n createdRow: true,\n indexed: await reindex(ref, deps, policy.expiresAt, undefined),\n };\n }\n assertNotDestroyed(record, ref);\n await deps.registry.compareAndSwap(ref, record.token, {\n retention: { ...record.retention, [EXPIRES_AT]: policy.expiresAt },\n });\n return {\n ...base,\n expiresAt: policy.expiresAt,\n createdRow: false,\n indexed: await reindex(ref, deps, policy.expiresAt, previousExpiresAt),\n };\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n // Lost the race (a load's publish, another policy write) \u2014 re-read and retry.\n }\n }\n throw new WriteConflictError(\n `setRetention: contention writing the retention policy for segment \"${ref.segment}\"`,\n );\n}\n\n/**\n * Remove a segment's expiry so no sweep will retire it. Returns whether a policy was actually removed (`false`\n * for a segment with no row, or one that had no policy) \u2014 a separate verb from setting one, because \"cancel the\n * expiry\" and \"expire at time T\" are different intents and a magic sentinel value for the first is how a typo\n * becomes a deletion.\n *\n * Other `retention` keys are left in place; if removing the expiry empties the metadata, the field is cleared.\n */\nexport async function clearSegmentRetention(\n ref: SegmentRef,\n deps: RetentionDeps,\n): Promise<boolean> {\n validateUserRef(ref);\n for (let attempt = 0; attempt < RETENTION_CAS_ATTEMPTS; attempt += 1) {\n const record = await deps.registry.get(ref);\n if (record === null) return false; // nothing to clear \u2014 and creating a row to say so would be litter\n if (record.status === 'destroyed') return false; // terminal; a tombstone has no expiry to cancel\n if (record.retention === undefined || !(EXPIRES_AT in record.retention)) return false;\n const rest = { ...record.retention };\n delete rest[EXPIRES_AT];\n try {\n // `retention: undefined` CLEARS the field (a patch clears by mentioning), which is what an emptied policy\n // object should become \u2014 a row carrying `{}` reads as \"has retention metadata\" to anything inspecting it.\n await deps.registry.compareAndSwap(ref, record.token, {\n retention: Object.keys(rest).length === 0 ? undefined : rest,\n });\n await reindex(ref, deps, undefined, readExpiresAt(record));\n return true;\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n }\n }\n throw new WriteConflictError(\n `clearRetention: contention clearing the retention policy for segment \"${ref.segment}\"`,\n );\n}\n\nfunction assertNotDestroyed(record: RegistryRecord, ref: SegmentRef): void {\n if (record.status === 'destroyed') {\n throw new ValidationError(\n `segment \"${ref.segment}\" is destroyed (crypto-shredded) \u2014 refusing to set a retention policy on a ` +\n `tombstone; there is nothing left to retire. Use a new segment.`,\n );\n }\n}\n", "/**\n * The retention **sweep** \u2014 the thing that acts on the policies `setSegmentRetention` records.\n *\n * `retireExpired` enumerates the registry, selects the segments whose `expiresAt` has passed, and retires each\n * one through {@link dropSegment}. It deliberately **delegates rather than reimplements**: the registry \u2192 Storage\n * ordering, the re-sweep for an object a load was still writing, and the `generationsRemaining` report are all\n * load-bearing and already live there. A sweep that open-coded the deletions would be a second\n * implementation of the most dangerous ordering in the library.\n *\n * **This is a call, not a daemon.** Nothing here schedules itself. You run it from whatever heartbeat your\n * deployment already has \u2014 an EventBridge rule, a Kubernetes CronJob, a queue consumer, the job that runs your\n * loads \u2014 and the library stays a library: it has to behave identically in a Lambda, an edge isolate and a\n * long-lived server, and a timer that only works in one of those is worse than none.\n *\n * Three properties make it safe to point at a fleet:\n *\n * - **`dryRun` at the sweep level.** `dropSegment`'s `confirmSegment` guard is vacuous in a loop (it is the same\n * variable twice), so the meaningful preview is here: it reports what it *would* retire, per segment, and\n * touches nothing.\n * - **A per-cycle `limit`.** Clock skew, a bad backfill, or a namespace-wide policy mistake should cost one\n * bounded batch, not the fleet. When the limit bites, the result says so (`limited: true`) rather than looking\n * like a completed sweep.\n * - **A ledger, never an exception.** A per-segment fault is recorded and the sweep continues \u2014 the caller's\n * load-bearing question is *which* segments were retired, and a throw from the middle of the loop answers it\n * for none of them while having already retired some. Same posture as `eraseNamespace`.\n *\n * It also **purges the tombstones its own retirements leave behind**. A retired segment that had a registry row\n * gets a `destroyed` row, and one dead row per retired daily bucket \u2014 or per retired dedup wave \u2014 is exactly the\n * registry litter `dropSegment` already refuses to create for a row-less accumulator. Purging is narrow on\n * purpose: only a tombstone carrying the **stamp this sweep writes on its own retirements** (so it is attributably\n * ours: a crypto-shred's tombstone does not carry it, unless the shred lands between this sweep's re-read of the\n * row and its drop, which then reports the tombstone dropped and stamps it), only after a grace period, and only\n * once Storage is provably empty for it \u2014\n * because deleting the row is what makes the name reusable and takes the segment out of reach of\n * the generation collection.\n */\nimport { type IAuditSink } from './audit';\nimport { BudgetExceededError, ValidationError, isWriteConflictError } from './errors';\nimport { gcOrphanGenerations } from './generation-gc';\nimport { drainRegistry } from './registry-scan';\nimport { dropSegment } from './erasure';\nimport type { DropDeps, DropResult } from './erasure';\nimport { MIN_EXPIRES_AT_MS, readRetentionPolicy } from './retention';\nimport { DEFAULT_MAX_SCAN_SEGMENTS } from './registry-scan';\nimport {\n canIndex,\n decodeDueName,\n dueBucket,\n dueBucketsAt,\n dueIndexRef,\n dueNamespace,\n} from './due-index';\nimport { segmentKey, shardOf } from './keys';\nimport type { IRegistryDriver, RegistryRecord } from './ports';\nimport type { GovernanceMeta, IStorageDriver, SegmentRef } from './ports';\nimport { isReservedNamespace, validateUserNamespace } from './validate';\n\n/** Default cap on retirements per sweep \u2014 a bounded batch, so a policy mistake costs one batch, not the fleet. */\nexport const DEFAULT_RETIRE_LIMIT = 100;\n\n/**\n * Default delay before a retirement's own tombstone row is purged: 24 h.\n *\n * The row is a fence \u2014 while it exists, a load refuses the segment, so\n * a load that was mid-write when the drop landed cannot resurrect it. That window is\n * seconds to minutes in practice; a day of margin costs one tiny row and removes any need to reason about it.\n */\nexport const DEFAULT_TOMBSTONE_GRACE_MS = 86_400_000;\n\nexport interface RetireExpiredOptions {\n /** Scope the sweep to one namespace. Omit to sweep every namespace the registry knows. */\n readonly namespace?: string;\n /**\n * The instant to compare policies against (epoch-ms). Required here because `core/` takes its time from the\n * caller, never from the platform \u2014 `store.retireExpired()` fills it in from the store's clock.\n */\n readonly now: number;\n /** Maximum segments to retire in this cycle (default 100). */\n readonly limit?: number;\n /** Report what would be retired and change nothing. */\n readonly dryRun?: boolean;\n /** Forwarded to each `dropSegment`, so every retirement lands in the audit trail as `segment.dispose`. */\n readonly audit?: IAuditSink;\n /** Ceiling on rows enumerated (default 250,000); exceeding it throws. */\n readonly maxScanSegments?: number;\n /**\n * **Where the candidates come from.**\n *\n * - `'fleet'` (default) \u2014 drain `registry.list()` and filter. Cost tracks the **fleet**, every cycle, even\n * when nothing expires. Complete by construction: it cannot miss a policy.\n * - `'index'` \u2014 read only the due buckets of the due index. Cost tracks **what is\n * expiring**. Each candidate's live row is still re-read before anything is decided, so a stale pointer\n * costs one read and retires nothing.\n *\n * **`'index'` is not a drop-in replacement for `'fleet'`; it is the fast half of a pair.** A policy whose\n * pointer write failed (`indexed: false`), or whose ref is too long to index, has no pointer \u2014 so a\n * deployment that *only* ever runs `'index'` will never retire those. Run `'fleet'` periodically as the\n * repair pass. The default is `'fleet'` because it is complete by construction.\n */\n readonly scan?: 'fleet' | 'index';\n /**\n * **The shards this worker owns**, with {@link totalShards}. Without them every replica sweeps the whole\n * fleet and contends over the same segments. A stable hash of the segment key, so a worker owns the same slice\n * across restarts.\n */\n readonly shards?: readonly number[];\n /** Total shards the fleet is split into. Required with {@link shards}; ignored without it. */\n readonly totalShards?: number;\n /**\n * How many **past** buckets an `'index'` scan reads besides the current one (default 7, one per day).\n * A sweep that did not run \u2014 scaled to zero, a failed deploy, a paused\n * schedule \u2014 leaves its buckets behind, and this is how far back a later cycle reaches for them. Bounded so a\n * long outage costs a bounded number of list calls per cycle rather than one per day since the epoch;\n * anything older is the `'fleet'` repair pass's job.\n */\n readonly lookbackBuckets?: number;\n /**\n * Whether to delete the tombstone rows this sweep's own past retirements left (default `true`). Set `false` to\n * keep every one of them \u2014 the right choice if something outside this library treats the presence of a\n * `destroyed` row as an attestation. That includes the row of a retirement whose drop found no Storage\n * generation to delete and left none behind: with the default that row is deleted in the same pass, since it\n * would do nothing but fence the name against every writer, and with `false` it stays, stamped like any other\n * of this sweep's tombstones, so a later sweep with purging on deletes it once `tombstoneGraceMs` has passed.\n * While it stays, it fences the name like every kept tombstone. What records a retirement either way is its\n * ledger entry, and its `segment.dispose` event (`generationsDeleted: 0` for an empty one) when an `audit` sink\n * is passed.\n *\n * Two knobs rather than one `number | 'never'`, deliberately: `0` would have had to mean \"purge immediately\"\n * here while `cache.genTtlMs: 0` in this same library means \"never refresh on a timer\", and one option whose\n * zero is the opposite of another's is a reading hazard for whoever tunes both.\n */\n readonly purgeTombstones?: boolean;\n /** How long a retirement's tombstone must age before this sweep deletes it (default 24 h). */\n readonly tombstoneGraceMs?: number;\n}\n\n/** What happened to one segment in a sweep. */\nexport type RetireEntry =\n | {\n readonly segment: string;\n readonly namespace?: string;\n /** Retired: `result` is `dropSegment`'s full report \u2014 **check `generationsRemaining`**. */\n readonly action: 'retired';\n readonly expiresAt: number;\n readonly result: DropResult;\n /**\n * Present when the retirement **completed the destructive part and then faulted** \u2014 the tombstone is written\n * and the segment reads empty, but something after that (the Storage sweep) threw. The storage may not be fully\n * reclaimed; re-run. Reported as `retired` rather than `skipped` because the segment really is retired, and\n * saying otherwise is the one thing a caller cannot recover from.\n */\n readonly fault?: `failed: ${string}`;\n }\n | {\n readonly segment: string;\n readonly namespace?: string;\n /** `dryRun` only \u2014 what a real sweep would have retired, with `dropSegment`'s own preview attached. */\n readonly action: 'would-retire';\n readonly expiresAt: number;\n readonly result: DropResult;\n }\n | {\n readonly segment: string;\n readonly namespace?: string;\n /** A tombstone row from an earlier retirement was deleted (or would be, under `dryRun`). */\n readonly action: 'purged-tombstone' | 'would-purge-tombstone';\n }\n | {\n readonly segment: string;\n readonly namespace?: string;\n readonly action: 'skipped';\n /**\n * `'invalid-policy'` \u2014 the row has an `expiresAt` that is not usable (hand-edited, or restored from another\n * schema). Reported rather than ignored: reading as \"never expires\" on a segment someone believes is\n * expiring is the silence that costs a retention commitment.\n * `'limit'` \u2014 eligible, but this cycle's `limit` was already spent. Re-run to continue.\n * `'tombstone-not-empty'` \u2014 a tombstone whose Storage generations are not gone even after a GC\n * attempt, so its row is kept: the row is what keeps the segment reachable by the generation collection and\n * refused by every writer. Several causes, all self-healing: the storage really could not be reclaimed,\n * the collection *declined* because the row changed underneath it (`WriteConflictError`, which this\n * sweep swallows deliberately), or this was a `dryRun`, which reports the reason without attempting the\n * collection at all. Except under `dryRun` the next cycle retries.\n * `'policy-changed'` \u2014 the live row no longer says \"expired\" (a `clearRetention`, a new `expiresAt`, or\n * someone else's drop landed between the enumeration and this segment's turn). Not an error: the sweep\n * re-reads before every deletion precisely so cancelling an expiry works on a sweep already in flight.\n * `` `failed: \u2026` `` \u2014 any fault, isolated to this segment. A fault that happened *after* the tombstone\n * landed is reported as `retired` with a `fault` instead, because that segment IS retired.\n */\n readonly reason:\n 'invalid-policy' | 'limit' | 'tombstone-not-empty' | 'policy-changed' | `failed: ${string}`;\n };\n\nexport interface RetireExpiredResult {\n /** Registry rows enumerated. */\n readonly scanned: number;\n /** Rows whose policy said \"expired\" \u2014 including any the `limit` deferred. */\n readonly eligible: number;\n /**\n * Segments **actually retired**. Zero under `dryRun` \u2014 see `wouldRetire`. Kept honest because this is the field\n * most likely to end up on a dashboard: a counter that means \"deleted\" in one mode and\n * \"would delete\" in another produces phantom deletions on any graph that does not also join on `dryRun`.\n */\n readonly retired: number;\n /** Segments a real sweep **would** have retired. Only ever non-zero under `dryRun`. */\n readonly wouldRetire: number;\n /** Tombstone rows actually deleted. Zero under `dryRun`; the `would-purge-tombstone` entries carry the preview. */\n readonly tombstonesPurged: number;\n /** True when `limit` cut the cycle short \u2014 **more segments are still eligible**. Re-run. */\n readonly limited: boolean;\n readonly dryRun: boolean;\n /** Per-segment ledger. Inspect it: a `skipped` or failed entry is a segment that still holds data. */\n readonly entries: readonly RetireEntry[];\n}\n\n/**\n * How many past buckets an index scan reads besides the current one. A week: long enough that a weekend outage\n * or a paused schedule recovers on its own, short enough that a cycle is eight list calls rather than hundreds.\n */\nexport const DEFAULT_LOOKBACK_BUCKETS = 7;\n\n/**\n * Candidates from the due index: read the buckets that are due, resolve each pointer, and **re-read the live\n * row**.\n *\n * That re-read is the load-bearing line. The index is a fast path and the segment's own row is the truth, so a\n * pointer whose policy has since been cleared, moved, or destroyed must cost one read and change nothing \u2014 the\n * ordinary eligibility check downstream then skips it, using exactly the same logic the fleet scan uses. There\n * is no second decision path to keep in step, which is the property that makes a second index safe here.\n *\n * A pointer we cannot decode, or one whose segment no longer exists, is skipped rather than repaired: this\n * function decides nothing irreversible, and cleaning up is the sweep's job once a retirement actually happens.\n */\nasync function rowsFromDueIndex(\n registry: IRegistryDriver,\n options: { now: number; lookbackBuckets: number; namespace?: string; maxScanSegments: number },\n): Promise<RegistryRecord[]> {\n const rows: RegistryRecord[] = [];\n const seen = new Set<string>();\n for (const bucket of dueBucketsAt(options.now, options.lookbackBuckets)) {\n for await (const pointer of registry.list(dueNamespace(bucket))) {\n const ref = decodeDueName(pointer.segment);\n if (ref === null) continue; // a foreign row in the reserved namespace \u2014 ignored, never acted on\n // A pointer to a segment in the reserved namespace itself: one written before that namespace was refused.\n // The fleet scan skips such a row as bookkeeping, so this scan does too, rather than fail on it each cycle.\n if (isReservedNamespace(ref.namespace)) continue;\n if (options.namespace !== undefined && ref.namespace !== options.namespace) continue;\n // A segment can appear in two buckets at once: `reindex` writes the new pointer before deleting the old,\n // so an interruption leaves both. De-duplicate here rather than retiring twice and reporting a phantom.\n const key = `${ref.namespace ?? ''}\\u0000${ref.segment}`;\n if (seen.has(key)) continue;\n seen.add(key);\n if (rows.length >= options.maxScanSegments) {\n throw new BudgetExceededError(\n `retireExpired: the due index yielded more than ${options.maxScanSegments} segments \u2014 the scan was ` +\n `abandoned there rather than completed. Raise \\`maxScanSegments\\`, narrow with \\`namespace\\`, or ` +\n `reduce \\`lookbackBuckets\\`.`,\n );\n }\n const live = await registry.get(ref);\n if (live === null) continue; // the segment is gone; the pointer is litter a later retirement will clear\n rows.push(live);\n }\n }\n return rows;\n}\n\n/**\n * Best-effort removal of a retired segment's due-index pointer. A failure here leaves litter that costs one\n * read when its bucket next comes due and is then skipped (the segment is gone, so the live re-read yields\n * `null`) \u2014 never a wrong retirement, so it must not turn a successful retirement into a fault.\n */\nasync function forgetDuePointer(\n registry: IRegistryDriver,\n ref: SegmentRef,\n expiresAt: number,\n): Promise<void> {\n if (!canIndex(ref)) return;\n try {\n await registry.delete(dueIndexRef(dueBucket(expiresAt), ref));\n } catch {\n // See above: litter, not a fault.\n }\n}\n\n/**\n * Retire every segment whose retention policy has expired, and clean up the tombstones earlier sweeps left.\n *\n * Returns a ledger; **never throws for a per-segment fault** (those become entries). It does throw for a bad\n * argument, and for a fleet larger than `maxScanSegments` \u2014 a scan that cannot be held in memory is a fail-loud\n * condition, not a partial result to be mistaken for a complete sweep.\n */\nexport async function retireExpired(\n deps: DropDeps,\n options: RetireExpiredOptions,\n): Promise<RetireExpiredResult> {\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const now = options.now;\n if (!Number.isFinite(now)) {\n throw new ValidationError(\n `retireExpired: \\`now\\` must be a finite epoch-ms; got ${String(now)}`,\n );\n }\n // A floor on `now` for the same reason `expiresAt` has one, and in the direction that actually destroys data: a\n // clock returning seconds makes every policy in the fleet look expired. (Too *small* a `now` is harmless \u2014\n // nothing expires \u2014 but a sweep against a pre-2001 instant cannot legitimately expire anything anyway, so\n // refusing it costs nothing and catches the units mistake.)\n if (now < MIN_EXPIRES_AT_MS) {\n throw new ValidationError(\n `retireExpired: \\`now\\` (${now}) is before ${MIN_EXPIRES_AT_MS} \u2014 that is almost certainly epoch SECONDS ` +\n `rather than milliseconds. A sweep with a wrong-units clock treats every policy as expired.`,\n );\n }\n const limit = options.limit ?? DEFAULT_RETIRE_LIMIT;\n if (!Number.isInteger(limit) || limit < 1) {\n throw new ValidationError(`retireExpired: \\`limit\\` must be a positive integer; got ${limit}`);\n }\n const purgeTombstones = options.purgeTombstones ?? true;\n const grace = options.tombstoneGraceMs ?? DEFAULT_TOMBSTONE_GRACE_MS;\n if (!Number.isFinite(grace) || grace < 0) {\n throw new ValidationError(\n `retireExpired: \\`tombstoneGraceMs\\` must be a non-negative finite number; got ${String(grace)}`,\n );\n }\n const maxScanSegments = options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS;\n if (!Number.isFinite(maxScanSegments) || maxScanSegments < 1) {\n throw new ValidationError(\n `retireExpired: \\`maxScanSegments\\` must be a finite number >= 1; got ${String(maxScanSegments)}`,\n );\n }\n const dryRun = options.dryRun === true;\n\n // Drain the enumeration first, bounded. The sweep mutates rows as it goes (a tombstone CAS, a row delete), and\n // iterating a live listing while doing so is driver-dependent \u2014 a paged listing may or may not observe its own\n // writes. Draining makes the candidate set a snapshot; the retire path then RE-READS each row before acting on\n // it, because deciding an irreversible deletion from a minutes-old copy is not the same as enumerating from one.\n const scan = options.scan ?? 'fleet';\n const rows =\n scan === 'index'\n ? await rowsFromDueIndex(deps.registry, {\n now,\n lookbackBuckets: options.lookbackBuckets ?? DEFAULT_LOOKBACK_BUCKETS,\n namespace: options.namespace,\n maxScanSegments,\n })\n : await drainRegistry(deps.registry, {\n namespace: options.namespace,\n maxScanSegments,\n op: 'retireExpired',\n });\n\n const shards = options.shards;\n const totalShards = options.totalShards ?? 0;\n const mine =\n shards === undefined || totalShards <= 1\n ? rows\n : rows.filter((r) => shards.includes(shardOf(segmentKey(r), totalShards)));\n\n const entries: RetireEntry[] = [];\n let eligible = 0;\n let retired = 0;\n let wouldRetire = 0;\n let tombstonesPurged = 0;\n let limited = false;\n // The budget is charged on ATTEMPT, not on success, and that distinction is the whole guard. `dropSegment`\n // writes the tombstone BEFORE sweeping Storage, so a fault in the Storage phase is a segment that is\n // already retired. Counting only successes would let a partial storage outage march through the entire fleet\n // with the cap never engaging, reporting `retired: 0, limited: false` (a \"completed sweep that retired nothing\")\n // while every segment in the namespace is tombstoned.\n let attempted = 0;\n\n for (const rec of mine) {\n const ref: SegmentRef = { namespace: rec.namespace, segment: rec.segment };\n const base = { segment: rec.segment, namespace: rec.namespace };\n const policy = readRetentionPolicy(rec.retention);\n\n if (rec.status === 'destroyed') {\n if (!purgeTombstones) continue;\n // Attribution is a POSITIVE MARKER the sweep writes on its own retirements, never an inference from\n // \"destroyed + an expired policy\". That inference would be wrong, and the consequence serious: `shredSegment`\n // never touches `retention`, so the ordinary ordering \u2014 set a 30-day policy, then a GDPR request arrives\n // mid-window and you `destroySegment` \u2014 leaves a **crypto-shred** tombstone carrying an expired policy.\n // Deleting that row destroys the local attestation for a right-to-erasure execution and un-fences the name\n // for every writer. A marker cannot be forged by that ordering.\n const retiredAt = retirementStamp(rec.retention);\n if (retiredAt === null) continue; // not ours \u2014 a GDPR tombstone, or one from a manual drop\n if (now - retiredAt < grace) continue; // inside the fence window; not ledger noise\n if (attempted >= limit) {\n limited = true;\n break;\n }\n attempted += 1;\n try {\n if (!(await isFullyReclaimed(deps, ref))) {\n // Self-heal rather than report-and-wait: `gcOrphanGenerations` takes EVERY generation of a destroyed\n // row, and nothing else will ever call it for this segment (no load publishes onto a tombstone, and the\n // erasure rewrite refuses one). Without this the row is stuck forever, the\n // objects are billed forever, and the sweep pays two list calls per cycle to say so again. Measured.\n if (!dryRun) await gcOrphanGenerations(ref, deps).catch(() => undefined);\n if (!(await isFullyReclaimed(deps, ref))) {\n entries.push({ ...base, action: 'skipped', reason: 'tombstone-not-empty' });\n continue;\n }\n }\n // Fenced on the token of the row this decision was made from (the marker and its grace window), so a\n // tombstone that was purged and re-created, or rewritten, since the scan is refused rather than deleted.\n if (!dryRun) await deps.registry.delete(ref, rec.token);\n if (!dryRun) tombstonesPurged += 1;\n entries.push({\n ...base,\n action: dryRun ? 'would-purge-tombstone' : 'purged-tombstone',\n });\n } catch (err) {\n entries.push({ ...base, action: 'skipped', reason: failureReason(err) });\n }\n continue;\n }\n\n if (policy === null) continue; // no policy \u2014 this segment is not the sweep's business\n if (policy === 'invalid') {\n entries.push({ ...base, action: 'skipped', reason: 'invalid-policy' });\n continue;\n }\n if (policy.expiresAt > now) continue; // not yet\n eligible += 1;\n if (attempted >= limit) {\n // Stop SCANNING, not just stop acting. A `limit` entry per deferred row would make the ledger scale with the\n // fleet rather than with the batch \u2014 250,000 rows behind a bad backfill is ~15 MB of entries the caller did\n // not ask for, all in one result object. They are not information: the next run picks them up, which is\n // what `limited` says.\n limited = true;\n break;\n }\n\n try {\n // Re-read the AUTHORITATIVE row before deleting anything. The enumeration is a snapshot, and on a large\n // fleet the gap between drawing it and reaching this segment is the whole sweep \u2014 minutes. Cancelling an\n // expiry is exactly the operator's recovery action for the bad-backfill case `limit` exists to survive, and\n // it did not work if a sweep was already running: the segment was retired from the stale copy. One strong\n // read per *eligible* segment, so the fleet enumeration stays a single `list()`. (`consistency.ts` takes the\n // same care for a read-only check; an irreversible deletion deserves at least as much.)\n const live = await deps.registry.get(ref);\n const livePolicy = live === null ? null : readRetentionPolicy(live.retention);\n if (\n live === null ||\n live.status === 'destroyed' ||\n livePolicy === null ||\n livePolicy === 'invalid' ||\n livePolicy.expiresAt > now\n ) {\n entries.push({ ...base, action: 'skipped', reason: 'policy-changed' });\n continue;\n }\n\n attempted += 1;\n // `confirmSegment` is satisfied structurally here \u2014 in a loop the guard is the same value twice, which is\n // why `dryRun` is the real preview. The dry run goes through `dropSegment` too, so the preview reports the\n // generations a real sweep would delete rather than a guess.\n const result = await dropSegment(ref, deps, {\n confirmSegment: rec.segment,\n dryRun,\n audit: options.audit,\n });\n if (dryRun) {\n wouldRetire += 1;\n entries.push({ ...base, action: 'would-retire', expiresAt: livePolicy.expiresAt, result });\n continue;\n }\n if (!result.dropped) {\n // `dropped: false` means the call found nothing to do, and the documented cause is a ref that does not\n // address what the caller meant. Counting that as a retirement is how a sweep addressing the wrong segment\n // reports success \u2014 so it is a ledger entry instead, carrying the reason `dropSegment` gave.\n //\n // Unreachable in practice now that the live re-read above proves the row exists (the only `dropped: false`\n // path needs no row at all), except through the microsecond in which someone else deletes it. Kept, and\n // labelled rather than claimed as covered: the invariant is \"never count a no-op as a retirement\", and it\n // must hold if either that re-read or `dropSegment`'s reporting ever changes.\n entries.push({\n ...base,\n action: 'skipped',\n reason: `failed: drop reported ${result.reason}`,\n });\n continue;\n }\n retired += 1;\n entries.push({ ...base, action: 'retired', expiresAt: livePolicy.expiresAt, result });\n // The pointer has done its job. Dropping it keeps a bucket from accumulating rows that every subsequent\n // lookback re-reads forever \u2014 the index would otherwise grow monotonically and slowly undo its own\n // purpose. Best-effort and unconditional on `scan`: a fleet sweep retires index-pointed segments too, and\n // leaving their pointers behind would make a later index scan re-read segments that no longer exist.\n await forgetDuePointer(deps.registry, ref, livePolicy.expiresAt);\n if (\n purgeTombstones &&\n result.generationsDeleted.length === 0 &&\n result.generationsRemaining.length === 0\n ) {\n // The segment really held nothing, so `dropSegment` has just written a tombstone for a name that was\n // empty. Left in place that row FENCES the name against every writer \u2014 and `setRetention` will mint a\n // row for any name, including a typo'd one, so this is reachable from a single mistake. Nothing\n // existed, so there is nothing a delete could resurrect: remove the row instead of bricking the name.\n //\n // **Only while `purgeTombstones` is on.** `false` is the caller asking for every row of this sweep's\n // retirements to stay, this one included; it falls through to the stamp below, so a later sweep with\n // purging on can still delete it after the grace window (an empty segment is trivially fully reclaimed).\n // Nothing re-processes it meanwhile: a `destroyed` row is skipped before any policy is read.\n //\n // **Both halves of the predicate are load-bearing.** `generationsDeleted: []` alone does NOT mean the\n // segment was empty \u2014 it is equally what a segment whose every `storage.delete` threw produces, because\n // `dropSegment`'s sweep loop stops once a pass deletes nothing. Purging the row on that reading left\n // the expired objects readable and billed, and then unreachable by everything that could have\n // collected them: `gcOrphanGenerations` returns `[]` with no row to compare against, the next sweep\n // never sees the name again (no row to enumerate), and `dropSegment` takes its `'absent'` path. The\n // data that was contractually supposed to expire stayed, silently, forever. `generationsRemaining` is\n // the honest question and `dropSegment` computes it one field over for exactly this reason.\n //\n // When something DOES remain, falling through is the right move rather than a special case: the row\n // keeps its tombstone, so reads stay refused, and the tombstone-purge pass above re-sweeps it with\n // `gcOrphanGenerations` (which takes every generation of a destroyed row) and purges the row once it is\n // genuinely empty. The residual is visible in this entry's `result.generationsRemaining` meanwhile.\n //\n // Fenced on the tombstone just written, so a name purged and re-created by someone else since is not\n // tombstoned by this delete: only a `destroyed` row is removed, and only at the token it was read with.\n //\n // **A delete that fails falls through to the stamp as well.** Left unstamped, the row is indistinguishable\n // from a crypto-shred's tombstone, which no sweep may ever delete, so one transient registry fault would\n // keep a fenced name for good. Stamped, it is this sweep's own tombstone, and a later sweep purges it. The\n // stamp itself only touches a `destroyed` row, so a name that was re-created meanwhile is left alone.\n const tombstone = await deps.registry.get(ref).catch(() => null);\n if (tombstone?.status === 'destroyed') {\n const deleted = await deps.registry.delete(ref, tombstone.token).then(\n () => true,\n () => false,\n );\n if (deleted) continue;\n }\n }\n // Stamp the tombstone as OURS, so a later sweep may purge the row (see the attribution note above). A\n // failure here only means the row is never auto-purged \u2014 never data loss \u2014 so it is best-effort.\n await stampRetirement(deps.registry, ref, now).catch(() => undefined);\n } catch (err) {\n // A fault AFTER the tombstone landed is a segment that IS retired, and reporting it as skipped told the\n // caller the opposite of the truth (\"a skipped entry is a segment that still holds data\"). One cheap read\n // settles which side of the tombstone we failed on.\n const after = await deps.registry.get(ref).catch(() => null);\n if (after?.status === 'destroyed') {\n // Stamp it here too. Without this a retirement that faulted after the tombstone landed is a row no later\n // sweep can attribute to itself, so it is never auto-purged \u2014 exactly the litter the purge exists to\n // prevent, and reachable from any transient Storage fault.\n await stampRetirement(deps.registry, ref, now).catch(() => undefined);\n retired += 1;\n entries.push({\n ...base,\n action: 'retired',\n expiresAt: policy.expiresAt,\n result: {\n ...base,\n dropped: true,\n generationsDeleted: [],\n generationsRemaining: [],\n cryptoShredded: false,\n reason: undefined,\n },\n fault: failureReason(err),\n });\n continue;\n }\n entries.push({ ...base, action: 'skipped', reason: failureReason(err) });\n }\n }\n\n return {\n scanned: mine.length,\n eligible,\n retired,\n wouldRetire,\n tombstonesPurged,\n limited,\n dryRun,\n entries,\n };\n}\n\n/** The key the sweep stamps on its own tombstones, so a purge is attributable rather than inferred. */\nconst RETIRED_AT = 'retiredBySweepAt';\n\n/** Read the sweep's own retirement stamp off a row, or `null` if this tombstone is not one of ours. */\nfunction retirementStamp(meta: GovernanceMeta | undefined): number | null {\n if (meta === undefined || meta === null || typeof meta !== 'object' || Array.isArray(meta)) {\n return null;\n }\n const raw = (meta as Record<string, unknown>)[RETIRED_AT];\n return typeof raw === 'number' && Number.isInteger(raw) && raw >= MIN_EXPIRES_AT_MS ? raw : null;\n}\n\n/**\n * Mark a freshly written tombstone as this sweep's own work, preserving whatever else the row's `retention`\n * metadata carried. Retried a couple of times on contention, then given up on: an unstamped tombstone is simply\n * never auto-purged, which is the safe direction.\n */\nasync function stampRetirement(\n registry: DropDeps['registry'],\n ref: SegmentRef,\n now: number,\n): Promise<void> {\n for (let attempt = 0; attempt < 3; attempt += 1) {\n const rec = await registry.get(ref);\n if (rec === null || rec.status !== 'destroyed') return; // nothing to stamp\n try {\n await registry.compareAndSwap(ref, rec.token, {\n retention: { ...rec.retention, [RETIRED_AT]: now },\n });\n return;\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n }\n }\n}\n\n/**\n * Whether a tombstoned segment's storage is provably gone \u2014 no Storage generations.\n *\n * The check is about what deleting the row would break rather than about tidiness: `gcOrphanGenerations` reads\n * the registry row to decide what to collect and returns empty when there is none, so deleting the row while\n * objects remain strands them permanently \u2014 billed forever, reachable by nothing. (An object a load was still\n * writing when the tombstone landed is exactly how they get there, which is why `dropSegment` reports\n * `generationsRemaining` at all.)\n *\n * Leaving the tombstone in place is self-healing: this sweep collects the orphan generations itself\n * (`gcOrphanGenerations` takes *every* generation of a destroyed row), and the next cycle purges the row.\n */\nasync function isFullyReclaimed(\n deps: { readonly storage: IStorageDriver },\n ref: SegmentRef,\n): Promise<boolean> {\n for await (const key of deps.storage.list(ref)) {\n void key;\n return false;\n }\n return true;\n}\n\nfunction failureReason(err: unknown): `failed: ${string}` {\n if (isWriteConflictError(err)) return 'failed: contended';\n return `failed: ${err instanceof Error ? err.message : String(err)}`;\n}\n", "/**\n * Fail-safe cross-store disaster-recovery check. The registry (`currentGen`) and the immutable `.crbm`\n * generations can be restored **independently**, so a failover can recover the registry *ahead of* the storage\n * objects \u2014 leaving `currentGen` pointing at a generation whose `.crbm` isn't present yet. That is likelier\n * than it sounds even with both in one bucket: a restore scoped to a prefix, or replayed\n * per-object from a version history, recovers the two prefixes at different points. That's a torn restore: reads of the affected segment then throw. This scan\n * detects it up front (run it at startup after a restore) instead of discovering it on the first read.\n *\n * Read-only; bounded fan-out. `destroyed` (crypto-shredded) segments are skipped \u2014 their Storage is intentionally\n * gone/unreadable, not a torn restore. A segment whose Storage/registry can't be read this pass is recorded in\n * `errored` (never aborts the scan). Each segment is checked against its **authoritative live pointer** \u2014 one\n * strong `registry.get` per segment \u2014 never the enumeration snapshot from `registry.list`, which a driver may\n * serve eventually-consistently and so lag a recent in-place pointer advance: trusting it would both\n * miss a torn *live* generation and cry torn on a generation the pointer has already advanced past (GC'd during\n * the scan). Residual: a load's publish plus a GC landing in the tiny per-segment get\u2192list gap can still yield a\n * transient false positive \u2014 run the scan against a quiesced fleet (the documented restore procedure), or re-run\n * to confirm a reported tear.\n */\n\nimport { mapWithConcurrency } from './concurrency';\nimport { ValidationError } from './errors';\nimport { DEFAULT_MAX_SCAN_SEGMENTS, drainRegistry } from './registry-scan';\nimport type { IStorageDriver, IRegistryDriver, SegmentRef } from './ports';\nimport { validateUserNamespace } from './validate';\n\n/** Default in-flight fan-out for the consistency scan \u2014 bounded, no thundering herd. */\n/**\n * Default ceiling on how many registry records one consistency check may hold resident: 250,000.\n *\n * **Named `maxScanSegments`, not `maxSegments`, on purpose.** A `maxSegments`-style option caps how much work\n * one cycle does and then *continues normally* (the retention sweep's `limit` is that shape). This one caps how\n * much a scan may hold and *refuses* past it. Same-sounding names with opposite behaviour at the limit is a\n * trap; the name says what it is \u2014 a ceiling on a scan that fails closed.\n *\n * Generous \u2014 fleets of 100K+ segments are the design target \u2014 while still bounding a DR drill's memory to\n * something a modest operator box survives. Raisable, because a ceiling you cannot lift is a landmine.\n */\n// Re-exported from its original home because this module is where the ceiling is documented; the value and the\n// loop that enforces it live in `registry-scan.ts`, shared with the retention sweep. This is an INTERNAL\n// re-export: it is not on `@cloudbitmaps/core`'s main entry, and a caller passes their own `maxScanSegments` rather\n// than read ours.\nexport { DEFAULT_MAX_SCAN_SEGMENTS } from './registry-scan';\nconst DEFAULT_CHECK_CONCURRENCY = 8;\n\nexport interface ConsistencyIssue {\n readonly segment: string;\n readonly namespace?: string;\n /** The registry's `currentGen` for the segment \u2014 the generation whose `.crbm` is missing from Storage. */\n readonly currentGen: number;\n /** The only issue class today: `currentGen` references a Storage generation that is not present (torn restore). */\n readonly issue: 'missing-storage-generation';\n}\n\n/** A segment that could not be checked this pass (Storage/registry read fault) \u2014 not proof of a torn restore. */\nexport interface ConsistencyErrorEntry {\n readonly segment: string;\n readonly namespace?: string;\n /** The read error's message (never the raw error, so the report stays serializable/loggable). */\n readonly error: string;\n}\n\nexport interface ConsistencyReport {\n /** Registered segments scanned (a `destroyed` segment counts as scanned but is never an issue). */\n readonly checked: number;\n /** Segments whose `currentGen` `.crbm` is absent \u2014 recover the object store (or restore to a coherent point). */\n readonly inconsistent: ConsistencyIssue[];\n /**\n * Segments that couldn't be read this pass (a transient/partial object store during a restore is exactly when\n * this runs) \u2014 **triage these too**: an unread segment is neither proven-coherent nor proven-torn. Empty on a\n * clean pass.\n */\n readonly errored: ConsistencyErrorEntry[];\n}\n\n/** Collect the set of generations the object store currently lists for a segment. */\nasync function generationsPresent(storage: IStorageDriver, ref: SegmentRef): Promise<Set<number>> {\n const present = new Set<number>();\n for await (const key of storage.list(ref)) present.add(key.generation);\n return present;\n}\n\ntype Outcome =\n | { readonly kind: 'ok' }\n | { readonly kind: 'issue'; readonly issue: ConsistencyIssue }\n | { readonly kind: 'error'; readonly error: ConsistencyErrorEntry };\n\n/**\n * Verify every registered segment's `currentGen` `.crbm` actually exists in Storage. Enumerates the registry\n * (optionally one namespace) and, for each non-`destroyed` segment, checks the object store lists that\n * generation. Returns the torn segments in `inconsistent` (empty \u21D2 coherent) and any unreadable segments in\n * `errored`.\n */\nexport async function runConsistencyCheck(\n deps: { readonly storage: IStorageDriver; readonly registry: IRegistryDriver },\n options: { namespace?: string; concurrency?: number; maxScanSegments?: number } = {},\n): Promise<ConsistencyReport> {\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const concurrency = options.concurrency ?? DEFAULT_CHECK_CONCURRENCY;\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n // Fail fast before the (possibly huge) registry scan, not after.\n throw new ValidationError(`concurrency must be a positive integer; got ${concurrency}`);\n }\n const maxScanSegments = options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS;\n // Bounded enumeration, shared with the retention sweep \u2014 see `registry-scan.ts` for why a fleet-wide scan is\n // drained rather than streamed, and why the ceiling is not optional.\n const recs = await drainRegistry(deps.registry, {\n namespace: options.namespace,\n maxScanSegments,\n op: 'checkConsistency',\n // Two callers reach this refusal, and only one of them can raise the ceiling: `store.checkConsistency` takes\n // no `maxScanSegments`, so the advice names the call that does.\n raise:\n 'raise `maxScanSegments` on `runConsistencyCheck` (`store.checkConsistency` takes no ceiling, so run ' +\n '`runConsistencyCheck({ storage: backend.storage, registry: backend.registry }, { maxScanSegments })` ' +\n 'over the same backend instead)',\n });\n const results = await mapWithConcurrency(recs, concurrency, async (rec): Promise<Outcome> => {\n if (rec.status === 'destroyed') return { kind: 'ok' }; // Storage intentionally gone \u2014 not a torn restore\n const ref: SegmentRef = { segment: rec.segment, namespace: rec.namespace };\n try {\n // Resolve the AUTHORITATIVE live pointer (strong read) \u2014 not the drained `rec.currentGen`, which the\n // enumeration may have read eventually-consistently and can lag the live pointer. `rec` is used only to\n // enumerate + skip destroyed segments.\n const live = await deps.registry.get(ref);\n if (!live || live.status === 'destroyed') return { kind: 'ok' }; // vanished/shredded \u2014 no live pointer\n // A row with no Storage generation is *deliberately* Storage-less \u2014 a retention policy recorded before the first\n // load, given a row so the segment is enumerable at all. There is no generation that ought to exist, so nothing can be missing. Reporting it\n // would make `missing-storage-generation` fire on the healthy steady state of every such segment, which is the\n // opposite of what a DR triage needs: the one real signal drowned in expected noise.\n if (live.currentGen === null) return { kind: 'ok' };\n const present = await generationsPresent(deps.storage, ref);\n if (present.has(live.currentGen)) return { kind: 'ok' };\n return {\n kind: 'issue',\n issue: {\n segment: rec.segment,\n namespace: rec.namespace,\n currentGen: live.currentGen,\n issue: 'missing-storage-generation',\n },\n };\n } catch (error) {\n // Fault isolation: one unreadable segment (a partial/transient object store mid-restore) must not abort\n // the whole triage. Record it and keep scanning the rest.\n return {\n kind: 'error',\n error: {\n segment: rec.segment,\n namespace: rec.namespace,\n error: error instanceof Error ? error.message : String(error),\n },\n };\n }\n });\n const inconsistent: ConsistencyIssue[] = [];\n const errored: ConsistencyErrorEntry[] = [];\n for (const r of results) {\n if (r.kind === 'issue') inconsistent.push(r.issue);\n else if (r.kind === 'error') errored.push(r.error);\n }\n return { checked: recs.length, inconsistent, errored };\n}\n", "/**\n * `withRetry` \u2014 the one retry/backoff primitive.\n *\n * A pure, storage-agnostic helper: it knows nothing about HTTP, AWS, or any SDK. It retries an operation\n * while the operation keeps failing with a **retryable** error (by default {@link TransientError}), waiting\n * a bounded, jittered, exponentially-growing delay between attempts. Time and randomness are **injected**\n * (the determinism seam) \u2014 production wires a `setTimeout`-backed clock + a real RNG; the\n * tests wire virtual ones \u2014 so a backoff schedule is replayable from a seed and unit tests run with no\n * real sleeping.\n *\n * It deliberately does **not** retry deterministic failures ({@link WriteConflictError},\n * {@link ValidationError}, {@link IntegrityError}, {@link NotFoundError}, \u2026): retrying those either can't\n * help or would be incorrect. A pointer conflict is retried by a *separate* loop inside\n * the publish, because each attempt must re-read the row and re-decide \u2014 a blind replay of the same\n * compare-and-swap would either fail again on a stale token or, worse, advance a pointer whose state has\n * changed underneath it. A write-once object collision is never replayed at all: the generation number is\n * taken, so the caller has to pick a new one.\n */\nimport type { Clock, Rng } from './determinism';\nimport { isTransientError, ValidationError } from './errors';\n\nexport interface RetryPolicy {\n /** Total attempts including the first (so `maxAttempts: 4` = 1 try + 3 retries). Must be \u2265 1. */\n readonly maxAttempts: number;\n /** Delay before the first retry, in ms. Grows by `backoffFactor` each subsequent retry. */\n readonly baseDelayMs: number;\n /** Upper bound on a single (pre-jitter) delay, in ms \u2014 caps the exponential growth. */\n readonly maxDelayMs: number;\n /** Exponential growth factor between retries (e.g. 2 \u21D2 base, 2\u00B7base, 4\u00B7base, \u2026). */\n readonly backoffFactor: number;\n /**\n * `'full'` \u21D2 the actual wait is uniform in `[0, computed]` (AWS-recommended full jitter: decorrelates\n * concurrent retriers so they don't reconverge into a thundering herd). `'none'` \u21D2 wait exactly `computed`.\n */\n readonly jitter: 'full' | 'none';\n}\n\n/**\n * Conservative defaults: 4 attempts, 50ms \u2192 100 \u2192 200 (\u00D72), capped at 2s, full jitter. Tuned for a cloud\n * backend's brief throttle/5xx blip \u2014 enough to ride out a transient fault without turning a hard outage\n * into a long hang. Override per store (`retry`) or per read source (`RetryingStorageChunkSource`) if your latency\n * budget differs.\n */\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = {\n maxAttempts: 4,\n baseDelayMs: 50,\n maxDelayMs: 2_000,\n backoffFactor: 2,\n jitter: 'full',\n};\n\nexport interface RetryDeps {\n readonly clock: Clock;\n readonly rng: Rng;\n /** Override which errors are retryable. Default: any {@link TransientError}. */\n readonly isRetryable?: (err: unknown) => boolean;\n /**\n * Optional hook (observability) fired before each backoff wait. `attempt` is 1-based (the one that failed).\n * Best-effort: an error it throws is swallowed, so it can neither stop the retry nor change the error thrown.\n */\n readonly onRetry?: (info: { attempt: number; delayMs: number; err: unknown }) => void;\n}\n\n/**\n * Backoff delay (ms) for the retry that follows a given 1-based attempt, before jitter is applied. Exposed\n * for tests. `attempt` 1 \u21D2 `baseDelayMs`, 2 \u21D2 `base\u00B7factor`, \u2026 capped at `maxDelayMs`.\n */\nexport function backoffDelayMs(policy: RetryPolicy, attempt: number): number {\n const raw = policy.baseDelayMs * policy.backoffFactor ** (attempt - 1);\n return Math.min(policy.maxDelayMs, raw);\n}\n\n/** Apply the policy's jitter to a computed delay using the injected RNG. */\nexport function applyJitter(policy: RetryPolicy, delayMs: number, rng: Rng): number {\n if (policy.jitter === 'none') return delayMs;\n return rng.next() * delayMs; // full jitter: uniform in [0, delayMs)\n}\n\n/**\n * Run `op`, retrying transient failures per `policy`. Resolves with `op`'s result, or rejects with the last\n * error once attempts are exhausted (or immediately for a non-retryable error). The thrown error is always\n * the *operation's* error \u2014 never a wrapper \u2014 so callers keep their typed-error branching.\n */\nexport async function withRetry<T>(\n op: () => Promise<T>,\n policy: RetryPolicy,\n deps: RetryDeps,\n): Promise<T> {\n const retryable = deps.isRetryable ?? isTransientError;\n // `Math.max(1, x)` guards 0 and negatives but NOT NaN \u2014 `Math.max(1, NaN)` is NaN, and `1 <= NaN` is false,\n // so the loop below would never execute: `op()` never called, and the function rejects with the literal\n // `undefined` from `lastErr`. Every retried read would fail without touching the backend, and callers would\n // catch a non-Error. Reachable from ordinary wiring \u2014 `maxAttempts: Number(process.env.X)` with the\n // var unset is NaN. Fail loudly instead, and floor it so a fractional value can't sleep on a final attempt\n // that never happens.\n if (!Number.isFinite(policy.maxAttempts) || policy.maxAttempts < 1) {\n throw new ValidationError(\n `retry.maxAttempts must be a finite number >= 1 (got ${String(policy.maxAttempts)})`,\n );\n }\n const attempts = Math.floor(policy.maxAttempts);\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n return await op();\n } catch (err) {\n lastErr = err;\n if (attempt >= attempts || !retryable(err)) throw err;\n const delayMs = applyJitter(policy, backoffDelayMs(policy, attempt), deps.rng);\n try {\n deps.onRetry?.({ attempt, delayMs, err });\n } catch {\n // Observability is best-effort, as for a metrics sink (`safeMetrics`): a throwing hook must neither abort\n // the retry nor replace the operation's error with its own. This module has no sink to report it to.\n }\n await deps.clock.sleep(delayMs);\n }\n }\n // Unreachable: the loop either returns or throws. Satisfies the type checker.\n throw lastErr;\n}\n", "/**\n * The store's read retry.\n *\n * {@link RetryingStorageChunkSource} wraps a {@link StorageChunkSource} so every read it serves retries\n * **transient** faults with bounded, jittered backoff, through the one shared `core/retry` primitive, so every\n * backend (S3, GCS, Azure Blob, LocalFS, \u2026) inherits the same policy instead of each rolling its own. `CloudRoaring`\n * wraps the source it reads segment data through in one \u2014 a `StorageChunkSource` passed as `storage` included, so\n * wrapping that one first multiplies each read's attempts \u2014 and a pinned handle's source too, unless it is built\n * with `retry: false`.\n * Pure composition over the port (no SDK, no I/O of its own); the driver under the source is responsible for\n * *classifying* its transient faults (raising {@link TransientError}), and this wrapper decides *whether and when*\n * to retry.\n *\n * **Reads only, on purpose.** A read changes nothing, so running it again is safe. A write is not: a conditional put\n * or compare-and-swap that lands and then loses its response would, replayed, find its own write already there and\n * report it as a conflict. So nothing here wraps a write, and the store's writes and admin calls report a transient\n * fault to their caller, who re-runs the call or checks what landed.\n *\n * What is **not** retried: every deterministic error (`ValidationError`/`IntegrityError`/`NotFoundError`/\u2026).\n * Default classifier: {@link isTransientError}.\n */\nimport type { Clock, Rng } from '../../core/determinism';\nimport { withRetry, DEFAULT_RETRY_POLICY } from '../../core/retry';\nimport { isTransientError } from '../../core/errors';\nimport type { RetryPolicy } from '../../core/retry';\nimport type { ChunkRef, StorageChunkSource, SegmentRef, SegmentSize } from '../../core/ports';\n\nexport interface RetryingOptions {\n readonly clock: Clock;\n readonly rng: Rng;\n /** Defaults to {@link DEFAULT_RETRY_POLICY}. */\n readonly policy?: RetryPolicy;\n /** Override which errors are retryable. Default: {@link isTransientError} (any `TransientError`). */\n readonly isRetryable?: (err: unknown) => boolean;\n /** Observability hook fired before each backoff wait. */\n readonly onRetry?: (info: { attempt: number; delayMs: number; err: unknown }) => void;\n}\n\n/** Internal: resolve the options into the `core/retry` shape once, at construction. */\nfunction toRetry(opts: RetryingOptions): {\n policy: RetryPolicy;\n deps: {\n clock: Clock;\n rng: Rng;\n isRetryable: (e: unknown) => boolean;\n onRetry?: RetryingOptions['onRetry'];\n };\n} {\n return {\n policy: opts.policy ?? DEFAULT_RETRY_POLICY,\n deps: {\n clock: opts.clock,\n rng: opts.rng,\n isRetryable: opts.isRetryable ?? isTransientError,\n onRetry: opts.onRetry,\n },\n };\n}\n\n/** Wrap a storage chunk source so its reads retry transient faults. */\nexport class RetryingStorageChunkSource implements StorageChunkSource {\n private readonly inner: StorageChunkSource;\n private readonly policy: RetryPolicy;\n private readonly deps: ReturnType<typeof toRetry>['deps'];\n /** Present only when the inner source supports it \u2014 so capability detection stays honest. */\n readonly sizeOf?: (ref: SegmentRef) => Promise<SegmentSize | null>;\n readonly cardinalities?: (ref: SegmentRef) => Promise<ReadonlyMap<number, number> | null>;\n readonly currentGeneration?: (ref: SegmentRef) => Promise<number | null>;\n readonly invalidate?: (ref: SegmentRef) => void;\n readonly exists?: (ref: SegmentRef) => Promise<boolean>;\n readonly currentVersion?: (ref: SegmentRef) => Promise<string | null>;\n\n /**\n * Forwarded, not retried: a property, with no request behind it. Without it the grounded cost report priced a\n * retrying store's refresh at the default TTL whatever the store was configured with.\n */\n get pointerRefreshMs(): number | undefined {\n return this.inner.pointerRefreshMs;\n }\n\n constructor(inner: StorageChunkSource, opts: RetryingOptions) {\n this.inner = inner;\n const r = toRetry(opts);\n this.policy = r.policy;\n this.deps = r.deps;\n const innerSizeOf = inner.sizeOf;\n if (innerSizeOf) {\n this.sizeOf = (ref) => withRetry(() => innerSizeOf.call(inner, ref), this.policy, this.deps);\n }\n const innerCardinalities = inner.cardinalities;\n if (innerCardinalities) {\n this.cardinalities = (ref) =>\n withRetry(() => innerCardinalities.call(inner, ref), this.policy, this.deps);\n }\n const innerCurrentGeneration = inner.currentGeneration;\n if (innerCurrentGeneration) {\n this.currentGeneration = (ref) =>\n withRetry(() => innerCurrentGeneration.call(inner, ref), this.policy, this.deps);\n }\n // Forwarded, NOT retried: it is synchronous and cannot fail, and dropping memoized state has no transient\n // mode to back off from. Forgetting to forward it would be silent \u2014 the wrapper would satisfy the port\n // while the invalidation stopped one layer short of the source that holds the snapshot and the DEK.\n const innerInvalidate = inner.invalidate;\n if (innerInvalidate) {\n this.invalidate = (ref) => {\n innerInvalidate.call(inner, ref);\n };\n }\n // Retried, unlike `invalidate`: this one reads the registry, so it has a transient mode to back off from.\n const innerExists = inner.exists;\n if (innerExists) {\n this.exists = (ref) => withRetry(() => innerExists.call(inner, ref), this.policy, this.deps);\n }\n const innerCurrentVersion = inner.currentVersion;\n if (innerCurrentVersion) {\n this.currentVersion = (ref) =>\n withRetry(() => innerCurrentVersion.call(inner, ref), this.policy, this.deps);\n }\n }\n\n getChunk(ref: ChunkRef): Promise<Uint8Array | null> {\n return withRetry(() => this.inner.getChunk(ref), this.policy, this.deps);\n }\n\n listChunkKeys(ref: SegmentRef): Promise<number[]> {\n return withRetry(() => this.inner.listChunkKeys(ref), this.policy, this.deps);\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMO,IAAM,UAAU;AAChB,IAAM,cAAc;AAC3B,IAAM,SAAS;AAUR,SAAS,QAAQ,IAAsB;AAC5C,MAAI,CAAC,OAAO,UAAU,EAAE,KAAK,KAAK,KAAK,KAAK,SAAS;AACnD,UAAM,IAAI,gBAAgB,+BAA+B,OAAO,SAAS,EAAE,EAAE;AAAA,EAC/E;AACA,SAAO,EAAE,UAAU,OAAO,IAAI,WAAW,KAAK,OAAO;AACvD;AAWO,IAAM,gBAAgB,cAAc;AAEpC,SAAS,OAAO,UAAkB,WAA2B;AAClE,WAAU,WAAW,WAAW,OAAQ,MAAM,YAAY;AAC5D;;;ACRO,IAAM,iBAAyB,EAAE,aAAa,IAAU;AAMxD,SAAS,cAAc,KAA+B,UAAiC;AAC5F,MAAI,QAAQ,MAAO,QAAO;AAC1B,QAAM,cAAc,KAAK,eAAe,SAAS;AACjD,MAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAAG;AACrD,UAAM,IAAI,gBAAgB,sDAAsD,WAAW,EAAE;AAAA,EAC/F;AACA,SAAO,EAAE,YAAY;AACvB;AASO,SAAS,mBACd,KACA,aACe;AACf,MAAI,QAAQ,OAAW,QAAO;AAC9B,SAAO,cAAc,KAAK,eAAe,cAAc;AACzD;AAOO,SAAS,YAAY,QAAuB,mBAA2B,IAAkB;AAC9F,MAAI,WAAW,QAAQ,oBAAoB,OAAO,aAAa;AAC7D,UAAM,IAAI;AAAA,MACR,GAAG,EAAE,qBAAqB,iBAAiB,qCACtC,OAAO,WAAW;AAAA,IACzB;AAAA,EACF;AACF;AAwBA,eAAsB,oBACpB,QACA,QACA,IACc;AACd,QAAM,MAAW,CAAC;AAClB,mBAAiB,QAAQ,QAAQ;AAC/B,QAAI,KAAK,IAAI;AAGb,QAAI,WAAW,QAAQ,IAAI,SAAS,OAAO,aAAa;AACtD,YAAM,IAAI;AAAA,QACR,GAAG,EAAE,+BAA+B,OAAO,WAAW;AAAA,MAGxD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACtFA,IAAM,QAAQ;AACd,IAAM,eAAe;AAEd,SAAS,WAAW,KAAyB;AAClD,QAAM,KAAK,IAAI,cAAc,SAAY,eAAe,iBAAiB,IAAI,SAAS;AACtF,SAAO,GAAG,EAAE,GAAG,KAAK,GAAG,iBAAiB,IAAI,OAAO,CAAC;AACtD;AAEO,SAAS,cAAc,KAAyB;AACrD,SAAO,GAAG,WAAW,GAAG,CAAC,GAAG,KAAK;AACnC;AAEO,SAAS,YAAY,KAAuB;AACjD,SAAO,GAAG,cAAc,GAAG,CAAC,GAAG,IAAI,QAAQ;AAC7C;AAcO,SAAS,YAAY,KAAe,SAAkC;AAC3E,SAAO,GAAG,YAAY,GAAG,CAAC,GAAG,KAAK,GAAG,OAAO;AAC9C;AAOO,SAAS,QAAQ,KAAa,aAA6B;AAChE,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,SAAK,IAAI,WAAW,CAAC;AACrB,QAAI,KAAK,KAAK,GAAG,QAAW;AAAA,EAC9B;AACA,UAAQ,MAAM,KAAK;AACrB;;;ACmBO,IAAM,eAA6B;AAAA,EACxC,UAAgB;AAAA,EAEhB;AACF;AAMO,SAAS,YAAY,MAAkC;AAC5D,MAAI,SAAS,aAAc,QAAO;AAClC,SAAO;AAAA,IACL,QAAQ,OAA0B;AAChC,UAAI;AACF,aAAK,QAAQ,KAAK;AAAA,MACpB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAiBA,IAAM,WAAoC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,sBAAN,MAAM,qBAA4C;AAAA,EAC/C,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,EAAE;AAAA,EAC1C,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE;AAAA,EAC7B,UAAU,EAAE,WAAW,EAAE;AAAA,EACzB,YAAY,EAAE,OAAO,GAAG,eAAe,GAAG,eAAe,EAAE;AAAA,EAC3D,MACN,qBAAoB,QAAQ;AAAA,EAE9B,OAAe,UAAoE;AAEjF,UAAM,MAAM,uBAAO,OAAO,IAAI;AAC9B,eAAW,QAAQ,SAAU,KAAI,IAAI,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE;AAChE,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,OAA0B;AAChC,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,aAAK,QAAQ,QAAQ;AACrB,aAAK,QAAQ,SAAS,MAAM;AAC5B,aAAK,QAAQ,WAAW,MAAM;AAC9B;AAAA,MACF,KAAK;AACH,YAAI,MAAM,IAAK,MAAK,MAAM,QAAQ;AAAA,YAC7B,MAAK,MAAM,UAAU;AAC1B;AAAA,MACF,KAAK;AACH,aAAK,QAAQ,aAAa;AAC1B;AAAA,MACF,KAAK;AACH,aAAK,UAAU,SAAS;AACxB,aAAK,UAAU,iBAAiB,MAAM;AACtC,aAAK,UAAU,iBAAiB,MAAM;AACtC;AAAA,MACF,KAAK,MAAM;AAET,cAAM,KAAK,KAAK,IAAI,MAAM,IAAI;AAC9B,YAAI,OAAO,OAAW;AACtB,WAAG,SAAS;AACZ,WAAG,WAAW,MAAM;AACpB;AAAA,MACF;AAAA,MACA,SAAS;AACP,cAAM,aAAoB;AAC1B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAA4B;AAC1B,UAAM,MAAM,uBAAO,OAAO,IAAI;AAC9B,eAAW,QAAQ,SAAU,KAAI,IAAI,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE;AAC7D,WAAO;AAAA,MACL,SAAS,EAAE,GAAG,KAAK,QAAQ;AAAA,MAC3B,OAAO,EAAE,GAAG,KAAK,MAAM;AAAA,MACvB,SAAS,EAAE,GAAG,KAAK,QAAQ;AAAA,MAC3B,WAAW,EAAE,GAAG,KAAK,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,EAAE;AAC/C,SAAK,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE;AAClC,SAAK,UAAU,EAAE,WAAW,EAAE;AAC9B,SAAK,YAAY,EAAE,OAAO,GAAG,eAAe,GAAG,eAAe,EAAE;AAChE,SAAK,MAAM,qBAAoB,QAAQ;AAAA,EACzC;AACF;;;AC7LA,IAAM,2BAA2B,KAAK;AAEtC,IAAM,gCAAgC;AAGtC,IAAM,aAAiC,EAAE,KAAK,MAAM,EAAE;AAoFtD,SAAS,SAAS,OAAuD;AACvE,QAAM,QAAQ,OAAO;AACrB,QAAM,UAAU,OAAO;AACvB,MAAI,UAAU,UAAa,YAAY,OAAW,QAAO;AACzD,aAAW,CAAC,MAAM,KAAK,KAAK;AAAA,IAC1B,CAAC,SAAS,KAAK;AAAA,IACf,CAAC,WAAW,OAAO;AAAA,EACrB,GAAY;AACV,QAAI,UAAU,WAAc,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,QAAQ,UAAU;AACrF,YAAM,MAAM,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK;AACzE,YAAM,IAAI,gBAAgB,GAAG,IAAI,6BAA6B,OAAO,SAAS,GAAG,EAAE;AAAA,IACrF;AAAA,EACF;AACA,QAAM,KAAK,UAAU,SAAY,IAAI,QAAQ;AAC7C,QAAM,KAAK,WAAW;AACtB,MAAI,KAAK,GAAI,QAAO;AACpB,SAAO;AAAA,IACL,OAAO,KAAK,MAAM,KAAK,WAAW;AAAA,IAClC,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK,MAAM,KAAK,WAAW;AAAA,IAClC,OAAO,KAAK;AAAA,EACd;AACF;AAGA,IAAM,iBAA2B;AAAA,EAC/B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO,cAAc;AAAA,EACrB,OAAO;AACT;AAMA,IAAM,SAAS,CAAC,UAAkB,MAC/B,aAAa,EAAE,SAAS,EAAE,QAAQ,KAAO,aAAa,EAAE,SAAS,EAAE,QAAQ;AAG9E,UAAU,QAAQ,OAAoB,UAAkB,GAAgC;AACtF,QAAM,OAAO,aAAa,EAAE,QAAQ,EAAE,QAAQ;AAC9C,QAAM,KAAK,aAAa,EAAE,QAAQ,EAAE,QAAQ;AAC5C,aAAW,aAAa,OAAO;AAC7B,QAAI,YAAY,GAAI;AACpB,QAAI,aAAa,KAAM,OAAM,OAAO,UAAU,SAAS;AAAA,EACzD;AACF;AAGA,SAAS,WAAW,MAAyB,GAAuB;AAClE,QAAM,eAAe,CAAC,QAAwB;AAC5C,QAAI,KAAK;AACT,QAAI,KAAK,KAAK;AACd,WAAO,KAAK,IAAI;AACd,YAAM,MAAO,KAAK,OAAQ;AAC1B,UAAI,KAAK,GAAG,IAAK,IAAK,MAAK,MAAM;AAAA,UAC5B,MAAK;AAAA,IACZ;AACA,WAAO;AAAA,EACT;AACA,SAAO,KAAK,MAAM,aAAa,EAAE,KAAK,GAAG,aAAa,EAAE,QAAQ,CAAC,CAAC;AACpE;AAWA,IAAM,cAAN,MAAkB;AAAA,EAMhB,YACmB,MACA,OACA,KACA,MACjB;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EATF,OACf,CAAC;AAAA,EACK,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EAUhB,MAAM,OAAoC;AACxC,UAAM,QAAQ,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,OAAO,EAAE,CAAC,IAAI,KAAK;AACnF,WAAO,KAAK,WAAW,KAAK,KAAK,UAAU,KAAK,WAAW,KAAK,QAAQ,OAAO;AAC7E,WAAK,KAAK;AAAA,QACR,KAAK,MAAM,KAAK,KAAK,KAAK,UAAU,CAAE,EAAE;AAAA,UACtC,CAAC,WAAW,EAAE,MAAM;AAAA,UACpB,CAAC,WAAoB,EAAE,OAAO,MAAM,OAAO,EAAE,MAAM,EAAE;AAAA,QACvD;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK,KAAK,MAAM;AACnC,SAAK,SAAS;AACd,QAAI,KAAK,MAAO,OAAM,KAAK,MAAM;AACjC,WAAO,KAAK;AAAA,EACd;AACF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EAEjB,YAAY,MAAkB;AAC5B,SAAK,UAAU,KAAK;AACpB,SAAK,QAAQ,KAAK;AAClB,SAAK,QAAQ,KAAK;AAClB,SAAK,iBAAiB,KAAK,kBAAkB;AAC7C,SAAK,QAAQ,KAAK,SAAS;AAE3B,SAAK,SAAS,KAAK,WAAW,SAAY,iBAAiB,KAAK;AAGhE,SAAK,UAAU,YAAY,KAAK,WAAW,YAAY;AACvD,SAAK,YAAY,KAAK,YAAY;AAAA,EACpC;AAAA;AAAA,EAGA,MAAM,IAAI,KAAiB,IAA8B;AACvD,UAAM,EAAE,UAAU,UAAU,IAAI,QAAQ,EAAE;AAC1C,UAAM,QAAQ,MAAM,KAAK,aAAa,EAAE,GAAG,KAAK,SAAS,GAAG,MAAM,KAAK,aAAa,GAAG,CAAC;AACxF,WAAO,QAAQ,MAAM,IAAI,SAAS,IAAI;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,MAAM,KAAkC;AAC5C,UAAM,gBAAgB,KAAK,QAAQ,gBAAgB,MAAM,KAAK,QAAQ,cAAc,GAAG,IAAI;AAC3F,QAAI,eAAe;AACjB,UAAIA,SAAQ;AACZ,iBAAW,CAAC,GAAG,CAAC,KAAK,eAAe;AAClC,aAAK,sBAAsB,CAAC;AAC5B,QAAAA,UAAS;AAAA,MACX;AACA,aAAOA;AAAA,IACT;AACA,UAAM,YAAY,MAAM,KAAK,UAAU,GAAG;AAC1C,gBAAY,KAAK,QAAQ,UAAU,QAAQ,OAAO;AAClD,UAAM,MAAM,MAAM,KAAK,aAAa,GAAG;AAEvC,UAAM,SAAS,KAAK,YAAY,KAAK,WAAW,KAAK,KAAK;AAC1D,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,IAAK,WAAU,MAAM,OAAO,KAAK,IAAI,QAAQ;AACnF,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,sBAA+B;AACjC,WAAO,OAAO,KAAK,QAAQ,WAAW;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,mBAAuC;AACzC,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,YAAY,KAA8C;AACxD,WAAO,KAAK,QAAQ,SAAS,KAAK,QAAQ,OAAO,GAAG,IAAI,QAAQ,QAAQ,IAAI;AAAA,EAC9E;AAAA;AAAA,EAGQ,YACN,KACA,WACA,KACA,MACa;AACb,WAAO,IAAI;AAAA,MACT;AAAA,MACA,CAAC,aAAa,KAAK,aAAa,EAAE,GAAG,KAAK,SAAS,GAAG,GAAG;AAAA,MACzD;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QAAQ,KAAiB,OAAyC;AAChE,WAAO,UAAU,SAAY,KAAK,WAAW,GAAG,IAAI,KAAK,aAAa,KAAK,KAAK;AAAA,EAClF;AAAA,EAEA,OAAe,WAAW,KAAyC;AACjE,UAAM,YAAY,MAAM,KAAK,UAAU,GAAG;AAC1C,gBAAY,KAAK,QAAQ,UAAU,QAAQ,SAAS;AACpD,UAAM,MAAM,MAAM,KAAK,aAAa,GAAG;AACvC,UAAM,SAAS,KAAK,YAAY,KAAK,WAAW,KAAK,IAAI;AACzD,eAAW,YAAY,WAAW;AAChC,YAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,UAAI,UAAU,KAAM;AAEpB,iBAAW,aAAa,MAAO,OAAM,OAAO,UAAU,SAAS;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,OAAe,aAAa,KAAiB,OAAwC;AACnF,UAAM,IAAI,SAAS,KAAK,KAAK;AAC7B,QAAI,MAAM,QAAS;AACnB,UAAM,YAAY,WAAW,MAAM,KAAK,UAAU,GAAG,GAAG,CAAC;AACzD,gBAAY,KAAK,QAAQ,UAAU,QAAQ,SAAS;AACpD,UAAM,MAAM,MAAM,KAAK,aAAa,GAAG;AACvC,UAAM,SAAS,KAAK,YAAY,KAAK,WAAW,KAAK,IAAI;AACzD,eAAW,YAAY,WAAW;AAChC,YAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,UAAI,UAAU,KAAM;AACpB,UAAI,OAAO,UAAU,CAAC,GAAG;AACvB,mBAAW,MAAM,QAAQ,OAAO,UAAU,CAAC,EAAG,OAAM;AAAA,MACtD,OAAO;AACL,mBAAW,aAAa,MAAO,OAAM,OAAO,UAAU,SAAS;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCA,UAAU,MAA6B,SAAkD;AACvF,WAAO,KAAK,QAAQ,MAAM,SAAS,WAAW,CAAC,GAAG,OAAO,aAAa,OAAO;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,MAA6B,SAAkD;AACnF,WAAO,KAAK,QAAQ,MAAM,SAAS,WAAW,CAAC,GAAG,OAAO,SAAS,OAAO;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OACE,KACA,UACA,SACwB;AACxB,WAAO,KAAK,QAAQ,CAAC,GAAG,GAAG,UAAU,OAAO,UAAU,OAAO;AAAA,EAC/D;AAAA,EAEA,OAAe,QACb,MACA,aACA,MACA,IACA,SACwB;AAOxB,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,gBAAgB,GAAG,EAAE,gCAAgC;AAAA,IACjE;AACA,QAAI,OAAO,YAAY,YAAY,WAAW,GAAG;AAC/C,YAAM,IAAI,gBAAgB,kDAAkD;AAAA,IAC9E;AACA,UAAM,QAAQ,SAAS,eAAe;AACtC,QAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACzC,YAAM,IAAI;AAAA,QACR,+CAA+C,SAAS,WAAW;AAAA,MACrE;AAAA,IACF;AAGA,UAAM,SAAS,mBAAmB,SAAS,QAAQ,KAAK,MAAM;AAG9D,UAAM,IAAI,SAAS,OAAO;AAC1B,QAAI,MAAM,QAAS;AAmBnB,UAAM,UAAU,OAAO,QAAsC;AAC3D,YAAM,MAAM,MAAM,KAAK,UAAU,GAAG;AACpC,YAAM,MAAM,MAAM,KAAK,aAAa,GAAG;AACvC,YAAM,OAAO,MAAM,OAAO,MAAM,WAAW,KAAK,CAAC;AACjD,aAAO,EAAE,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,WAAW,IAAI,WAAW,GAAG,IAAI;AAAA,IACtE;AACA,UAAM,CAAC,UAAU,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC7C,QAAQ,IAAI,KAAK,IAAI,OAAO,CAAC;AAAA,MAC7B,QAAQ,IAAI,YAAY,IAAI,OAAO,CAAC;AAAA,IACtC,CAAC;AACD,UAAM,KAAK,qBAAqB,CAAC,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,qBAAqB,EAAE;AAS5F,UAAM,aAAuB,CAAC;AAC9B,QAAI,SAAS,OAAO;AAClB,YAAM,QAAQ,SAAS,OAAO,CAAC,GAAG,MAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,IAAI,CAAE;AAC5E,iBAAW,KAAK,MAAM,MAAM;AAC1B,YAAI,SAAS,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,EAAG,YAAW,KAAK,CAAC;AAAA,MAC7D;AAAA,IACF,OAAO;AACL,YAAM,OAAO,oBAAI,IAAY;AAC7B,iBAAW,KAAK,SAAU,YAAW,KAAK,EAAE,KAAM,MAAK,IAAI,CAAC;AAC5D,iBAAW,KAAK,KAAM,YAAW,KAAK,CAAC;AAAA,IACzC;AACA,eAAW,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/B,UAAM,SAAS;AAUf,QAAI,QAAQ;AACZ,eAAW,OAAO,QAAQ;AACxB,iBAAW,KAAK,SAAU,KAAI,SAAS,SAAS,EAAE,KAAK,IAAI,GAAG,EAAG,UAAS;AAC1E,iBAAW,KAAK,SAAU,KAAI,EAAE,KAAK,IAAI,GAAG,EAAG,UAAS;AAAA,IAC1D;AACA,gBAAY,QAAQ,OAAO,EAAE;AAI7B,QAAI,KAAK,WAAW;AAClB,YAAM,eAAe,oBAAI,IAAY;AACrC,iBAAW,KAAK,SAAU,YAAW,KAAK,EAAE,KAAM,cAAa,IAAI,CAAC;AACpE,iBAAW,KAAK,SAAU,YAAW,KAAK,EAAE,KAAM,cAAa,IAAI,CAAC;AAKpE,WAAK,QAAQ,QAAQ;AAAA,QACnB,MAAM;AAAA,QACN;AAAA,QACA,UAAU,KAAK,SAAS,SAAS;AAAA,QACjC,eAAe,OAAO;AAAA;AAAA;AAAA,QAGtB,eAAe,aAAa,OAAO,OAAO;AAAA,MAC5C,CAAC;AAAA,IACH;AAOA,UAAM,UAAU,CAAC,QACf,KAAK,aAAa,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,MAC/C,CAAC,YAAY,EAAE,KAAK,OAAO;AAAA,MAC3B,CAAC,WAAoB,EAAE,KAAK,QAAQ,MAAM,MAAM;AAAA,IAClD;AAEF,UAAM,WAAiC,CAAC;AACxC,QAAI,OAAO;AACX,WAAO,OAAO,OAAO,UAAU,SAAS,SAAS,MAAO,UAAS,KAAK,QAAQ,OAAO,MAAM,CAAE,CAAC;AAC9F,WAAO,SAAS,SAAS,GAAG;AAC1B,YAAM,OAAO,MAAM,SAAS,MAAM;AAClC,UAAI,OAAO,OAAO,OAAQ,UAAS,KAAK,QAAQ,OAAO,MAAM,CAAE,CAAC;AAChE,UAAI,KAAK,UAAU,OAAW,OAAM,KAAK;AACzC,UAAI,KAAK,UAAU,CAAC,KAAK,OAAO,SAAS;AACvC,YAAI,MAAM,QAAQ,OAAO,KAAK,KAAK,CAAC,GAAG;AACrC,qBAAW,MAAM,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAG,OAAM;AAAA,QAC5D,OAAO;AACL,qBAAW,aAAa,KAAK,OAAQ,OAAM,OAAO,KAAK,KAAK,SAAS;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,aACZ,UACA,UACA,MACA,UAC6B;AAG7B,UAAM,UAAU,SAAS,QAAQ,WAAW,SAAS,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,QAAQ,CAAC;AACvF,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,UAAM,SAAS,MAAM,QAAQ;AAAA,MAC3B,QAAQ,IAAI,CAAC,OAAO,KAAK,aAAa,EAAE,GAAG,GAAG,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AAAA,IACxE;AAGA,QAAI,SAAS,SAAS,OAAO,KAAK,CAAC,MAAM,MAAM,IAAI,EAAG,QAAO;AAC7D,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,MAAM,QAAQ,MAAM,MAAM,IAAI,KAAK,MAAM,MAAM;AACrD,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,QAAQ,OAAO,CAAC;AACtB,UAAI,UAAU,QAAQ,UAAU,OAAW;AAC3C,UAAI,SAAS,OAAO;AAClB,YAAI,WAAW,KAAK;AACpB,YAAI,IAAI,QAAS,QAAO;AAAA,MAC1B,OAAO;AACL,YAAI,UAAU,KAAK;AAAA,MACrB;AAAA,IACF;AACA,QAAI,IAAI,QAAS,QAAO;AAKxB,UAAM,WAAW,SAAS,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,QAAQ,CAAC;AAC5D,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,OAAO,MAAM,QAAQ;AAAA,QACzB,SAAS,IAAI,CAAC,MAAM,KAAK,aAAa,EAAE,GAAG,EAAE,KAAK,SAAS,GAAG,EAAE,GAAG,CAAC;AAAA,MACtE;AACA,iBAAW,OAAO,MAAM;AACtB,YAAI,QAAQ,KAAM;AAClB,YAAI,cAAc,GAAG;AACrB,YAAI,IAAI,QAAS,QAAO;AAAA,MAC1B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,UAAU,KAAoC;AAC1D,UAAM,OAAO,CAAC,GAAI,MAAM,KAAK,QAAQ,cAAc,GAAG,CAAE;AACxD,eAAW,KAAK,KAAM,MAAK,sBAAsB,CAAC;AAClD,SAAK,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAEzB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,GAAG;AAC3B,cAAM,IAAI,eAAe,0CAA0C,KAAK,CAAC,CAAC,EAAE;AAAA,MAC9E;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAsB,GAAiB;AAC7C,QAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,KAAK,aAAa;AACrD,YAAM,IAAI,eAAe,0CAA0C,CAAC,EAAE;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBQ,0BAA0B,QAAqB,UAAwB;AAC7E,UAAM,MAAM,OAAO,UAAU;AAC7B,QAAI,QAAQ,UAAa,MAAM,eAAe;AAC5C,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ,wBAAwB,GAAG,4CACnC,aAAa;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAc,qBACZ,UACA,OACA,IACe;AACf,QAAI,UAAU,QAAQ,KAAK,QAAQ,WAAW,OAAW;AACzD,UAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS;AAChD,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,MAAM,IAAI,OAAO,OAAO,EAAE,KAAK,EAAE,KAAK,QAAQ,MAAM,KAAK,QAAQ,OAAQ,EAAE,GAAG,EAAE,EAAE;AAAA,IACpF;AACA,UAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAChE,QAAI,OAAO,WAAW,EAAG;AACzB,UAAM,QAAQ,OACX,IAAI,CAAC,MAAO,EAAE,cAAc,SAAY,IAAI,EAAE,OAAO,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,OAAO,GAAI,EAC3F,KAAK,IAAI;AACZ,UAAM,IAAI;AAAA,MACR,GAAG,EAAE,aAAa,KAAK;AAAA,IAIzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,WAAW,KAAuB;AAChC,UAAM,SAAS,cAAc,GAAG;AAChC,SAAK,OAAO,YAAY,CAAC,QAAQ,IAAI,WAAW,MAAM,CAAC;AACvD,SAAK,QAAQ,aAAa,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGQ,WAAW,KAAqD;AACtE,WAAO,KAAK,QAAQ,oBAChB,KAAK,QAAQ,kBAAkB,GAAG,IAClC,QAAQ,QAAQ,MAAS;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,aAAa,KAA8D;AACvF,QAAI,KAAK,QAAQ,eAAgB,QAAO,KAAK,QAAQ,eAAe,GAAG;AACvE,WAAO,KAAK,WAAW,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,aACZ,KACA,KAC6B;AAC7B,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,WAAW,QAAQ,SAAY,YAAY,GAAG,IAAI,YAAY,KAAK,GAAG;AAC5E,QAAI,KAAK,OAAO;AACd,YAAM,SAAS,KAAK,MAAM,IAAI,QAAQ;AACtC,UAAI,QAAQ;AACV,YAAI,KAAK,UAAW,MAAK,QAAQ,QAAQ,EAAE,MAAM,SAAS,KAAK,KAAK,CAAC;AACrE,eAAO;AAAA,MACT;AACA,UAAI,KAAK,UAAW,MAAK,QAAQ,QAAQ,EAAE,MAAM,SAAS,KAAK,MAAM,CAAC;AAAA,IACxE;AACA,UAAM,YAAY,KAAK,YAAY,KAAK,MAAM,IAAI,IAAI;AACtD,UAAM,QAAQ,MAAM,KAAK,QAAQ,SAAS,GAAG;AAC7C,QAAI,KAAK,WAAW;AAClB,WAAK,QAAQ,QAAQ;AAAA,QACnB,MAAM;AAAA,QACN,WAAW,IAAI;AAAA,QACf,SAAS,IAAI;AAAA,QACb,OAAO,QAAQ,MAAM,SAAS;AAAA,QAC9B,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,IAAI,SAAS;AAAA,MAC9C,CAAC;AAAA,IACH;AACA,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,SAAS,KAAK,MAAM,gBAAgB,OAAO,KAAK,cAAc;AACpE,SAAK,0BAA0B,QAAQ,IAAI,QAAQ;AACnD,SAAK,OAAO,IAAI,UAAU,MAAM;AAChC,WAAO;AAAA,EACT;AACF;;;ACjvBO,IAAM,aAAN,MAAuB;AAAA,EACX,MAAM,oBAAI,IAAiB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,aAAa;AAAA,EAErB,YAAY,SAAqB;AAC/B,QAAI,CAAC,OAAO,UAAU,QAAQ,UAAU,KAAK,QAAQ,aAAa,GAAG;AACnE,YAAM,IAAI,gBAAgB,8CAA8C,QAAQ,UAAU,EAAE;AAAA,IAC9F;AACA,QACE,QAAQ,aAAa,WACpB,EAAE,QAAQ,WAAW,MAAM,CAAC,OAAO,SAAS,QAAQ,QAAQ,IAC7D;AACA,YAAM,IAAI;AAAA,QACR,kDAAkD,QAAQ,QAAQ;AAAA,MACpE;AAAA,IACF;AACA,QAAI,QAAQ,UAAU,WAAc,EAAE,QAAQ,QAAQ,MAAM,CAAC,OAAO,SAAS,QAAQ,KAAK,IAAI;AAC5F,YAAM,IAAI,gBAAgB,+CAA+C,QAAQ,KAAK,EAAE;AAAA,IAC1F;AACA,SAAK,aAAa,QAAQ;AAC1B,SAAK,WAAW,QAAQ;AACxB,SAAK,QAAQ,QAAQ;AACrB,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,IAAI,KAAuB;AACzB,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,UAAU,OAAW,QAAO;AAChC,QAAI,MAAM,aAAa,KAAK,MAAM,IAAI,GAAG;AACvC,WAAK,KAAK,KAAK,KAAK;AACpB,aAAO;AAAA,IACT;AAEA,SAAK,IAAI,OAAO,GAAG;AACnB,SAAK,IAAI,IAAI,KAAK,KAAK;AACvB,WAAO,MAAM;AAAA,EACf;AAAA;AAAA,EAGA,KAAK,KAAuB;AAC1B,WAAO,KAAK,IAAI,IAAI,GAAG,GAAG;AAAA,EAC5B;AAAA,EAEA,IAAI,KAAQ,OAAgB;AAC1B,UAAM,OAAO,KAAK,IAAI,IAAI,GAAG;AAC7B,QAAI,SAAS,OAAW,MAAK,cAAc,KAAK;AAChD,SAAK,IAAI,OAAO,GAAG;AACnB,UAAM,YAAY,KAAK,UAAU,SAAY,WAAW,KAAK,MAAM,IAAI,IAAI,KAAK;AAChF,SAAK,IAAI,IAAI,KAAK,EAAE,OAAO,WAAW,OAAO,EAAE,CAAC;AAChD,WAAO,KAAK,IAAI,OAAO,KAAK,YAAY;AACtC,YAAM,SAAS,KAAK,IAAI,KAAK,EAAE,KAAK;AACpC,UAAI,OAAO,SAAS,KAAM;AAC1B,WAAK,QAAQ,OAAO,KAAK;AAAA,IAC3B;AACA,SAAK,cAAc,GAAG;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,KAAQ,OAAqB;AACrC,QAAI,EAAE,SAAS,MAAM,CAAC,OAAO,SAAS,KAAK,GAAG;AAE5C,YAAM,IAAI,gBAAgB,oDAAoD,KAAK,EAAE;AAAA,IACvF;AACA,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,UAAU,OAAW;AACzB,SAAK,cAAc,QAAQ,MAAM;AACjC,UAAM,QAAQ;AACd,SAAK,cAAc,GAAG;AAAA,EACxB;AAAA,EAEQ,cAAc,YAAqB;AACzC,QAAI,KAAK,aAAa,OAAW;AACjC,WAAO,KAAK,aAAa,KAAK,YAAY,KAAK,IAAI,OAAO,GAAG;AAC3D,YAAM,SAAS,KAAK,aAAa,UAAU;AAC3C,UAAI,WAAW,OAAW;AAC1B,WAAK,QAAQ,MAAM;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGQ,aAAa,YAA8B;AACjD,eAAW,OAAO,KAAK,IAAI,KAAK,GAAG;AACjC,UAAI,QAAQ,WAAY,QAAO;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,QAAQ,KAAc;AAC5B,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,UAAU,OAAW,MAAK,KAAK,KAAK,KAAK;AAAA,EAC/C;AAAA,EAEQ,KAAK,KAAQ,OAAuB;AAC1C,SAAK,cAAc,MAAM;AACzB,SAAK,IAAI,OAAO,GAAG;AAAA,EACrB;AAAA;AAAA,EAGA,IAAI,KAAiB;AACnB,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,UAAU,OAAW,QAAO;AAChC,QAAI,MAAM,aAAa,KAAK,MAAM,IAAI,GAAG;AACvC,WAAK,KAAK,KAAK,KAAK;AACpB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,KAAiB;AACtB,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,UAAU,OAAW,QAAO;AAChC,SAAK,KAAK,KAAK,KAAK;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,MAAmC;AAC7C,QAAI,UAAU;AAEd,eAAW,OAAO,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC,GAAG;AACtC,UAAI,CAAC,KAAK,GAAG,EAAG;AAChB,YAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,UAAI,UAAU,OAAW;AACzB,WAAK,KAAK,KAAK,KAAK;AACpB,iBAAW;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,IAAI,MAAM;AACf,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA,EAGA,IAAI,cAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AACF;;;AC5LO,IAAM,6BAA6B;AAQnC,IAAM,mBAAmB;AAGzB,IAAM,4BAA4B;AAGlC,IAAM,+BAA+B,KAAK,OAAO;;;ACqBxD,SAAS,WAAc,OAAuB;AAC5C,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;AAC1E,WAAO,OAAO,KAAK;AACnB,eAAW,SAAS,OAAO,OAAO,KAAK,EAAG,YAAW,KAAK;AAAA,EAC5D;AACA,SAAO;AACT;AA6EO,IAAM,uCAAoD,WAAW;AAAA,EAC1E,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,wBAAwB;AAAA,EACxB,WAAW;AAAA,IACT,EAAE,MAAM,mBAAmB,WAAW,KAAK,WAAW,OAAO,WAAW,EAAE;AAAA,IAC1E,EAAE,MAAM,mBAAmB,WAAW,MAAM,WAAW,OAAO,WAAW,EAAE;AAAA,IAC3E,EAAE,MAAM,oBAAoB,WAAW,MAAM,WAAW,OAAO,WAAW,EAAE;AAAA,IAC5E,EAAE,MAAM,mBAAmB,WAAW,MAAM,WAAW,MAAM;AAAA,IAC7D,EAAE,MAAM,mBAAmB,WAAW,OAAO,WAAW,MAAM;AAAA,IAC9D,EAAE,MAAM,oBAAoB,WAAW,OAAO,WAAW,MAAM;AAAA,IAC/D,EAAE,MAAM,qBAAqB,WAAW,OAAO,WAAW,MAAM;AAAA,IAChE,EAAE,MAAM,qBAAqB,WAAW,QAAQ,WAAW,MAAM;AAAA,IACjE,EAAE,MAAM,qBAAqB,WAAW,QAAQ,WAAW,MAAM;AAAA,IACjE,EAAE,MAAM,sBAAsB,WAAW,QAAQ,WAAW,MAAM;AAAA,IAClE,EAAE,MAAM,sBAAsB,WAAW,QAAQ,WAAW,MAAM;AAAA,IAClE,EAAE,MAAM,qBAAqB,WAAW,OAAO,QAAQ,OAAO,WAAW,MAAM;AAAA,IAC/E,EAAE,MAAM,sBAAsB,WAAW,OAAO,QAAQ,QAAQ,WAAW,KAAK;AAAA,IAChF,EAAE,MAAM,sBAAsB,WAAW,QAAQ,QAAQ,QAAQ,WAAW,KAAK;AAAA,IACjF,EAAE,MAAM,sBAAsB,WAAW,QAAQ,QAAQ,QAAQ,WAAW,KAAK;AAAA,IACjF,EAAE,MAAM,uBAAuB,WAAW,QAAQ,QAAQ,SAAS,WAAW,MAAM;AAAA,IACpF,EAAE,MAAM,uBAAuB,WAAW,QAAQ,QAAQ,SAAS,WAAW,OAAO;AAAA,EACvF;AACF,CAAC;AASM,IAAM,uBAAuB,WAAW,EAAE,YAAY,IAAI,CAAC;AAM3D,IAAM,yBAAyC,WAAW;AAAA,EAC/D,MAAM;AAAA,EACN,SAAS,EAAE,eAAe,KAAK,eAAe,GAAK,oBAAoB,MAAM;AAAA,EAC7E,OAAO,EAAE,aAAa,qCAAqC;AAC7D,CAAC;AAoJD,IAAM,kBAAkB;AACxB,IAAM,oBAAoB,kBAAkB;AAC5C,IAAM,MAAM,QAAQ;AAMpB,IAAM,+BAA+B;AASrC,IAAM,uBAAuB;AAC7B,IAAM,yBAAyB;AAG/B,SAAS,oBAAoB,GAAuB,OAAuB;AACzE,MAAI,MAAM,UAAa,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,GAAG;AACnD,UAAM,IAAI,gBAAgB,GAAG,KAAK,sCAAsC,CAAC,EAAE;AAAA,EAC7E;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,GAAmB;AAClC,SAAO,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;AACjC;AAGA,IAAM,UAAU;AAUhB,SAAS,UAAU,QAAqB,UAAiC;AACvE,SAAO,SAAS,aAAa,IAAI,OAAO,2BAA2B,SAAS,UAAU;AACxF;AAGA,SAAS,eAAe,QAAgE;AACtF,QAAM,KAAK;AACX,MAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,UAAM,IAAI,gBAAgB,GAAG,EAAE,wBAAwB;AAAA,EACzD;AACA,MAAI,OAAO,OAAO,WAAW,YAAY,OAAO,WAAW,IAAI;AAC7D,UAAM,IAAI,gBAAgB,GAAG,EAAE,6CAA6C;AAAA,EAC9E;AACA,MAAI,CAAC,MAAM,QAAQ,OAAO,SAAS,KAAK,OAAO,UAAU,WAAW,GAAG;AACrE,UAAM,IAAI,gBAAgB,GAAG,EAAE,6CAA6C;AAAA,EAC9E;AACA,MAAI,CAAC,OAAO,UAAU,OAAO,gBAAgB,KAAK,OAAO,mBAAmB,GAAG;AAC7E,UAAM,IAAI;AAAA,MACR,GAAG,EAAE,kDAAkD,OAAO,gBAAgB;AAAA,IAChF;AAAA,EACF;AACA,QAAM,WAAW;AAAA,IACf,OAAO;AAAA,IACP,GAAG,EAAE;AAAA,EACP;AACA,MAAI,YAAY,GAAG;AACjB,UAAM,IAAI,gBAAgB,GAAG,EAAE,gDAAgD,QAAQ,EAAE;AAAA,EAC3F;AACA,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,KAAK,OAAO,WAAW;AAChC,QAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,OAAO,EAAE,SAAS,YAAY,EAAE,SAAS,IAAI;AACtF,YAAM,IAAI,gBAAgB,GAAG,EAAE,0CAA0C;AAAA,IAC3E;AACA,UAAM,MAAM,GAAG,EAAE,cAAc,KAAK,UAAU,EAAE,IAAI,CAAC;AACrD,QAAI,MAAM,IAAI,EAAE,IAAI,EAAG,OAAM,IAAI,gBAAgB,GAAG,GAAG,kBAAkB;AACzE,UAAM,IAAI,EAAE,IAAI;AAChB,QAAI,EAAE,oBAAoB,EAAE,WAAW,GAAG,GAAG,YAAY,IAAI,IAAI;AAC/D,YAAM,IAAI,gBAAgB,GAAG,GAAG,4BAA4B;AAAA,IAC9D;AACA,wBAAoB,EAAE,UAAU,GAAG,GAAG,GAAG,SAAS;AAClD,wBAAoB,EAAE,WAAW,GAAG,GAAG,YAAY;AACnD,QAAI,EAAE,cAAc,WAAc,CAAC,OAAO,UAAU,EAAE,SAAS,KAAK,EAAE,YAAY,IAAI;AACpF,YAAM,IAAI,gBAAgB,GAAG,GAAG,2CAA2C,EAAE,SAAS,EAAE;AAAA,IAC1F;AAEA,QAAI,EAAE,UAAU,QAAQ,CAAC,IAAI,IAAI;AAC/B,YAAM,IAAI;AAAA,QACR,GAAG,GAAG,mCAAmC,QAAQ;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,gBAAgB,QAAqB,SAA+B;AAC3E,QAAM,OAAuB,CAAC;AAC9B,aAAW,YAAY,OAAO,WAAW;AACvC,UAAM,SAAS,UAAU,QAAQ,QAAQ;AACzC,QAAI,SAAS,KAAK,IAAI,GAAG,KAAK,KAAK,UAAU,MAAM,CAAC;AAEpD,QAAI,SAAS,MAAM,SAAS,KAAK,UAAU,QAAS,WAAU;AAC9D,QAAI,SAAS,cAAc,UAAa,SAAS,SAAS,UAAW;AACrE,UAAM,QAAQ,UAAU,IAAI,OAAO;AACnC,SAAK,KAAK;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,QAAQ,SAAS,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AACA,QAAM,OAAO,WAAW,UAAU,GAAG;AACrC,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI;AAAA,MACR,6DAA6D,IAAI;AAAA,IACnE;AAAA,EACF;AAGA,QAAM,WAAW,KAAK,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,EAAE,UAAU,GAAG,QAAQ;AAC1E,QAAM,CAAC,IAAI,IAAI,KACZ,OAAO,CAAC,MAAM,EAAE,cAAc,WAAW,OAAO,EAChD;AAAA,IACC,CAAC,GAAG,MACF,EAAE,QAAQ,EAAE,SACZ,EAAE,aAAa,EAAE,eAChB,EAAE,SAAS,OAAO,EAAE,SAAS,OAAO,KAAK,EAAE,SAAS,OAAO,EAAE,SAAS,OAAO,IAAI;AAAA,EACtF;AACF,MAAI,SAAS,UAAa,CAAC,OAAO,SAAS,KAAK,UAAU,GAAG;AAC3D,UAAM,IAAI;AAAA,MACR,oCAAoC,IAAI;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,eAAe,QAA0D;AAChF,MAAI,WAAW,QAAQ,OAAO,WAAW,SAAU,QAAO;AAC1D,QAAM,EAAE,QAAQ,WAAW,kBAAkB,uBAAuB,IAAI;AACxE,QAAM,MAAM,CAAC,MAAoC;AAC/C,QAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO;AAChD,UAAM,EAAE,MAAM,WAAW,QAAQ,WAAW,UAAU,IAAI;AAC1D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,MACzC,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,MAAM,QAAQ,SAAS,IAAI,MAAM,KAAK,WAAW,GAAG,IAAI;AAAA,EACrE;AACF;AASA,SAAS,gBAAgB,OAAgC,WAA6B;AACpF,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,UAAM,IAAI,gBAAgB,yDAAyD;AAAA,EACrF;AAGA,QAAM,EAAE,YAAY,YAAY,IAAI;AACpC,QAAM,QAAQ,eAAe;AAC7B,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,UAAU,OAAO;AACnB,UAAM,IAAI;AAAA,MACR,6EACG,QACG,kHACA;AAAA,IACR;AAAA,EACF;AACA,MAAI,OAAO;AACT,UAAM,SAAS,eAAe,WAAW;AACzC,mBAAe,MAAM;AACrB,UAAM,UAAU,gBAAgB,QAAQ,SAAS;AACjD,WAAO,EAAE,YAAY,QAAQ,YAAY,OAAO,EAAE,QAAQ,QAAQ,EAAE;AAAA,EACtE;AACA,SAAO,EAAE,YAAY,oBAAoB,YAAY,0BAA0B,EAAE;AACnF;AAMA,SAAS,WAAW,OAAuB;AACzC,QAAM,QAAQ,CAAC,SAAS,OAAO,OAAO,OAAO,OAAO,KAAK;AACzD,MAAI,QAAQ;AACZ,MAAI,OAAO;AAEX,QAAM,QAAQ,CAAC,GAAW,MAAuB,MAAM,IAAI,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC9F,SAAO,OAAO,MAAM,SAAS,KAAK,MAAM,OAAO,IAAI,KAAK,MAAM;AAC5D,aAAS;AACT,YAAQ;AAAA,EACV;AACA,MAAI,OAAO,EAAG,QAAO,GAAG,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC;AACvD,QAAM,IAAI,KAAK,MAAM,KAAK;AAC1B,SAAO,GAAG,CAAC,QAAQ,MAAM,IAAI,KAAK,GAAG;AACvC;AAGA,SAAS,aAAa,GAAyB;AAC7C,QAAM,QAAQ,CAAC,MAAsB,EAAE,eAAe,OAAO;AAC7D,QAAM,QAAQ,GAAG,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,IAAI,QAAQ,EAAE,UAAU,IAAI,KAAK,GAAG;AAClF,SAAO,EAAE,WAAW,IAAI,cAAc,KAAK,KAAK,GAAG,MAAM,EAAE,MAAM,CAAC,YAAY,KAAK;AACrF;AAGA,SAAS,YAAY,MAA6B;AAChD,MAAI,KAAK,cAAc,OAAW,QAAO,oBAAoB,KAAK,WAAW,mBAAmB;AAChG,MAAI,KAAK,gBAAgB,QAAW;AAClC,WAAO,oBAAoB,KAAK,aAAa,qBAAqB,IAAI;AAAA,EACxE;AACA,SAAO;AACT;AAGA,SAAS,YAAY,OAQN;AACb,QAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,QAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,QAAM,IAAI;AAIV,sBAAoB,QAAQ,eAAe,+BAA+B;AAC1E,sBAAoB,QAAQ,eAAe,+BAA+B;AAC1E,sBAAoB,QAAQ,oBAAoB,oCAAoC;AACpF,QAAM,YAAY;AAAA,IAChB,QAAQ,wBAAwB;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,eAAe,oBAAoB,MAAM,cAAc,cAAc;AAC3E,QAAM,YAAY,eAAe;AAGjC,QAAM,WAAW,gBAAgB,OAAO,SAAS;AACjD,QAAM,cAAc,SAAS;AAE7B,QAAM,eAAe;AAAA,IACnB,oBAAoB,MAAM,SAAS,gBAAgB,GAAG,cAAc;AAAA,EACtE;AACA,QAAM,cAAc,oBAAoB,MAAM,SAAS,eAAe,GAAG,aAAa;AACtF,QAAM,mBAAmB;AAAA,IACvB,MAAM,SAAS,oBAAoB;AAAA,IACnC;AAAA,EACF;AACA,QAAM,qBAAqB;AAAA,IACzB,MAAM,SAAS,sBAAsB;AAAA,IACrC;AAAA,EACF;AACA,QAAM,gBAAgB,oBAAoB,MAAM,SAAS,iBAAiB,GAAG,eAAe;AAC5F,QAAM,kBAAkB;AAAA,IACtB,MAAM,SAAS,mBAAmB;AAAA,IAClC;AAAA,EACF;AACA,QAAM,uBAAuB;AAAA,IAC3B,MAAM,SAAS,wBAAwB;AAAA,IACvC;AAAA,EACF;AACA,MAAI,uBAAuB,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR,0FAA0F,oBAAoB;AAAA,IAChH;AAAA,EACF;AACA,QAAM,cAAc,oBAAoB,MAAM,SAAS,eAAe,GAAG,aAAa;AACtF,QAAM,kBAAkB;AAAA,IACtB,MAAM,SAAS,mBAAmB;AAAA,IAClC;AAAA,EACF;AACA,MAAI,kBAAkB,GAAG;AACvB,UAAM,IAAI,gBAAgB,2CAA2C,eAAe,EAAE;AAAA,EACxF;AACA,QAAM,WAAW;AAAA,IACf,MAAM,SAAS,YAAY;AAAA,IAC3B;AAAA,EACF;AAGA,QAAM,gBAAgB,QAAQ,gBAAgB;AAC9C,QAAM,SAAS,QAAQ,gBAAgB;AAGvC,QAAM,eAAe,IAAI;AACzB,QAAM,aAAa,cAAc,IAAI;AACrC,QAAM,aAAa,mBAAmB;AAItC,QAAM,YACJ,WAAW,IACP,KAAK,IAAK,kBAAkB,cAAc,IAAI,MAAQ,UAAU,cAAc,CAAC,IAC/E;AAEN,QAAM,aAAa,YAAY,QAAQ;AACvC,QAAM,WAAW,aAAa;AAC9B,QAAM,gBACJ,qBAAqB,+BAA+B,uBAAuB;AAC7E,QAAM,gBAAgB,aAAa,gBAAgB;AACnD,QAAM,WAAW,yBAAyB;AAC1C,QAAM,WACJ,kBAAkB,kBAAkB,wBAAwB,SAAS,WAAW;AAClF,QAAM,aAAa,YAAY,YAAY;AAC3C,QAAM,QAAQ,WAAW,gBAAgB,aAAa,WAAW;AAIjE,QAAM,WAAW,KAAK,IAAI,GAAG,cAAc,aAAa,UAAU;AAClE,QAAM,aACJ,gBAAgB,KAAK,eAAe,IAChC,YAAY,IAAI,gBAAgB,gBAChC;AAEN,QAAM,UACJ,SAAS,cAAc,MAAM,YAAY,QAAQ,cAAc,QAAQ;AAEzE,QAAM,WAAW,KAAK,IAAI,UAAU,eAAe,YAAY,UAAU,UAAU;AACnF,MAAI,SAAS;AACb,MAAI,aAAa,YAAY,WAAW,EAAG,UAAS;AAAA,WAC3C,aAAa,iBAAiB,gBAAgB,GAAG;AACxD,aAAS;AAAA,EACX,WAAW,aAAa,YAAY,WAAW;AAC7C,aAAS;AAAA,WACF,aAAa,cAAc,aAAa,EAAG,UAAS;AAG7D,QAAM,QAAQ,SAAS;AAGvB,QAAM,UAAU,KAAK,MAAM,YAAY,MAAM;AAC7C,QAAM,aACJ,UAAU,SACN,SACA,UACE,IAAI,YAAY,QAAQ,CAAC,CAAC,yEACtB,aAAa,MAAM,OAAO,CAAC,MAC/B,IAAI,YAAY,QAAQ,CAAC,CAAC,6BAA6B,WAAW,YAAY,CAAC,KAC3E,aAAa,MAAM,OAAO,CAAC;AACvC,QAAM,YACJ,YAAY,cACR,sBAAsB,MAAM,QAAQ,CAAC,CAAC,mBAAmB,cAAc,IAAI,WAAW,mBAAmB,kBAAkB,MAAM,KACjI,YAAY,YACV,IAAI,MAAM,QAAQ,CAAC,CAAC,+BAAqB,cAAc,IAAI,WAAW,cAAc,kBAAkB,MAAM,KAC5G,IAAI,MAAM,QAAQ,CAAC,CAAC,kBAAkB,cAAc,IAAI,WAAW,cAAc,kBAAkB,MAAM;AAEjH,QAAM,cAAc,UAAU,WAAc,MAAM,QAAQ,SAAS,UAAU,KAAK;AAClF,QAAM,YACJ,UAAU,SACN,8BAA8B,WAAW,wCACxC,UACG,sIAEA,wEAAwE,WAAW,YAAY,CAAC,aACpG,KAAK,aAAa,MAAM,OAAO,CAAC,8BAA8B,MAAM,OAAO,gBAAgB,gBAC5E,QAAQ,MAAM,OAAO,yBAAyB,KAAK,QAAQ,CAAC,CAAC,CAAC,yBACzE,MAAM,OAAO,MAAM,QACtB,UACG,gFACA,yIAEJ,wIAEC,cACG,IAAI,MAAM,QAAQ,SAAS,IAAI,sOAG/B,OACH,MAAM,uBACH,kNAGA;AAEV,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,gBAAgB,IACZ,kBAAkB,aAAa,aAAa,eAAe,6CACxD,oBAAoB,kBAAkB,QAAQ,6DACjD;AAAA,IACJ,GAAI,aAAa,IACb;AAAA,MACE,8BAA8B,aAAa,eACtC,+BAA+B,SAAS,gBAAgB,oBAAoB,oBAC5E,kBAAkB;AAAA,IACzB,IACA,CAAC;AAAA,IACL,cAAc,IACV,WAAW,IACT,4BAA4B,WAAW,8BAA8B,eAAe,kEAC1B,QAAQ,wCAClE,6LAEF,cAAc,IACZ,yLAEA;AAAA,IACN,cAAc,4BACV,qEAAqE,yBAAyB,wLAG9F;AAAA,IACJ,GAAI,MAAM,cAAc,CAAC;AAAA;AAAA,IAEzB;AAAA,EACF,EAAE,OAAO,CAAC,MAAmB,MAAM,IAAI;AAEvC,SAAO;AAAA,IACL,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAAA,IACA,eACE,UAAU,SACN,EAAE,OAAO,SAAS,YAAY,YAAY,IAC1C;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,SAAS;AAAA,QACP,UAAU,MAAM,QAAQ,SAAS;AAAA,QACjC,QAAQ,MAAM,QAAQ;AAAA,QACtB,OAAO,MAAM,QAAQ;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,IACN,gBAAgB,EAAE,aAAa,WAAW;AAAA,IAC1C;AAAA,IACA;AAAA,IACA,aAAa,EAAE,cAAc,aAAa,QAAQ,MAAM,UAAU,MAAM;AAAA,EAC1E;AACF;AAOO,SAAS,aAAa,OAAkC;AAC7D,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,WAAW,MAAM,YAAY,CAAC;AACpC,MAAI,eAAe;AACnB,MAAI,uBAAuB;AAC3B,aAAW,QAAQ,MAAM,UAAU;AACjC,UAAM,QAAQ,KAAK,MAAM,oBAAoB,KAAK,SAAS,GAAG,eAAe,CAAC;AAC9E,oBAAgB,YAAY,IAAI,IAAI;AACpC,QACE,KAAK,cAAc,UACnB,KAAK,gBAAgB,UACrB,YAAY,IAAI,IAAI,QAAQ,GAC5B;AACA,6BAAuB;AAAA,IACzB;AAAA,EACF;AACA,SAAO,YAAY,EAAE,cAAc,UAAU,SAAS,UAAU,OAAO,qBAAqB,CAAC;AAC/F;AAOO,SAAS,eAAe,OAMhB;AACb,SAAO,YAAY;AAAA,IACjB,cAAc,MAAM;AAAA,IACpB,UAAU,MAAM,YAAY,CAAC;AAAA,IAC7B,SAAS,MAAM,WAAW;AAAA,IAC1B,UAAU,MAAM,YAAY;AAAA,IAC5B,YAAY,MAAM;AAAA,EACpB,CAAC;AACH;;;AChwBO,IAAM,gBAAgB;AActB,SAAS,UAAU,WAA2B;AACnD,SAAO,KAAK,MAAM,YAAY,aAAa;AAC7C;AAGO,SAAS,aAAa,QAAwB;AACnD,SAAO,GAAG,yBAAyB,GAAG,MAAM;AAC9C;AAGO,SAAS,cAAc,QAAoD;AAChF,SAAO,oBAAoB,OAAO,SAAS;AAC7C;AAUO,SAAS,cAAc,KAAyB;AACrD,QAAM,KAAK,IAAI,aAAa;AAC5B,SAAO,GAAG,GAAG,MAAM,IAAI,EAAE,GAAG,IAAI,OAAO;AACzC;AASO,SAAS,SAAS,KAA0B;AAGjD,SAAO,kBAAkB,cAAc,GAAG,CAAC,KAAK;AAClD;AAGO,SAAS,YAAY,QAAgB,KAA6B;AACvE,QAAM,UAAU,cAAc,GAAG;AACjC,MAAI,QAAQ,SAAS,iBAAiB;AACpC,UAAM,IAAI;AAAA,MACR,cAAc,KAAK,UAAU,GAAG,CAAC,eAAe,QAAQ,MAAM,yBACzD,eAAe;AAAA,IAEtB;AAAA,EACF;AACA,SAAO,EAAE,WAAW,aAAa,MAAM,GAAG,QAAQ;AACpD;AAMO,SAAS,cAAc,MAAiC;AAC7D,QAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,MAAI,OAAO,EAAG,QAAO;AACrB,QAAM,aAAa,KAAK,MAAM,GAAG,GAAG;AACpC,MAAI,CAAC,oBAAoB,KAAK,UAAU,EAAG,QAAO;AAClD,QAAM,WAAW,OAAO,UAAU;AAClC,QAAM,OAAO,KAAK,MAAM,MAAM,CAAC;AAC/B,QAAM,YAAY,KAAK,MAAM,GAAG,QAAQ;AACxC,QAAM,UAAU,KAAK,MAAM,QAAQ;AAInC,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,UAAU,WAAW,IAAI,EAAE,QAAQ,IAAI,EAAE,WAAW,QAAQ;AACrE;AAUO,SAAS,aAAa,KAAa,iBAAmC;AAC3E,MAAI,CAAC,OAAO,cAAc,eAAe,KAAK,kBAAkB,GAAG;AACjE,UAAM,IAAI;AAAA,MACR,kEAAkE,eAAe;AAAA,IACnF;AAAA,EACF;AACA,QAAM,UAAU,UAAU,GAAG;AAC7B,QAAM,UAAoB,CAAC;AAC3B,WAAS,IAAI,UAAU,iBAAiB,KAAK,SAAS,IAAK,SAAQ,KAAK,CAAC;AACzE,SAAO;AACT;;;AC5IO,IAAM,4BAA4B;AAGlC,SAAS,wBAAwB,OAAe,IAAkB;AACvE,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACxC,UAAM,IAAI,gBAAgB,GAAG,EAAE,uDAAuD,KAAK,EAAE;AAAA,EAC/F;AACF;AASA,eAAsB,cACpB,UACA,SAe2B;AAC3B,QAAM,EAAE,iBAAiB,GAAG,IAAI;AAChC,0BAAwB,iBAAiB,EAAE;AAC3C,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SACJ,QAAQ,eAAe,QACnB,GAAG,MAAM,CAAC,EAAG,YAAY,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,8DAC3C,oCAAoC,KAAK;AAC/C,QAAM,OAAyB,CAAC;AAChC,mBAAiB,OAAO,SAAS,KAAK,QAAQ,SAAS,GAAG;AAKxD,QAAI,QAAQ,cAAc,UAAa,cAAc,GAAG,EAAG;AAC3D,QAAI,KAAK,UAAU,iBAAiB;AAClC,YAAM,IAAI;AAAA,QACR,GAAG,EAAE,8BAA8B,eAAe,wEAClC,MAAM;AAAA,MACxB;AAAA,IACF;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AACA,SAAO;AACT;AAWO,SAAS,cAAc,QAAoD;AAChF,SAAO,cAAc,MAAM;AAC7B;AAQA,gBAAuB,sBACrB,QAC+B;AAC/B,mBAAiB,UAAU,QAAQ;AACjC,QAAI,CAAC,cAAc,MAAM,EAAG,OAAM;AAAA,EACpC;AACF;;;ACqBO,SAAS,aAAa,OAAmC,KAA6B;AAC3F,MAAI,UAAU,QAAW;AACvB,UAAM,IAAI;AAAA,MACR,GAAG,GAAG;AAAA,IAGR;AAAA,EACF;AACA,SAAO;AACT;;;ACzBA,IAAM,6BAA6B,KAAK;AAExC,IAAM,aAAa,CAAC,QAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAoB7F,eAAsB,UACpB,QACA,UACA,MACA,UAAyB,CAAC,GACD;AACzB,MAAI,QAAQ,cAAc,OAAW,uBAAsB,QAAQ,SAAS;AAC5E,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,MAAM,WAAW,YAAY,aAAa;AAChD,QAAM,WAAW,QAAQ,oBAAoB;AAC7C,QAAM,WAA8B,CAAC;AACrC,QAAM,SAA0B,CAAC;AACjC,MAAI,WAAW;AAEf,QAAM,YAAY,OAAO,QAAmC;AAC1D,QAAI;AACJ,QAAI,QAAQ;AACZ,QAAI,QAAQ;AACZ,QAAI;AACF,yBAAmB,GAAG;AACtB,YAAM,MAAM,OAAO,QAAQ,IAAI,SAAS,EAAE,WAAW,IAAI,UAAU,CAAC,EAAE,QAAQ;AAC9E,eAAS,MAAM,KAAK,KAAK,KAAK,GAAG;AACjC,UAAI,WAAW,WAAW;AACxB,cAAM,KAAK,aAAa,QAAQ,OAAO,kCAAkC,EAAE,MAAM;AACjF,yBAAiB,MAAM,KAAK;AAC1B,aAAG,IAAI,EAAE;AACT,mBAAS;AAAA,QACX;AACA,cAAM,MAAM,GAAG,UAAU;AACzB,cAAM,OAAO,MAAM,GAAG;AACtB,gBAAQ,IAAI;AAAA,MACd,OAAO;AACL,YAAI,MAAM;AACV,yBAAiB,MAAM,KAAK;AAC1B,iBAAO,GAAG,EAAE;AAAA;AACZ,mBAAS;AACT,cAAI,IAAI,UAAU,UAAU;AAC1B,kBAAM,IAAI,OAAO,KAAK,KAAK,MAAM;AACjC,kBAAM,OAAO,MAAM,CAAC;AACpB,qBAAS,EAAE;AACX,kBAAM;AAAA,UACR;AAAA,QACF;AACA,YAAI,IAAI,SAAS,GAAG;AAClB,gBAAM,IAAI,OAAO,KAAK,KAAK,MAAM;AACjC,gBAAM,OAAO,MAAM,CAAC;AACpB,mBAAS,EAAE;AAAA,QACb;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AAGZ,UAAI,WAAW,QAAW;AACxB,YAAI;AACF,gBAAM,OAAO,QAAQ;AAAA,QACvB,QAAQ;AAAA,QAER;AAAA,MACF;AACA,aAAO,KAAK,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,WAAW,OAAO,WAAW,GAAG,EAAE,CAAC;AACtF;AAAA,IACF;AAIA,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,IACrB,SAAS,KAAK;AACZ,aAAO,KAAK,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,WAAW,OAAO,WAAW,GAAG,EAAE,CAAC;AACtF;AAAA,IACF;AACA,aAAS,KAAK,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,WAAW,OAAO,MAAM,CAAC;AAC9E,gBAAY;AAAA,EACd;AAEA,mBAAiB,OAAO,SAAS,KAAK,QAAQ,SAAS,GAAG;AAGxD,QAAI,QAAQ,cAAc,UAAa,cAAc,GAAG,EAAG;AAC3D,QAAI,IAAI,WAAW,YAAa;AAChC,UAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU,CAAC;AAAA,EACpE;AACA,SAAO,EAAE,SAAS,GAAG,QAAQ,eAAe,SAAS,QAAQ,UAAU,UAAU,OAAO;AAC1F;;;ACnKA,eAAsB,cAAc,KAAiB,UAA6C;AAChG,kBAAgB,GAAG;AACnB,QAAM,SAAS,MAAM,SAAS,IAAI,GAAG;AACrC,SAAO,WAAW,QAAQ,OAAO,WAAW,eAAe,OAAO,eAAe;AACnF;AA2BA,gBAAuB,aACrB,UACA,UAAkC,CAAC,GACP;AAI5B,MAAI,QAAQ,cAAc,OAAW,uBAAsB,QAAQ,SAAS;AAC5E,QAAM,MAAM,SAAS,KAAK,QAAQ,SAAS;AAC3C,QAAM,SAAS,QAAQ,cAAc,SAAY,sBAAsB,GAAG,IAAI;AAC9E,mBAAiB,UAAU,QAAQ;AACjC,UAAM;AAAA,MACJ,GAAI,OAAO,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,OAAO,UAAU;AAAA,MACxE,SAAS,OAAO;AAAA,MAChB,YAAY,OAAO;AAAA,MACnB,QAAQ,OAAO;AAAA,IACjB;AAAA,EACF;AACF;;;AC3FA,SAAS,QAAAC,aAAY;;;ACHrB,SAAS,kBAAkB;;;ACoBpB,IAAM,aAAN,MAAqC;AAAA,EACzB,SAAuB,CAAC;AAAA,EACjC,SAAS;AAAA,EAEjB,MAAM,OAAkC;AAEtC,SAAK,OAAO,KAAK,MAAM,MAAM,CAAC;AAC9B,SAAK,UAAU,MAAM;AACrB,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,QAAoB;AAClB,UAAM,MAAM,IAAI,WAAW,KAAK,MAAM;AACtC,QAAI,SAAS;AACb,eAAW,SAAS,KAAK,QAAQ;AAC/B,UAAI,IAAI,OAAO,MAAM;AACrB,gBAAU,MAAM;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AACF;AAGO,IAAM,eAAN,MAAyC;AAAA,EAC9C,YAA6B,QAAoB;AAApB;AAAA,EAAqB;AAAA,EAArB;AAAA,EAE7B,SAAS,QAAgB,QAAqC;AAC5D,QACE,CAAC,OAAO,UAAU,MAAM,KACxB,CAAC,OAAO,UAAU,MAAM,KACxB,SAAS,KACT,SAAS,KACT,SAAS,SAAS,KAAK,OAAO,QAC9B;AACA,YAAM,IAAI;AAAA,QACR,UAAU,MAAM,KAAK,SAAS,MAAM,uBAAuB,KAAK,OAAO,MAAM;AAAA,MAC/E;AAAA,IACF;AACA,WAAO,QAAQ,QAAQ,KAAK,OAAO,SAAS,QAAQ,SAAS,MAAM,CAAC;AAAA,EACtE;AAAA,EAEA,QAAQ,UAAgE;AACtE,UAAM,OAAO,KAAK,OAAO;AACzB,UAAM,OAAO,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,IAAI;AACjD,WAAO,QAAQ,QAAQ,EAAE,OAAO,KAAK,OAAO,SAAS,OAAO,IAAI,GAAG,KAAK,CAAC;AAAA,EAC3E;AACF;;;AD/BO,IAAM,uBAAN,MAAsD;AAAA,EAC1C,OAAO,oBAAI,IAA4B;AAAA,EACvC;AAAA,EACT,MAAM;AAAA,EAEd,YAAY,UAAuC,CAAC,GAAG;AACrD,SAAK,MAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAAA,EAC5C;AAAA,EAEQ,YAAmB;AACzB,SAAK,OAAO;AACZ,WAAO,OAAO,KAAK,GAAG;AAAA,EACxB;AAAA,EAEA,eAAwB;AACtB,WAAO,EAAE,YAAY,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAM,IAAI,KAAiD;AACzD,uBAAmB,GAAG;AACtB,UAAM,MAAM,KAAK,KAAK,IAAI,WAAW,GAAG,CAAC;AACzC,WAAO,MAAM,gBAAgB,GAAG,IAAI;AAAA,EACtC;AAAA,EAEA,MAAM,OAAO,KAAiB,QAAsD;AAClF,uBAAmB,GAAG;AACtB,8BAA0B,MAAM;AAChC,UAAM,MAAM,WAAW,GAAG;AAC1B,QAAI,KAAK,KAAK,IAAI,GAAG,GAAG;AACtB,YAAM,IAAI,mBAAmB,2CAA2C,IAAI,OAAO,EAAE;AAAA,IACvF;AACA,UAAM,QAAQ,KAAK,UAAU;AAG7B,SAAK,KAAK,IAAI,KAAK,gBAAgB,cAAc,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC;AACjF,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EAEA,MAAM,eACJ,KACA,UACA,OAC2B;AAC3B,uBAAmB,GAAG;AACtB,0BAAsB,KAAK;AAC3B,UAAM,MAAM,WAAW,GAAG;AAC1B,UAAM,WAAW,KAAK,KAAK,IAAI,GAAG;AAClC,QAAI,CAAC,YAAY,SAAS,UAAU,UAAU;AAC5C,YAAM,IAAI,mBAAmB,uCAAuC,IAAI,OAAO,EAAE;AAAA,IACnF;AACA,UAAM,QAAQ,KAAK,UAAU;AAC7B,SAAK,KAAK,IAAI,KAAK,gBAAgB,mBAAmB,UAAU,OAAO,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC;AAC1F,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EAEA,OAAO,KAAK,WAAmD;AAC7D,eAAW,OAAO,KAAK,KAAK,OAAO,GAAG;AACpC,UAAI,cAAc,UAAa,IAAI,cAAc,UAAW,OAAM,gBAAgB,GAAG;AAAA,IACvF;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAAiB,UAAiC;AAC7D,uBAAmB,GAAG;AACtB,UAAM,MAAM,WAAW,GAAG;AAC1B,QAAI,aAAa,UAAa,KAAK,KAAK,IAAI,GAAG,GAAG,UAAU,UAAU;AACpE,YAAM,IAAI,mBAAmB,uCAAuC,IAAI,OAAO,EAAE;AAAA,IACnF;AACA,SAAK,KAAK,OAAO,GAAG;AAAA,EACtB;AACF;AAOO,IAAM,sBAAN,MAAoD;AAAA,EACxC,UAAU,oBAAI,IAAwB;AAAA,EAEvD,eAA4B;AAC1B,WAAO,EAAE,WAAW,MAAM,gBAAgB,OAAO,kBAAkB,gBAAgB,KAAK;AAAA,EAC1F;AAAA,EAEA,MAAM,aACJ,KACA,OAC2C;AAC3C,UAAM,IAAI,aAAa,GAAG;AAC1B,UAAM,OAAO,IAAI,WAAW;AAC5B,UAAM,MAAM,IAAI;AAChB,UAAM,OAAO,KAAK,MAAM;AACxB,QAAI,KAAK,QAAQ,IAAI,CAAC,GAAG;AACvB,YAAM,IAAI;AAAA,QACR,2CAA2C,IAAI,OAAO,IAAI,IAAI,UAAU;AAAA,MAC1E;AAAA,IACF;AACA,SAAK,QAAQ,IAAI,GAAG,IAAI;AACxB,WAAO,EAAE,MAAM,KAAK,QAAQ,QAAQ,WAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK,EAAE;AAAA,EACtF;AAAA,EAEA,MAAM,SAAS,KAAa,QAAgB,QAAqC;AAC/E,QAAI,CAAC,OAAO,UAAU,MAAM,KAAK,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,KAAK,SAAS,GAAG;AACtF,YAAM,IAAI,gBAAgB,wBAAwB,MAAM,WAAW,MAAM,EAAE;AAAA,IAC7E;AACA,UAAM,OAAO,KAAK,QAAQ,GAAG;AAC7B,QAAI,SAAS,SAAS,KAAK,QAAQ;AACjC,YAAM,IAAI;AAAA,QACR,UAAU,MAAM,KAAK,SAAS,MAAM,uBAAuB,KAAK,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAO,KAAK,MAAM,QAAQ,SAAS,MAAM;AAAA,EAC3C;AAAA,EAEA,MAAM,QAAQ,KAAa,UAAgE;AACzF,UAAM,OAAO,KAAK,QAAQ,GAAG;AAC7B,UAAM,OAAO,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,KAAK,MAAM;AACxD,WAAO,EAAE,OAAO,KAAK,MAAM,KAAK,SAAS,IAAI,GAAG,MAAM,KAAK,OAAO;AAAA,EACpE;AAAA,EAEA,MAAM,OAAO,KAA4B;AACvC,SAAK,QAAQ,OAAO,aAAa,GAAG,CAAC;AAAA,EACvC;AAAA,EAEA,OAAO,KAAK,KAAwC;AAClD,uBAAmB,GAAG;AACtB,UAAM,SAAS,GAAG,WAAW,GAAG,CAAC;AACjC,eAAW,KAAK,KAAK,QAAQ,KAAK,GAAG;AACnC,UAAI,EAAE,WAAW,MAAM,GAAG;AACxB,cAAM;AAAA,UACJ,WAAW,IAAI;AAAA,UACf,SAAS,IAAI;AAAA,UACb,YAAY,OAAO,EAAE,MAAM,OAAO,MAAM,CAAC;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,QAAQ,KAAyB;AACvC,UAAM,OAAO,KAAK,QAAQ,IAAI,aAAa,GAAG,CAAC;AAC/C,QAAI,SAAS,QAAW;AACtB,YAAM,IAAI,cAAc,uBAAuB,IAAI,OAAO,IAAI,IAAI,UAAU,EAAE;AAAA,IAChF;AACA,WAAO;AAAA,EACT;AACF;AAGA,SAAS,aAAa,KAAqB;AACzC,qBAAmB,GAAG;AACtB,MAAI,CAAC,OAAO,UAAU,IAAI,UAAU,KAAK,IAAI,aAAa,GAAG;AAC3D,UAAM,IAAI,gBAAgB,kDAAkD,IAAI,UAAU,EAAE;AAAA,EAC9F;AACA,SAAO,GAAG,WAAW,GAAG,CAAC,IAAI,IAAI,UAAU;AAC7C;;;AE1LA,SAAS,aAAaC,WAAU;AAChC,SAAS,cAAAC,aAAY,kBAAkB;AACvC,SAAS,MAAM,OAAO,QAAAC,OAAM,SAAS,cAAc;AACnD,SAAS,eAAe;;;ACHxB,SAAS,YAAY;AAYd,SAAS,YAAY,MAAc,KAAyB;AACjE,qBAAmB,GAAG;AACtB,SAAO,KAAK,MAAM,kBAAkB,IAAI,SAAS,GAAG,UAAU;AAChE;AAGO,SAAS,kBAAkB,MAAc,KAAqB;AACnE,qBAAmB,GAAG;AACtB,MAAI,CAAC,OAAO,UAAU,IAAI,UAAU,KAAK,IAAI,aAAa,GAAG;AAC3D,UAAM,IAAI,gBAAgB,kDAAkD,IAAI,UAAU,EAAE;AAAA,EAC9F;AACA,SAAO,KAAK,YAAY,MAAM,GAAG,GAAG,sBAAsB,IAAI,SAAS,IAAI,UAAU,CAAC;AACxF;AAGO,SAAS,sBAAsB,SAAiB,YAA4B;AACjF,SAAO,GAAG,kBAAkB,OAAO,CAAC,IAAI,UAAU;AACpD;AAGO,SAAS,gBAAgB,SAAiB,UAAiC;AAChF,QAAM,SAAS,GAAG,kBAAkB,OAAO,CAAC;AAC5C,QAAM,SAAS;AACf,MAAI,CAAC,SAAS,WAAW,MAAM,KAAK,CAAC,SAAS,SAAS,MAAM,EAAG,QAAO;AACvE,QAAM,SAAS,SAAS,MAAM,OAAO,QAAQ,SAAS,SAAS,OAAO,MAAM;AAG5E,MAAI,CAAC,iBAAiB,KAAK,MAAM,EAAG,QAAO;AAC3C,QAAM,MAAM,OAAO,MAAM;AACzB,SAAO,OAAO,cAAc,GAAG,IAAI,MAAM;AAC3C;AAEA,IAAM,kBAAkB;AAGjB,SAAS,YAAY,MAAc,WAAuC;AAK/E,MAAI,cAAc,OAAW,oBAAmB,EAAE,SAAS,KAAK,UAAU,CAAC;AAC3E,SAAO,KAAK,MAAM,kBAAkB,SAAS,GAAG,UAAU;AAC5D;AAGO,SAAS,gBAAgB,MAAc,KAAyB;AACrE,qBAAmB,GAAG;AACtB,SAAO;AAAA,IACL,YAAY,MAAM,IAAI,SAAS;AAAA,IAC/B,GAAG,kBAAkB,IAAI,OAAO,CAAC,GAAG,eAAe;AAAA,EACrD;AACF;AAOO,SAAS,iBAAiB,UAAiC;AAChE,MAAI,CAAC,SAAS,SAAS,eAAe,EAAG,QAAO;AAChD,QAAM,OAAO,SAAS,MAAM,GAAG,SAAS,SAAS,gBAAgB,MAAM;AACvE,QAAM,UAAU,mBAAmB,IAAI;AAKvC,MAAI,kBAAkB,OAAO,MAAM,KAAM,QAAO;AAChD,MAAI;AACF,uBAAmB,EAAE,QAAQ,CAAC;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAaO,SAAS,kBAAkB,OAAyD;AACzF,MAAI,UAAU,kBAAmB,QAAO,EAAE,WAAW,OAAU;AAC/D,QAAM,YAAY,mBAAmB,KAAK;AAC1C,MAAI,kBAAkB,SAAS,MAAM,MAAO,QAAO;AACnD,MAAI;AACF,uBAAmB,EAAE,SAAS,KAAK,UAAU,CAAC;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,EAAE,UAAU;AACrB;;;ACjHA,SAAS,aAAa,UAAU;AAChC,SAAS,YAAY;AAQd,IAAM,SAAS,CAAC,KAAc,SACnC,eAAe,SAAU,IAAkB,SAAS;AAQtD,IAAM,qBAAqB,oBAAI,IAAI,CAAC,SAAS,UAAU,UAAU,UAAU,WAAW,CAAC;AAEhF,IAAM,gBAAgB,CAAC,QAC5B,eAAe,SAAS,mBAAmB,IAAK,IAAkB,QAAQ,EAAE;AAOvE,IAAM,aAAa,CAAC,QACzB,cAAc,GAAG,IACb,IAAI,eAAe,+BAAgC,IAAkB,IAAI,IAAI,EAAE,OAAO,IAAI,CAAC,IAC3F;AAOC,IAAM,aAAa,GAAG,cAAc;AAO3C,eAAsB,SAAS,KAA4B;AACzD,MAAI;AACF,UAAM,SAAS,MAAM,KAAK,KAAK,GAAG;AAClC,QAAI;AACF,YAAM,OAAO,KAAK;AAAA,IACpB,UAAE;AACA,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,EACF,QAAQ;AAAA,EAER;AACF;;;AFzCO,IAAM,uBAAN,MAAqD;AAAA,EAC1D,YAA6B,MAAc;AAAd;AAAA,EAAe;AAAA,EAAf;AAAA,EAE7B,eAA4B;AAC1B,WAAO,EAAE,WAAW,MAAM,gBAAgB,OAAO,kBAAkB,gBAAgB,MAAM;AAAA,EAC3F;AAAA,EAEA,MAAM,aACJ,KACA,OAC2C;AAC3C,UAAM,YAAY,kBAAkB,KAAK,MAAM,GAAG;AAClD,UAAM,MAAM,QAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAEnD,UAAM,UAAU,GAAG,SAAS,IAAI,WAAW,CAAC;AAE5C,UAAM,SAAS,MAAMC,MAAK,SAASC,IAAG,UAAUA,IAAG,SAASA,IAAG,WAAW,UAAU;AACpF,UAAM,OAAOC,YAAW,QAAQ;AAChC,QAAI,OAAO;AACX,UAAM,OAAiB;AAAA,MACrB,MAAM,MAAM,OAAO;AACjB,cAAM,OAAO,MAAM,KAAK;AACxB,aAAK,OAAO,KAAK;AACjB,gBAAQ,MAAM;AAAA,MAChB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,IAAI;AAChB,YAAM,OAAO,KAAK;AAAA,IACpB,SAAS,KAAK;AACZ,YAAM,OAAO,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACnC,YAAM,OAAO,OAAO,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACpC,YAAM,WAAW,GAAG;AAAA,IACtB;AACA,UAAM,OAAO,MAAM;AAGnB,QAAI;AACF,YAAM,KAAK,SAAS,SAAS;AAAA,IAC/B,SAAS,KAAK;AACZ,YAAM,OAAO,OAAO,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACpC,UAAI,OAAO,KAAK,QAAQ,GAAG;AAGzB,cAAM,IAAI;AAAA,UACR,2CAA2C,IAAI,OAAO,IAAI,IAAI,UAAU;AAAA,QAC1E;AAAA,MACF;AACA,YAAM,WAAW,GAAG;AAAA,IACtB;AACA,UAAM,OAAO,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACpC,UAAM,SAAS,QAAQ,SAAS,CAAC;AACjC,WAAO,EAAE,MAAM,QAAQ,KAAK,OAAO,KAAK,EAAE;AAAA,EAC5C;AAAA,EAEA,MAAM,SAAS,KAAa,QAAgB,QAAqC;AAC/E,QAAI,CAAC,OAAO,UAAU,MAAM,KAAK,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,KAAK,SAAS,GAAG;AACtF,YAAM,IAAI,gBAAgB,wBAAwB,MAAM,WAAW,MAAM,EAAE;AAAA,IAC7E;AACA,UAAM,SAAS,MAAM,KAAK,SAAS,GAAG;AACtC,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,KAAK;AACnC,UAAI,SAAS,SAAS,MAAM;AAC1B,cAAM,IAAI;AAAA,UACR,UAAU,MAAM,KAAK,SAAS,MAAM,uBAAuB,IAAI;AAAA,QACjE;AAAA,MACF;AACA,YAAM,MAAM,OAAO,MAAM,MAAM;AAC/B,UAAI,SAAS,EAAG,OAAM,OAAO,KAAK,KAAK,GAAG,QAAQ,MAAM;AACxD,aAAO,IAAI,WAAW,IAAI,QAAQ,IAAI,YAAY,IAAI,UAAU;AAAA,IAClE,UAAE;AACA,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,KAAa,UAAgE;AACzF,UAAM,SAAS,MAAM,KAAK,SAAS,GAAG;AACtC,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,KAAK;AACnC,YAAM,OAAO,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,IAAI;AACjD,YAAM,MAAM,OAAO,MAAM,IAAI;AAC7B,UAAI,OAAO,EAAG,OAAM,OAAO,KAAK,KAAK,GAAG,MAAM,OAAO,IAAI;AACzD,aAAO,EAAE,OAAO,IAAI,WAAW,IAAI,QAAQ,IAAI,YAAY,IAAI,UAAU,GAAG,KAAK;AAAA,IACnF,UAAE;AACA,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAA4B;AAEvC,UAAM,OAAO,kBAAkB,KAAK,MAAM,GAAG,CAAC,EAAE,MAAM,CAAC,QAAQ;AAC7D,UAAI,CAAC,OAAO,KAAK,QAAQ,EAAG,OAAM,WAAW,GAAG;AAAA,IAClD,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,KAAK,KAAwC;AAClD,UAAM,MAAM,YAAY,KAAK,MAAM,GAAG;AACtC,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,QAAQ,GAAG;AAAA,IAC3B,SAAS,KAAK;AACZ,UAAI,OAAO,KAAK,QAAQ,EAAG;AAC3B,YAAM,WAAW,GAAG;AAAA,IACtB;AACA,eAAW,QAAQ,OAAO;AACxB,YAAM,aAAa,gBAAgB,IAAI,SAAS,IAAI;AACpD,UAAI,eAAe,MAAM;AACvB,cAAM,EAAE,WAAW,IAAI,WAAW,SAAS,IAAI,SAAS,WAAW;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,KAAwD;AAC7E,QAAI;AACF,aAAO,MAAMF,MAAK,kBAAkB,KAAK,MAAM,GAAG,GAAGC,IAAG,WAAW,UAAU;AAAA,IAC/E,SAAS,KAAK;AACZ,UAAI,OAAO,KAAK,QAAQ,GAAG;AACzB,cAAM,IAAI,cAAc,uBAAuB,IAAI,OAAO,IAAI,IAAI,UAAU,EAAE;AAAA,MAChF;AAEA,UAAI,OAAO,KAAK,OAAO,GAAG;AACxB,cAAM,IAAI;AAAA,UACR,2CAA2C,IAAI,OAAO,IAAI,IAAI,UAAU;AAAA,QAC1E;AAAA,MACF;AACA,YAAM,WAAW,GAAG;AAAA,IACtB;AAAA,EACF;AACF;;;AGzIA,SAAS,aAAaE,WAAU;AAChC,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,SAAAC,QAAO,QAAAC,OAAM,WAAAC,UAAS,UAAU,QAAQ,UAAAC,eAAc;AAC/D,SAAS,UAAU,WAAAC,UAAS,QAAAC,OAAM,eAAe;AAyBjD,IAAM,wBAAwB,IAAI,OAAO;AAOzC,IAAM,YAAY,oBAAI,IAA8B;AAWpD,eAAe,WAAW,MAA+B;AACvD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,OAAiB,CAAC,SAAS,GAAG,CAAC;AACrC,MAAI,MAAMC,SAAQ,GAAG;AACrB,aAAS;AACP,QAAI;AACF,aAAOC,MAAK,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI;AAAA,IAC1C,SAAS,KAAK;AACZ,YAAM,SAASD,SAAQ,GAAG;AAC1B,UAAI,CAAC,OAAO,KAAK,QAAQ,KAAK,WAAW,IAAK,OAAM,WAAW,GAAG;AAClE,WAAK,QAAQ,SAAS,GAAG,CAAC;AAC1B,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAOO,IAAM,wBAAN,MAAuD;AAAA,EAG5D,YACmB,MACjB,UAAwC,CAAC,GACzC;AAFiB;AAGjB,SAAK,MAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAAA,EAC5C;AAAA,EAJmB;AAAA,EAHF;AAAA,EASjB,eAAwB;AACtB,WAAO,EAAE,YAAY,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAM,IAAI,KAAiD;AACzD,UAAM,MAAM,MAAM,KAAK,QAAQ,gBAAgB,KAAK,MAAM,GAAG,CAAC;AAC9D,WAAO,OAAO,CAAC,IAAI,UAAU,IAAI,SAAS;AAAA,EAC5C;AAAA,EAEA,MAAM,OAAO,KAAiB,QAAsD;AAClF,8BAA0B,MAAM;AAChC,UAAM,OAAO,gBAAgB,KAAK,MAAM,GAAG;AAC3C,WAAO,KAAK,YAAY,MAAM,YAAY;AACxC,YAAM,UAAU,MAAM,KAAK,QAAQ,IAAI;AACvC,UAAI,YAAY,QAAQ,CAAC,QAAQ,SAAS;AACxC,cAAM,IAAI,mBAAmB,2CAA2C,IAAI,OAAO,EAAE;AAAA,MACvF;AACA,YAAM,UAAU,UAAU,kBAAkB,QAAQ,MAAM,IAAI,IAAI;AAClE,YAAM,QAAQ,OAAO,OAAO;AAC5B,YAAM,KAAK,SAAS,MAAM,OAAO,cAAc,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,CAAC;AAC9E,aAAO,EAAE,MAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,eACJ,KACA,UACA,OAC2B;AAC3B,0BAAsB,KAAK;AAC3B,UAAM,OAAO,gBAAgB,KAAK,MAAM,GAAG;AAC3C,WAAO,KAAK,YAAY,MAAM,YAAY;AACxC,YAAM,UAAU,MAAM,KAAK,QAAQ,IAAI;AACvC,UAAI,YAAY,QAAQ,QAAQ,WAAW,QAAQ,OAAO,UAAU,UAAU;AAC5E,cAAM,IAAI,mBAAmB,uCAAuC,IAAI,OAAO,EAAE;AAAA,MACnF;AACA,YAAM,QAAQ,OAAO,kBAAkB,QAAQ,MAAM,IAAI,CAAC;AAC1D,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA,mBAAmB,QAAQ,QAAQ,OAAO,KAAK,IAAI,GAAG,KAAK;AAAA,MAC7D;AACA,aAAO,EAAE,MAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,KAAK,WAAmD;AAC7D,eAAW,MAAM,MAAM,KAAK,cAAc,SAAS,GAAG;AACpD,YAAM,MAAM,YAAY,KAAK,MAAM,EAAE;AACrC,UAAI;AACJ,UAAI;AACF,gBAAQ,MAAME,SAAQ,GAAG;AAAA,MAC3B,SAAS,KAAK;AACZ,YAAI,OAAO,KAAK,QAAQ,EAAG;AAC3B,cAAM,WAAW,GAAG;AAAA,MACtB;AACA,iBAAW,QAAQ,OAAO;AACxB,cAAM,UAAU,iBAAiB,IAAI;AACrC,YAAI,YAAY,KAAM;AACtB,cAAM,MAAM,MAAM,KAAK,QAAQ,gBAAgB,KAAK,MAAM,EAAE,WAAW,IAAI,QAAQ,CAAC,CAAC;AACrF,YAAI,OAAO,CAAC,IAAI,QAAS,OAAM,IAAI;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAAiB,UAAiC;AAC7D,UAAM,OAAO,gBAAgB,KAAK,MAAM,GAAG;AAC3C,WAAO,KAAK,YAAY,MAAM,YAAY;AACxC,YAAM,UAAU,MAAM,KAAK,QAAQ,IAAI;AACvC,UAAI,aAAa,QAAW;AAC1B,YAAI,YAAY,QAAQ,QAAQ,WAAW,QAAQ,OAAO,UAAU,UAAU;AAC5E,gBAAM,IAAI,mBAAmB,uCAAuC,IAAI,OAAO,EAAE;AAAA,QACnF;AAAA,MACF,WAAW,YAAY,QAAQ,QAAQ,SAAS;AAC9C;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,kBAAkB,QAAQ,MAAM,IAAI,CAAC;AAC1D,YAAM,KAAK,SAAS,MAAM,MAAM,EAAE,GAAG,QAAQ,QAAQ,OAAO,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IACrF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAc,cAAc,WAAmE;AAC7F,QAAI,cAAc,OAAW,QAAO,CAAC,SAAS;AAC9C,QAAI;AACJ,QAAI;AACF,gBAAU,MAAMA,SAAQ,KAAK,IAAI;AAAA,IACnC,SAAS,KAAK;AACZ,UAAI,OAAO,KAAK,QAAQ,EAAG,QAAO,CAAC;AACnC,YAAM,WAAW,GAAG;AAAA,IACtB;AAKA,UAAM,aAAwC,CAAC;AAC/C,eAAW,SAAS,SAAS;AAC3B,YAAM,SAAS,kBAAkB,KAAK;AACtC,UAAI,WAAW,KAAM,YAAW,KAAK,OAAO,SAAS;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,QAAQ,MAAgD;AACpE,QAAI;AACJ,QAAI;AACF,eAAS,MAAMC,MAAK,MAAMC,IAAG,WAAW,UAAU;AAAA,IACpD,SAAS,KAAK;AACZ,UAAI,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,OAAO,EAAG,QAAO;AAC1D,YAAM,WAAW,GAAG;AAAA,IACtB;AACA,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,KAAK;AACnC,UAAI,OAAO,uBAAuB;AAChC,cAAM,IAAI,eAAe,gBAAgB,IAAI,iBAAiB,qBAAqB,GAAG;AAAA,MACxF;AACA,YAAM,QAAQ,MAAM,OAAO,SAAS,GAAG,SAAS,MAAM;AACtD,aAAO,sBAAsB,MAAM,IAAI;AAAA,IACzC,UAAE;AACA,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,MAAc,SAAkB,QAAuC;AAC5F,UAAMC,OAAML,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAM,MAAM,OAAO,KAAK,0BAA0B,EAAE,SAAS,OAAO,CAAC,GAAG,MAAM;AAG9E,QAAI,IAAI,SAAS,uBAAuB;AACtC,YAAM,IAAI;AAAA,QACR,gBAAgB,IAAI,MAAM,iBAAiB,qBAAqB;AAAA,MAClE;AAAA,IACF;AAEA,UAAM,MAAM,GAAG,IAAI,IAAIM,YAAW,CAAC;AACnC,QAAI;AACJ,QAAI;AACF,eAAS,MAAMH,MAAK,KAAKC,IAAG,UAAUA,IAAG,SAASA,IAAG,WAAW,UAAU;AAAA,IAC5E,SAAS,KAAK;AACZ,YAAM,WAAW,GAAG;AAAA,IACtB;AACA,QAAI;AACF,YAAM,OAAO,MAAM,GAAG;AACtB,YAAM,OAAO,KAAK;AAAA,IACpB,UAAE;AACA,YAAM,OAAO,MAAM;AAAA,IACrB;AACA,UAAM,OAAO,KAAK,IAAI,EAAE,MAAM,OAAO,QAAQ;AAC3C,YAAMG,QAAO,GAAG,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAChC,YAAM,WAAW,GAAG;AAAA,IACtB,CAAC;AACD,UAAM,SAASP,SAAQ,IAAI,CAAC;AAAA,EAC9B;AAAA;AAAA,EAGA,MAAc,YAAe,MAAc,IAAkC;AAC3E,UAAM,MAAM,MAAM,WAAW,IAAI;AACjC,UAAM,OAAO,UAAU,IAAI,GAAG,KAAK,QAAQ,QAAQ;AACnD,UAAM,SAAS,KAAK,KAAK,IAAI,EAAE;AAC/B,UAAM,OAAO,OAAO;AAAA,MAClB,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AACA,cAAU,IAAI,KAAK,IAAI;AACvB,SAAK,KAAK,KAAK,MAAM;AACnB,UAAI,UAAU,IAAI,GAAG,MAAM,KAAM,WAAU,OAAO,GAAG;AAAA,IACvD,CAAC;AACD,WAAO;AAAA,EACT;AACF;;;ANtOO,IAAM,gBAAN,MAA8C;AAAA,EAG1C;AAAA,EACA;AAAA,EAET,YAAY,UAAgC,CAAC,GAAG;AAC9C,SAAK,UAAU,IAAI,oBAAoB;AACvC,SAAK,WAAW,IAAI,qBAAqB,QAAQ,QAAQ,SAAY,CAAC,IAAI,EAAE,KAAK,QAAQ,IAAI,CAAC;AAC9F,mBAAe,IAAI;AAAA,EACrB;AACF;AAmBO,IAAM,iBAAN,MAA+C;AAAA,EAMpD,YACW,MACT,UAAiC,CAAC,GAClC;AAFS;AAGT,SAAK,UAAU,IAAI,qBAAqBQ,MAAK,MAAM,SAAS,CAAC;AAC7D,SAAK,WAAW,IAAI;AAAA,MAClBA,MAAK,MAAM,UAAU;AAAA,MACrB,QAAQ,QAAQ,SAAY,CAAC,IAAI,EAAE,KAAK,QAAQ,IAAI;AAAA,IACtD;AACA,mBAAe,IAAI;AAAA,EACrB;AAAA,EATW;AAAA,EAJF;AAAA,EACA;AAaX;;;AOiDO,IAAM,aAAyB;AAAA,EACpC,UAAgB;AAAA,EAEhB;AACF;AAMO,SAAS,UAAU,MAA8B;AACtD,MAAI,SAAS,WAAY,QAAO;AAChC,SAAO;AAAA,IACL,QAAQ,OAAyB;AAC/B,UAAI;AACF,aAAK,QAAQ,KAAK;AAAA,MACpB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,qBAAN,MAA+C;AAAA,EACnC,WAAyB,CAAC;AAAA,EAE3C,QAAQ,OAAyB;AAC/B,SAAK,SAAS,KAAK,KAAK;AAAA,EAC1B;AAAA;AAAA,EAGA,WAAyB;AACvB,WAAO,KAAK,SAAS,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,EAC5C;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS,SAAS;AAAA,EACzB;AACF;;;AC9GO,IAAM,cAAc;AA0BpB,SAAS,WACd,OACA,SAAiB,aACW;AAC5B,MAAI,UAAU,OAAW,QAAO,MAAM;AAQtC,QAAM,QAAQ,MAAM;AACpB,QAAM,QACJ,MAAM,aAAa,SACf,MAAqB,MAAM,SAAU,IACrC,UAAU,SACR,MAAqB,MAAM,KAAK,OAAO,CAAC,IACxC;AACR,MAAI,UAAU,OAAW,QAAO,MAAM;AACtC,MAAI,IAAI;AACR,SAAO,MAAM;AACX,QAAI,EAAE,IAAI,OAAQ,QAAO;AACzB,QAAI;AACJ,WAAO,MAAM;AAAA,EACf;AACF;;;AC/BA,IAAM,cAAc;AACpB,IAAM,cAAc;AACpB,IAAM,cAAc;AAOb,SAAS,OACd,KACA,YACA,OACY;AACZ,QAAM,MAAM,IAAI,YAAY;AAC5B,QAAM,KAAK,IAAI,OAAO,IAAI,aAAa,EAAE;AACzC,QAAM,MAAM,IAAI,OAAO,IAAI,OAAO;AAClC,QAAM,MAAM,IAAI,WAAW,IAAI,IAAI,GAAG,SAAS,IAAI,IAAI,SAAS,IAAI,IAAI,CAAC;AACzE,QAAM,OAAO,IAAI,SAAS,IAAI,MAAM;AACpC,MAAI,IAAI;AACR,MAAI,CAAC,IAAI;AACT,OAAK;AACL,OAAK,UAAU,GAAG,GAAG,QAAQ,IAAI;AACjC,OAAK;AACL,MAAI,IAAI,IAAI,CAAC;AACb,OAAK,GAAG;AACR,OAAK,UAAU,GAAG,IAAI,QAAQ,IAAI;AAClC,OAAK;AACL,MAAI,IAAI,KAAK,CAAC;AACd,OAAK,IAAI;AACT,OAAK,aAAa,GAAG,OAAO,UAAU,GAAG,IAAI;AAC7C,OAAK;AACL,MAAI,CAAC,IAAI,UAAU,UAAU,cAAc;AAC3C,OAAK;AACL,OAAK,UAAU,GAAG,UAAU,UAAU,IAAI,OAAO,IAAI;AACrD,SAAO;AACT;;;AC9FA,IAAM,UAAU,MAAqB;AACnC,QAAM,OAAO;AACb,QAAM,KAAK,IAAI,YAAY,GAAG;AAC9B,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,GAAG,IAAK,KAAI,IAAI,IAAI,OAAQ,MAAM,IAAK,MAAM;AACjE,OAAG,CAAC,IAAI,MAAM;AAAA,EAChB;AACA,QAAM,SAAS,CAAC,EAAE;AAClB,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,OAAO,OAAO,IAAI,CAAC;AACzB,UAAM,OAAO,IAAI,YAAY,GAAG;AAChC,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,YAAM,IAAI,KAAK,CAAC;AAChB,WAAK,CAAC,KAAM,MAAM,IAAM,GAAG,IAAI,GAAI,OAAkB;AAAA,IACvD;AACA,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACT,GAAG;AACH,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AACnB,IAAM,KAAK,OAAO,CAAC;AAEZ,SAAS,OAAO,OAAmB,OAAO,GAAW;AAC1D,MAAI,OAAO,OAAO,gBAAgB;AAClC,MAAI,IAAI;AAMR,QAAM,KAAK,MAAM,SAAU,MAAM,SAAS;AAC1C,SAAO,IAAI,IAAI,KAAK,GAAG;AACrB,WACG,MAAM,CAAC,IACN,MAAM,IAAI,CAAC,KAAgB,IAC3B,MAAM,IAAI,CAAC,KAAgB,KAC3B,MAAM,IAAI,CAAC,KAAgB;AAC/B,WACI,GAAG,MAAM,GAAI,IACZ,GAAI,QAAQ,IAAK,GAAI,IACrB,GAAI,QAAQ,KAAM,GAAI,IACtB,GAAI,QAAQ,KAAM,GAAI,IACtB,GAAG,MAAM,IAAI,CAAC,CAAW,IACzB,GAAG,MAAM,IAAI,CAAC,CAAW,IACzB,GAAG,MAAM,IAAI,CAAC,CAAW,IACzB,GAAG,MAAM,IAAI,CAAC,CAAW,OAC5B;AAAA,EACJ;AAEA,SAAO,IAAI,MAAM,QAAQ,KAAK;AAC5B,WAAQ,IAAI,MAAO,MAAM,CAAC,KAAgB,GAAI,IAAgB,QAAQ,OAAQ;AAAA,EAChF;AACA,UAAQ,MAAM,gBAAgB;AAChC;;;AClEA,IAAM,WAAW;AAGV,SAAS,YAAY,KAAe,OAAqB;AAC9D,MAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,SAAS,UAAU;AAC9D,UAAM,IAAI,WAAW,mCAAmC,KAAK,EAAE;AAAA,EACjE;AACA,MAAI,IAAI;AACR,SAAO,IAAI,KAAM;AACf,QAAI,KAAM,IAAI,MAAQ,GAAI;AAC1B,WAAO;AAAA,EACT;AACA,MAAI,KAAK,CAAC;AACZ;AAaO,SAAS,WAAW,OAAmB,QAA4B;AACxE,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,OAAO,MAAM,QAAQ;AACvB,YAAM,IAAI,eAAe,qCAAqC;AAAA,IAChE;AACA,UAAM,OAAO,MAAM,GAAG;AACtB,WAAO;AAEP,QAAI,MAAM,KAAK,OAAO,IAAM;AAC1B,YAAM,IAAI,eAAe,0BAA0B;AAAA,IACrD;AAEA,cAAU,UAAW,OAAO,QAAS,WAAY;AACjD,SAAK,OAAO,SAAU,GAAG;AACvB,aAAO,EAAE,OAAO,QAAQ,MAAM,IAAI;AAAA,IACpC;AACA,aAAS;AAAA,EACX;AACA,QAAM,IAAI,eAAe,0BAA0B;AACrD;;;AClDO,IAAM,QAAQ,WAAW,GAAG,IAAM,IAAM,IAAM,EAAI;AAElD,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAGtB,IAAM,iBAAiB;AAGvB,IAAM,gBAAgB;AAGtB,IAAM,eAAe;AAGrB,IAAM,SAAS;AAAA,EACpB,aAAa;AAAA;AAAA,EACb,aAAa;AAAA;AAAA,EACb,aAAa;AAAA;AAAA,EACb,OAAO;AAAA;AAAA,EACP,gBAAgB;AAAA;AAAA,EAChB,cAAc;AAAA;AAAA,EACd,gBAAgB;AAAA;AAAA,EAChB,cAAc;AAAA;AAAA,EACd,cAAc;AAAA;AAAA,EACd,WAAW;AAAA;AAAA,EACX,YAAY;AAAA;AAAA,EACZ,YAAY;AAAA;AAAA,EACZ,UAAU;AAAA;AAAA,EACV,OAAO;AAAA;AAAA,EACP,YAAY;AAAA;AAAA,EACZ,kBAAkB;AAAA;AAAA,EAClB,cAAc;AAAA;AAAA,EACd,UAAU;AAAA;AACZ;AAGO,IAAM,sBAAsB,OAAO;AAGnC,IAAM,iBAAiB,KAAK;AAC5B,IAAM,wBAAwB,KAAK;AACnC,IAAM,qBAAqB,KAAK;AAGhC,IAAM,cAAc,iBAAiB;AASrC,IAAM,mBAAmB;AAmBzB,IAAM,iCAAiC;AAYvC,IAAM,0BAA+C,oBAAI,IAAI;AAAA,EAClE;AACF,CAAC;AAOM,IAAM,uBAAuB;AAO7B,IAAM,qBAAqB,MAAM;AAOjC,IAAM,0BAA0B,IAAI,OAAO;AAG3C,IAAM,4BAA4B,KAAK,OAAO;AAG9C,IAAM,wBAAwB;AAG9B,IAAM,eAAe;AAQrB,IAAM,mBAAmB;AACzB,IAAM,iBAAiB;;;AClE9B,IAAM,yBAAyB,IAAI;AAM5B,SAAS,cAAc,aAA6B;AACzD,SAAO,KAAK,IAAI,OAAU,KAAK,MAAM,cAAc,sBAAsB,CAAC;AAC5E;AAoBA,SAAS,aAAa,OAAmB,QAAyB;AAChE,SACE,MAAM,MAAM,MAAM,MAAM,CAAC,KACzB,MAAM,SAAS,CAAC,MAAM,MAAM,CAAC,KAC7B,MAAM,SAAS,CAAC,MAAM,MAAM,CAAC,KAC7B,MAAM,SAAS,CAAC,MAAM,MAAM,CAAC;AAEjC;AAGA,IAAM,WAAW,CAAC,SAAyB,GAAG,IAAI;AAClD,IAAM,iBAAiB,CAAC,MAAc,cAA8B,GAAG,SAAS,IAAI,CAAC,GAAG,SAAS;AAMjG,SAAS,UAAU,MAAkB,MAAoB;AACvD,MAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,QAAQ;AACrD,UAAM,IAAI,eAAe,oDAAoD,IAAI,EAAE;AAAA,EACrF;AACF;AAGA,SAAS,cACP,MACA,MACkE;AAClE,YAAU,MAAM,IAAI;AACpB,MAAI,OAAO,iBAAiB,gBAAgB,KAAK,SAAS,cAAc;AACtE,UAAM,IAAI,eAAe,oBAAoB,IAAI,GAAG;AAAA,EACtD;AACA,QAAM,SAAS,KAAK,SAAS,KAAK,SAAS,YAAY;AACvD,MAAI,CAAC,aAAa,QAAQ,OAAO,QAAQ,GAAG;AAC1C,UAAM,IAAI,eAAe,0BAA0B;AAAA,EACrD;AACA,QAAM,QAAQ,IAAI,SAAS,OAAO,QAAQ,OAAO,YAAY,OAAO,UAAU;AAC9E,QAAM,kBAAkB,MAAM,UAAU,OAAO,cAAc,IAAI;AACjE,MAAI,OAAO,OAAO,SAAS,GAAG,mBAAmB,CAAC,MAAM,iBAAiB;AACvE,UAAM,IAAI,eAAe,2BAA2B;AAAA,EACtD;AACA,SAAO,EAAE,QAAQ,OAAO,gBAAgB;AAC1C;AAGA,SAAS,QAAQ,MAAgB,QAAgB,OAAuB;AACtE,QAAM,MAAM,KAAK,aAAa,QAAQ,IAAI;AAC1C,MAAI,MAAM,OAAO,OAAO,gBAAgB,GAAG;AACzC,UAAM,IAAI,eAAe,SAAS,KAAK,IAAI,GAAG,6BAA6B;AAAA,EAC7E;AACA,SAAO,OAAO,GAAG;AACnB;AAEO,IAAM,aAAN,MAAM,YAAW;AAAA,EACd,YACW,MACA,YACR,YAEA,SACA,kBACQ,OAER,gBAEQ,QAEA,WACjB;AAbiB;AACA;AACR;AAEA;AACA;AACQ;AAER;AAEQ;AAEA;AAAA,EAChB;AAAA,EAbgB;AAAA,EACA;AAAA,EACR;AAAA,EAEA;AAAA,EACA;AAAA,EACQ;AAAA,EAER;AAAA,EAEQ;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQnB,IAAI,cAAsB;AACxB,WAAO,eAAe,KAAK,YAAY,KAAK,SAAS;AAAA,EACvD;AAAA;AAAA,EAGA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,qBAA6B;AAC/B,UAAM,EAAE,MAAM,qBAAqB,SAAS,MAAM,QAAQ,IAAI,KAAK;AACnE,WACE,KAAK,aACL,oBAAoB,aACpB,QAAQ,aACR,KAAK,aACL,QAAQ;AAAA,EAEZ;AAAA;AAAA,EAGA,gBAAqC;AACnC,UAAM,EAAE,MAAM,oBAAoB,IAAI,KAAK;AAC3C,UAAM,MAAM,oBAAI,IAAoB;AACpC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,KAAI,IAAI,KAAK,CAAC,GAAI,oBAAoB,CAAC,IAAK,CAAC;AACnF,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,aAAa,WAAW,MAAkB,aAAuC;AAC/E,UAAM,EAAE,OAAO,MAAM,KAAK,IAAI,MAAM,KAAK,QAAQ,YAAY;AAC7D,cAAU,MAAM,IAAI;AACpB,QAAI,CAAC,YAAY,WAAW,SAAS,IAAI,CAAC,EAAG,QAAO;AACpD,WAAO,eAAe,MAAM,cAAc,MAAM,IAAI,EAAE,eAAe,MAAM;AAAA,EAC7E;AAAA,EAEA,aAAa,KAAK,MAAkB,UAA6B,CAAC,GAAwB;AAExF,UAAM,YAAY,KAAK,IAAI,QAAQ,aAAa,oBAAoB,YAAY;AAChF,UAAM,kBAAkB,QAAQ,mBAAmB;AACnD,UAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,UAAM,EAAE,OAAO,MAAM,KAAK,IAAI,MAAM,KAAK,QAAQ,SAAS;AAC1D,UAAM,EAAE,QAAQ,OAAO,gBAAgB,IAAI,cAAc,MAAM,IAAI;AACnE,UAAM,eAAe,OAAO,OAAO,YAAY;AAC/C,QAAI,iBAAiB,eAAe;AAClC,YAAM,IAAI,iBAAiB,uBAAuB,YAAY,gBAAgB;AAAA,IAChF;AACA,UAAM,QAAQ,MAAM,UAAU,OAAO,OAAO,IAAI;AAChD,UAAM,aAAa,QAAQ,oBAAoB;AAC/C,QAAI,aAAa,QAAQ,WAAW,QAAW;AAC7C,YAAM,IAAI,gBAAgB,gEAAgE;AAAA,IAC5F;AACA,SAAK,QAAQ,wBAAwB,GAAG;AACtC,YAAM,IAAI,iBAAiB,6DAA6D;AAAA,IAC1F;AACA,SAAK,QAAQ,CAAC,iBAAiB,GAAG;AAChC,YAAM,IAAI,iBAAiB,sCAAsC,MAAM,SAAS,EAAE,CAAC,EAAE;AAAA,IACvF;AAQA,UAAM,eAAe,OAAO,OAAO,YAAY;AAC/C,QAAI,iBAAiB,kBAAkB;AACrC,YAAM,IAAI;AAAA,QACR,uBAAuB,YAAY;AAAA,MACrC;AAAA,IACF;AAIA,UAAM,iBAAiB,MAAM,UAAU,OAAO,gBAAgB,IAAI;AAClE,QAAI,CAAC,wBAAwB,IAAI,cAAc,GAAG;AAChD,YAAM,IAAI;AAAA,QACR,0BAA0B,cAAc,wCAC3B,CAAC,GAAG,uBAAuB,EAAE,KAAK,IAAI,CAAC,KAC/C,8BAA8B;AAAA,MAErC;AAAA,IACF;AACA,UAAM,iBAAiB,OAAO,OAAO,cAAc;AACnD,QAAI,mBAAmB,sBAAsB;AAC3C,YAAM,IAAI;AAAA,QACR,yBAAyB,cAAc,mCAAmC,oBAAoB;AAAA,MAChG;AAAA,IACF;AAEA,UAAM,cAAc,QAAQ,OAAO,OAAO,aAAa,cAAc;AACrE,UAAM,cAAc,QAAQ,OAAO,OAAO,aAAa,cAAc;AACrE,UAAM,WAAW,MAAM,UAAU,OAAO,aAAa,IAAI;AACzD,UAAM,aAAa,MAAM,UAAU,OAAO,YAAY,IAAI;AAC1D,UAAM,mBAAmB,QAAQ,OAAO,OAAO,kBAAkB,mBAAmB;AACpF,UAAM,aAAa,QAAQ,OAAO,OAAO,YAAY,YAAY;AAGjE,QAAI,cAAc,iBAAiB,cAAc,cAAc,OAAO,cAAc;AAClF,YAAM,IAAI;AAAA,QACR,uBAAuB,WAAW,MAAM,WAAW;AAAA,MACrD;AAAA,IACF;AACA,QAAI,cAAc,eAAe;AAC/B,YAAM,IAAI,eAAe,eAAe,WAAW,iBAAiB,aAAa,GAAG;AAAA,IACtF;AAGA,UAAM,iBAAiB,OAAO,KAAK;AACnC,QAAI,mBAAmB,GAAG;AACxB,UAAI,CAAC,aAAa,MAAM,CAAC,KAAK,KAAK,CAAC,MAAM,cAAc;AACtD,cAAM,IAAI,eAAe,mDAAmD;AAAA,MAC9E;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACJ,QAAI,eAAe,gBAAgB;AACjC,YAAM,QAAQ,cAAc;AAC5B,mBAAa,KAAK,SAAS,OAAO,QAAQ,WAAW;AACrD,uBAAiB;AAAA,IACnB,OAAO;AACL,mBAAa,MAAM,KAAK,SAAS,aAAa,WAAW;AACzD,uBAAiB;AAAA,IACnB;AACA,QAAI,OAAO,UAAU,MAAM,UAAU;AACnC,YAAM,IAAI,eAAe,0BAA0B;AAAA,IACrD;AAIA,UAAM,gBAAgB,YAClB,QAAQ,OAAQ,KAAK;AAAA,MACnB;AAAA,QACE,OAAO,OAAO,SAAS,OAAO,YAAY,OAAO,aAAa,gBAAgB;AAAA,QAC9E,YAAY;AAAA,QACZ,KAAK,OAAO,SAAS,OAAO,UAAU,OAAO,WAAW,cAAc;AAAA,MACxE;AAAA,MACA,QAAQ,OAAQ,OAAO,OAAO;AAAA,IAChC,IACA;AAGJ,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,mBAAmB,iBAAiB;AAAA,IAClD;AACA,QAAI,WAAW;AAGb,UAAI,eAAe,KAAK,qBAAqB,GAAG;AAC9C,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AAEL,UAAI,MAAM,KAAK,WAAW,YAAY;AACpC,cAAM,IAAI;AAAA,UACR,qBAAqB,UAAU,OAAO,MAAM,KAAK,MAAM;AAAA,QACzD;AAAA,MACF;AACA,UAAI,MAAM,mBAAmB,kBAAkB;AAC7C,cAAM,IAAI;AAAA,UACR,2BAA2B,gBAAgB,gCAA2B,MAAM,cAAc;AAAA,QAC5F;AAAA,MACF;AAAA,IACF;AAEA,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,YAAsB;AACpB,WAAO,MAAM,KAAK,KAAK,MAAM,IAAI;AAAA,EACnC;AAAA;AAAA,EAGA,QAAgB;AACd,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGQ,OAAO,UAA0B;AACvC,UAAM,OAAO,KAAK,MAAM;AACxB,QAAI,KAAK;AACT,QAAI,KAAK,KAAK,SAAS;AACvB,WAAO,MAAM,IAAI;AACf,YAAM,MAAO,KAAK,OAAQ;AAC1B,YAAM,IAAI,KAAK,GAAG;AAClB,UAAI,MAAM,SAAU,QAAO;AAC3B,UAAI,IAAI,SAAU,MAAK,MAAM;AAAA,UACxB,MAAK,MAAM;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,UAA2B;AAC7B,WAAO,KAAK,OAAO,QAAQ,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAS,UAA8C;AAC3D,UAAM,OAAO,KAAK,OAAO,QAAQ;AACjC,QAAI,OAAO,EAAG,QAAO;AACrB,UAAM,SAAS,KAAK,MAAM,QAAQ,IAAI;AACtC,UAAM,SAAS,KAAK,MAAM,QAAQ,IAAI;AAEtC,QAAI,SAAS,iBAAiB,SAAS,SAAS,KAAK,aAAa,cAAc;AAC9E,YAAM,IAAI,eAAe,SAAS,QAAQ,wBAAwB;AAAA,IACpE;AACA,UAAM,QAAQ,MAAM,KAAK,KAAK,SAAS,QAAQ,MAAM;AACrD,QAAI,OAAO,KAAK,MAAM,KAAK,MAAM,KAAK,IAAI,GAAG;AAC3C,YAAM,IAAI,eAAe,SAAS,QAAQ,uBAAuB;AAAA,IACnE;AACA,QAAI,KAAK,WAAW,OAAW,QAAO;AAGtC,QAAI,MAAM,SAAS,mBAAmB,gBAAgB;AACpD,YAAM,IAAI,eAAe,SAAS,QAAQ,8BAA8B;AAAA,IAC1E;AACA,WAAO,KAAK,OAAO,KAAK;AAAA,MACtB;AAAA,QACE,OAAO,MAAM,SAAS,GAAG,gBAAgB;AAAA,QACzC,YAAY,MAAM,SAAS,kBAAkB,MAAM,SAAS,cAAc;AAAA,QAC1E,KAAK,MAAM,SAAS,MAAM,SAAS,cAAc;AAAA,MACnD;AAAA,MACA,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC7B;AAAA,EACF;AACF;AAiBO,SAAS,WACd,YACA,YACA,iBACA,kBAAkB,GACL;AAIb,QAAM,WAAW,cAAc,WAAW,MAAM;AAChD,QAAM,OAAO,IAAI,YAAY,QAAQ;AACrC,QAAM,sBAAsB,IAAI,YAAY,QAAQ;AACpD,QAAM,UAAU,IAAI,YAAY,QAAQ;AACxC,QAAM,OAAO,IAAI,YAAY,QAAQ;AACrC,QAAM,UAAU,IAAI,aAAa,QAAQ;AACzC,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,MAAI,UAAU;AACd,MAAI,UAAU;AACd,MAAI,iBAAiB;AAIrB,WAAS,IAAI,GAAG,MAAM,WAAW,QAAQ,KAAK;AAC5C,UAAM,eAAe,WAAW,YAAY,GAAG;AAC/C,UAAM,eAAe,WAAW,YAAY,aAAa,IAAI;AAC7D,UAAM,UAAU,WAAW,YAAY,aAAa,IAAI;AACxD,UAAM,WAAW,WAAW,YAAY,QAAQ,IAAI;AACpD,UAAM,WAAW,aAAa;AAC9B,UAAM,WAAW,aAAa;AAC9B,UAAM,MAAM,QAAQ;AACpB,UAAM,OAAO,SAAS;AACtB,UAAM,SAAS;AACf,QAAI,MAAM,eAAe,WAAW,QAAQ;AAC1C,YAAM,IAAI,eAAe,6CAA6C;AAAA,IACxE;AACA,UAAM,OACH,WAAW,GAAG,IACZ,WAAW,MAAM,CAAC,KAAM,IACxB,WAAW,MAAM,CAAC,KAAM,KACxB,WAAW,MAAM,CAAC,KAAM,QAC3B;AACF,WAAO;AAEP,UAAM,WAAW,UAAU;AAC3B,UAAM,SAAS,UAAU;AAEzB,QAAI,IAAI,KAAK,aAAa,GAAG;AAC3B,YAAM,IAAI,eAAe,wCAAwC,QAAQ,EAAE;AAAA,IAC7E;AACA,QAAI,WAAW,MAAQ,OAAM,IAAI,eAAe,kBAAkB,QAAQ,eAAe;AACzF,QAAI,MAAM,mBAAmB,MAAM,iBAAiB;AAClD,YAAM,IAAI,eAAe,eAAe,QAAQ,WAAW,GAAG,UAAU;AAAA,IAC1E;AACA,QAAI,OAAO,KAAK,OAAO,uBAAuB;AAC5C,YAAM,IAAI,eAAe,eAAe,QAAQ,gBAAgB,IAAI,UAAU;AAAA,IAChF;AAEA,QAAI,SAAS,MAAM,YAAY;AAC7B,YAAM,IAAI,eAAe,eAAe,QAAQ,wBAAwB;AAAA,IAC1E;AAEA,SAAK,KAAK,IAAI;AACd,wBAAoB,KAAK,IAAI,OAAO;AACpC,YAAQ,KAAK,IAAI;AACjB,SAAK,KAAK,IAAI;AACd,YAAQ,KAAK,IAAI;AACjB;AACA,sBAAkB;AAClB,cAAU;AACV,cAAU,SAAS;AAAA,EACrB;AAIA,MAAI,UAAU,UAAU;AACtB,WAAO,EAAE,MAAM,qBAAqB,SAAS,MAAM,SAAS,eAAe;AAAA,EAC7E;AACA,SAAO;AAAA,IACL,MAAM,KAAK,MAAM,GAAG,KAAK;AAAA,IACzB,qBAAqB,oBAAoB,MAAM,GAAG,KAAK;AAAA,IACvD,SAAS,QAAQ,MAAM,GAAG,KAAK;AAAA,IAC/B,MAAM,KAAK,MAAM,GAAG,KAAK;AAAA,IACzB,SAAS,QAAQ,MAAM,GAAG,KAAK;AAAA,IAC/B;AAAA,EACF;AACF;;;AC7dO,IAAM,aAAN,MAAiB;AAAA,EAQtB,YACmB,MACA,SACjB;AAFiB;AACA;AAEjB,QAAI,CAAC,OAAO,UAAU,QAAQ,UAAU,KAAK,QAAQ,aAAa,GAAG;AACnE,YAAM,IAAI;AAAA,QACR,kDAAkD,QAAQ,UAAU;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA,EARmB;AAAA,EACA;AAAA,EATF,UAAwB,CAAC;AAAA,EAClC,SAAS;AAAA,EACT,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBnB,MAAM,SAAS,UAAkB,SAAqB,aAAoC;AACxF,QAAI,KAAK,SAAU,OAAM,IAAI,gBAAgB,6BAA6B;AAC1E,QAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,WAAW,KAAK,WAAW,OAAQ;AACpE,YAAM,IAAI,gBAAgB,+BAA+B,QAAQ,EAAE;AAAA,IACrE;AACA,QAAI,YAAY,KAAK,SAAS;AAC5B,YAAM,IAAI;AAAA,QACR,YAAY,QAAQ,iCAAiC,KAAK,OAAO;AAAA,MACnE;AAAA,IACF;AACA,QAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,KAAK,cAAc,uBAAuB;AAC5F,YAAM,IAAI;AAAA,QACR,8BAA8B,qBAAqB,UAAU,WAAW;AAAA,MAC1E;AAAA,IACF;AACA,QAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,gBAAgB,iCAAiC;AAErF,UAAM,KAAK,eAAe;AAE1B,UAAM,SAAS,KAAK,QAAQ,SAAS,KAAK,UAAU,UAAU,OAAO,IAAI;AACzE,UAAM,KAAK,KAAK,MAAM,MAAM;AAC5B,SAAK,QAAQ,KAAK;AAAA,MAChB;AAAA,MACA,QAAQ,KAAK;AAAA,MACb,QAAQ,OAAO;AAAA,MACf;AAAA,MACA,QAAQ,OAAO,MAAM;AAAA,IACvB,CAAC;AACD,SAAK,UAAU,OAAO;AACtB,SAAK,oBAAoB;AACzB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGQ,UAAU,UAAkB,SAAiC;AACnE,UAAM,SAAS,KAAK,QAAQ;AAC5B,UAAM,SAAS,OAAO,KAAK,KAAK,SAAS,OAAO,OAAO,QAAQ,CAAC;AAChE,UAAM,MAAM,IAAI,WAAW,OAAO,MAAM,SAAS,OAAO,WAAW,SAAS,OAAO,IAAI,MAAM;AAC7F,QAAI,IAAI,OAAO,OAAO,CAAC;AACvB,QAAI,IAAI,OAAO,YAAY,OAAO,MAAM,MAAM;AAC9C,QAAI,IAAI,OAAO,KAAK,OAAO,MAAM,SAAS,OAAO,WAAW,MAAM;AAClE,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,QAAI,KAAK,SAAU,OAAM,IAAI,gBAAgB,6BAA6B;AAC1E,UAAM,KAAK,eAAe;AAE1B,UAAM,aAAa,KAAK,YAAY;AAEpC,QAAI,cAAc;AAClB,QAAI;AACJ,QAAI;AACJ,QAAI,KAAK,QAAQ,WAAW,QAAW;AACrC,YAAM,SAAS,KAAK,QAAQ,OAAO,KAAK,KAAK,YAAY,KAAK,QAAQ,OAAO,OAAO,OAAO,CAAC;AAC5F,oBAAc,OAAO;AACrB,mBAAa,OAAO;AACpB,iBAAW,OAAO;AAAA,IACpB;AACA,UAAM,cAAc,KAAK;AACzB,UAAM,KAAK,KAAK,MAAM,WAAW;AACjC,SAAK,UAAU,YAAY;AAE3B,UAAM,SAAS,KAAK,YAAY,aAAa,aAAa,YAAY,QAAQ;AAC9E,UAAM,KAAK,KAAK,MAAM,MAAM;AAC5B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,MAAc,iBAAgC;AAC5C,QAAI,KAAK,gBAAiB;AAC1B,UAAM,WAAW,IAAI,WAAW,cAAc;AAC9C,aAAS,IAAI,OAAO,CAAC;AACrB,aAAS,CAAC,IAAI;AACd,aAAS,CAAC,IAAI;AAEd,UAAM,KAAK,KAAK,MAAM,QAAQ;AAC9B,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEQ,cAA0B;AAChC,UAAM,MAAgB,CAAC;AACvB,QAAI,UAAU;AACd,QAAI,UAAU;AACd,eAAW,KAAK,KAAK,SAAS;AAC5B,kBAAY,KAAK,EAAE,WAAW,OAAO;AACrC,kBAAY,KAAK,EAAE,SAAS,OAAO;AACnC,kBAAY,KAAK,EAAE,MAAM;AACzB,kBAAY,KAAK,EAAE,WAAW;AAE9B,UAAI;AAAA,QACF,EAAE,SAAS;AAAA,QACV,EAAE,WAAW,IAAK;AAAA,QAClB,EAAE,WAAW,KAAM;AAAA,QACnB,EAAE,WAAW,KAAM;AAAA,MACtB;AACA,gBAAU,EAAE;AACZ,gBAAU,EAAE,SAAS,EAAE;AAAA,IACzB;AACA,WAAO,WAAW,KAAK,GAAG;AAAA,EAC5B;AAAA,EAEQ,YACN,aACA,aACA,YACA,UACY;AACZ,UAAM,YAAY,KAAK,QAAQ,WAAW;AAC1C,UAAM,SAAS,IAAI,WAAW,YAAY;AAC1C,UAAM,OAAO,IAAI,SAAS,OAAO,MAAM;AACvC,SAAK,aAAa,OAAO,aAAa,OAAO,WAAW,GAAG,IAAI;AAC/D,SAAK,aAAa,OAAO,aAAa,OAAO,YAAY,MAAM,GAAG,IAAI;AACtE,SAAK,UAAU,OAAO,aAAa,OAAO,WAAW,GAAG,IAAI;AAC5D,SAAK,UAAU,OAAO,OAAO,sBAAsB,YAAY,iBAAiB,IAAI,IAAI;AACxF,SAAK;AAAA,MACH,OAAO;AAAA,MACP,KAAK,QAAQ,kBAAkB;AAAA,MAC/B;AAAA,IACF;AACA,WAAO,OAAO,YAAY,IAAI,KAAK,QAAQ,gBAAgB;AAC3D,WAAO,OAAO,cAAc,IAAI;AAChC,WAAO,OAAO,YAAY,IAAI;AAC9B,WAAO,OAAO,YAAY,IAAI;AAG9B,QAAI,eAAe,OAAW,QAAO,IAAI,YAAY,OAAO,UAAU;AACtE,QAAI,aAAa,OAAW,QAAO,IAAI,UAAU,OAAO,QAAQ;AAChE,SAAK,aAAa,OAAO,YAAY,OAAO,KAAK,QAAQ,UAAU,GAAG,IAAI;AAG1E,SAAK,UAAU,OAAO,YAAY,YAAY,IAAI,KAAK,QAAQ,QAAQ,IAAI;AAC3E,SAAK,aAAa,OAAO,kBAAkB,OAAO,YAAY,IAAI,KAAK,gBAAgB,GAAG,IAAI;AAC9F,SAAK,UAAU,OAAO,cAAc,OAAO,OAAO,SAAS,GAAG,mBAAmB,CAAC,GAAG,IAAI;AACzF,WAAO,IAAI,OAAO,OAAO,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;;;ACtGA,SAAS,kBAAkB,QAAwB,KAAyB;AAC1E,SAAO;AAAA,IACL,SAAS,QAAQ,QAAQ;AACvB,aAAO,OAAO,SAAS,KAAK,QAAQ,MAAM;AAAA,IAC5C;AAAA,IACA,QAAQ,UAAU;AAChB,aAAO,OAAO,QAAQ,KAAK,QAAQ;AAAA,IACrC;AAAA,EACF;AACF;AAwBA,IAAM,eAAe,CAAC,KAAiB,eACrC,IAAI;AAAA,EACF,YAAY,IAAI,OAAO,gBAAgB,UAAU;AACnD;AAMF,SAAS,UAAU,YAAoB,SAA0B;AAC/D,SAAO,YAAY,SAAY,OAAO,UAAU,IAAI,GAAG,UAAU,IAAI,OAAO,OAAO,CAAC;AACtF;AAcA,IAAM,iBAAiB,KAAK;AAO5B,IAAM,kBAAkB,KAAK;AAEtB,IAAM,yBAAN,MAA2D;AAAA,EA2ChE,YACmB,QACjB,UAAyC,CAAC,GAC1C;AAFiB;AAGjB,QAAI,CAAC,OAAO,aAAa,EAAE,WAAW;AACpC,YAAM,IAAI,gBAAgB,qDAAqD;AAAA,IACjF;AACA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL,IAAI;AACJ,QAAI,aAAa,UAAa,aAAa,QAAW;AACpD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,sBAAsB,QAAQ,aAAa,QAAW;AAGxD,YAAM,IAAI,gBAAgB,oCAAoC;AAAA,IAChE;AACA,SAAK,WAAW;AAChB,SAAK,WAAW;AAChB,SAAK,oBAAoB,qBAAqB;AAC9C,SAAK,gBAAgB;AACrB,SAAK,QAAQ;AACb,SAAK,kBAAkB,mBAAmB;AAK1C,SAAK,YAAY,IAAI,WAA6B;AAAA,MAChD,YAAY,mBAAmB;AAAA,MAC/B,UAAU,qBAAqB;AAAA,MAC/B,OAAO,SAAS,EAAE,KAAK,MAAM,EAAE;AAAA,IACjC,CAAC;AACD,SAAK,eAAe,IAAI,WAAyB;AAAA,MAC/C,YAAY,mBAAmB;AAAA,MAC/B,OAAO,EAAE,KAAK,MAAM,EAAE;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EA7CmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA/BF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,oBAAI,IAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtD,gBAAgB;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAmDT,eAAe,KAA6C;AAClE,UAAM,MAAM,WAAW,GAAG;AAC1B,UAAM,WAAW,KAAK,UAAU,IAAI,GAAG;AACvC,QAAI,aAAa,OAAW,QAAO,KAAK,QAAQ,KAAK,KAAK,cAAc,GAAG,CAAC,EAAE;AAC9E,QAAI,CAAC,KAAK,QAAQ,SAAS,aAAa,EAAG,QAAO,SAAS;AAI3D,UAAM,OAAiB,KAAK;AAAA,MAC1B;AAAA,MACA,KAAK,uBAAuB,KAAK,UAAU,MAAM,KAAK,UAAU,IAAI,CAAC;AAAA,IACvE;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,MAAsB;AACtC,SAAK,gBACH,KAAK,IAAI,KAAK,KAAK,kBAAkB,KAAK,IAAI,KAAK,iBAAiB,gBAAgB;AAAA,EACxF;AAAA;AAAA,EAGA,MAAc,uBACZ,KACA,UACA,kBAC4B;AAC5B,UAAM,UAAU,MAAM,SAAS,OAAO,MAAM,MAAM,IAAI;AACtD,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,KAAK,cAAc,GAAG;AAAA,IACvC,SAAS,KAAK;AAIZ,UAAI,CAAC,iBAAiB,GAAG,EAAG,OAAM;AAClC,eAAS;AAAA,IACX;AAIA,QAAI,WAAW,QAAW;AACxB,UAAI,YAAY,KAAM,QAAO,KAAK,cAAc,GAAG;AACnD,uBAAiB;AACjB,aAAO;AAAA,IACT;AACA,QAAI,WAAW,KAAM,QAAO;AAK5B,QACE,YAAY,QACZ,OAAO,eAAe,QAAQ,cAC9B,OAAO,YAAY,QAAQ,SAC3B;AACA,aAAO;AAAA,IACT;AACA,WAAO,KAAK,cAAc,KAAK,MAAM;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,QAAQ,KAAa,QAA8C;AACzE,UAAM,OAAiB,EAAE,QAAQ,eAAe,KAAK,IAAI,EAAE;AAC3D,SAAK,UAAU,IAAI,KAAK,IAAI;AAC5B,UAAM,gBAAgB,MAAY;AAChC,UAAI,KAAK,UAAU,IAAI,GAAG,MAAM,KAAM,MAAK,UAAU,OAAO,GAAG;AAAA,IACjE;AACA,WAAO,KAAK,CAAC,MAAM;AACjB,UAAI,MAAM,MAAM;AACd,sBAAc;AACd;AAAA,MACF;AAGA,UAAI,KAAK,UAAU,KAAK,GAAG,MAAM,KAAM,MAAK,UAAU,UAAU,KAAK,EAAE,kBAAkB;AAAA,IAC3F,GAAG,aAAa;AAChB,WAAO;AAAA,EACT;AAAA,EAEQ,MAAc;AACpB,WAAO,KAAK,QAAQ,KAAK,MAAM,IAAI,IAAI;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,UAAU,UAAa,KAAK,aAAa,SAAY,KAAK,kBAAkB;AAAA,EAC1F;AAAA,EAEQ,QAAQ,eAAgC;AAK9C,WACE,KAAK,UAAU,UACf,KAAK,aAAa,UAClB,KAAK,kBAAkB,KACvB,KAAK,MAAM,IAAI,IAAI,iBAAiB,KAAK;AAAA,EAE7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,kBAAkB,KAAyC;AAC/D,UAAM,UAAU,KAAK,eAAe,GAAG;AACvC,QAAI;AACF,cAAQ,MAAM,UAAU,cAAc;AAAA,IACxC,SAAS,KAAK;AACZ,UAAI,CAAC,gBAAgB,GAAG,EAAG,OAAM;AACjC,WAAK,UAAU,WAAW,GAAG,GAAG,OAAO;AACvC,cAAQ,MAAM,KAAK,eAAe,GAAG,IAAI,cAAc;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,WAAW,KAAuB;AAChC,SAAK,iBAAiB;AACtB,UAAM,MAAM,WAAW,GAAG;AAC1B,SAAK,UAAU,OAAO,GAAG;AAIzB,UAAM,eAAe,GAAG,GAAG;AAC3B,SAAK,UAAU,YAAY,CAAC,MAAM,EAAE,WAAW,YAAY,CAAC;AAG5D,SAAK,aAAa,YAAY,CAAC,MAAM,EAAE,WAAW,YAAY,CAAC;AAC/D,eAAW,KAAK,KAAK,SAAS,KAAK,EAAG,KAAI,EAAE,WAAW,YAAY,EAAG,MAAK,SAAS,OAAO,CAAC;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,eAAe,KAAyC;AAC5D,UAAM,UAAU,KAAK,eAAe,GAAG;AACvC,QAAI;AACJ,QAAI;AACF,eAAS,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,CAAC,gBAAgB,GAAG,EAAG,OAAM;AACjC,WAAK,UAAU,WAAW,GAAG,GAAG,OAAO;AACvC,eAAS,MAAM,KAAK,eAAe,GAAG;AAAA,IACxC;AACA,WAAO,WAAW,OAAO,OAAO,UAAU,OAAO,YAAY,OAAO,OAAO;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,cACJ,KACmE;AACnE,QAAI;AACF,aAAO,MAAM,KAAK,QAAQ,GAAG;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI,CAAC,gBAAgB,GAAG,EAAG,OAAM;AACjC,aAAO,KAAK,QAAQ,GAAG;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,KACmE;AAKnE,UAAM,SAAS,MAAM,KAAK,cAAc,GAAG;AAC3C,QAAI,WAAW,KAAM,QAAO;AAC5B,UAAM,UAAU,UAAU,OAAO,YAAY,OAAO,OAAO;AAQ3D,QAAI;AACJ,QAAI,SAAS;AACb,QAAI,OAAO,YAAY,QAAW;AAChC,eAAS,MAAM,KAAK,cAAc,KAAK,MAAM;AAC7C,WAAK,QAAQ,KAAK,UAAU,KAAK,SAAS,OAAO,WAAW,GAAG,QAAQ,QAAQ,MAAM,CAAC;AAAA,IACxF,OAAO;AACL,YAAM,QAAQ,KAAK;AACnB,YAAM,MAAM,KAAK,UAAU,KAAK,OAAO;AACvC,UAAI,QAAQ,KAAK,UAAU,IAAI,GAAG;AAClC,eAAS,UAAU;AACnB,gBAAU,KAAK,QAAQ,KAAK,KAAK,cAAc,KAAK,MAAM,CAAC;AAC3D,eAAS,MAAM,MAAM;AAGrB,UAAI,WAAW,KAAM,QAAO;AAC5B,YAAM,WAAW,CAAC,MAChB,KAAK,aAAa,IAAI,KAAK,QAAQ,KAAK,SAAS,EAAE,WAAW,CAAC,MAAM;AAKvE,YAAM,QAAQ,CAAC,UAAU,SAAS,MAAM;AAIxC,UAAI,CAAC,SAAS,KAAK,kBAAkB,SAAS,KAAK,UAAU,KAAK,GAAG,MAAM,QAAW;AACpF,aAAK,QAAQ,KAAK,QAAQ,QAAQ,MAAM,CAAC;AAAA,MAC3C;AACA,UAAI,OAAO;AACT,cAAM,UAAU,KAAK,UAAU,KAAK,GAAG;AACvC,YAAI,QACF,YAAY,UAAa,YAAY,QACjC,UACA,KAAK,QAAQ,KAAK,KAAK,cAAc,KAAK,MAAM,CAAC;AACvD,iBAAS,MAAM,MAAM;AAErB,YAAI,WAAW,KAAM,QAAO;AAE5B,YAAI,UAAU,WAAW,SAAS,MAAM,GAAG;AACzC,kBAAQ,KAAK,QAAQ,KAAK,KAAK,cAAc,KAAK,MAAM,CAAC;AACzD,mBAAS,MAAM,MAAM;AACrB,cAAI,WAAW,KAAM,QAAO;AAAA,QAC9B;AACA,iBAAS,UAAU;AAAA,MACrB;AAAA,IACF;AAGA,QAAI,QAAQ;AACV,YAAM,OAAO,KAAK,QAAQ,KAAK,SAAS,OAAO,WAAW;AAC1D,WAAK,aAAa,OAAO,IAAI;AAC7B,WAAK,SAAS,OAAO,IAAI;AAAA,IAC3B;AACA,WAAO,EAAE,YAAY,OAAO,YAAY,SAAS,aAAa,OAAO,YAAY;AAAA,EACnF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,KAAiB,SAAiB,aAA8B;AAChF,WAAO,KAAK,aAAa,UAAa,gBAAgB,SAClD,KAAK,QAAQ,KAAK,SAAS,WAAW,IACtC,GAAG,WAAW,GAAG,CAAC,IAAI,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,QAAQ,KAAiB,SAAiB,aAA6B;AAC7E,WAAO,GAAG,WAAW,GAAG,CAAC,IAAI,OAAO,IAAI,WAAW;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAc,SACZ,KACA,YACA,SACA,aAC4B;AAC5B,UAAM,KAAK,WAAW,OAAO,UAAU;AACvC,UAAM,MAAM,KAAK,UAAU,KAAK,IAAI,WAAW;AAC/C,UAAM,QAAQ,KAAK;AACnB,QAAI,QAAQ,KAAK,UAAU,IAAI,GAAG;AAClC,UAAM,UAAU,UAAU;AAC1B,QAAI,UAAU,QAAW;AAGvB,UACE,gBAAgB,UAChB,KAAK,aAAa,IAAI,KAAK,QAAQ,KAAK,IAAI,WAAW,CAAC,MAAM;AAE9D,cAAM,aAAa,KAAK,UAAU;AACpC,cAAQ,KAAK,QAAQ,KAAK,KAAK,OAAO,KAAK,UAAU,CAAC;AAAA,IACxD;AACA,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,MAAM;AAAA,IACvB,SAAS,KAAK;AAIZ,UAAI,gBAAgB,UAAa,iBAAiB,GAAG,KAAK,gBAAgB,GAAG,EAAG,OAAM;AACtF,YAAM,KAAK,gBAAgB,KAAK,YAAY,IAAI,WAAW;AAC3D,YAAM;AAAA,IACR;AAGA,QAAI,WAAW,MAAM;AACnB,UAAI,YAAY,OAAW,QAAO;AAClC,YAAM,IAAI;AAAA,QACR,YAAY,IAAI,OAAO,gBAAgB,UAAU;AAAA,MAEnD;AAAA,IACF;AACA,QAAI,gBAAgB,UAAa,OAAO,gBAAgB,aAAa;AAMnE,UAAI,KAAK,kBAAkB;AACzB,aAAK,aAAa,IAAI,KAAK,QAAQ,KAAK,IAAI,WAAW,GAAG,IAAI;AAChE,UAAI,WAAW,KAAK,UAAU,KAAK,GAAG,MAAM,MAAO,MAAK,UAAU,OAAO,GAAG;AAC5E,YAAM,aAAa,KAAK,UAAU;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,OAAO,KAAiB,YAAgD;AACpF,QAAI,KAAK,aAAa,OAAW,QAAO,KAAK,cAAc,KAAK,EAAE,WAAW,CAAC;AAE9E,UAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAG1C,QAAI,WAAW,QAAQ,OAAO,WAAW,YAAa,QAAO;AAC7D,WAAO,KAAK,cAAc,KAAK;AAAA,MAC7B;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,aAAa,OAAO;AAAA,IACtB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WACJ,KACA,YACA,MAC4B;AAC5B,oBAAgB,GAAG;AACnB,UAAM,SAAS,MAAM,KAAK,SAAS,KAAK,YAAY,MAAM,SAAS,MAAM,WAAW;AACpF,QAAI,WAAW,KAAM,QAAO;AAC5B,UAAM,cAAc,MAAM;AAC1B,QACE,SAAS,UACT,gBAAgB,UAChB,KAAK,aAAa,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,WAAW,CAAC,MAAM;AAExE,YAAM,aAAa,KAAK,UAAU;AACpC,QAAI;AACF,aAAO,MAAM,OAAO,SAAS,IAAI,QAAQ;AAAA,IAC3C,SAAS,KAAK;AAOZ,UAAI,SAAS,UAAa,gBAAgB,OAAW,OAAM;AAC3D,UAAI,EAAE,iBAAiB,GAAG,KAAK,kBAAkB,GAAG,GAAI,OAAM;AAC9D,YAAM,KAAK,gBAAgB,KAAK,YAAY,KAAK,SAAS,WAAW;AACrE,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,gBACZ,KACA,YACA,SACA,aACe;AACf,UAAM,OAAO,KAAK,QAAQ,KAAK,SAAS,WAAW;AACnD,QAAI,KAAK,aAAa,IAAI,IAAI,MAAM,OAAW,OAAM,aAAa,KAAK,UAAU;AACjF,QAAI,QAAQ,KAAK,SAAS,IAAI,IAAI;AAClC,QAAI,UAAU,QAAW;AACvB,YAAM,KAAa,EAAE,WAAW,IAAI,WAAW,SAAS,IAAI,SAAS,WAAW;AAGhF,YAAM,QAAQ,MACZ,KAAK,SAAS,IAAI,IAAI,MAAM,WAAW,KAAK,SAAS,OAAO,IAAI;AAElE,YAAM,UAA4B,WAAW;AAAA,QAC3C,kBAAkB,KAAK,QAAQ,EAAE;AAAA,QACjC;AAAA,MACF,EAAE;AAAA,QACA,CAAC,SAAS;AACR,cAAI,MAAM,KAAK,CAAC,KAAM,MAAK,aAAa,IAAI,MAAM,IAAI;AACtD,iBAAO,CAAC;AAAA,QACV;AAAA,QACA,CAAC,QAAiB;AAChB,gBAAM;AACN,cAAI,gBAAgB,GAAG,EAAG,QAAO;AACjC,gBAAM;AAAA,QACR;AAAA,MACF;AACA,WAAK,SAAS,IAAI,MAAM,OAAO;AAC/B,cAAQ;AAAA,IACV;AACA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM;AAAA,IACnB,SAAS,KAAK;AACZ,UAAI,iBAAiB,GAAG,EAAG,OAAM;AACjC;AAAA,IACF;AACA,QAAI,SAAU,OAAM,aAAa,KAAK,UAAU;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,gBACJ,KACA,YACA,MACmB;AACnB,oBAAgB,GAAG;AACnB,UAAM,SAAS,MAAM,KAAK,SAAS,KAAK,YAAY,MAAM,SAAS,MAAM,WAAW;AACpF,WAAO,WAAW,OAAO,CAAC,IAAI,OAAO,UAAU;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBACJ,KACA,YACA,MAC6C;AAC7C,oBAAgB,GAAG;AACnB,UAAM,SAAS,MAAM,KAAK,SAAS,KAAK,YAAY,MAAM,SAAS,MAAM,WAAW;AACpF,WAAO,WAAW,OAAO,OAAO,OAAO,cAAc;AAAA,EACvD;AAAA;AAAA,EAGA,MAAM,SACJ,KACA,YACA,MAC6B;AAC7B,oBAAgB,GAAG;AACnB,UAAM,SAAS,MAAM,KAAK,SAAS,KAAK,YAAY,MAAM,SAAS,MAAM,WAAW;AACpF,WAAO,WAAW,OAAO,OAAO,EAAE,WAAW,OAAO,UAAU;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,OAAO,KAAmC;AAC9C,QAAI,KAAK,aAAa,OAAW,QAAQ,MAAM,KAAK,SAAS,IAAI,GAAG,MAAO;AAC3E,qBAAiB,OAAO,KAAK,OAAO,KAAK,GAAG,GAAG;AAC7C,WAAK;AACL,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,KAAa,SAA2C;AACxE,UAAM,MAAM,KAAK,UAAU,IAAI,GAAG;AAClC,QAAI,QAAQ,UAAa,IAAI,WAAW,QAAS,MAAK,UAAU,OAAO,GAAG;AAAA,EAC5E;AAAA,EAEA,MAAc,cAAc,KAA6C;AACvE,UAAM,SAAS,MAAM,KAAK,cAAc,GAAG;AAC3C,QAAI,WAAW,KAAM,QAAO;AAC5B,WAAO,KAAK,cAAc,KAAK,MAAM;AAAA,EACvC;AAAA;AAAA,EAGA,MAAc,cAAc,KAAiB,QAAqC;AAChF,UAAM,SAAiB;AAAA,MACrB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,YAAY,OAAO;AAAA,IACrB;AACA,UAAM,SAAS,MAAM,KAAK,cAAc,KAAK,OAAO,YAAY,OAAO,WAAW;AAClF,WAAO,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACtC,GAAG,KAAK;AAAA,MACR;AAAA,MACA,SAAS,OAAO;AAAA,IAClB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,cAAc,KAAyC;AACnE,QAAI,KAAK,aAAa,QAAW;AAC/B,YAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,UAAI,WAAW,KAAM,QAAO;AAE5B,UAAI,OAAO,WAAW,YAAa,QAAO;AAK1C,UAAI,OAAO,eAAe,KAAM,QAAO;AACvC,aAAO;AAAA,QACL,YAAY,OAAO;AAAA,QACnB,SAAS,OAAO;AAAA,QAChB,aAAa,OAAO;AAAA,MACtB;AAAA,IACF;AACA,QAAI,SAAS;AACb,qBAAiB,OAAO,KAAK,OAAO,KAAK,GAAG,GAAG;AAC7C,UAAI,IAAI,aAAa,OAAQ,UAAS,IAAI;AAAA,IAC5C;AACA,WAAO,SAAS,IAAI,OAAO,EAAE,YAAY,OAAO;AAAA,EAClD;AAAA;AAAA,EAGA,MAAc,cACZ,KACA,YACA,aACiC;AACjC,QAAI,gBAAgB,UAAa,YAAY,WAAW,GAAG;AACzD,UAAI,KAAK,mBAAmB;AAC1B,cAAM,IAAI;AAAA,UACR,+BAA+B,IAAI,OAAO;AAAA,QAC5C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AACA,QAAI,KAAK,aAAa,QAAW;AAC/B,YAAM,IAAI;AAAA,QACR,YAAY,IAAI,OAAO;AAAA,MACzB;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK,SAAS,QAAQ,WAAW;AACpD,WAAO,EAAE,MAAM,QAAQ,CAAC,UAAU,OAAO,KAAK,YAAY,KAAK,EAAE;AAAA,EACnE;AAAA,EAEA,MAAM,SAAS,KAA2C;AACxD,qBAAiB,GAAG;AACpB,WAAO,KAAK,kBAAkB,KAAK,CAAC,WAAW,OAAO,SAAS,IAAI,QAAQ,GAAG,IAAI;AAAA,EACpF;AAAA,EAEA,MAAM,cAAc,KAAoC;AACtD,oBAAgB,GAAG;AACnB,WAAO,KAAK,kBAAkB,KAAK,CAAC,WAAW,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,EACvE;AAAA,EAEA,MAAM,OAAO,KAA8C;AACzD,oBAAgB,GAAG;AACnB,WAAO,KAAK;AAAA,MACV;AAAA,MACA,CAAC,YAAY,EAAE,WAAW,OAAO,UAAU;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,KAA8D;AAChF,oBAAgB,GAAG;AACnB,WAAO,KAAK;AAAA,MACV;AAAA,MACA,CAAC,WAAW,OAAO,cAAc;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAc,kBACZ,KACA,MACA,QACY;AACZ,aAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,YAAM,UAAU,KAAK,eAAe,GAAG;AACvC,UAAI;AACF,cAAM,SAAS,MAAM;AACrB,YAAI,WAAW,KAAM,QAAO;AAC5B,eAAO,MAAM,KAAK,MAAM;AAAA,MAC1B,SAAS,KAAK;AAGZ,YAAI,CAAC,gBAAgB,GAAG,KAAK,YAAY,EAAG,OAAM;AAClD,aAAK,UAAU,WAAW,GAAG,GAAG,OAAO;AAAA,MACzC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;AAQO,SAAS,oBACd,QACA,KACA,QACA,UAAoD,CAAC,GACV;AAC3C,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAIjE,QAAM,OAAO,WAAW,QAAQ,KAAK;AACrC,SAAO,OAAO,aAAa,KAAK,OAAO,SAAS;AAC9C,UAAM,SAAS,IAAI,WAAW,MAAM,EAAE,YAAY,IAAI,YAAY,QAAQ,QAAQ,OAAO,CAAC;AAC1F,eAAW,EAAE,UAAU,OAAO,KAAK,QAAQ;AACzC,UAAI,OAAO,QAAS;AAGpB,aAAO,WAAW;AAClB,YAAM,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,OAAO,IAAI;AAC/D,YAAM,QAAQ,KAAK;AACnB,UAAI,UAAU,KAAM,OAAM;AAAA,IAC5B;AACA,UAAM,OAAO,OAAO;AAAA,EACtB,CAAC;AACH;AAoBA,eAAsB,0BACpB,QACA,KACA,QACA,UAAoD,CAAC,GACzB;AAC5B,QAAM,YAAsB,CAAC;AAC7B,MAAI,cAAc;AAIlB,QAAM,OAAO,WAAW,QAAQ,KAAK;AACrC,QAAM,EAAE,MAAM,OAAO,IAAI,MAAM,OAAO,aAAa,KAAK,OAAO,SAAS;AACtE,UAAM,SAAS,IAAI,WAAW,MAAM,EAAE,YAAY,IAAI,YAAY,QAAQ,QAAQ,OAAO,CAAC;AAC1F,qBAAiB,EAAE,UAAU,OAAO,KAAK,QAAQ;AAC/C,UAAI,OAAO,QAAS;AACpB,aAAO,WAAW;AAClB,YAAM,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,OAAO,IAAI;AAC/D,gBAAU,KAAK,QAAQ;AACvB,qBAAe,OAAO;AACtB,YAAM,QAAQ,KAAK;AACnB,UAAI,UAAU,KAAM,OAAM;AAAA,IAC5B;AACA,UAAM,OAAO,OAAO;AAAA,EACtB,CAAC;AACD,SAAO,EAAE,MAAM,QAAQ,WAAW,YAAY;AAChD;AAsCA,eAAsB,kBACpB,UACA,KACA,UAoBI,CAAC,GACa;AAClB,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,UAAM,SAAS,MAAM,SAAS,IAAI,GAAG;AACrC,QAAI;AACF,UAAI,QAAQ,eAAe,UAAa,QAAQ,eAAe,QAAQ,YAAY;AAGjF,eAAO;AAAA,MACT;AACA,UAAI,QAAQ,iBAAiB,QAAQ,WAAW,MAAM;AAGpD,eAAO;AAAA,MACT;AACA,UAAI,QAAQ,gBAAgB,UAAa,QAAQ,UAAU,QAAQ,aAAa;AAI9E,eAAO;AAAA,MACT;AACA,UAAI,WAAW,MAAM;AAEnB,cAAM,SAAS,OAAO,KAAK;AAAA,UACzB,YAAY,IAAI;AAAA,UAChB,aAAa,QAAQ;AAAA,QACvB,CAAC;AAAA,MACH,WAAW,OAAO,WAAW,aAAa;AAaxC,cAAM,IAAI;AAAA,UACR,YAAY,IAAI,OAAO,sDAAsD,IAAI,UAAU;AAAA,QAE7F;AAAA,MACF,WAAW,OAAO,eAAe,MAAM;AAUrC,cAAM,SAAS,eAAe,KAAK,OAAO,OAAO;AAAA,UAC/C,YAAY,IAAI;AAAA,UAChB,GAAI,QAAQ,gBAAgB,SAAY,CAAC,IAAI,EAAE,aAAa,QAAQ,YAAY;AAAA,QAClF,CAAC;AAAA,MACH,WAAW,OAAO,aAAa,IAAI,YAAY;AAC7C,eAAO;AAAA,MACT,WAAW,OAAO,eAAe,IAAI,YAAY;AAC/C,eAAO;AAAA,MACT,OAAO;AAcL,YAAI,QAAQ,gBAAgB,UAAa,QAAQ,YAAY,SAAS,GAAG;AACvE,gBAAM,IAAI;AAAA,YACR,qDAAqD,IAAI,UAAU,QAAQ,IAAI,OAAO,sEACzB,OAAO,UAAU;AAAA,UAEhF;AAAA,QACF;AACA,cAAM,SAAS,eAAe,KAAK,OAAO,OAAO,EAAE,YAAY,IAAI,WAAW,CAAC;AAAA,MACjF;AACA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,qBAAqB,GAAG,EAAG;AAC/B,YAAM;AAAA,IACR;AAAA,EACF;AAIA,QAAM,QAAQ,MAAM,SAAS,IAAI,GAAG;AAGpC,MAAI,UAAU,QAAQ,MAAM,eAAe,QAAQ,MAAM,cAAc,IAAI;AACzE,WAAO,MAAM,eAAe,IAAI;AAClC,QAAM,IAAI;AAAA,IACR,wDAAwD,IAAI,OAAO;AAAA,EACrE;AACF;AAwDA,eAAsB,uBACpB,QACA,KACA,KACA,UA+BI,CAAC,GACoB;AACzB,MAAI,QAAQ,aAAa,UAAa,QAAQ,sBAAsB,MAAM;AACxE,UAAM,IAAI,gBAAgB,+DAA+D;AAAA,EAC3F;AACA,QAAM,QAAQ,aAAa,QAAQ,OAAO,wBAAwB;AAClE,QAAM,UAAU,oBAAI,IAAyB;AAkB7C,QAAM,iBAAiB,oBAAI,IAAsB;AACjD,MAAI,UAAU;AAEd,QAAM,YAAY,WAAW,QAAQ,KAAK;AAC1C,QAAM,SAAS,WAAW,QAAQ,OAAO,eAAe;AAYxD,QAAM,eAAe,YAA2B;AAC9C,eAAW,CAAC,UAAU,IAAI,KAAK,gBAAgB;AAC7C,UAAI,KAAK,WAAW,EAAG;AACvB,YAAM,iBAAiB,QAAQ,IAAI,QAAQ;AAC3C,UAAI,mBAAmB,OAAW,SAAQ,IAAI,UAAU,MAAM,WAAW,IAAI,CAAC;AAAA,UACzE,gBAAe,QAAQ,IAAI;AAChC,WAAK,SAAS;AACd,YAAM,QAAQ,UAAU;AACxB,UAAI,UAAU,KAAM,OAAM;AAAA,IAC5B;AACA,cAAU;AAAA,EACZ;AAEA,QAAM,SAAS,CAAC,OAAwB;AACtC,UAAM,EAAE,UAAU,UAAU,IAAI,QAAQ,EAAE;AAC1C,QAAI,SAAS,eAAe,IAAI,QAAQ;AACxC,QAAI,WAAW,QAAW;AACxB,eAAS,CAAC;AACV,qBAAe,IAAI,UAAU,MAAM;AAAA,IACrC;AACA,WAAO,KAAK,SAAS;AACrB,WAAO,EAAE,WAAW;AAAA,EACtB;AAUA,MAAI,OAAO,iBAAiB,KAAK;AAC/B,qBAAiB,MAAM,KAA8B;AACnD,UAAI,OAAO,EAAE,EAAG,OAAM,aAAa;AACnC,YAAM,QAAQ,OAAO;AACrB,UAAI,UAAU,KAAM,OAAM;AAAA,IAC5B;AAAA,EACF,OAAO;AACL,eAAW,MAAM,KAAyB;AACxC,UAAI,OAAO,EAAE,EAAG,OAAM,aAAa;AACnC,YAAM,QAAQ,OAAO;AACrB,UAAI,UAAU,KAAM,OAAM;AAAA,IAC5B;AAAA,EACF;AACA,QAAM,aAAa;AAEnB,MAAI,cAAc;AAClB,QAAM,SAA2D,CAAC;AAClE,aAAW,CAAC,UAAU,MAAM,KAAK,SAAS;AAExC,mBAAe,OAAO;AACtB,WAAO,KAAK,EAAE,UAAU,OAAO,CAAC;AAChC,UAAM,QAAQ,UAAU;AACxB,QAAI,UAAU,KAAM,OAAM;AAAA,EAC5B;AAEA,MAAI,QAAQ,aAAa,UAAa,QAAQ,aAAa,QAAW;AACpE,UAAM,IAAI,gBAAgB,gEAAgE;AAAA,EAC5F;AAGA,QAAM,WAAW,QAAQ,aAAa,SAAY,MAAM,QAAQ,SAAS,IAAI,GAAG,IAAI;AACpF,MAAI,UAAU,WAAW,aAAa;AACpC,UAAM,IAAI;AAAA,MACR,YAAY,IAAI,OAAO;AAAA,IACzB;AAAA,EACF;AAQA,MACE,UAAU,gBAAgB,UAC1B,SAAS,YAAY,SAAS,KAC9B,QAAQ,aAAa,QACrB;AACA,UAAM,IAAI;AAAA,MACR,YAAY,IAAI,OAAO;AAAA,IAEzB;AAAA,EACF;AAUA,MAAI;AACJ,MAAI;AACJ,MAAI,QAAQ,aAAa,QAAW;AAClC,QAAI,UAAU,gBAAgB,UAAa,SAAS,YAAY,SAAS,GAAG;AAC1E,YAAM,OAAO,MAAM,QAAQ,SAAS,QAAQ,SAAS,WAAW;AAChE,eAAS,EAAE,MAAM,QAAQ,CAAC,UAAU,OAAO,KAAK,IAAI,YAAY,KAAK,EAAE;AAAA,IACzE,WAAW,aAAa,QAAQ,SAAS,eAAe,MAAM;AAK5D,UAAI,QAAQ,sBAAsB,MAAM;AACtC,cAAM,IAAI;AAAA,UACR,+BAA+B,IAAI,OAAO,4BAA4B,SAAS,UAAU;AAAA,QAG3F;AAAA,MACF;AAAA,IAEF,OAAO;AACL,YAAM,SAAS,MAAM,QAAQ,SAAS,UAAU;AAChD,mBAAa,OAAO;AACpB,eAAS,EAAE,MAAM,OAAO,MAAM,QAAQ,CAAC,UAAU,OAAO,KAAK,IAAI,YAAY,KAAK,EAAE;AAAA,IACtF;AAAA,EACF;AAEA,QAAM,EAAE,MAAM,OAAO,IAAI,MAAM,oBAAoB,QAAQ,KAAK,QAAQ;AAAA,IACtE;AAAA,IACA,OAAO,QAAQ;AAAA,EACjB,CAAC;AAGD,MAAI,QAAQ,aAAa,UAAa,QAAQ,YAAY,OAAO;AAC/D,UAAM,gBAAgB,MAAM,kBAAkB,QAAQ,UAAU,KAAK;AAAA,MACnE,aAAa;AAAA,IACf,CAAC;AAID,QAAI,eAAe;AACjB,gBAAU,QAAQ,SAAS,UAAU,EAAE,QAAQ;AAAA,QAC7C,MAAM;AAAA,QACN,WAAW,IAAI;AAAA,QACf,SAAS,IAAI;AAAA,QACb,YAAY,IAAI;AAAA,MAClB,CAAC;AAAA,IACH;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,YAAY,OAAO;AAAA,MACnB;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf;AAAA,EACF;AACA,SAAO,EAAE,MAAM,QAAQ,YAAY,OAAO,QAAQ,aAAa,aAAa,WAAW;AACzF;AAOO,SAAS,qBACd,SACA,KACA,QACA,UAA6C,CAAC,GACzB;AACrB,SAAO,YAAY,SAAS,KAAK,EAAE,GAAG,SAAS,OAAO,CAAC;AACzD;AAOA,eAAe,YACb,SACA,KACA,SACqB;AACrB,QAAM,SAAS,MAAM,WAAW,KAAK,kBAAkB,SAAS,GAAG,GAAG,OAAO;AAC7E,MAAI,OAAO,eAAe,IAAI,YAAY;AACxC,UAAM,IAAI;AAAA,MACR,YAAY,IAAI,OAAO,gBAAgB,IAAI,UAAU,gCAAgC,OAAO,UAAU;AAAA,IACxG;AAAA,EACF;AACA,SAAO;AACT;AASA,eAAsB,iBACpB,SACA,KACA,UACA,QACe;AACf,QAAM,eAAe,CAAC,GAAG,SAAS,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACjE,QAAM,SAAS,MAAM,qBAAqB,SAAS,KAAK,MAAM;AAC9D,QAAM,aAAa,CAAC,GAAG,OAAO,UAAU,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/D,QAAM,YACJ,WAAW,WAAW,aAAa,UAAU,WAAW,MAAM,CAAC,GAAG,MAAM,MAAM,aAAa,CAAC,CAAC;AAC/F,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,qBAAqB,IAAI,OAAO,IAAI,IAAI,UAAU,6BAA6B,WAAW,MAAM,OAAO,aAAa,MAAM;AAAA,IAC5H;AAAA,EACF;AACA,MAAI,OAAO,MAAM,MAAM,SAAS,aAAa;AAC3C,UAAM,IAAI;AAAA,MACR,qBAAqB,IAAI,OAAO,IAAI,IAAI,UAAU,iBAAiB,OAAO,MAAM,CAAC,OAAO,SAAS,WAAW;AAAA,IAC9G;AAAA,EACF;AACF;;;AC9+CA,IAAM,SAAS,CAAC,QAA4C;AAC1D,MAAI,IAAI,YAAY,KAAM,QAAO;AACjC,SAAO,IAAI,eAAe,OACtB,EAAE,SAAS,IAAI,QAAQ,IACvB,EAAE,SAAS,IAAI,SAAS,aAAa,IAAI,YAAY;AAC3D;AAEO,IAAM,2BAAN,MAA6D;AAAA,EAClE,YACmB,OAOA,MACjB;AARiB;AAOA;AAAA,EAChB;AAAA,EARgB;AAAA,EAOA;AAAA;AAAA,EAInB,IAAI,mBAA2B;AAC7B,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,OAAO,KAAuC;AACpD,WAAO,KAAK,KAAK,IAAI,WAAW,GAAG,CAAC;AAAA,EACtC;AAAA,EAEA,SAAS,KAA2C;AAClD,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,QAAQ,OAAW,QAAO,KAAK,MAAM,SAAS,GAAG;AACrD,WAAO,IAAI,eAAe,OACtB,QAAQ,QAAQ,IAAI,IACpB,KAAK,MAAM,WAAW,KAAK,IAAI,YAAY,OAAO,GAAG,CAAC;AAAA,EAC5D;AAAA,EAEA,cAAc,KAAoC;AAChD,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,QAAQ,OAAW,QAAO,KAAK,MAAM,cAAc,GAAG;AAC1D,WAAO,IAAI,eAAe,OACtB,QAAQ,QAAQ,CAAC,CAAC,IAClB,KAAK,MAAM,gBAAgB,KAAK,IAAI,YAAY,OAAO,GAAG,CAAC;AAAA,EACjE;AAAA,EAEA,OAAO,KAA8C;AACnD,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,QAAQ,OAAW,QAAO,KAAK,MAAM,OAAO,GAAG;AACnD,WAAO,IAAI,eAAe,OACtB,QAAQ,QAAQ,IAAI,IACpB,KAAK,MAAM,SAAS,KAAK,IAAI,YAAY,OAAO,GAAG,CAAC;AAAA,EAC1D;AAAA,EAEA,cAAc,KAA8D;AAC1E,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,QAAQ,OAAW,QAAO,KAAK,MAAM,cAAc,GAAG;AAC1D,WAAO,IAAI,eAAe,OACtB,QAAQ,QAAQ,IAAI,IACpB,KAAK,MAAM,gBAAgB,KAAK,IAAI,YAAY,OAAO,GAAG,CAAC;AAAA,EACjE;AAAA,EAEA,kBAAkB,KAAyC;AACzD,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,WAAO,QAAQ,SAAY,KAAK,MAAM,kBAAkB,GAAG,IAAI,QAAQ,QAAQ,IAAI,UAAU;AAAA,EAC/F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,eAAe,KAAyC;AACtD,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,QAAQ,OAAW,QAAO,KAAK,MAAM,eAAe,GAAG;AAG3D,QAAI,IAAI,YAAY,KAAM,QAAO,QAAQ,QAAQ,IAAI;AACrD,WAAO,QAAQ;AAAA,MACb,OAAO,IAAI,OAAO,GAAG,IAAI,eAAe,OAAO,KAAK,IAAI,IAAI,WAAW,EAAE;AAAA,IAC3E;AAAA,EACF;AAAA,EAEA,OAAO,KAAmC;AACxC,WAAO,KAAK,MAAM,OAAO,GAAG;AAAA,EAC9B;AAAA,EAEA,WAAW,KAAuB;AAChC,SAAK,MAAM,WAAW,GAAG;AAAA,EAC3B;AACF;;;AChHA,eAAsB,eAAe,KAAiB,MAAuC;AAC3F,QAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,MAAI,UAAU,QAAQ,cAAc;AACpC,mBAAiB,OAAO,KAAK,QAAQ,KAAK,GAAG,GAAG;AAC9C,QAAI,IAAI,aAAa,QAAS,WAAU,IAAI;AAAA,EAC9C;AACA,SAAO,UAAU;AACnB;AAoCA,eAAsB,oBACpB,KACA,MACA,UAA6B,CAAC,GACX;AACnB,QAAM,OAAO,QAAQ,QAAQ;AAG7B,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,GAAG;AACvC,UAAM,IAAI,gBAAgB,4CAA4C,OAAO,IAAI,CAAC,EAAE;AAAA,EACtF;AACA,QAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,MAAI,WAAW,KAAM,QAAO,CAAC;AAC7B,QAAM,UAAU,OAAO;AAIvB,QAAM,OAAO,oBAAI,IAAY;AAC7B,mBAAiB,OAAO,KAAK,QAAQ,KAAK,GAAG,EAAG,MAAK,IAAI,IAAI,UAAU;AACvE,QAAM,OAAO,CAAC,GAAG,IAAI;AAQrB,QAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,GAAG;AACzC,MAAI,UAAU;AACZ,UAAM,IAAI,mBAAmB,4BAA4B,IAAI,OAAO,sBAAsB;AAM5F,MAAI,OAAO,WAAW,eAAe,MAAM,UAAU,OAAO,OAAO;AACjE,UAAM,IAAI;AAAA,MACR,WAAW,IAAI,OAAO;AAAA,IACxB;AAAA,EACF;AAUA,QAAM,SACJ,YAAY,QAAQ,MAAM,eAAe,OAAO,OAAO,KAAK,IAAI,SAAS,MAAM,UAAU;AAa3F,QAAM,mBAAmB,YAA2B;AAClD,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,GAAG;AACzC,QAAI,UAAU,MAAM;AAClB,YAAM,IAAI,mBAAmB,4BAA4B,IAAI,OAAO,sBAAsB;AAAA,IAC5F;AACA,UAAM,KACJ,OAAO,WAAW,cACd,MAAM,UAAU,MAAM,QACtB,WAAW,QAAS,MAAM,eAAe,QAAQ,MAAM,cAAc;AAC3E,QAAI,CAAC,IAAI;AACP,YAAM,IAAI;AAAA,QACR,WAAW,IAAI,OAAO;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WACJ,OAAO,WAAW,cACd,KAAK,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,IACzB,WAAW;AAAA;AAAA;AAAA;AAAA,IAIT,CAAC;AAAA;AAAA;AAAA,IAED,KACG,OAAO,CAAC,MAAM,IAAI,MAAM,EACxB,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,EACpB,MAAM,IAAI;AAAA;AAYrB,aAAW,cAAc,UAAU;AACjC,UAAM,iBAAiB;AACvB,UAAM,KAAK,QAAQ,OAAO,EAAE,WAAW,IAAI,WAAW,SAAS,IAAI,SAAS,WAAW,CAAC;AAAA,EAC1F;AACA,SAAO;AACT;;;AC1CA,eAAe,mBACb,KACA,MACA,QACwB;AACxB,MAAI,WAAW,QAAQ,OAAO,eAAe,QAAQ,OAAO,WAAW,YAAa,QAAO;AAC3F,QAAM,aAAa,OAAO;AAC1B,MAAI;AACJ,QAAM,UAAU,OAAO;AACvB,MAAI,YAAY,UAAa,QAAQ,SAAS,GAAG;AAC/C,QAAI,KAAK,aAAa,QAAW;AAC/B,YAAM,IAAI;AAAA,QACR,YAAY,IAAI,OAAO;AAAA,MACzB;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK,SAAS,QAAQ,OAAO;AAChD,aAAS,EAAE,MAAM,QAAQ,CAAC,UAAU,OAAO,KAAK,YAAY,KAAK,EAAE;AAAA,EACrE;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,qBAAqB,KAAK,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,MAAM;AAAA,EAClF,SAAS,KAAK;AASZ,QAAI,CAAC,gBAAgB,GAAG,EAAG,OAAM;AACjC,WAAO;AAAA,EACT;AACA,MAAI,QAAQ;AACZ,aAAW,KAAK,OAAO,cAAc,EAAE,OAAO,EAAG,UAAS;AAC1D,SAAO;AACT;AASA,eAAsB,YACpB,KACA,KACA,MACA,UAAuB,CAAC,GACH;AACrB,kBAAgB,GAAG;AAEnB,eAAa,KAAK,OAAO,aAAa;AACtC,QAAM,OAAO,QAAQ,QAAQ;AAC7B,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,GAAG;AACvC,UAAM,IAAI,gBAAgB,4CAA4C,OAAO,IAAI,CAAC,EAAE;AAAA,EACtF;AACA,QAAM,QAAQ,QAAQ;AACtB,MAAI,OAAO,mBAAmB,QAAW;AACvC,UAAM,IAAI,MAAM;AAChB,QAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,GAAG;AACjC,YAAM,IAAI;AAAA,QACR,4DAA4D,OAAO,CAAC,CAAC;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,gBAAgB,QAAW;AACpC,UAAM,IAAI,MAAM;AAChB,QAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,KAAK,IAAI,GAAG;AACzC,YAAM,IAAI,gBAAgB,qDAAqD,OAAO,CAAC,CAAC,EAAE;AAAA,IAC5F;AAAA,EACF;AACA,QAAM,QAAQ,UAAU,QAAQ,SAAS,UAAU;AAInD,QAAM,MAAM,MAAM,KAAK,SAAS,IAAI,GAAG;AACvC,QAAM,YAA+B,KAAK;AAC1C,QAAM,iBAAiB,KAAK,cAAc;AAK1C,QAAM,cAAc,OAAO,gBAAgB,UAAa,QAAQ,eAAe;AAC/E,QAAM,SAAS,cAAc,MAAM,mBAAmB,KAAK,MAAM,GAAG,IAAI;AAExE,QAAM,aAAa,MAAM,eAAe,KAAK,IAAI;AACjD,QAAM,MAAM,EAAE,WAAW,IAAI,WAAW,SAAS,IAAI,SAAS,WAAW;AAMzE,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,uBAAuB,KAAK,SAAS,KAAK,KAAK;AAAA,MAC7D,UAAU,KAAK;AAAA,MACf,SAAS;AAAA,MACT,UAAU,KAAK;AAAA,MACf,mBAAmB,KAAK;AAAA,MACxB,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,IACd,CAAC;AAAA,EACH,SAAS,KAAK;AAMZ,QAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AACtC,UAAM,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,MACb,WAAW,IAAI;AAAA,MACf;AAAA,MACA,QAAQ;AAAA,MACR,aAAa;AAAA,IACf,CAAC;AACD,WAAO;AAAA,MACL;AAAA,MACA,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,mBAAmB;AAAA,MACnB,WAAW,CAAC;AAAA,IACd;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,WAA6C;AAUjE,UAAM,MAAM,MAAM,KAAK,SAAS,IAAI,GAAG;AACvC,QAAI,QAAQ,QAAQ,IAAI,UAAU,UAAW,OAAM,KAAK,QAAQ,OAAO,GAAG;AAC1E,UAAM,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,MACb,WAAW,IAAI;AAAA,MACf;AAAA,MACA;AAAA,MACA,aAAa,QAAQ;AAAA,IACvB,CAAC;AAID,WAAO;AAAA,MACL;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA,MAAM,QAAQ;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB,YAAY,QAAQ;AAAA,MACpB,aAAa,QAAQ;AAAA,MACrB,mBAAmB;AAAA,MACnB,WAAW,CAAC;AAAA,IACd;AAAA,EACF;AAEA,MAAI,QAAQ,gBAAgB,KAAK,QAAQ,eAAe,SAAS,UAAU,KAAK,GAAG;AACjF,WAAO,OAAO,OAAO;AAAA,EACvB;AACA,MAAI,OAAO,mBAAmB,UAAa,QAAQ,cAAc,MAAM,gBAAgB;AACrF,WAAO,OAAO,iBAAiB;AAAA,EACjC;AACA,MAAI,OAAO,gBAAgB,UAAa,WAAW,MAAM;AACvD,QAAI,QAAQ,cAAc,SAAS,MAAM,YAAa,QAAO,OAAO,cAAc;AAAA,EACpF;AAcA,QAAM,YAAY,MAAM,kBAAkB,KAAK,UAAU,KAAK;AAAA,IAC5D,aAAa,QAAQ;AAAA,IACrB,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,aAAa,UAAU;AAAA,IAC5D,GAAI,eAAe,mBAAmB,SAAY,EAAE,YAAY,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQpF,GAAI,eAAe,QAAQ,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAAA,EAC9D,CAAC;AACD,MAAI,CAAC,UAAW,QAAO,OAAO,YAAY;AAE1C,QAAM,QAAQ;AAAA,IACZ,MAAM;AAAA,IACN,SAAS,IAAI;AAAA,IACb,WAAW,IAAI;AAAA,IACf;AAAA,EACF,CAAC;AAED,QAAM,YAAY,MAAM,oBAAoB,KAAK,MAAM,EAAE,KAAK,CAAC;AAC/D,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,IACX,MAAM,QAAQ;AAAA,IACd,QAAQ,QAAQ;AAAA,IAChB,YAAY,QAAQ;AAAA,IACpB,aAAa,QAAQ;AAAA,IACrB,mBAAmB;AAAA,IACnB;AAAA,EACF;AACF;;;ACjUA,eAAsB,gBACpB,KACA,MAC4B;AAC5B,kBAAgB,GAAG;AACnB,QAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,QAAM,UAAU,QAAQ,cAAc;AACtC,QAAM,OAAO,oBAAI,IAAY;AAC7B,mBAAiB,OAAO,KAAK,QAAQ,KAAK,GAAG,EAAG,MAAK,IAAI,IAAI,UAAU;AACvE,SAAO,CAAC,GAAG,IAAI,EACZ,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,EACpB,IAAI,CAAC,gBAAgB,EAAE,YAAY,SAAS,eAAe,QAAQ,EAAE;AAC1E;AA+BA,eAAsB,gBACpB,KACA,cACA,MACA,UAA0D,CAAC,GAClC;AACzB,kBAAgB,GAAG;AACnB,MAAI,CAAC,OAAO,UAAU,YAAY,KAAK,eAAe,GAAG;AACvD,UAAM,IAAI;AAAA,MACR,4DAA4D,OAAO,YAAY,CAAC;AAAA,IAClF;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,MAAI,WAAW,MAAM;AACnB,UAAM,IAAI,cAAc,0CAA0C,IAAI,OAAO,GAAG;AAAA,EAClF;AACA,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI;AAAA,MACR,sBAAsB,IAAI,OAAO;AAAA,IACnC;AAAA,EACF;AACA,MAAI,OAAO,eAAe,cAAc;AACtC,WAAO,EAAE,gBAAgB,OAAO,YAAY,YAAY,aAAa;AAAA,EACvE;AAIA,QAAM,UAAU,oBAAI,IAAY;AAChC,mBAAiB,OAAO,KAAK,QAAQ,KAAK,GAAG,EAAG,SAAQ,IAAI,IAAI,UAAU;AAC1E,MAAI,CAAC,QAAQ,IAAI,YAAY,GAAG;AAC9B,UAAM,YAAY,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACnD,UAAM,IAAI;AAAA,MACR,wBAAwB,YAAY,QAAQ,IAAI,OAAO,4BACpD,UAAU,WAAW,IAClB,6BACA,oBAAe,UAAU,KAAK,IAAI,CAAC;AAAA,IAC3C;AAAA,EACF;AACA,MACE,OAAO,eAAe,QACtB,eAAe,OAAO,cACtB,QAAQ,iBAAiB,MACzB;AAMA,UAAM,IAAI;AAAA,MACR,wBAAwB,YAAY,QAAQ,IAAI,OAAO,mCACjD,OAAO,UAAU;AAAA,IAEzB;AAAA,EACF;AAKA,QAAM,EAAE,MAAM,IAAI,MAAM,KAAK,SAAS,eAAe,KAAK,OAAO,OAAO;AAAA,IACtE,YAAY;AAAA,EACd,CAAC;AAWD,MAAI,aAAa;AACjB,mBAAiB,OAAO,KAAK,QAAQ,KAAK,GAAG,GAAG;AAC9C,QAAI,IAAI,eAAe,cAAc;AACnC,mBAAa;AACb;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,YAAY;AACf,QAAI,SAAS;AACb,QAAI;AACF,YAAM,KAAK,SAAS,eAAe,KAAK,OAAO,EAAE,YAAY,OAAO,WAAW,CAAC;AAChF,eAAS;AAAA,IACX,QAAQ;AAAA,IAGR;AACA,UAAM,IAAI;AAAA,MACR,wBAAwB,YAAY,QAAQ,IAAI,OAAO,kDACpD,SACG,qCACA,6DAA6D,YAAY;AAAA,IAGjF;AAAA,EACF;AAEA,YAAU,QAAQ,SAAS,UAAU,EAAE,QAAQ;AAAA,IAC7C,MAAM;AAAA,IACN,SAAS,IAAI;AAAA,IACb,WAAW,IAAI;AAAA,IACf,gBAAgB,OAAO;AAAA,IACvB,YAAY;AAAA,EACd,CAAC;AAED,SAAO,EAAE,gBAAgB,OAAO,YAAY,YAAY,aAAa;AACvE;;;ACxGA,IAAMC,4BAA2B,KAAK;AAwGtC,eAAsB,mBACpB,KACA,IACA,MACA,UAAkC,CAAC,GACX;AACxB,kBAAgB,GAAG;AACnB,QAAM,EAAE,UAAU,UAAU,IAAI,QAAQ,EAAE;AAC1C,QAAM,QAAQ,aAAa,KAAK,OAAO,oBAAoB;AAC3D,QAAM,WAAW,KAAK,kBAAkBA;AACxC,QAAM,OAAO,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AAE9D,QAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,MAAI,WAAW,KAAM,QAAO,EAAE,GAAG,MAAM,QAAQ,OAAO,QAAQ,UAAU,WAAW,CAAC,EAAE;AACtF,MAAI,OAAO,WAAW;AACpB,WAAO,EAAE,GAAG,MAAM,QAAQ,OAAO,QAAQ,aAAa,WAAW,CAAC,EAAE;AACtE,MAAI,OAAO,eAAe;AACxB,WAAO,EAAE,GAAG,MAAM,QAAQ,OAAO,QAAQ,iBAAiB,WAAW,CAAC,EAAE;AAC1E,QAAM,OAAO,OAAO;AAOpB,QAAM,YAAY,OAAO;AAIzB,MAAI;AACJ,MAAI,OAAO,gBAAgB,UAAa,OAAO,YAAY,SAAS,GAAG;AACrE,QAAI,KAAK,aAAa,QAAW;AAC/B,YAAM,IAAI;AAAA,QACR,YAAY,IAAI,OAAO;AAAA,MACzB;AAAA,IACF;AACA,WAAO,MAAM,KAAK,SAAS,QAAQ,OAAO,WAAW;AAAA,EACvD,WAAW,KAAK,sBAAsB,MAAM;AAC1C,UAAM,IAAI,gBAAgB,+BAA+B,IAAI,OAAO,gBAAgB;AAAA,EACtF;AAGA,QAAM,WAAW,CAACC,gBAChB,SAAS,SAAY,SAAY,EAAE,MAAM,QAAQ,CAAC,UAAU,OAAO,KAAKA,aAAY,KAAK,EAAE;AAE7F,QAAM,UAAkB,EAAE,GAAG,MAAM,YAAY,KAAK;AACpD,QAAM,UAAU,CACd,QACAA,iBACmB;AAAA,IACnB,GAAG;AAAA,IACH,QAAQ;AAAA,IACR;AAAA,IACA,gBAAgB;AAAA,IAChB,YAAAA;AAAA,IACA,WAAW,CAAC;AAAA,EACd;AAaA,QAAM,aAAa,CACjB,QACmE;AACnE,QAAI,QAAQ,KAAM,QAAO;AACzB,QAAI,IAAI,WAAW,YAAa,QAAO;AACvC,QAAI,IAAI,eAAe,KAAM,QAAO;AAEpC,WAAO,IAAI,eAAe,QAAQ,IAAI,UAAU,YAAY,OAAO;AAAA,EACrE;AAOA,QAAM,QAAQ,OAAOA,gBAAgD;AACnE,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB,KAAK;AAAA,QACL,EAAE,GAAG,MAAM,YAAAA,YAAW;AAAA,QACtB,SAASA,WAAU;AAAA,MACrB;AACA,YAAM,QAAQ,MAAM,OAAO,SAAS,QAAQ;AAC5C,aAAO,UAAU,QAAQ,MAAM,gBAAgB,OAAO,QAAQ,EAAE,IAAI,SAAS;AAAA,IAC/E,SAAS,KAAK;AACZ,UAAI,gBAAgB,GAAG,EAAG,QAAO;AACjC,YAAM;AAAA,IACR;AAAA,EACF;AAqBA,QAAM,aAAa,OAAO,UAA4D;AACpF,UAAM,UAAU,oBAAI,IAAY;AAChC,qBAAiBC,QAAO,KAAK,QAAQ,KAAK,GAAG,EAAG,SAAQ,IAAIA,KAAI,UAAU;AAC1E,eAAWD,eAAc,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG;AAC3D,UAAI,CAAC,MAAM,IAAIA,WAAU,KAAM,MAAM,MAAMA,WAAU,MAAO,KAAM,QAAOA;AAAA,IAC3E;AACA,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,CAACA,gBACpB,IAAI;AAAA,IACF,sBAAsB,IAAI,OAAO,gCAAgCA,WAAU;AAAA,EAC7E;AAiDF,QAAM,eAAe,YAAoC;AACvD,UAAM,YAA2B;AAAA,MAC/B,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,WAAW,CAAC;AAAA,IACd;AACA,UAAM,SAAS,oBAAI,IAAY;AAC/B,qBAAiBC,QAAO,KAAK,QAAQ,KAAK,GAAG,GAAG;AAC9C,UAAIA,KAAI,eAAe,KAAM,QAAO,IAAIA,KAAI,UAAU;AAAA,IACxD;AACA,QAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,UAAM,cAAc,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAEpD,UAAM,QAAQ,oBAAI,IAAY,CAAC,IAAI,CAAC;AACpC,UAAM,eAAyB,CAAC;AAChC,eAAWD,eAAc,YAAY,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG;AAC5D,YAAM,OAAO,MAAM,MAAMA,WAAU;AACnC,UAAI,SAAS,KAAM,cAAa,KAAKA,WAAU;AAAA,eACtC,SAAS,MAAO,OAAM,IAAIA,WAAU;AAAA,IAC/C;AACA,QAAI;AACJ,QAAI,aAAa,WAAW,GAAG;AAC7B,iBAAWA,eAAc,YAAY,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG;AAC5D,cAAM,OAAO,MAAM,MAAMA,WAAU;AACnC,YAAI,SAAS,MAAM;AACjB,wBAAcA;AACd;AAAA,QACF;AACA,YAAI,SAAS,MAAO,OAAM,IAAIA,WAAU;AAAA,MAC1C;AAAA,IACF;AACA,UAAM,SAAS,aAAa,CAAC,KAAK;AAClC,QAAI,WAAW,OAAW,QAAO;AAEjC,UAAME,aAAY,CAAC,GAAI,MAAM,oBAAoB,KAAK,MAAM,EAAE,MAAM,EAAE,CAAC,CAAE;AACzE,QAAI,QAAuC;AAC3C,eAAWF,eAAc,cAAc;AACrC,cAAQ,WAAW,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC;AAC/C,UAAI,UAAU,KAAM;AACpB,YAAM,KAAK,QAAQ,OAAO,EAAE,GAAG,MAAM,YAAAA,YAAW,CAAC;AACjD,MAAAE,WAAU,KAAKF,WAAU;AAAA,IAC3B;AAEA,UAAMG,QAAO,MAAM,WAAW,KAAK;AACnC,QAAIA,UAAS,OAAW,QAAO,EAAE,GAAG,MAAM,QAAQ,MAAM,gBAAgB,QAAQ,WAAAD,WAAU;AAC1F,QAAI,UAAU,MAAM;AAClB,aAAO,EAAE,GAAG,MAAM,QAAQ,OAAO,QAAQ,OAAO,gBAAgB,QAAQ,WAAAA,WAAU;AAAA,IACpF;AACA,UAAM,aAAaC,KAAI;AAAA,EACzB;AAiCA,QAAM,QAAQ,YAA0E;AAEtF,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,qBAAqB,KAAK,SAAS,SAAS,SAAS,IAAI,CAAC;AAC/E,YAAM,QAAQ,MAAM,OAAO,SAAS,QAAQ;AAG5C,UAAI,UAAU,KAAM,QAAO,MAAM,aAAa;AAC9C,YAAM,SAAS,MAAM,gBAAgB,OAAO,QAAQ;AACpD,8BAAwB,QAAQ,QAAQ;AACxC,UAAI,CAAC,OAAO,IAAI,SAAS,EAAG,QAAO,MAAM,aAAa;AACtD,aAAO,OAAO,SAAS;AAEvB,YAAMH,cAAa,MAAM,eAAe,KAAK,IAAI;AACjD,YAAMC,OAAc,EAAE,GAAG,MAAM,YAAAD,YAAW;AAC1C,YAAM,QAAQ,MAAM;AAAA,QAClB,KAAK;AAAA,QACLC;AAAA,QACA,QAAQ,QAAQ,UAAU,QAAQ,OAAO,QAAQ;AAAA,QACjD,EAAE,QAAQ,SAASD,WAAU,GAAG,OAAO,KAAK,MAAM;AAAA,MACpD;AACA,gBAAUA;AAGV,YAAM,eAAe,MAAM,KAAK,SAAS,IAAI,GAAG;AAChD,YAAM,QAAQ,WAAW,YAAY;AACrC,UAAI,UAAU,KAAM,QAAO,QAAQ,OAAO,OAAO;AACjD,YAAM,iBAAiB,KAAK,SAASC,MAAK,OAAO,SAASD,WAAU,CAAC;AACrE,aAAO,EAAE,YAAAA,aAAY,KAAAC,KAAI;AAAA,IAC3B,SAAS,KAAK;AACZ,UAAI,CAAC,gBAAgB,GAAG,EAAG,OAAM;AAIjC,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,KAAK,SAAS,IAAI,GAAG;AAAA,MACnC,QAAQ;AACN,cAAM;AAAA,MACR;AACA,YAAM,UAAU,WAAW,GAAG;AAC9B,UAAI,YAAY,KAAM,QAAO,QAAQ,SAAS,OAAO;AACrD,YAAM;AAAA,IACR;AAAA,EACF;AAoBA,QAAM,iBAAiB,OAAO,YAAmC;AAC/D,UAAM,MAAM,MAAM,KAAK,SAAS,IAAI,GAAG;AACvC,QAAI,QAAQ,QAAQ,IAAI,WAAW,YAAY,IAAI,eAAe,KAAM;AACxE,QAAI,IAAI,cAAc,QAAQ,IAAI,cAAc,QAAS;AACzD,UAAM,KAAK,QAAQ,OAAO,EAAE,GAAG,MAAM,YAAY,QAAQ,CAAC;AAAA,EAC5D;AAEA,QAAM,SAAS,MAAM,MAAM;AAC3B,MAAI,YAAY,QAAQ;AACtB,QAAI,CAAC,OAAO,UAAU,OAAO,eAAe,OAAW,OAAM,eAAe,OAAO,UAAU;AAC7F,WAAO;AAAA,EACT;AACA,QAAM,EAAE,YAAY,IAAI,IAAI;AAe5B,QAAM,YAAY,MAAM,kBAAkB,KAAK,UAAU,KAAK;AAAA,IAC5D,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AACD,MAAI,CAAC,WAAW;AACd,UAAM,eAAe,UAAU;AAC/B,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB;AAAA,MACA,WAAW,CAAC;AAAA,IACd;AAAA,EACF;AACA,YAAU,QAAQ,SAAS,UAAU,EAAE,QAAQ;AAAA,IAC7C,MAAM;AAAA,IACN,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,IACb,gBAAgB;AAAA,IAChB;AAAA,EACF,CAAC;AASD,QAAM,YAAY,MAAM,oBAAoB,KAAK,MAAM,EAAE,MAAM,EAAE,CAAC;AAClE,QAAM,OAAO,MAAM,WAAW,oBAAI,IAAI,CAAC,UAAU,CAAC,CAAC;AACnD,MAAI,SAAS,OAAW,OAAM,aAAa,IAAI;AAC/C,SAAO,EAAE,GAAG,MAAM,QAAQ,MAAM,gBAAgB,MAAM,YAAY,UAAU;AAC9E;AAkBA,gBAAgB,QACd,QACA,UACA,aACA,OACA,UAC2D;AAC3D,QAAM,OAAO,CAAC,GAAG,OAAO,UAAU,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACzD,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,UAAU;AAClB,YAAM,EAAE,UAAU,GAAG,QAAQ,YAAY;AACzC;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,OAAO,SAAS,CAAC;AACrC,QAAI,UAAU,KAAM;AACpB,UAAM,SAAS,MAAM,gBAAgB,OAAO,QAAQ;AACpD,4BAAwB,QAAQ,CAAC;AACjC,UAAM,EAAE,UAAU,GAAG,OAAO;AAAA,EAC9B;AACF;AAOA,SAAS,wBAAwB,QAAqB,UAAwB;AAC5E,QAAM,MAAM,OAAO,UAAU;AAC7B,MAAI,QAAQ,UAAa,MAAM,eAAe;AAC5C,UAAM,IAAI;AAAA,MACR,SAAS,QAAQ,wBAAwB,GAAG,4CAA4C,aAAa;AAAA,IAEvG;AAAA,EACF;AACF;;;ACpnBA,SAAS,gBAAgB,kBAAkB,mBAAmB;AAI9D,IAAM,YAAY;AAClB,IAAM,cAAc;AACpB,IAAM,YAAY;AAOX,IAAM,WAAN,MAA+B;AAAA,EACnB;AAAA,EAEjB,YAAY,KAAiB;AAC3B,QAAI,IAAI,WAAW;AACjB,YAAM,IAAI,gBAAgB,oBAAoB,SAAS,eAAe,IAAI,MAAM,EAAE;AACpF,SAAK,MAAM,OAAO,KAAK,GAAG;AAAA,EAC5B;AAAA,EAEA,KAAK,WAAuB,KAA6B;AACvD,UAAM,QAAQ,YAAY,WAAW;AACrC,UAAM,SAAS,eAAe,eAAe,KAAK,KAAK,KAAK;AAC5D,WAAO,OAAO,GAAG;AACjB,UAAM,aAAa,OAAO,OAAO,CAAC,OAAO,OAAO,SAAS,GAAG,OAAO,MAAM,CAAC,CAAC;AAC3E,WAAO,EAAE,OAAO,YAAY,KAAK,OAAO,WAAW,EAAE;AAAA,EACvD;AAAA,EAEA,KAAK,QAAoB,KAA6B;AACpD,QAAI,OAAO,MAAM,WAAW,eAAe,OAAO,IAAI,WAAW;AAC/D,YAAM,IAAI,eAAe,sCAAsC;AACjE,UAAM,WAAW,iBAAiB,eAAe,KAAK,KAAK,OAAO,KAAK;AACvE,aAAS,OAAO,GAAG;AACnB,aAAS,WAAW,OAAO,KAAK,OAAO,GAAG,CAAC;AAC3C,QAAI;AACF,aAAO,OAAO,OAAO,CAAC,SAAS,OAAO,OAAO,UAAU,GAAG,SAAS,MAAM,CAAC,CAAC;AAAA,IAC7E,QAAQ;AAEN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAIA,SAAS,QAAQ,KAAiB,KAAiB,OAAuB;AACxE,QAAM,SAAS,IAAI,SAAS,GAAG,EAAE,KAAK,KAAK,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAC1E,SAAO,OAAO,OAAO,CAAC,OAAO,OAAO,OAAO,YAAY,OAAO,GAAG,CAAC,EAAE,SAAS,QAAQ;AACvF;AAEA,SAAS,UAAU,SAAiB,KAAiB,OAA2B;AAC9E,QAAM,OAAO,OAAO,KAAK,SAAS,QAAQ;AAC1C,MAAI,KAAK,SAAS,cAAc;AAC9B,UAAM,IAAI,eAAe,sCAAsC;AACjE,QAAM,QAAQ,KAAK,SAAS,GAAG,WAAW;AAC1C,QAAM,MAAM,KAAK,SAAS,KAAK,SAAS,SAAS;AACjD,QAAM,aAAa,KAAK,SAAS,aAAa,KAAK,SAAS,SAAS;AACrE,SAAO,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,OAAO,YAAY,IAAI,GAAG,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAC3F;AAkBO,IAAM,oBAAN,MAA6C;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAAmC;AAC7C,UAAM,UAAU,OAAO,QAAQ,QAAQ,IAAI;AAC3C,QAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,gBAAgB,iCAAiC;AACrF,eAAW,CAAC,IAAI,CAAC,KAAK,SAAS;AAC7B,UAAI,EAAE,WAAW;AACf,cAAM,IAAI,gBAAgB,QAAQ,EAAE,aAAa,SAAS,eAAe,EAAE,MAAM,EAAE;AAAA,IACvF;AACA,QAAI,EAAE,QAAQ,eAAe,QAAQ;AACnC,YAAM,IAAI,gBAAgB,gBAAgB,QAAQ,WAAW,kBAAkB;AACjF,QAAI,QAAQ,kBAAkB,UAAa,EAAE,QAAQ,iBAAiB,QAAQ;AAC5E,YAAM,IAAI,gBAAgB,kBAAkB,QAAQ,aAAa,kBAAkB;AACrF,SAAK,OAAO,IAAI,IAAI,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClE,SAAK,cAAc,QAAQ;AAC3B,SAAK,gBACH,QAAQ,kBAAkB,QAAQ,cAAc,SAAY,QAAQ;AAAA,EACxE;AAAA;AAAA;AAAA,EAIA,MAAM,YAA4D;AAChE,UAAM,MAAM,YAAY,SAAS;AACjC,UAAM,MAAM,KAAK,gBAAgB,CAAC,KAAK,aAAa,KAAK,aAAa,IAAI,CAAC,KAAK,WAAW;AAC3F,UAAM,UAAU,IAAI,IAAI,CAAC,WAAW;AAAA,MAClC;AAAA,MACA,SAAS,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,GAAI,KAAK;AAAA,IACpD,EAAE;AACF,WAAO,EAAE,SAAS,MAAM,IAAI,SAAS,GAAG,EAAE;AAAA,EAC5C;AAAA;AAAA,EAGA,MAAM,QAAQ,SAA+C;AAM3D,QAAI,aAAa;AACjB,QAAI;AACJ,eAAW,KAAK,SAAS;AACvB,YAAM,MAAM,KAAK,KAAK,IAAI,EAAE,KAAK;AACjC,UAAI,QAAQ,OAAW;AACvB,mBAAa;AACb,UAAI;AACF,eAAO,IAAI,SAAS,UAAU,EAAE,SAAS,KAAK,EAAE,KAAK,CAAC;AAAA,MACxD,SAAS,KAAK;AACZ,0BAAkB;AAAA,MACpB;AAAA,IACF;AAGA,QAAI,WAAY,OAAM;AACtB,UAAM,OAAO,CAAC,GAAG,KAAK,KAAK,KAAK,CAAC,EAAE,KAAK,IAAI,KAAK;AACjD,UAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,KAAK;AACvD,UAAM,IAAI;AAAA,MACR,gDAA2C,IAAI,2BAA2B,IAAI;AAAA,IAEhF;AAAA,EACF;AACF;;;ACxGA,IAAM,mBAAmB;AAMzB,IAAM,oBAAoB;AAU1B,IAAM,qBAAqB;AAQ3B,eAAsB,eACpB,KACA,MACA,SACwB;AACxB,kBAAgB,GAAG;AACnB,MAAI,QAAQ,mBAAmB,IAAI,SAAS;AAC1C,UAAM,IAAI;AAAA,MACR,+DAA+D,IAAI,OAAO;AAAA,IAC5E;AAAA,EACF;AACA,QAAM,SAAS,MAAM,aAAa,KAAK,MAAM,QAAQ,kBAAkB,KAAK;AAG5E,MAAI,OAAO,gBAAgB;AACzB,cAAU,QAAQ,SAAS,UAAU,EAAE,QAAQ;AAAA,MAC7C,MAAM;AAAA,MACN,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,IACf,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAYA,eAAsB,eACpB,WACA,MACA,SAMyC;AACzC,MAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG;AAC3D,UAAM,IAAI,gBAAgB,sDAAsD;AAAA,EAClF;AACA,wBAAsB,SAAS;AAC/B,MAAI,QAAQ,qBAAqB,WAAW;AAC1C,UAAM,IAAI;AAAA,MACR,8DAA8D,SAAS;AAAA,IACzE;AAAA,EACF;AAGA,QAAM,OAAO,MAAM,cAAc,KAAK,UAAU;AAAA,IAC9C;AAAA,IACA,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,IAAI;AAAA,IACJ,YAAY;AAAA;AAAA,EACd,CAAC;AACD,QAAM,OAAqB,KAAK,IAAI,CAAC,SAAS;AAAA,IAC5C,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,EACf,EAAE;AACF,QAAM,QAAQ,UAAU,QAAQ,SAAS,UAAU;AACnD,QAAM,YAA6B,CAAC;AACpC,MAAI,mBAAmB;AACvB,aAAW,OAAO,MAAM;AActB,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,aAAa,KAAK,MAAM,QAAQ,kBAAkB,KAAK;AAAA,IACxE,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,SAAS,IAAI;AAAA,QACb,WAAW,IAAI;AAAA,QACf,WAAW;AAAA,QACX,gBAAgB;AAAA,QAChB,QAAQ,qBAAqB,GAAG,IAC5B,cACA,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACjE;AAAA,IACF;AACA,cAAU,KAAK,MAAM;AAGrB,QAAI,OAAO,gBAAgB;AACzB,0BAAoB;AACpB,YAAM,QAAQ,EAAE,MAAM,iBAAiB,WAAW,IAAI,WAAW,SAAS,IAAI,QAAQ,CAAC;AAAA,IACzF;AAAA,EACF;AAIA,QAAM,QAAQ,EAAE,MAAM,mBAAmB,WAAW,iBAAiB,CAAC;AACtE,SAAO,EAAE,UAAU;AACrB;AAiHA,eAAsB,YACpB,KACA,MACA,SACqB;AACrB,kBAAgB,GAAG;AACnB,MAAI,QAAQ,mBAAmB,IAAI,SAAS;AAC1C,UAAM,IAAI;AAAA,MACR,4DAA4D,IAAI,OAAO;AAAA,IACzE;AAAA,EACF;AACA,QAAM,OAAO,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AAE9D,MAAI,QAAQ,WAAW,MAAM;AAC3B,UAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,UAAM,cAAc,MAAMG,iBAAgB,KAAK,SAAS,GAAG;AAC3D,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS;AAAA,MACT,oBAAoB,CAAC;AAAA,MACrB,sBAAsB,CAAC;AAAA,MACvB;AAAA,MACA,kBAAkB,QAAQ,gBAAgB,UAAa,OAAO,YAAY,SAAS;AAAA,MACnF,gBAAgB;AAAA,MAChB,QACE,WAAW,OACP,YAAY,WAAW,IACrB,WACA,SACF,OAAO,WAAW,cAChB,YACA;AAAA,IACV;AAAA,EACF;AAIA,MAAI,QAAQ,MAAM,aAAa,KAAK,MAAM,MAAM,aAAa;AAiB7D,MAAI,MAAM,WAAW,UAAU;AAC7B,UAAM,UAAU,MAAMA,iBAAgB,KAAK,SAAS,GAAG;AACvD,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,oBAAoB,CAAC;AAAA,QACrB,sBAAsB,CAAC;AAAA,QACvB,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,IACF;AACA,QAAI;AACF,YAAM,KAAK,SAAS,OAAO,KAAK;AAAA;AAAA;AAAA,QAG9B,YAAY,QAAQ,QAAQ,SAAS,CAAC;AAAA,QACtC,QAAQ;AAAA,MACV,CAAC;AACD,cAAQ,EAAE,GAAG,OAAO,WAAW,MAAM,QAAQ,OAAU;AAAA,IACzD,SAAS,KAAK;AACZ,UAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAGtC,cAAQ,MAAM,aAAa,KAAK,MAAM,MAAM,aAAa;AAAA,IAC3D;AAAA,EACF;AAqBA,MAAI,MAAM,gBAAgB;AACxB,cAAU,QAAQ,SAAS,UAAU,EAAE,QAAQ;AAAA,MAC7C,MAAM;AAAA,MACN,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,IACf,CAAC;AAAA,EACH;AAMA,QAAM,qBAA+B,CAAC;AACtC,WAAS,OAAO,GAAG,OAAO,oBAAoB,QAAQ;AACpD,UAAM,UAAU,MAAMA,iBAAgB,KAAK,SAAS,GAAG;AACvD,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,WAAW,MAAM,mBAAmB,SAAS,mBAAmB,OAAO,eAAe;AAC1F,UAAI;AACF,cAAM,KAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,WAAW,CAAC;AAChD,eAAO;AAAA,MACT,QAAQ;AAGN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AACD,QAAI,kBAAkB;AACtB,eAAW,KAAK,UAAU;AACxB,UAAI,MAAM,MAAM;AACd,2BAAmB,KAAK,CAAC;AACzB,2BAAmB;AAAA,MACrB;AAAA,IACF;AACA,QAAI,oBAAoB,EAAG;AAAA,EAC7B;AAKA,QAAM,uBAAuB,MAAMA,iBAAgB,KAAK,SAAS,GAAG;AAKpE,MAAI,MAAM,WAAW;AACnB,cAAU,QAAQ,SAAS,UAAU,EAAE,QAAQ;AAAA,MAC7C,MAAM;AAAA,MACN,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,oBAAoB,mBAAmB;AAAA,IACzC,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,MAAM;AAAA,IACf;AAAA,IACA;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,QAAQ,MAAM;AAAA,EAChB;AACF;AAeA,eAAeA,iBAAgB,SAAyB,KAAoC;AAC1F,QAAM,cAAwB,CAAC;AAC/B,mBAAiB,OAAO,QAAQ,KAAK,GAAG,EAAG,aAAY,KAAK,IAAI,UAAU;AAC1E,SAAO,YAAY,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACzC;AAQA,eAAe,aACb,KACA,MACA,gBACA,KAAuC,kBACf;AACxB,QAAM,OAAO,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AAC9D,WAAS,UAAU,GAAG,UAAU,kBAAkB,WAAW;AAC3D,UAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,QAAI,WAAW,MAAM;AAEnB,aAAO,EAAE,GAAG,MAAM,WAAW,OAAO,gBAAgB,OAAO,QAAQ,SAAS;AAAA,IAC9E;AACA,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,EAAE,GAAG,MAAM,WAAW,MAAM,gBAAgB,OAAO,QAAQ,UAAU;AAAA,IAC9E;AACA,UAAM,YAAY,OAAO,gBAAgB,UAAa,OAAO,YAAY,SAAS;AAClF,QAAI,CAAC,aAAa,CAAC,gBAAgB;AACjC,aAAO,EAAE,GAAG,MAAM,WAAW,OAAO,gBAAgB,OAAO,QAAQ,YAAY;AAAA,IACjF;AACA,QAAI;AACF,YAAM,KAAK,SAAS,eAAe,KAAK,OAAO,OAAO;AAAA,QACpD,QAAQ;AAAA,QACR,aAAa;AAAA;AAAA,QACb,OAAO;AAAA,MACT,CAAC;AAGD,aAAO,EAAE,GAAG,MAAM,WAAW,MAAM,gBAAgB,UAAU;AAAA,IAC/D,SAAS,KAAK;AACZ,UAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAAA,IAExC;AAAA,EACF;AACA,QAAM,IAAI,mBAAmB,GAAG,EAAE,2BAA2B,IAAI,OAAO,gBAAW;AACrF;;;ACneA,IAAM,aAAa;AAGnB,IAAM,yBAAyB;AAWxB,IAAM,oBAAoB;AAwCjC,SAAS,wBAAwB,QAA+B;AAC9D,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,OAAO,cAAc,YAAY,CAAC,OAAO,UAAU,SAAS,GAAG;AACjE,UAAM,IAAI;AAAA,MACR,wDAAwD,OAAO,SAAS,CAAC;AAAA,IAC3E;AAAA,EACF;AACA,MAAI,YAAY,mBAAmB;AACjC,UAAM,IAAI;AAAA,MACR,wBAAwB,SAAS,eAAe,iBAAiB;AAAA,IAGnE;AAAA,EACF;AACF;AAWO,SAAS,oBACd,MACoC;AACpC,MAAI,SAAS,OAAW,QAAO;AAO/B,MAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,EAAG,QAAO;AAC7E,MAAI,EAAE,cAAc,MAAO,QAAO;AAClC,QAAM,MAAM,KAAK,UAAU;AAC3B,MAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM;AAC7D,WAAO;AACT,SAAO,EAAE,WAAW,IAAI;AAC1B;AAGA,eAAsB,oBACpB,KACA,MAC6C;AAC7C,kBAAgB,GAAG;AACnB,QAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAa,QAAO;AAC7D,SAAO,oBAAoB,OAAO,SAAS;AAC7C;AAYA,SAAS,cAAc,QAAmE;AACxF,QAAM,SAAS,WAAW,OAAO,OAAO,oBAAoB,OAAO,SAAS;AAC5E,SAAO,WAAW,QAAQ,WAAW,YAAY,SAAY,OAAO;AACtE;AAeA,eAAe,QACb,KACA,MACA,WACA,mBACkB;AAKlB,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO;AAC3B,QAAM,aAAa,cAAc,SAAY,SAAY,UAAU,SAAS;AAC5E,QAAM,YAAY,sBAAsB,SAAY,SAAY,UAAU,iBAAiB;AAC3F,MAAI,UAAU,cAAc;AAC5B,MAAI,eAAe,QAAW;AAC5B,QAAI;AACF,YAAM,KAAK,SAAS,OAAO,YAAY,YAAY,GAAG,GAAG,EAAE,YAAY,KAAK,CAAC;AAC7E,gBAAU;AAAA,IACZ,SAAS,KAAK;AAEZ,UAAI,qBAAqB,GAAG,EAAG,WAAU;AAAA,IAC3C;AAAA,EACF;AACA,MAAI,cAAc,UAAa,cAAc,YAAY;AACvD,QAAI;AACF,YAAM,KAAK,SAAS,OAAO,YAAY,WAAW,GAAG,CAAC;AAAA,IACxD,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,oBACpB,KACA,MACA,QAC6B;AAC7B,kBAAgB,GAAG;AACnB,0BAAwB,MAAM;AAC9B,QAAM,OAAO,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AAE9D,MAAI;AACJ,WAAS,UAAU,GAAG,UAAU,wBAAwB,WAAW,GAAG;AACpE,UAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,QAAI;AACF,0BAAoB,cAAc,MAAM;AACxC,UAAI,WAAW,MAAM;AAInB,cAAM,KAAK,SAAS,OAAO,KAAK;AAAA,UAC9B,YAAY;AAAA,UACZ,WAAW,EAAE,CAAC,UAAU,GAAG,OAAO,UAAU;AAAA,QAC9C,CAAC;AACD,eAAO;AAAA,UACL,GAAG;AAAA,UACH,WAAW,OAAO;AAAA,UAClB,YAAY;AAAA,UACZ,SAAS,MAAM,QAAQ,KAAK,MAAM,OAAO,WAAW,MAAS;AAAA,QAC/D;AAAA,MACF;AACA,yBAAmB,QAAQ,GAAG;AAC9B,YAAM,KAAK,SAAS,eAAe,KAAK,OAAO,OAAO;AAAA,QACpD,WAAW,EAAE,GAAG,OAAO,WAAW,CAAC,UAAU,GAAG,OAAO,UAAU;AAAA,MACnE,CAAC;AACD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,WAAW,OAAO;AAAA,QAClB,YAAY;AAAA,QACZ,SAAS,MAAM,QAAQ,KAAK,MAAM,OAAO,WAAW,iBAAiB;AAAA,MACvE;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAAA,IAExC;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IACR,sEAAsE,IAAI,OAAO;AAAA,EACnF;AACF;AAUA,eAAsB,sBACpB,KACA,MACkB;AAClB,kBAAgB,GAAG;AACnB,WAAS,UAAU,GAAG,UAAU,wBAAwB,WAAW,GAAG;AACpE,UAAM,SAAS,MAAM,KAAK,SAAS,IAAI,GAAG;AAC1C,QAAI,WAAW,KAAM,QAAO;AAC5B,QAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAI,OAAO,cAAc,UAAa,EAAE,cAAc,OAAO,WAAY,QAAO;AAChF,UAAM,OAAO,EAAE,GAAG,OAAO,UAAU;AACnC,WAAO,KAAK,UAAU;AACtB,QAAI;AAGF,YAAM,KAAK,SAAS,eAAe,KAAK,OAAO,OAAO;AAAA,QACpD,WAAW,OAAO,KAAK,IAAI,EAAE,WAAW,IAAI,SAAY;AAAA,MAC1D,CAAC;AACD,YAAM,QAAQ,KAAK,MAAM,QAAW,cAAc,MAAM,CAAC;AACzD,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAAA,IACxC;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IACR,yEAAyE,IAAI,OAAO;AAAA,EACtF;AACF;AAEA,SAAS,mBAAmB,QAAwB,KAAuB;AACzE,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI;AAAA,MACR,YAAY,IAAI,OAAO;AAAA,IAEzB;AAAA,EACF;AACF;;;AChPO,IAAM,uBAAuB;AAS7B,IAAM,6BAA6B;AAuJnC,IAAM,2BAA2B;AAcxC,eAAe,iBACb,UACA,SAC2B;AAC3B,QAAM,OAAyB,CAAC;AAChC,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,UAAU,aAAa,QAAQ,KAAK,QAAQ,eAAe,GAAG;AACvE,qBAAiB,WAAW,SAAS,KAAK,aAAa,MAAM,CAAC,GAAG;AAC/D,YAAM,MAAM,cAAc,QAAQ,OAAO;AACzC,UAAI,QAAQ,KAAM;AAGlB,UAAI,oBAAoB,IAAI,SAAS,EAAG;AACxC,UAAI,QAAQ,cAAc,UAAa,IAAI,cAAc,QAAQ,UAAW;AAG5E,YAAM,MAAM,GAAG,IAAI,aAAa,EAAE,KAAS,IAAI,OAAO;AACtD,UAAI,KAAK,IAAI,GAAG,EAAG;AACnB,WAAK,IAAI,GAAG;AACZ,UAAI,KAAK,UAAU,QAAQ,iBAAiB;AAC1C,cAAM,IAAI;AAAA,UACR,kDAAkD,QAAQ,eAAe;AAAA,QAG3E;AAAA,MACF;AACA,YAAM,OAAO,MAAM,SAAS,IAAI,GAAG;AACnC,UAAI,SAAS,KAAM;AACnB,WAAK,KAAK,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAe,iBACb,UACA,KACA,WACe;AACf,MAAI,CAAC,SAAS,GAAG,EAAG;AACpB,MAAI;AACF,UAAM,SAAS,OAAO,YAAY,UAAU,SAAS,GAAG,GAAG,CAAC;AAAA,EAC9D,QAAQ;AAAA,EAER;AACF;AASA,eAAsB,cACpB,MACA,SAC8B;AAC9B,MAAI,QAAQ,cAAc,OAAW,uBAAsB,QAAQ,SAAS;AAC5E,QAAM,MAAM,QAAQ;AACpB,MAAI,CAAC,OAAO,SAAS,GAAG,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,yDAAyD,OAAO,GAAG,CAAC;AAAA,IACtE;AAAA,EACF;AAKA,MAAI,MAAM,mBAAmB;AAC3B,UAAM,IAAI;AAAA,MACR,2BAA2B,GAAG,eAAe,iBAAiB;AAAA,IAEhE;AAAA,EACF;AACA,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACzC,UAAM,IAAI,gBAAgB,4DAA4D,KAAK,EAAE;AAAA,EAC/F;AACA,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,QAAQ,QAAQ,oBAAoB;AAC1C,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACxC,UAAM,IAAI;AAAA,MACR,iFAAiF,OAAO,KAAK,CAAC;AAAA,IAChG;AAAA,EACF;AACA,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,MAAI,CAAC,OAAO,SAAS,eAAe,KAAK,kBAAkB,GAAG;AAC5D,UAAM,IAAI;AAAA,MACR,wEAAwE,OAAO,eAAe,CAAC;AAAA,IACjG;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,WAAW;AAMlC,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,OACJ,SAAS,UACL,MAAM,iBAAiB,KAAK,UAAU;AAAA,IACpC;AAAA,IACA,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,WAAW,QAAQ;AAAA,IACnB;AAAA,EACF,CAAC,IACD,MAAM,cAAc,KAAK,UAAU;AAAA,IACjC,WAAW,QAAQ;AAAA,IACnB;AAAA,IACA,IAAI;AAAA,EACN,CAAC;AAEP,QAAM,SAAS,QAAQ;AACvB,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,OACJ,WAAW,UAAa,eAAe,IACnC,OACA,KAAK,OAAO,CAAC,MAAM,OAAO,SAAS,QAAQ,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC;AAE7E,QAAM,UAAyB,CAAC;AAChC,MAAI,WAAW;AACf,MAAI,UAAU;AACd,MAAI,cAAc;AAClB,MAAI,mBAAmB;AACvB,MAAI,UAAU;AAMd,MAAI,YAAY;AAEhB,aAAW,OAAO,MAAM;AACtB,UAAM,MAAkB,EAAE,WAAW,IAAI,WAAW,SAAS,IAAI,QAAQ;AACzE,UAAM,OAAO,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AAC9D,UAAM,SAAS,oBAAoB,IAAI,SAAS;AAEhD,QAAI,IAAI,WAAW,aAAa;AAC9B,UAAI,CAAC,gBAAiB;AAOtB,YAAM,YAAY,gBAAgB,IAAI,SAAS;AAC/C,UAAI,cAAc,KAAM;AACxB,UAAI,MAAM,YAAY,MAAO;AAC7B,UAAI,aAAa,OAAO;AACtB,kBAAU;AACV;AAAA,MACF;AACA,mBAAa;AACb,UAAI;AACF,YAAI,CAAE,MAAM,iBAAiB,MAAM,GAAG,GAAI;AAKxC,cAAI,CAAC,OAAQ,OAAM,oBAAoB,KAAK,IAAI,EAAE,MAAM,MAAM,MAAS;AACvE,cAAI,CAAE,MAAM,iBAAiB,MAAM,GAAG,GAAI;AACxC,oBAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,QAAQ,sBAAsB,CAAC;AAC1E;AAAA,UACF;AAAA,QACF;AAGA,YAAI,CAAC,OAAQ,OAAM,KAAK,SAAS,OAAO,KAAK,IAAI,KAAK;AACtD,YAAI,CAAC,OAAQ,qBAAoB;AACjC,gBAAQ,KAAK;AAAA,UACX,GAAG;AAAA,UACH,QAAQ,SAAS,0BAA0B;AAAA,QAC7C,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,gBAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,QAAQ,cAAc,GAAG,EAAE,CAAC;AAAA,MACzE;AACA;AAAA,IACF;AAEA,QAAI,WAAW,KAAM;AACrB,QAAI,WAAW,WAAW;AACxB,cAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,QAAQ,iBAAiB,CAAC;AACrE;AAAA,IACF;AACA,QAAI,OAAO,YAAY,IAAK;AAC5B,gBAAY;AACZ,QAAI,aAAa,OAAO;AAKtB,gBAAU;AACV;AAAA,IACF;AAEA,QAAI;AAOF,YAAM,OAAO,MAAM,KAAK,SAAS,IAAI,GAAG;AACxC,YAAM,aAAa,SAAS,OAAO,OAAO,oBAAoB,KAAK,SAAS;AAC5E,UACE,SAAS,QACT,KAAK,WAAW,eAChB,eAAe,QACf,eAAe,aACf,WAAW,YAAY,KACvB;AACA,gBAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,QAAQ,iBAAiB,CAAC;AACrE;AAAA,MACF;AAEA,mBAAa;AAIb,YAAM,SAAS,MAAM,YAAY,KAAK,MAAM;AAAA,QAC1C,gBAAgB,IAAI;AAAA,QACpB;AAAA,QACA,OAAO,QAAQ;AAAA,MACjB,CAAC;AACD,UAAI,QAAQ;AACV,uBAAe;AACf,gBAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,gBAAgB,WAAW,WAAW,WAAW,OAAO,CAAC;AACzF;AAAA,MACF;AACA,UAAI,CAAC,OAAO,SAAS;AASnB,gBAAQ,KAAK;AAAA,UACX,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,QAAQ,yBAAyB,OAAO,MAAM;AAAA,QAChD,CAAC;AACD;AAAA,MACF;AACA,iBAAW;AACX,cAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,WAAW,WAAW,WAAW,OAAO,CAAC;AAKpF,YAAM,iBAAiB,KAAK,UAAU,KAAK,WAAW,SAAS;AAC/D,UACE,mBACA,OAAO,mBAAmB,WAAW,KACrC,OAAO,qBAAqB,WAAW,GACvC;AAgCA,cAAM,YAAY,MAAM,KAAK,SAAS,IAAI,GAAG,EAAE,MAAM,MAAM,IAAI;AAC/D,YAAI,WAAW,WAAW,aAAa;AACrC,gBAAM,UAAU,MAAM,KAAK,SAAS,OAAO,KAAK,UAAU,KAAK,EAAE;AAAA,YAC/D,MAAM;AAAA,YACN,MAAM;AAAA,UACR;AACA,cAAI,QAAS;AAAA,QACf;AAAA,MACF;AAGA,YAAM,gBAAgB,KAAK,UAAU,KAAK,GAAG,EAAE,MAAM,MAAM,MAAS;AAAA,IACtE,SAAS,KAAK;AAIZ,YAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,GAAG,EAAE,MAAM,MAAM,IAAI;AAC3D,UAAI,OAAO,WAAW,aAAa;AAIjC,cAAM,gBAAgB,KAAK,UAAU,KAAK,GAAG,EAAE,MAAM,MAAM,MAAS;AACpE,mBAAW;AACX,gBAAQ,KAAK;AAAA,UACX,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,WAAW,OAAO;AAAA,UAClB,QAAQ;AAAA,YACN,GAAG;AAAA,YACH,SAAS;AAAA,YACT,oBAAoB,CAAC;AAAA,YACrB,sBAAsB,CAAC;AAAA,YACvB,gBAAgB;AAAA,YAChB,QAAQ;AAAA,UACV;AAAA,UACA,OAAO,cAAc,GAAG;AAAA,QAC1B,CAAC;AACD;AAAA,MACF;AACA,cAAQ,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,QAAQ,cAAc,GAAG,EAAE,CAAC;AAAA,IACzE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS,KAAK;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGA,IAAM,aAAa;AAGnB,SAAS,gBAAgB,MAAiD;AACxE,MAAI,SAAS,UAAa,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AAC1F,WAAO;AAAA,EACT;AACA,QAAM,MAAO,KAAiC,UAAU;AACxD,SAAO,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG,KAAK,OAAO,oBAAoB,MAAM;AAC9F;AAOA,eAAe,gBACb,UACA,KACA,KACe;AACf,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,UAAM,MAAM,MAAM,SAAS,IAAI,GAAG;AAClC,QAAI,QAAQ,QAAQ,IAAI,WAAW,YAAa;AAChD,QAAI;AACF,YAAM,SAAS,eAAe,KAAK,IAAI,OAAO;AAAA,QAC5C,WAAW,EAAE,GAAG,IAAI,WAAW,CAAC,UAAU,GAAG,IAAI;AAAA,MACnD,CAAC;AACD;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAAA,IACxC;AAAA,EACF;AACF;AAcA,eAAe,iBACb,MACA,KACkB;AAClB,mBAAiB,OAAO,KAAK,QAAQ,KAAK,GAAG,GAAG;AAC9C,SAAK;AACL,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,cAAc,KAAmC;AACxD,MAAI,qBAAqB,GAAG,EAAG,QAAO;AACtC,SAAO,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AACpE;;;ACzlBA,IAAM,4BAA4B;AAiClC,eAAe,mBAAmB,SAAyB,KAAuC;AAChG,QAAM,UAAU,oBAAI,IAAY;AAChC,mBAAiB,OAAO,QAAQ,KAAK,GAAG,EAAG,SAAQ,IAAI,IAAI,UAAU;AACrE,SAAO;AACT;AAaA,eAAsB,oBACpB,MACA,UAAkF,CAAC,GACvD;AAC5B,MAAI,QAAQ,cAAc,OAAW,uBAAsB,QAAQ,SAAS;AAC5E,QAAM,cAAc,QAAQ,eAAe;AAC3C,MAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAAG;AAErD,UAAM,IAAI,gBAAgB,+CAA+C,WAAW,EAAE;AAAA,EACxF;AACA,QAAM,kBAAkB,QAAQ,mBAAmB;AAGnD,QAAM,OAAO,MAAM,cAAc,KAAK,UAAU;AAAA,IAC9C,WAAW,QAAQ;AAAA,IACnB;AAAA,IACA,IAAI;AAAA;AAAA;AAAA,IAGJ,OACE;AAAA,EAGJ,CAAC;AACD,QAAM,UAAU,MAAM,mBAAmB,MAAM,aAAa,OAAO,QAA0B;AAC3F,QAAI,IAAI,WAAW,YAAa,QAAO,EAAE,MAAM,KAAK;AACpD,UAAM,MAAkB,EAAE,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AACzE,QAAI;AAIF,YAAM,OAAO,MAAM,KAAK,SAAS,IAAI,GAAG;AACxC,UAAI,CAAC,QAAQ,KAAK,WAAW,YAAa,QAAO,EAAE,MAAM,KAAK;AAK9D,UAAI,KAAK,eAAe,KAAM,QAAO,EAAE,MAAM,KAAK;AAClD,YAAM,UAAU,MAAM,mBAAmB,KAAK,SAAS,GAAG;AAC1D,UAAI,QAAQ,IAAI,KAAK,UAAU,EAAG,QAAO,EAAE,MAAM,KAAK;AACtD,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,SAAS,IAAI;AAAA,UACb,WAAW,IAAI;AAAA,UACf,YAAY,KAAK;AAAA,UACjB,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAGd,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,SAAS,IAAI;AAAA,UACb,WAAW,IAAI;AAAA,UACf,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACD,QAAM,eAAmC,CAAC;AAC1C,QAAM,UAAmC,CAAC;AAC1C,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,SAAS,QAAS,cAAa,KAAK,EAAE,KAAK;AAAA,aACxC,EAAE,SAAS,QAAS,SAAQ,KAAK,EAAE,KAAK;AAAA,EACnD;AACA,SAAO,EAAE,SAAS,KAAK,QAAQ,cAAc,QAAQ;AACvD;;;ACtHO,IAAM,uBAAoC;AAAA,EAC/C,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,QAAQ;AACV;AAkBO,SAAS,eAAe,QAAqB,SAAyB;AAC3E,QAAM,MAAM,OAAO,cAAc,OAAO,kBAAkB,UAAU;AACpE,SAAO,KAAK,IAAI,OAAO,YAAY,GAAG;AACxC;AAGO,SAAS,YAAY,QAAqB,SAAiB,KAAkB;AAClF,MAAI,OAAO,WAAW,OAAQ,QAAO;AACrC,SAAO,IAAI,KAAK,IAAI;AACtB;AAOA,eAAsB,UACpB,IACA,QACA,MACY;AACZ,QAAM,YAAY,KAAK,eAAe;AAOtC,MAAI,CAAC,OAAO,SAAS,OAAO,WAAW,KAAK,OAAO,cAAc,GAAG;AAClE,UAAM,IAAI;AAAA,MACR,uDAAuD,OAAO,OAAO,WAAW,CAAC;AAAA,IACnF;AAAA,EACF;AACA,QAAM,WAAW,KAAK,MAAM,OAAO,WAAW;AAC9C,MAAI;AACJ,WAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,KAAK;AACZ,gBAAU;AACV,UAAI,WAAW,YAAY,CAAC,UAAU,GAAG,EAAG,OAAM;AAClD,YAAM,UAAU,YAAY,QAAQ,eAAe,QAAQ,OAAO,GAAG,KAAK,GAAG;AAC7E,UAAI;AACF,aAAK,UAAU,EAAE,SAAS,SAAS,IAAI,CAAC;AAAA,MAC1C,QAAQ;AAAA,MAGR;AACA,YAAM,KAAK,MAAM,MAAM,OAAO;AAAA,IAChC;AAAA,EACF;AAEA,QAAM;AACR;;;ACjFA,SAAS,QAAQ,MAQf;AACA,SAAO;AAAA,IACL,QAAQ,KAAK,UAAU;AAAA,IACvB,MAAM;AAAA,MACJ,OAAO,KAAK;AAAA,MACZ,KAAK,KAAK;AAAA,MACV,aAAa,KAAK,eAAe;AAAA,MACjC,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAGO,IAAM,6BAAN,MAA+D;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAER;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,IAAI,mBAAuC;AACzC,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,YAAY,OAA2B,MAAuB;AAC5D,SAAK,QAAQ;AACb,UAAM,IAAI,QAAQ,IAAI;AACtB,SAAK,SAAS,EAAE;AAChB,SAAK,OAAO,EAAE;AACd,UAAM,cAAc,MAAM;AAC1B,QAAI,aAAa;AACf,WAAK,SAAS,CAAC,QAAQ,UAAU,MAAM,YAAY,KAAK,OAAO,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,IAC7F;AACA,UAAM,qBAAqB,MAAM;AACjC,QAAI,oBAAoB;AACtB,WAAK,gBAAgB,CAAC,QACpB,UAAU,MAAM,mBAAmB,KAAK,OAAO,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,IAC/E;AACA,UAAM,yBAAyB,MAAM;AACrC,QAAI,wBAAwB;AAC1B,WAAK,oBAAoB,CAAC,QACxB,UAAU,MAAM,uBAAuB,KAAK,OAAO,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,IACnF;AAIA,UAAM,kBAAkB,MAAM;AAC9B,QAAI,iBAAiB;AACnB,WAAK,aAAa,CAAC,QAAQ;AACzB,wBAAgB,KAAK,OAAO,GAAG;AAAA,MACjC;AAAA,IACF;AAEA,UAAM,cAAc,MAAM;AAC1B,QAAI,aAAa;AACf,WAAK,SAAS,CAAC,QAAQ,UAAU,MAAM,YAAY,KAAK,OAAO,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,IAC7F;AACA,UAAM,sBAAsB,MAAM;AAClC,QAAI,qBAAqB;AACvB,WAAK,iBAAiB,CAAC,QACrB,UAAU,MAAM,oBAAoB,KAAK,OAAO,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,IAChF;AAAA,EACF;AAAA,EAEA,SAAS,KAA2C;AAClD,WAAO,UAAU,MAAM,KAAK,MAAM,SAAS,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,EACzE;AAAA,EAEA,cAAc,KAAoC;AAChD,WAAO,UAAU,MAAM,KAAK,MAAM,cAAc,GAAG,GAAG,KAAK,QAAQ,KAAK,IAAI;AAAA,EAC9E;AACF;",
6
- "names": ["total", "join", "FS", "createHash", "open", "open", "FS", "createHash", "FS", "randomUUID", "mkdir", "open", "readdir", "unlink", "dirname", "join", "dirname", "join", "readdir", "open", "FS", "mkdir", "randomUUID", "unlink", "join", "DEFAULT_MAX_BITMAP_BYTES", "generation", "key", "collected", "left", "listGenerations"]
3
+ "sources": ["../src/core/bit-route.ts", "../src/core/budget.ts", "../src/core/chunk-window.ts", "../src/core/crbm/format.ts", "../src/core/keys.ts", "../src/core/metrics.ts", "../src/core/engine.ts", "../src/core/lru.ts", "../src/core/generation-gc.ts", "../src/core/reader-defaults.ts", "../src/core/cost.ts", "../src/core/due-index.ts", "../src/core/registry-scan.ts", "../src/core/codec.ts", "../src/export/index.ts", "../src/core/discover.ts", "../src/drivers/backends.ts", "../src/drivers/memory.ts", "../src/core/blob.ts", "../src/drivers/localfs/storage.ts", "../src/drivers/localfs/paths.ts", "../src/drivers/localfs/fs-util.ts", "../src/drivers/localfs/registry.ts", "../src/core/audit.ts", "../src/core/cooperative.ts", "../src/core/crypto.ts", "../src/core/crbm/crc32c.ts", "../src/core/crbm/varint.ts", "../src/core/crbm/reader.ts", "../src/core/crbm/writer.ts", "../src/core/load-input.ts", "../src/core/summary.ts", "../src/core/crbm-storage-source.ts", "../src/core/pinned-storage-source.ts", "../src/core/retry.ts", "../src/core/load.ts", "../src/core/rollback.ts", "../src/core/erase-id.ts", "../src/drivers/crypto.ts", "../src/core/erasure.ts", "../src/core/retention.ts", "../src/core/retention-sweep.ts", "../src/core/consistency.ts", "../src/drivers/retry/retrying-chunk-source.ts"],
4
+ "sourcesContent": ["/**\n * BitRouteSplitter \u2014 maps a 32-bit id to its chunk (top 16 bits) and remainder\n * (bottom 16 bits), per the data model.\n */\nimport { ValidationError } from './errors';\n\nexport const U32_MAX = 0xffff_ffff;\nexport const CHUNK_COUNT = 0x1_0000; // 65,536 possible chunk keys\nconst LOW_16 = 0xffff;\n\nexport interface BitRoute {\n /** Top 16 bits \u2014 the chunk key (0..65535). */\n readonly chunkKey: number;\n /** Bottom 16 bits \u2014 the value within the chunk (0..65535). */\n readonly remainder: number;\n}\n\n/** Split a u32 id into `{ chunkKey, remainder }`. Throws `ValidationError` for non-u32 input. */\nexport function splitId(id: number): BitRoute {\n if (!Number.isInteger(id) || id < 0 || id > U32_MAX) {\n throw new ValidationError(`id must be an integer in 0..${U32_MAX}; got ${id}`);\n }\n return { chunkKey: id >>> 16, remainder: id & LOW_16 };\n}\n\n/**\n * Recombine a chunk key + remainder back into the u32 id. Precondition: `chunkKey` is a validated\n * 16-bit value (tier-derived keys are validated upstream in the engine before reaching here, per\n * invariant 5); `remainder` is masked to 16 bits.\n */\n/**\n * Largest legal value inside a chunk payload. A chunk covers `CHUNK_COUNT` ids, so remainders run\n * `[0, CHUNK_COUNT - 1]`. Exported so the engine can assert it on untrusted tier bytes.\n */\nexport const MAX_REMAINDER = CHUNK_COUNT - 1;\n\nexport function joinId(chunkKey: number, remainder: number): number {\n return (((chunkKey & LOW_16) << 16) >>> 0) + (remainder & LOW_16);\n}\n", "/**\n * Per-op denial-of-wallet budget \u2014 hard invariant 6 (bounded memory and cost). A read/admin op is refused\n * **before it fans out** if its projected fan-out would exceed `maxRequests` \u2014 so one runaway\n * `count`/`iterate`/`intersect`/`subjectReport`/`eraseSubject` can't drive unbounded RCU/GET cost on a shared\n * backend. The check is O(1) (a single comparison against the already-known fan-out size), so the hot path is\n * untouched. Each request's payload is separately size-capped (the safe-deserialize ceiling), so bounding the\n * fan-out count transitively bounds bytes \u2014 the budget is intentionally count-based, not a second byte\n * accumulator that would add per-chunk work.\n *\n * **What the count is.** For the read ops it is the number of Storage **chunk fetches** (`count`/`iterate`: one per\n * effective chunk; `intersect`: surviving keys \u00D7 operands). For the admin scans it is the number of **segments\n * fanned out to** (`subjectReport`: one `has()` each; `eraseSubject`: one per segment) \u2014 i.e. it bounds the\n * *breadth* of the fan-out, not the request total of each segment's own rewrite. That's the right lever for a\n * runaway (a million-segment sweep); it is not a byte/request meter. One addition: a segment `eraseSubject` has to\n * search generation by generation is also charged one unit for each generation it opens beyond the current one,\n * since that is O(generations) work the segment count alone does not show.\n */\n\nimport { BudgetExceededError, ValidationError } from './errors';\n\nexport interface Budget {\n /** Max units of fan-out (Storage chunk fetches, or segments scanned) one op may issue before it is refused. */\n readonly maxRequests: number;\n}\n\n/**\n * Default: **on but generous** \u2014 even a full single-segment scan (\u2264 65 536 chunks) or a large single-namespace\n * admin scan stays well under it; it exists to catch a true runaway (a million-segment scan, a pathological\n * intersection). Tune it down for tighter multi-tenant control, or set `budget: false` to disable.\n */\nexport const DEFAULT_BUDGET: Budget = { maxRequests: 1_000_000 };\n\n/** A store/per-op budget option: partial overrides, or `false` to disable entirely. */\nexport type BudgetOption = Partial<Budget> | false;\n\n/** Resolve a budget option against a fallback to a concrete `Budget`, or `null` when disabled. Validates. */\nexport function resolveBudget(opt: BudgetOption | undefined, fallback: Budget): Budget | null {\n if (opt === false) return null; // explicitly disabled\n const maxRequests = opt?.maxRequests ?? fallback.maxRequests;\n if (!Number.isInteger(maxRequests) || maxRequests < 1) {\n throw new ValidationError(`budget.maxRequests must be a positive integer; got ${maxRequests}`);\n }\n return { maxRequests };\n}\n\n/**\n * Resolve a **per-op** budget override against the store's own resolved budget (never the raw global default):\n * `undefined` inherits the store budget as-is (including a disabled `null`); a partial `{}` / omitted\n * `maxRequests` inherits the store's *tightening* (so a per-op passthrough of an absent config value can't\n * silently lift a tight tenant ceiling back to the generous default); `{ maxRequests }` replaces it; `false`\n * disables. Falls back to {@link DEFAULT_BUDGET} only when the store itself has no budget (disabled).\n */\nexport function resolvePerOpBudget(\n opt: BudgetOption | undefined,\n storeBudget: Budget | null,\n): Budget | null {\n if (opt === undefined) return storeBudget;\n return resolveBudget(opt, storeBudget ?? DEFAULT_BUDGET);\n}\n\n/**\n * Refuse an op whose projected fan-out exceeds the budget. **Call before fan-out**, with the exact fan-out size\n * the op is about to issue (Storage chunk fetches, or segments to scan). O(1). No-op when `budget` is `null`\n * (disabled).\n */\nexport function checkBudget(budget: Budget | null, projectedRequests: number, op: string): void {\n if (budget !== null && projectedRequests > budget.maxRequests) {\n throw new BudgetExceededError(\n `${op} would fan out to ${projectedRequests} units, over the per-op budget of ` +\n `${budget.maxRequests} \u2014 raise \\`budget.maxRequests\\`, override it per-op, or set \\`budget: false\\``,\n );\n }\n}\n\n/**\n * Drain an async iterable into an array, refusing **as soon as** the budget is exceeded.\n *\n * This exists because the obvious spelling is wrong in a way that is easy to miss:\n *\n * ```ts\n * for await (const rec of registry.list(ns)) recs.push(rec);\n * checkBudget(budget, recs.length, op); // \u2190 too late; the memory is already spent\n * ```\n *\n * That pattern bounds the **fan-out** but not the **enumeration**, so a tight budget refuses the work only\n * after paying for the list \u2014 which defeats the purpose. It was measured buffering 20,000 registry records\n * under `budget: { maxRequests: 2 }` before throwing. Draining incrementally bounds resident memory to\n * `O(budget)` instead of `O(fleet)`.\n *\n * **The error message is deliberately vaguer than `checkBudget`'s.** It reports \"more than N\" rather than an\n * exact total, because knowing the exact total requires finishing the scan \u2014 which is precisely the cost being\n * refused. An honest approximation beats an expensive exact number.\n *\n * A `null` budget (`budget: false`) means the caller has explicitly opted out of fan-out limits, so this\n * degrades to a plain drain.\n */\nexport async function collectWithinBudget<T>(\n source: AsyncIterable<T>,\n budget: Budget | null,\n op: string,\n): Promise<T[]> {\n const out: T[] = [];\n for await (const item of source) {\n out.push(item);\n // Checked INSIDE the loop, and `>` not `>=`, so the threshold matches `checkBudget` exactly: a budget of N\n // admits exactly N units and refuses the N+1'th.\n if (budget !== null && out.length > budget.maxRequests) {\n throw new BudgetExceededError(\n `${op} would fan out to more than ${budget.maxRequests} units, over the per-op budget \u2014 the scan was ` +\n `abandoned at that point rather than completed, so no exact total is available. Raise ` +\n `\\`budget.maxRequests\\`, override it per-op, or set \\`budget: false\\``,\n );\n }\n }\n return out;\n}\n", "/**\n * An ordered window of reads: up to `max` reads open ahead of the one being consumed, handed back in key order.\n * With `ramp`, the window opens 1, 2, 4 \u2026 wide up to `max` instead of `max` at once, so a consumer that stops\n * after a few chunks has fetched a handful, not a full window.\n *\n * `T` is whatever one read yields (a decoded chunk, or the raw bytes of one). Each read is wrapped to resolve and\n * never reject, so a read nobody consumes (the consumer stopped, or an earlier chunk failed) cannot raise an\n * unhandled rejection; its error surfaces from {@link take} only if the consumer reaches that chunk. Memory is\n * bounded by the window: at most `max` results are held ahead.\n *\n * Internal to core: not exported from any entry point.\n */\nexport class ChunkWindow<T> {\n private readonly open: Array<Promise<{ value: T; error?: { cause: unknown } }>> = [];\n private launched = 0;\n private taken = 0;\n\n constructor(\n private readonly keys: readonly number[],\n private readonly fetch: (chunkKey: number) => Promise<T>,\n private readonly max: number,\n private readonly ramp: boolean,\n ) {}\n\n /** The next read's result in key order, or the error that read raised. */\n async take(): Promise<T> {\n const width = this.ramp ? Math.min(this.max, 2 ** Math.min(this.taken, 30)) : this.max;\n while (this.launched < this.keys.length && this.launched - this.taken < width) {\n this.open.push(\n this.fetch(this.keys[this.launched++]!).then(\n (value) => ({ value }),\n (cause: unknown) => ({ value: undefined as T, error: { cause } }),\n ),\n );\n }\n const slot = await this.open.shift()!;\n this.taken += 1;\n if (slot.error) throw slot.error.cause;\n return slot.value;\n }\n}\n", "/**\n * Frozen `.crbm` v1 layout constants (format 1.0, with its optional extension block).\n *\n * These byte widths/offsets are pinned by the golden corpus and must never change for v1 \u2014\n * a new layout is a new format version. All multi-byte integers are little-endian (v1 fixes LE).\n */\n\n/** 4-byte magic at both file ends: \"CRBM\". */\nexport const MAGIC = Uint8Array.of(0x43, 0x52, 0x42, 0x4d);\n\nexport const VERSION_MAJOR = 1;\nexport const VERSION_MINOR = 0;\n\n/** Front preamble: magic(4) + version_major(1) + version_minor(1) + reserved(2). */\nexport const PREAMBLE_BYTES = 8;\n\n/** Payloads begin immediately after the preamble. */\nexport const PAYLOAD_START = PREAMBLE_BYTES;\n\n/** Fixed footer size (v1.0). */\nexport const FOOTER_BYTES = 104;\n\n/** Byte offsets of each field within the 104-byte footer. */\nexport const FOOTER = {\n indexOffset: 0, // u64\n indexLength: 8, // u64\n indexCrc32c: 16, // u32\n flags: 20, // u32\n payloadCodecId: 24, // u16 \u2014 which codec wrote the chunk payloads\n elementWidth: 26, // u8\n containerCodec: 27, // u8\n versionMajor: 28, // u8\n versionMinor: 29, // u8\n reserved2: 30, // u16\n generation: 32, // u64\n indexNonce: 40, // 12 B\n indexTag: 52, // 16 B\n keyId: 68, // 16 B\n chunkCount: 84, // u32\n totalCardinality: 88, // u64\n footerCrc32c: 96, // u32 \u2014 covers footer bytes [0, 96)\n endMagic: 100, // char[4]\n} as const;\n\n/** Footer bytes covered by `footer_crc32c` (everything before the CRC field). */\nexport const FOOTER_CRC_COVERAGE = FOOTER.footerCrc32c; // 96\n\n/** `flags` bit positions. */\nexport const FLAG_ENCRYPTED = 1 << 0;\nexport const FLAG_INDEX_COMPRESSED = 1 << 1; // reserved\nexport const FLAG_LITTLE_ENDIAN = 1 << 2; // =1 in v1\n/**\n * The object carries an extension block just before its index (a generation's metadata). Set only when there is\n * something to put in it, so an object without metadata is the same bytes as before the block existed; a reader that\n * does not know the bit refuses the object rather than reading past what it cannot see.\n */\nexport const FLAG_EXTENSION = 1 << 3;\n\n/** Flag bits a v1 reader understands; any bit outside this mask is an unsupported feature. */\nexport const KNOWN_FLAGS = FLAG_ENCRYPTED | FLAG_LITTLE_ENDIAN | FLAG_EXTENSION;\n\n/**\n * Default element width: 32-bit ids (the u32 member space). `64` is the *reserved* escape above the\n * ~4.29 B-ids/segment u32 ceiling; a v1 (32-bit) reader must **reject** a 64-bit generation rather than feed\n * it to the 32-bit deserializer (which would mis-count fleet-wide). A future 64-bit generation is therefore a\n * **major**-version bump (auto-rejected by old readers), never a silent minor one \u2014 see\n * {@link CrbmReader.open} validates this.\n */\nexport const ELEMENT_WIDTH_32 = 32;\n\n/**\n * **Which codec produced the chunk payloads.** Recorded in the footer, validated on read.\n *\n * `.crbm` is a shared container: the index, the CRC32Cs, the AEAD framing and the generation model are all\n * codec-independent, and **only the chunk payload bytes belong to a flavor** (hence *Chunked Remote BitMap* \u2014\n * see the `.crbm` format section of the API reference). This field is what lets one container hold either, the same way ZIP tags each member\n * with a compression method.\n *\n * **A codec id, not a roaring-specific one.** A second codec is genuinely expected \u2014 `soaring` is a planned\n * Roaring *variant*, so its serialized bytes are unlikely to be roaring-portable, and it lands *after* the `1.0`\n * format freeze. A field frozen under a codec-specific meaning could not be reinterpreted later without a major\n * format version.\n *\n * **Ids are permanent once published.** Add to {@link KNOWN_PAYLOAD_CODEC_IDS} when a codec ships; never\n * reuse or renumber. Ids are deliberately *not* pre-allocated for codecs that do not exist \u2014 a reserved number\n * for an unbuilt codec is a guess about a format nobody has designed.\n */\nexport const PAYLOAD_CODEC_ROARING_PORTABLE = 1;\n\n/**\n * Every payload codec id this reader can decode.\n *\n * The reader validates membership rather than equality with a single constant. That is the whole point of an\n * id registry: an unknown id is rejected with a typed error naming it, so an old reader meeting a\n * future-codec generation **fails closed** \u2014 the correct direction, and the reason a store built on one codec\n * can never silently misread another's bytes as its own. (The homogeneity contract means one store is one\n * codec, so meeting a foreign generation implies misconfiguration, and a loud rejection is exactly what you\n * want there.)\n */\nexport const KNOWN_PAYLOAD_CODEC_IDS: ReadonlySet<number> = new Set([\n PAYLOAD_CODEC_ROARING_PORTABLE,\n]);\n\n/**\n * The only container codec v1 defines: `0` = none (payloads are stored as the portable roaring serialization\n * with no extra container-level transform). A non-zero codec is a future format feature; a v1 reader rejects\n * it ({@link UnsupportedError}) rather than mis-decode. Stamped by the writer, validated by the reader.\n */\nexport const CONTAINER_CODEC_NONE = 0;\n\n/**\n * Default speculative-tail-read size. The spec's worst-case index (a full 16-bit span) is ~400\u2013650 KB,\n * so 256 KB collapses the read to a single GET for the vast majority of real (sparse/medium) segments;\n * only near-full-span segments take the documented second GET.\n */\nexport const DEFAULT_TAIL_BYTES = 256 * 1024;\n\n/**\n * Hard cap on the index region a reader will fetch/parse from one (untrusted) object. The index is read\n * whole on the cheap `open()`/`count()` path, so it must be bounded before allocation \u2014 generously\n * above the ~650 KB full-span worst case, well below a denial-of-wallet allocation.\n */\nexport const DEFAULT_MAX_INDEX_BYTES = 8 * 1024 * 1024;\n\n/**\n * Largest serialized bitmap the engine decodes (1 MiB, per chunk). A well-formed 16-bit chunk serializes to at most\n * about 8.2 KiB, so no legitimate chunk is near it; both the read engine and the erase path decode under this one cap.\n */\nexport const DEFAULT_MAX_BITMAP_BYTES = 1 << 20;\n\n/** A chunk's cardinality is in `[1, 65536]` (empty chunks are never written). */\nexport const MAX_CHUNK_CARDINALITY = 0x1_0000;\n\n/**\n * The extension block, present when the footer sets {@link FLAG_EXTENSION}: `section* \u2016 u32 sectionsLength \u2016 u32\n * crc32c \u2016 \"CRBX\"`, between the last payload and the index, so the index starts right after it. A reader finds it from\n * `indexOffset`: the trailer is the {@link EXT_TRAILER_BYTES} bytes before the index, and the CRC32C covers the sections\n * and the length field. Each section is `u8 type \u2016 u32 length \u2016 bytes`, in strictly ascending type order; a reader\n * skips a type it does not know, so a section must be safe to ignore.\n */\nexport const EXT_MAGIC = Uint8Array.of(0x43, 0x52, 0x42, 0x58); // \"CRBX\"\n/** The trailer: the sections' length (u32), their CRC32C (u32) and {@link EXT_MAGIC}. */\nexport const EXT_TRAILER_BYTES = 12;\n/** A section's header: its type (u8) and its length (u32). */\nexport const EXT_SECTION_HEADER_BYTES = 5;\n/**\n * Cap on the sections of one block, in bytes. A reader refuses a larger block from its trailer alone, before it reads\n * any sections beyond what it already holds (the read that brings the index may already hold up to this much before\n * it), and a writer of any 1.x minor keeps within it, so a later section type stays readable by this reader.\n */\nexport const MAX_EXT_BYTES = 4 * 1024;\n/**\n * Section type 1: the generation's metadata, its canonical JSON in UTF-8. On an encrypted object the section is\n * `nonce \u2016 ciphertext \u2016 tag`, sealed under the segment's key with the metadata scope of the associated data.\n * Type 0 is not a section type: a reader refuses it.\n */\nexport const EXT_SECTION_METADATA = 1;\n\n/** Fixed width of a per-chunk CRC32C field in the index (high-entropy \u2192 not varint). */\nexport const CRC32C_BYTES = 4;\n\n/**\n * v1 AEAD framing sizes (AES-256-GCM), fixed by the format exactly like the payload codec id \u2014 a\n * different cipher is a new format version. An encrypted chunk payload is stored as `nonce \u2016 ciphertext \u2016 tag`;\n * the encrypted **index**'s nonce/tag live in the footer's reserved `indexNonce`/`indexTag` slots. Matches\n * {@link FOOTER.indexNonce} (12 B) and {@link FOOTER.indexTag} (16 B).\n */\nexport const AEAD_NONCE_BYTES = 12;\nexport const AEAD_TAG_BYTES = 16;\n\n/**\n * Hard cap on one encrypted chunk's stored payload (a cleartext one is capped at the decode cap): the decode cap plus the AEAD framing, so an\n * entry the engine would refuse to decode is refused when the object is opened, before any payload is read. No writer\n * exceeds it: a stored payload is a serialized 16-bit chunk (at most about 8.2 KiB) plus, when encrypted, that framing.\n */\nexport const DEFAULT_MAX_PAYLOAD_BYTES =\n DEFAULT_MAX_BITMAP_BYTES + AEAD_NONCE_BYTES + AEAD_TAG_BYTES;\n", "/**\n * Canonical key encoding for a segment / chunk \u2014 one source of truth, used by both the storage\n * drivers and the cache (DRY). The encoding is collision-proof and injection-proof.\n *\n * **The name parts are encoded, and that is what makes the delimiters safe.** A name is any non-empty string,\n * so it may contain a space or a `/`, and with the parts left literal two distinct segments could produce one\n * key:\n *\n * ```\n * segmentKey({ segment: 's' }) \u2192 \"/ s\"\n * segmentKey({ namespace: '/', segment: 's' }) \u2192 \"/ s\" \u2190 the same key\n * segmentKey({ namespace: 'a', segment: 'b c' }) \u2192 \"a b c\"\n * segmentKey({ namespace: 'a b', segment: 'c' }) \u2192 \"a b c\" \u2190 the same key\n * ```\n *\n * These keys reach the reader LRU and the decoded-chunk cache, so a collision is one segment serving\n * another's data on the read path, on every backend \u2014 invariant 3, and a tenant-isolation break.\n *\n * `encodeNameForKey` escapes both delimiters (`/` \u2192 `%2F`, space \u2192 `%20`) along with control characters, so\n * neither can be injected across a field \u2014 a property of the encoding, not of what a caller chooses to name\n * things. The absent-namespace sentinel keeps the same ours-vs-theirs asymmetry the\n * storage keys use: it is emitted literally while a caller's namespace is encoded, so a namespace actually\n * named `/` encodes to `%2F` and cannot impersonate it.\n *\n * These keys are in-memory and driver-internal; nothing on disk is named by them.\n */\nimport { encodeNameForKey } from './name-codec';\nimport type { ChunkRef, SegmentRef } from './ports';\n\nconst FIELD = ' ';\nconst NO_NAMESPACE = '/';\n\nexport function segmentKey(ref: SegmentRef): string {\n const ns = ref.namespace === undefined ? NO_NAMESPACE : encodeNameForKey(ref.namespace);\n return `${ns}${FIELD}${encodeNameForKey(ref.segment)}`;\n}\n\nexport function segmentPrefix(ref: SegmentRef): string {\n return `${segmentKey(ref)}${FIELD}`;\n}\n\nexport function chunkRefKey(ref: ChunkRef): string {\n return `${segmentPrefix(ref)}${ref.chunkKey}`;\n}\n\n/**\n * Cache key for a chunk scoped to a specific **version** of the segment \u2014 {@link chunkRefKey} plus that\n * version (space-delimited, injection-proof exactly as above \u2014 the name parts are already encoded). A publish bumps the version, so it naturally\n * misses the cache instead of serving a stale superseded chunk, and the superseded entries age out under the\n * LRU ceiling (no active purge needed).\n *\n * `version` is whatever the Storage source reports as identifying the bytes a read will see \u2014 its\n * `currentVersion` where it has one, otherwise the bare generation number. **A generation number alone is not\n * an identity**: a load's generation number restarts at 0 once a registry row is purged and the bucket emptied, so a\n * retired-and-re-created name serves different data at the same `currentGen`, and a key built on the number\n * hands the new incarnation the old one's decoded chunks.\n */\nexport function chunkGenKey(ref: ChunkRef, version: string | number): string {\n return `${chunkRefKey(ref)}${FIELD}${version}`;\n}\n\n/**\n * Deterministic FNV-1a shard assignment for a segment key \u2014 dependency-free, stable across workers and\n * restarts. One definition, so every sharded fleet operation (today the retention sweep) agrees on it exactly:\n * two definitions that disagreed would make the union across workers neither disjoint nor complete.\n */\nexport function shardOf(key: string, totalShards: number): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x0100_0193);\n }\n return (h >>> 0) % totalShards;\n}\n", "/**\n * Metrics seam \u2014 an injected sink the engine pushes typed events to, exactly like the `Clock`/driver seams\n * keep `core/` pure and storage-agnostic.\n *\n * Design: the library emits **neutral domain events** and stays vendor-agnostic \u2014 you (or a ~12-line\n * adapter) map them to OpenTelemetry / Datadog / a log line. The default is a **no-op**, and emission is\n * skipped entirely when no sink is wired (near-zero overhead, no telemetry dependency). Events carry **raw\n * observations** (bytes, counts, ms); turning those into billable units / dollars is the cost model's job,\n * which keeps rates pluggable.\n *\n * Two caveats for sink authors: `onEvent` runs **synchronously on the I/O path**, so keep it cheap and\n * non-blocking (offload batching/network to your own async queue); and `segment`/`namespace` are\n * caller-controlled strings that may be PII and are **unbounded-cardinality** \u2014 do not map them to\n * per-series metric labels unless they're known low-cardinality and PII-free. See the observability\n * guide.\n */\n\n/** The segment operations that emit an `op` latency event (timed with the injected clock, at the facade). */\nexport type MetricOpName = 'has' | 'count' | 'intersectInto' | 'unionInto' | 'andNotInto';\n\n/**\n * One observability event. A discriminated union on `kind` \u2014 new variants can be added over time without\n * breaking consumers (they switch on the kinds they care about). `namespace` is the segment's namespace\n * (absent \u21D2 the default namespace). Segment names are identifiers, never bitmap contents (threat model:\n * never log PII / bitmap bits).\n */\nexport type MetricEvent =\n | {\n readonly kind: 'storage.get';\n readonly namespace?: string;\n readonly segment: string;\n /** One event per request, however many callers were waiting on it. Bytes returned (0 if the chunk was absent \u2014 a GET still happened). */\n readonly bytes: number;\n /**\n * Elapsed wall time of the read \u2014 includes any transient-retry backoff on the storage call. From the\n * injected clock, clamped to \u2265 0 (0 under the no-wait test clock).\n */\n readonly ms: number;\n }\n | {\n /**\n * One decoded-chunk cache lookup (emitted only when a cache is configured). `hit: false` means the lookup found\n * no cached chunk. A caller that missed while another caller's read of the same chunk was open waits on that\n * read and adds no `storage.get`, so the misses can outnumber the `storage.get` events; the `storage.get`\n * count is the number of requests.\n */\n readonly kind: 'cache';\n readonly hit: boolean;\n }\n | {\n readonly kind: 'retry';\n /** Infrastructure-fault backoff (throttling, 5xx, a dropped connection) \u2014 the one kind of retry the store does. */\n readonly reason: 'transient';\n /** 1-based number of the attempt about to be retried. */\n readonly attempt: number;\n readonly delayMs: number;\n }\n | {\n readonly kind: 'intersect';\n /**\n * Which chunk-aligned combine this was. **Optional** \u2014 absent means `'intersect'`.\n *\n * For `'union'` over its *include* operands alone, `skippedChunks` is 0 by construction \u2014 union reads\n * every chunk of every operand, so there is nothing to prune. It can still be non-zero when a union\n * carries `exclude` operands, because a suppression list may hold keys no include does and those are\n * legitimately never fetched. So: 0 is the expected shape for a plain union, not a guarantee.\n */\n readonly op?: 'intersect' | 'union' | 'andNot';\n /** Operand count, including any `exclude` (suppression) operands. */\n readonly operands: number;\n /**\n * Distinct chunk **keys** selected to fetch \u2014 for `'intersect'`, those present in every operand.\n *\n * **Keys, not requests.** The actual storage GETs are roughly `fetchedChunks \u00D7 operands`, plus one per\n * `exclude` that holds a given key, which is why this number is smaller than what the per-op budget\n * charges for the same call. The two are different units by design; if you are reconciling a bill, the\n * budget's accounting is the one that models requests.\n */\n readonly fetchedChunks: number;\n /**\n * **Distinct** chunk-keys pruned \u2014 never fetched (the chunk-skipping saving). Counts distinct keys,\n * not per-operand GETs, so it under-states the true GET saving when 3+ operands partially overlap.\n *\n * On a read bounded by `after` / `through`, this and `fetchedChunks` count only the keys inside the range:\n * the keys outside it are never considered, so they are not counted as pruned either. Both count the keys the\n * call would fetch if read to the end, so a page that stops early reports every key up to `through`, or to the\n * end of the segment when `through` is left out.\n */\n readonly skippedChunks: number;\n }\n | { readonly kind: 'op'; readonly name: MetricOpName; readonly ms: number };\n\n/**\n * The sink you plug in. One method, synchronous, fire-and-forget. It must never throw back into the\n * caller \u2014 `CloudRoaring` wraps a user sink with {@link safeMetrics} so a buggy sink can't break I/O.\n */\nexport interface IMetricsSink {\n onEvent(event: MetricEvent): void;\n}\n\n/** The default sink: discards everything, zero overhead. Used whenever no sink is wired. */\nexport const NOOP_METRICS: IMetricsSink = {\n onEvent(): void {\n /* discard */\n },\n};\n\n/**\n * Wrap a sink so a throwing/buggy `onEvent` can never break the data path \u2014 observability is strictly\n * best-effort. `CloudRoaring` applies this to the user-supplied sink at construction.\n */\nexport function safeMetrics(sink: IMetricsSink): IMetricsSink {\n if (sink === NOOP_METRICS) return sink;\n return {\n onEvent(event: MetricEvent): void {\n try {\n sink.onEvent(event);\n } catch {\n /* swallow \u2014 a metrics sink must never break a read */\n }\n },\n };\n}\n\n/** Accumulated totals \u2014 the shape returned by {@link CountingMetricsSink.snapshot}. */\nexport interface MetricsSnapshot {\n readonly storage: { readonly gets: number; readonly bytes: number; readonly totalMs: number };\n readonly cache: { readonly hits: number; readonly misses: number };\n readonly retries: { readonly transient: number };\n readonly intersect: {\n readonly calls: number;\n readonly fetchedChunks: number;\n readonly skippedChunks: number;\n };\n readonly ops: Readonly<\n Record<MetricOpName, { readonly count: number; readonly totalMs: number }>\n >;\n}\n\nconst OP_NAMES: readonly MetricOpName[] = [\n 'has',\n 'count',\n 'intersectInto',\n 'unionInto',\n 'andNotInto',\n];\n\n/**\n * A ready-made sink that tallies events into a {@link MetricsSnapshot} \u2014 handy for tests, quick scripts,\n * and as the grounded-request-cost source behind `costReport()`. `snapshot()` returns an independent copy;\n * `reset()` zeroes the counters.\n */\nexport class CountingMetricsSink implements IMetricsSink {\n private storage = { gets: 0, bytes: 0, totalMs: 0 };\n private cache = { hits: 0, misses: 0 };\n private retries = { transient: 0 };\n private intersect = { calls: 0, fetchedChunks: 0, skippedChunks: 0 };\n private ops: Record<MetricOpName, { count: number; totalMs: number }> =\n CountingMetricsSink.zeroOps();\n\n private static zeroOps(): Record<MetricOpName, { count: number; totalMs: number }> {\n // null-proto so a type-violating event.name (e.g. '__proto__') can never reach Object.prototype\n const ops = Object.create(null) as Record<MetricOpName, { count: number; totalMs: number }>;\n for (const name of OP_NAMES) ops[name] = { count: 0, totalMs: 0 };\n return ops;\n }\n\n onEvent(event: MetricEvent): void {\n switch (event.kind) {\n case 'storage.get':\n this.storage.gets += 1;\n this.storage.bytes += event.bytes;\n this.storage.totalMs += event.ms;\n break;\n case 'cache':\n if (event.hit) this.cache.hits += 1;\n else this.cache.misses += 1;\n break;\n case 'retry':\n this.retries.transient += 1;\n break;\n case 'intersect':\n this.intersect.calls += 1;\n this.intersect.fetchedChunks += event.fetchedChunks;\n this.intersect.skippedChunks += event.skippedChunks;\n break;\n case 'op': {\n // A name outside `OP_NAMES` (a JS caller) is counted nowhere rather than crashing the sink.\n const op = this.ops[event.name] as { count: number; totalMs: number } | undefined;\n if (op === undefined) break;\n op.count += 1;\n op.totalMs += event.ms;\n break;\n }\n default: {\n const exhaustive: never = event;\n return exhaustive;\n }\n }\n }\n\n snapshot(): MetricsSnapshot {\n const ops = Object.create(null) as Record<MetricOpName, { count: number; totalMs: number }>;\n for (const name of OP_NAMES) ops[name] = { ...this.ops[name] };\n return {\n storage: { ...this.storage },\n cache: { ...this.cache },\n retries: { ...this.retries },\n intersect: { ...this.intersect },\n ops,\n };\n }\n\n reset(): void {\n this.storage = { gets: 0, bytes: 0, totalMs: 0 };\n this.cache = { hits: 0, misses: 0 };\n this.retries = { transient: 0 };\n this.intersect = { calls: 0, fetchedChunks: 0, skippedChunks: 0 };\n this.ops = CountingMetricsSink.zeroOps();\n }\n}\n", "/**\n * SegmentEngine \u2014 the read side of the loaded store: id routing, the chunk-skipping combines, and the cache\n * of decoded Storage chunks, over the {@link StorageChunkSource} port.\n *\n * **Read-only by design.** Every write in this library is a new immutable generation \u2014 a load, the facade's\n * `*Into` verbs, `eraseIdFromSegment` \u2014 published through the registry pointer. Nothing here mutates\n * stored bytes, so a read never merges tiers: one chunk read is one whole, checksum-verified, immutable chunk of\n * one generation. Storage-agnostic and time/random-free (the determinism seam): all I/O is via the injected\n * source; the cache carries its own `Clock`.\n */\nimport { splitId, joinId, CHUNK_COUNT, MAX_REMAINDER, U32_MAX } from './bit-route';\nimport type { CodecBitmap, CodecInterface } from './codec';\nimport { checkBudget, DEFAULT_BUDGET, resolvePerOpBudget } from './budget';\nimport type { Budget, BudgetOption } from './budget';\nimport { ChunkWindow } from './chunk-window';\nimport type { Clock } from './determinism';\nimport { IntegrityError, ValidationError } from './errors';\nimport { DEFAULT_MAX_BITMAP_BYTES } from './crbm/format';\nimport { chunkGenKey, chunkRefKey, segmentPrefix } from './keys';\nimport type { BoundedLru } from './lru';\nimport { NOOP_METRICS, safeMetrics } from './metrics';\nimport type { IMetricsSink } from './metrics';\nimport type {\n ChunkRef,\n GenerationMetadata,\n StorageChunkSource,\n SegmentRef,\n SegmentSize,\n} from './ports';\n\n/**\n * Chunk keys a combine resolves at once by default, and how far `iterate` and `count` read ahead \u2014 bounds memory and\n * concurrent reads (invariant 6). A read of `n` chunks takes about `n / 32` sequential round trips, so this, not the\n * network, is what sets how long a long read takes.\n */\nconst DEFAULT_INTERSECT_CONCURRENCY = 32;\n/** A combine's window opens this many keys wide (or `concurrency` wide, if that is less) and doubles per key taken. */\nconst COMBINE_WINDOW_START = 8;\n\n/** A clock that always reads 0 \u2014 used when nothing is injected, so the `storage.get` latency metric reports 0 ms. */\nconst ZERO_CLOCK: Pick<Clock, 'now'> = { now: () => 0 };\n\nexport interface EngineDeps {\n readonly storage: StorageChunkSource;\n /** Optional cache of decoded (immutable) Storage chunks. */\n readonly cache?: BoundedLru<string, CodecBitmap>;\n /** Per-chunk decode ceiling (invariant 5); defaults to 1 MiB. The `.crbm` reader refuses an entry above its own `maxPayloadBytes` (1 MiB, plus 28 bytes when encrypted) at open, so raise that on the chunk source too. */\n readonly maxBitmapBytes?: number;\n /**\n * The bitmap codec \u2014 **required**. `core/` is codec-agnostic: it can have no default, because the concrete codec\n * lives in a *flavor* package that depends on core (a default here would invert that arrow). A flavor's facade\n * injects it \u2014 `@cloudbitmaps/roaring` passes the roaring codec \u2014 so applications never see this.\n */\n readonly codec: CodecInterface;\n /** Time source for the `storage.get` latency metric only; defaults to a clock that reads 0. */\n readonly clock?: Pick<Clock, 'now'>;\n /** Observability sink; defaults to a no-op. Assumed exception-safe (the facade wraps it). */\n readonly metrics?: IMetricsSink;\n /**\n * Per-op denial-of-wallet budget, already resolved by the facade: a {@link Budget} to enforce, or `null` to\n * disable. Undefined \u21D2 {@link DEFAULT_BUDGET} (a direct engine construction still gets the generous default).\n * Read ops refuse before fan-out if they'd exceed it.\n */\n readonly budget?: Budget | null;\n}\n\n/**\n * A range of ids for a read to yield: those greater than `after` and no greater than `through`, `(after, through]`.\n *\n * Built for keyset paging: `after` is the last id the caller already has, `through` the end of its window. Each is\n * optional: left out, the read starts at the first id or runs to the last. Each is an integer in `0..4294967295`, or\n * the read throws {@link ValidationError} when it is first read. `after >= through` is an empty range, not an error,\n * because a cursor that has reached the end of its window is normal; an empty range reads nothing, not even whether\n * a combine's operands exist.\n *\n * A read with a range fetches only the chunks the range overlaps, on a combine for every operand and every\n * `exclude`. The per-op budget is charged once, before the first fetch, for every chunk in the range, so with\n * `after` alone it is charged to the end of the segment however early the caller stops; pass `through` to bound it.\n */\nexport interface IdRange {\n /** Exclusive lower bound: the read yields only ids greater than this. */\n readonly after?: number | undefined;\n /** Inclusive upper bound: the read yields only ids up to and including this. */\n readonly through?: number | undefined;\n}\n\n/** Options common to the chunk-aligned combines. */\nexport interface CombineOptions extends IdRange {\n /**\n * Max chunk keys resolved concurrently \u2014 bounds the Storage footprint (about `concurrency \u00D7 operands` chunks in\n * flight). A positive integer; default 32. The window opens 8 keys wide (or `concurrency`, if lower) and widens\n * as keys are taken, so a read that stops early fetches no more than a few keys ahead; one that runs to the end\n * spends nearly all of its round trips at the full width. A read that stops early has requested up to\n * `concurrency` keys per operand past the last one it used.\n */\n readonly concurrency?: number;\n /** Override the store's per-op budget for this call (`false` lifts it). */\n readonly budget?: BudgetOption;\n /** Segments whose ids are subtracted from the result. */\n readonly exclude?: readonly SegmentRef[];\n /**\n * Allow an operand that names a segment which does not exist. Default `false` \u2014 a combine refuses one,\n * because a misspelled or mis-namespaced operand is indistinguishable from a correct one in the result.\n * Set `true` when you genuinely intend to combine against a name that may not have been created yet.\n */\n readonly allowAbsentOperands?: boolean;\n}\n\n/** One resolved operand of a chunk-aligned combine: its chunk-key set + pinned generation. */\ninterface Operand {\n readonly seg: SegmentRef;\n /** The operand's chunk keys, inside the read's range when it has one. */\n readonly keys: Set<number>;\n /** Whether the segment has no chunks at all, whatever the range: what the absent-operand check judges. */\n readonly chunkless: boolean;\n /** The cache-key component \u2014 a `currentVersion` string, a generation number, or absent. */\n readonly gen: string | number | null | undefined;\n}\n\n/** A validated {@link IdRange}: inclusive id bounds, and the chunk key and remainder at each end. */\ninterface IdWindow {\n readonly loKey: number;\n readonly loRem: number;\n readonly hiKey: number;\n readonly hiRem: number;\n}\n\n/**\n * Validate a read's range. `null` when none was asked for, so the read does no extra work; `'empty'` when no id can\n * be in it.\n */\nfunction windowOf(range: IdRange | undefined): IdWindow | 'empty' | null {\n const after = range?.after;\n const through = range?.through;\n if (after === undefined && through === undefined) return null;\n for (const [name, bound] of [\n ['after', after],\n ['through', through],\n ] as const) {\n if (bound !== undefined && (!Number.isInteger(bound) || bound < 0 || bound > U32_MAX)) {\n const got = typeof bound === 'number' ? String(bound) : `a ${typeof bound}`;\n throw new ValidationError(`${name} must be an integer in 0..${U32_MAX}; got ${got}`);\n }\n }\n const lo = after === undefined ? 0 : after + 1;\n const hi = through ?? U32_MAX;\n if (lo > hi) return 'empty';\n return {\n loKey: Math.floor(lo / CHUNK_COUNT),\n loRem: lo % CHUNK_COUNT,\n hiKey: Math.floor(hi / CHUNK_COUNT),\n hiRem: hi % CHUNK_COUNT,\n };\n}\n\n/** The window a range with neither bound set describes: every id. */\nconst WHOLE_ID_SPACE: IdWindow = {\n loKey: 0,\n loRem: 0,\n hiKey: CHUNK_COUNT - 1,\n hiRem: MAX_REMAINDER,\n};\n\n/**\n * Whether the range cuts inside `chunkKey`: one of its two edge chunks, and only where the bound falls inside the\n * chunk rather than on its boundary. A chunk the range covers whole is yielded by the plain loop.\n */\nconst isEdge = (chunkKey: number, w: IdWindow): boolean =>\n (chunkKey === w.loKey && w.loRem > 0) || (chunkKey === w.hiKey && w.hiRem < MAX_REMAINDER);\n\n/** The ids of an edge chunk that lie inside the window. The chunk iterates ascending, so it stops at the top. */\nfunction* edgeIds(chunk: CodecBitmap, chunkKey: number, w: IdWindow): Generator<number> {\n const from = chunkKey === w.loKey ? w.loRem : 0;\n const to = chunkKey === w.hiKey ? w.hiRem : MAX_REMAINDER;\n for (const remainder of chunk) {\n if (remainder > to) return;\n if (remainder >= from) yield joinId(chunkKey, remainder);\n }\n}\n\n/** The ascending `keys` inside the window's chunk span: two binary searches, no scan. */\nfunction keysWithin(keys: readonly number[], w: IdWindow): number[] {\n const firstAtLeast = (key: number): number => {\n let lo = 0;\n let hi = keys.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (keys[mid]! < key) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n return keys.slice(firstAtLeast(w.loKey), firstAtLeast(w.hiKey + 1));\n}\n\n/** A chunk read in flight: `token` tells the read whether its entry is still the registered one. */\ninterface OpenRead {\n readonly token: object;\n readonly read: Promise<CodecBitmap | null>;\n}\n\nexport class SegmentEngine {\n private readonly storage: StorageChunkSource;\n private readonly cache: BoundedLru<string, CodecBitmap> | undefined;\n private readonly codec: CodecInterface;\n private readonly maxBitmapBytes: number;\n private readonly clock: Pick<Clock, 'now'>;\n private readonly metrics: IMetricsSink;\n private readonly metricsOn: boolean;\n /** Resolved per-op budget (null = disabled); undefined deps \u21D2 the generous default. */\n private readonly budget: Budget | null;\n /**\n * The chunk reads open right now, by cache key: a caller that misses the cache and finds its key here awaits that\n * read instead of making its own. An entry lives from the request until it settles, so the map holds at most one\n * promise per distinct key in flight.\n */\n private readonly openReads = new Map<string, OpenRead>();\n\n constructor(deps: EngineDeps) {\n this.storage = deps.storage;\n this.cache = deps.cache;\n this.codec = deps.codec;\n this.maxBitmapBytes = deps.maxBitmapBytes ?? DEFAULT_MAX_BITMAP_BYTES;\n this.clock = deps.clock ?? ZERO_CLOCK;\n // undefined \u21D2 the generous default; null \u21D2 explicitly disabled (must not be re-defaulted by `??`).\n this.budget = deps.budget === undefined ? DEFAULT_BUDGET : deps.budget;\n // Wrap for defense-in-depth (idempotent for the no-op) so even a direct engine construction with a\n // throwing sink can't break the data path; `metricsOn` short-circuits all emission when unused.\n this.metrics = safeMetrics(deps.metrics ?? NOOP_METRICS);\n this.metricsOn = this.metrics !== NOOP_METRICS;\n }\n\n /** Membership: one chunk lookup \u2014 the cache, else one Storage fetch of that chunk. */\n async has(seg: SegmentRef, id: number): Promise<boolean> {\n const { chunkKey, remainder } = splitId(id);\n const chunk = await this.storageChunk({ ...seg, chunkKey }, await this.cacheVersion(seg));\n return chunk ? chunk.has(remainder) : false;\n }\n\n /**\n * Cardinality. When the Storage source can describe the current generation (the `.crbm` source can), the count is\n * that description's: the registry row's summary when the row has one it can use, which is one request when cold\n * and no read of the object, and otherwise the sum of the object's index. Either way, **zero payload reads**. A\n * source that can only serve per-chunk cardinality sums those, and one with neither (the in-memory source) falls back\n * to fetching every chunk.\n *\n * The row's summary and the index sum are trusted, not confirmed: the source holds the summary against the object\n * whenever it opens it, and the reader checked the index for internal consistency when it opened\n * the object, and a corrupt index that is still internally consistent yields a wrong count. `iterate()` and the\n * combines decode the payloads.\n */\n async count(seg: SegmentRef): Promise<number> {\n if (this.storage.summary) return (await this.storage.summary(seg))?.cardinality ?? 0;\n const cardinalities = this.storage.cardinalities ? await this.storage.cardinalities(seg) : null;\n if (cardinalities) {\n let total = 0;\n for (const [k, n] of cardinalities) {\n this.assertChunkKeyInRange(k);\n total += n;\n }\n return total;\n }\n const chunkKeys = await this.chunkKeys(seg);\n checkBudget(this.budget, chunkKeys.length, 'count'); // one storage fetch per chunk (before fan-out)\n const gen = await this.cacheVersion(seg); // after the shape read \u2014 see `combine`\n // Order does not matter to a sum, but the window hands chunks back in key order: no more state than that.\n const window = this.chunkWindow(seg, chunkKeys, gen, false);\n let total = 0;\n for (let i = 0; i < chunkKeys.length; i++) total += (await window.take())?.size ?? 0;\n return total;\n }\n\n /**\n * What the segment's current generation is: its number, its id count and its metadata, from one resolution, so the\n * three cannot straddle a publish. A source with no summary answers from `count()` and the generation it resolves.\n */\n async stat(\n seg: SegmentRef,\n ): Promise<{ generation: number | null; cardinality: number; metadata?: GenerationMetadata }> {\n if (this.storage.summary) {\n return (await this.storage.summary(seg)) ?? { generation: null, cardinality: 0 };\n }\n const generation = this.storage.currentGeneration\n ? await this.storage.currentGeneration(seg)\n : null;\n return { generation, cardinality: await this.count(seg) };\n }\n\n /** Whether the Storage source can measure segment size (for grounded cost); false \u21D2 storage isn't grounded. */\n get supportsStorageSize(): boolean {\n return typeof this.storage.sizeOf === 'function';\n }\n\n /** How often the Storage source re-reads a pointer while reading (cost reporting); undefined if it does not say. */\n get pointerRefreshMs(): number | undefined {\n return this.storage.pointerRefreshMs;\n }\n\n /** Grounded Storage size of a segment's current generation (cost reporting), or null if it has no generation. */\n segmentSize(seg: SegmentRef): Promise<SegmentSize | null> {\n return this.storage.sizeOf ? this.storage.sizeOf(seg) : Promise.resolve(null);\n }\n\n /** The chunks of `seg` at `chunkKeys`, read through a window of {@link DEFAULT_INTERSECT_CONCURRENCY}. */\n private chunkWindow(\n seg: SegmentRef,\n chunkKeys: readonly number[],\n gen: string | number | null | undefined,\n ramp: boolean,\n ): ChunkWindow<CodecBitmap | null> {\n return new ChunkWindow(\n chunkKeys,\n (chunkKey) => this.storageChunk({ ...seg, chunkKey }, gen),\n DEFAULT_INTERSECT_CONCURRENCY,\n ramp,\n );\n }\n\n /**\n * Every id, ascending, reading ahead through a window of up to 32 chunk fetches that opens 1, 2, 4 \u2026 32 wide, so a\n * read that stops early has fetched only a handful of chunks past the one it stopped in; with `range`, only the ids in `(after, through]`, fetching only the\n * chunks the range overlaps (see {@link IdRange}).\n *\n * Two generators, not one with a branch: a second `yield` site in the full read's generator grows its frame, and\n * every id of a read that asked for no range paid for it (measured at about 5% per id). The full read keeps the\n * loop it always had.\n */\n iterate(seg: SegmentRef, range?: IdRange): AsyncGenerator<number> {\n return range === undefined ? this.iterateAll(seg) : this.iterateRange(seg, range);\n }\n\n private async *iterateAll(seg: SegmentRef): AsyncGenerator<number> {\n const chunkKeys = await this.chunkKeys(seg);\n checkBudget(this.budget, chunkKeys.length, 'iterate'); // one storage fetch per chunk (before fan-out)\n const gen = await this.cacheVersion(seg); // after the shape read \u2014 see `combine`\n const window = this.chunkWindow(seg, chunkKeys, gen, true);\n for (const chunkKey of chunkKeys) {\n const chunk = await window.take();\n if (chunk === null) continue;\n // Read straight off the (possibly cached) instance: iteration does not mutate it.\n for (const remainder of chunk) yield joinId(chunkKey, remainder);\n }\n }\n\n private async *iterateRange(seg: SegmentRef, range: IdRange): AsyncGenerator<number> {\n const w = windowOf(range) ?? WHOLE_ID_SPACE;\n if (w === 'empty') return;\n const chunkKeys = keysWithin(await this.chunkKeys(seg), w);\n checkBudget(this.budget, chunkKeys.length, 'iterate'); // one storage fetch per chunk (before fan-out)\n const gen = await this.cacheVersion(seg); // after the shape read \u2014 see `combine`\n const window = this.chunkWindow(seg, chunkKeys, gen, true);\n for (const chunkKey of chunkKeys) {\n const chunk = await window.take();\n if (chunk === null) continue;\n if (isEdge(chunkKey, w)) {\n for (const id of edgeIds(chunk, chunkKey, w)) yield id;\n } else {\n for (const remainder of chunk) yield joinId(chunkKey, remainder);\n }\n }\n }\n\n /**\n * `segs[0] \u2229 segs[1] \u2229 \u2026`, minus every id in `options.exclude` \u2014 streamed ascending.\n *\n * The `exclude` operands are what make suppression **compose without materialising**. Applying suppression\n * afterwards (intersect into a temp segment, then subtract) writes an intermediate segment that nobody wants;\n * applying it here folds it into the same chunk-aligned pass. It is also cheaper than it looks: an exclude\n * operand is read **only at the keys that survived the intersection** \u2014 the cost is bounded by the\n * audience's surviving key count and **never scales with the size of the suppression list**. Subtracting a\n * 61,000-chunk global opt-out list costs at most one read per surviving key, not 61,000.\n *\n * ---\n *\n * Chunk-skipping intersection. Aligns each segment's chunk-key set (read from the `.crbm` index, no payload),\n * keeps only keys present in **all** operands (a key missing from any operand can't contribute \u2192 its Storage\n * chunks are never fetched \u2014 the core saving), then for each surviving key fetches the operands' chunks in\n * parallel and hands them to the codec for the AND, streaming results through a bounded in-flight window.\n *\n * **Memory:** the Storage payload footprint is bounded by the window (`concurrency \u00D7 operands \u00D7 chunk`), not by\n * segment size \u2014 that's the Lambda-friendly property.\n *\n * Generation-consistent within the call (normal case): each operand's current generation is resolved **once**\n * up front (before the fan-out) and threaded into every chunk read, so a concurrent load can't corrupt or tear\n * the result \u2014 every chunk read is a whole, checksum-verified, immutable generation. The edge a *long* call can\n * hit is invariant 3's: if it straddles a mid-call `cache.genTtlMs` boundary and a load has published, an\n * operand's not-yet-requested chunks may re-resolve forward to the newer generation (a generation hop within one long\n * call) \u2014 the call never crashes or returns a torn object, but may mix generations. Three things hop it without\n * waiting for the TTL, so a shorter call can meet them too: **the reader cache evicting an operand mid-call**\n * (`maxOpenSegments`), whose re-read re-resolves fresh; a sweep deleting the generation it was reading, which\n * heals the read forward; and an invalidation, which this store's own `load`, `rollback` and `eraseSubject` make\n * and `invalidate()` makes on request. Still whole/immutable per read, never torn.\n */\n intersect(segs: readonly SegmentRef[], options?: CombineOptions): AsyncGenerator<number> {\n return this.combine(segs, options?.exclude ?? [], 'all', 'intersect', options);\n }\n\n /**\n * `segs[0] \u222A segs[1] \u222A \u2026`, minus every id in `options.exclude` \u2014 streamed ascending.\n *\n * **Union is the one composite read with no chunk-skipping**, and that is a property of the operation rather\n * than of this implementation: an id in *any* operand belongs to the result, so every chunk of every operand\n * has to be read. `intersect` can prune a key absent from any operand; union cannot prune anything. It is\n * budgeted exactly like `intersect` so a wide union is refused rather than quietly billed.\n */\n union(segs: readonly SegmentRef[], options?: CombineOptions): AsyncGenerator<number> {\n return this.combine(segs, options?.exclude ?? [], 'any', 'union', options);\n }\n\n /**\n * `seg \\ (excludes[0] \u222A excludes[1] \u222A \u2026)` \u2014 streamed ascending. Suppression on its own.\n *\n * Reads every chunk of `seg` (any of them may survive) but each exclude **only where it overlaps `seg`**, so\n * the cost scales with the segment being filtered rather than with the size of the suppression list.\n */\n andNot(\n seg: SegmentRef,\n excludes: readonly SegmentRef[],\n options?: Omit<CombineOptions, 'exclude'>,\n ): AsyncGenerator<number> {\n return this.combine([seg], excludes, 'all', 'andNot', options);\n }\n\n private async *combine(\n segs: readonly SegmentRef[],\n excludeSegs: readonly SegmentRef[],\n mode: 'all' | 'any',\n op: 'intersect' | 'union' | 'andNot',\n options?: Omit<CombineOptions, 'exclude'>,\n ): AsyncGenerator<number> {\n // Validated HERE rather than in the public wrappers, deliberately. `combine` is an async generator, so a\n // throw surfaces when the caller first iterates \u2014 which is the behaviour `intersect` has always had, and\n // `await expect(collect(engine.intersect([]))).rejects` in the suite depends on it. Guarding in the\n // wrappers would make them throw synchronously instead, a silent breaking change for anyone whose\n // try/catch sits around the iteration. Delegating through `yield*` would restore it at the cost of a\n // microtask per id on the hottest streaming path, which is not a trade worth making for an error case.\n if (segs.length === 0) {\n throw new ValidationError(`${op} requires at least one segment`);\n }\n if (op === 'andNot' && excludeSegs.length === 0) {\n throw new ValidationError('andNot requires at least one segment to subtract');\n }\n const limit = options?.concurrency ?? DEFAULT_INTERSECT_CONCURRENCY;\n if (!Number.isInteger(limit) || limit < 1) {\n throw new ValidationError(\n `concurrency must be a positive integer; got ${options?.concurrency}`,\n );\n }\n // Per-op budget override (else the store budget); resolved before any I/O so a bad value fails fast. A\n // partial override inherits the store's tightening (not the generous default) \u2014 see resolvePerOpBudget.\n const budget = resolvePerOpBudget(options?.budget, this.budget);\n // The range, if any, cuts every operand's keys once, here, so what follows \u2014 alignment, the budget, the\n // metrics, the fan-out \u2014 sees only chunks inside it. Only the two edge chunks are cut again, where ids leave.\n const w = windowOf(options);\n if (w === 'empty') return;\n\n // \u2460 Index-map extraction: each operand's chunk-key set + its current generation, resolved ONCE here (not\n // per chunk) so the fan-out below adds no per-chunk generation re-resolve. Only metadata so far.\n //\n // Generation resolution ordering: resolve `gen` AFTER the shape read (`listChunkKeys`). A non-empty shape\n // read means the source resolved and cached a non-null snapshot, so `gen` cannot then come back null while\n // storage data is present, and `storageChunk` cannot skip every chunk on a stale null.\n //\n // Honest note on how much this ordering buys, so a future reader does not take an untested claim for a tested\n // one. Against a `CrbmStorageChunkSource` \u2014 the\n // only source with a generation to resolve \u2014 both reads go through the same resolved-reader memo, so the\n // pathological state (`keys` non-empty, `gen` null) does not arise and swapping the two is observably\n // identical. The ordering is kept because it is the order that is correct for *any* source\n // satisfying the port \u2014 a custom source that resolves its shape and its generation independently can still\n // produce that state \u2014 not because a test can currently tell the difference.\n //\n // Within a call shorter than `cache.genTtlMs` the two share one snapshot, so the read is\n // generation-consistent \u2014 absent cache-pressure eviction (see `intersect`).\n const extract = async (seg: SegmentRef): Promise<Operand> => {\n const all = await this.chunkKeys(seg);\n const gen = await this.cacheVersion(seg);\n const keys = w === null ? all : keysWithin(all, w);\n return { seg, keys: new Set(keys), chunkless: all.length === 0, gen };\n };\n const [operands, excludes] = await Promise.all([\n Promise.all(segs.map(extract)),\n Promise.all(excludeSegs.map(extract)),\n ]);\n await this.refuseAbsentOperands([...operands, ...excludes], options?.allowAbsentOperands, op);\n\n // \u2461 Key alignment. Candidate keys come from the INCLUDE operands only \u2014 an exclude can subtract ids from a\n // chunk but can never introduce one, so a key no include holds cannot appear in the result however large\n // the suppression list is. That asymmetry is what makes suppression cheap here.\n //\n // 'all' (intersect / andNot): keys present in EVERY include. Start from the smallest set to minimize the\n // scan \u2014 a key missing from any operand cannot survive the AND.\n // 'any' (union): keys present in ANY include. No pruning is possible; see `union`'s note.\n const candidates: number[] = [];\n if (mode === 'all') {\n const pivot = operands.reduce((a, b) => (a.keys.size <= b.keys.size ? a : b));\n for (const k of pivot.keys) {\n if (operands.every((o) => o.keys.has(k))) candidates.push(k);\n }\n } else {\n const seen = new Set<number>();\n for (const o of operands) for (const k of o.keys) seen.add(k);\n for (const k of seen) candidates.push(k);\n }\n candidates.sort((a, b) => a - b); // defensive: `chunkKeys` already returns sorted; don't rely on it\n const common = candidates;\n\n // Denial-of-wallet budget (before the fan-out AND before we emit a \"work done\" metric): the heavy cost is\n // one storage fetch per surviving key per operand \u2014 refuse up front if that would exceed the budget. Placed\n // above the metric so a refused intersect doesn't report chunks it never fetched.\n //\n // Units are the chunk reads this will actually issue, counted per key: every include present at that key\n // (all of them under 'all'), plus only those excludes that hold the key. Counting excludes as always-present\n // would refuse work that costs nothing \u2014 the whole point of the asymmetry above. For a plain `intersect`\n // with no excludes this reduces to `common.length * operands.length`.\n let units = 0;\n for (const key of common) {\n for (const o of operands) if (mode === 'all' || o.keys.has(key)) units += 1;\n for (const e of excludes) if (e.keys.has(key)) units += 1;\n }\n checkBudget(budget, units, op);\n\n // Emit the chunk-skipping efficiency: `fetched` = shared keys we'll read, `skipped` = distinct keys\n // pruned across all operands (never fetched \u2014 the core saving). Bounded metadata work, no extra I/O.\n if (this.metricsOn) {\n const distinctKeys = new Set<number>();\n for (const o of operands) for (const k of o.keys) distinctKeys.add(k);\n for (const e of excludes) for (const k of e.keys) distinctKeys.add(k);\n // `fetchedChunks` counts **distinct chunk keys**, not per-operand reads \u2014 its documented unit, asserted by\n // the bench anchors and the metrics tests. It deliberately differs from the budget's request count: they\n // measure different things on purpose (keys vs billable requests). The\n // relationship is documented on the event instead.\n this.metrics.onEvent({\n kind: 'intersect',\n op,\n operands: segs.length + excludes.length,\n fetchedChunks: common.length,\n // Never negative: under 'any' every candidate key came from some operand, so `distinctKeys` is a\n // superset of `common`; excludes only ever add keys here.\n skippedChunks: distinctKeys.size - common.length,\n });\n }\n\n // \u2462\u2013\u2464 Surgical streaming AND through a bounded, order-preserving window: fetch+intersect at most\n // `limit` keys concurrently, yield each key's ids before priming far ahead (bounded Storage footprint).\n // Each task resolves to a value (never rejects) so an error on one key can't leave the other in-flight\n // promises unhandled \u2014 we surface it, in key order, when its slot is drained.\n //\n // The window opens COMBINE_WINDOW_START keys wide and doubles with each key taken until it is `limit` wide,\n // so a read that stops after its first few keys has fetched no further ahead than that, while a long one\n // spends nearly all of its round trips at the full width.\n type Slot = { key: number; result: CodecBitmap | null; error?: unknown };\n const startAt = (key: number): Promise<Slot> =>\n this.combineChunk(operands, excludes, mode, key).then(\n (result) => ({ key, result }),\n (error: unknown) => ({ key, result: null, error }),\n );\n\n const inFlight: Array<Promise<Slot>> = [];\n let next = 0;\n let taken = 0;\n const fill = (): void => {\n const width = Math.min(limit, Math.max(COMBINE_WINDOW_START, 2 ** Math.min(taken, 30)));\n while (next < common.length && inFlight.length < width)\n inFlight.push(startAt(common[next++]!));\n };\n fill();\n while (inFlight.length > 0) {\n const slot = await inFlight.shift()!; // FIFO over ascending keys \u21D2 ascending output\n taken += 1;\n fill();\n if (slot.error !== undefined) throw slot.error;\n if (slot.result && !slot.result.isEmpty) {\n if (w !== null && isEdge(slot.key, w)) {\n for (const id of edgeIds(slot.result, slot.key, w)) yield id;\n } else {\n for (const remainder of slot.result) yield joinId(slot.key, remainder);\n }\n }\n }\n }\n\n /**\n * The AND (or OR) of one chunk key across all operands, minus the excludes that hold it, or `null` if empty.\n * Operand chunks are fetched **in parallel** (the spec's parallel byte-range reads).\n *\n * The accumulator is a **clone** of the first operand's chunk, and that clone is load-bearing: every other\n * bitmap here is the cached, shared Storage instance and must never be mutated, or the cache is poisoned for\n * every later reader. The remaining operands and the excludes are only *read* by the in-place ops, so they are\n * used as-is \u2014 one clone per key, not one per operand.\n */\n private async combineChunk(\n operands: ReadonlyArray<Operand>,\n excludes: ReadonlyArray<Operand>,\n mode: 'all' | 'any',\n chunkKey: number,\n ): Promise<CodecBitmap | null> {\n // Under 'any' an include may simply not hold this key; skip it rather than fetching a certainly-empty\n // chunk. Under 'all' every include holds every candidate key by construction, so the filter is a no-op.\n const present = mode === 'all' ? operands : operands.filter((o) => o.keys.has(chunkKey));\n if (present.length === 0) return null;\n // Only excludes that actually hold this key are fetched \u2014 this is where a large global opt-out list stops\n // being expensive. An AND of two or more includes can come out empty, leaving nothing to suppress, so their\n // excludes wait for it. One include, or an OR, cannot (every key here is one an include holds), so waiting\n // would only cost a round trip: those excludes are read in the same round as the includes.\n const relevant = excludes.filter((e) => e.keys.has(chunkKey));\n const together = relevant.length > 0 && (mode === 'any' || present.length === 1);\n const reads = together ? [...present, ...relevant] : present;\n const fetched = await Promise.all(\n reads.map((op) => this.storageChunk({ ...op.seg, chunkKey }, op.gen)),\n );\n const chunks = together ? fetched.slice(0, present.length) : fetched;\n // A key the index lists but the source cannot produce bytes for reads as empty \u2014 and under AND an empty\n // operand empties the result.\n if (mode === 'all' && chunks.some((c) => c === null)) return null;\n const first = chunks[0];\n const acc = first ? first.clone() : this.codec.empty();\n for (let i = 1; i < chunks.length; i++) {\n const other = chunks[i];\n if (other === null || other === undefined) continue; // 'any' only \u2014 'all' returned above\n if (mode === 'all') {\n acc.andInPlace(other);\n if (acc.isEmpty) return null; // once empty, the remaining operands can't revive it\n } else {\n acc.orInPlace(other);\n }\n }\n if (acc.isEmpty) return null;\n\n // Suppression, folded into the same pass.\n if (relevant.length > 0) {\n const cuts = together\n ? fetched.slice(present.length)\n : await Promise.all(relevant.map((e) => this.storageChunk({ ...e.seg, chunkKey }, e.gen)));\n for (const cut of cuts) {\n if (cut === null) continue;\n acc.andNotInPlace(cut);\n if (acc.isEmpty) return null;\n }\n }\n return acc;\n }\n\n /** The segment's chunk keys, ascending \u2014 a shape read off the index, no payload. Keys are untrusted (invariant 5). */\n private async chunkKeys(seg: SegmentRef): Promise<number[]> {\n const keys = [...(await this.storage.listChunkKeys(seg))];\n for (const k of keys) this.assertChunkKeyInRange(k);\n keys.sort((a, b) => a - b);\n // A key listed twice would be read and yielded twice; the `.crbm` reader refuses one, a custom source may not.\n for (let i = 1; i < keys.length; i++) {\n if (keys[i] === keys[i - 1]) {\n throw new IntegrityError(`chunk key from a tier is listed twice: ${keys[i]}`);\n }\n }\n return keys;\n }\n\n /**\n * Tier-derived keys are untrusted (invariant 5) \u2014 fail fast on a corrupt/out-of-range key rather than\n * letting it flow into the id-routing step and produce a bogus id.\n */\n private assertChunkKeyInRange(k: number): void {\n if (!Number.isInteger(k) || k < 0 || k >= CHUNK_COUNT) {\n throw new IntegrityError(`chunk key from a tier is out of range: ${k}`);\n }\n }\n\n /**\n * The other half of invariant 5: a chunk payload holds **remainders** \u2014 16-bit offsets within one chunk \u2014 so\n * every value must be `<= 0xffff`.\n *\n * Nothing upstream establishes that. The byte cap bounds *size*, and CRC/AEAD prove the bytes are the bytes\n * that were written \u2014 which anyone able to write the bucket satisfies trivially. A value `>= 65536` would then\n * reach the id-routing step, which masks it (`remainder & 0xffff`) and emits a **fabricated id belonging to a different\n * chunk's id space**: indistinguishable from real data, inflating `count()` and creating spurious `intersect`\n * matches.\n *\n * Costs one `maximum()` per chunk, not per id \u2014 `maximum` is optional on the codec seam precisely so a codec\n * that cannot answer in O(1) opts out instead of making the read path walk every value.\n *\n * One value stands for all of them only because the decode is structurally checked. Roaring answers `maximum()`\n * from its last container, so a payload listing its containers out of order would report the wrong one's\n * largest value and pass here while holding values above 65,535. The codec's `safeDeserialize` refuses that\n * payload, and every other shape that would make this answer wrong, before it reaches this check.\n */\n private assertChunkPayloadInRange(bitmap: CodecBitmap, chunkKey: number): void {\n const max = bitmap.maximum?.();\n if (max !== undefined && max > MAX_REMAINDER) {\n throw new IntegrityError(\n `chunk ${chunkKey} payload holds value ${max}, outside the 16-bit remainder range ` +\n `[0, ${MAX_REMAINDER}] \u2014 the stored object is corrupt or was not written by this codec`,\n );\n }\n }\n\n /**\n * Refuse a combine whose operand names a segment that was never created.\n *\n * A segment that resolves to nothing is ambiguous in a way that matters only here. Read it directly and\n * \"empty\" is the right answer either way. Pass it as an **operand** and the two states diverge: a\n * suppression list with nobody on it correctly suppresses nothing, while one whose namespace you omitted\n * *silently* suppresses nothing \u2014 and the result is not empty and obviously wrong, it is the full audience\n * and plausibly right. That direction is the dangerous one: the failure mode is mailing the people who\n * opted out.\n *\n * The asymmetry is why this is checked rather than left to the caller. A mistyped **include** collapses an\n * intersect to nothing, which you notice; a mistyped **exclude** removes a safeguard, which you do not. Both\n * are refused, because both are wiring errors and one rule is easier to trust than two.\n *\n * **Costs nothing on a normal combine.** `exists` is consulted only for an operand that resolved to zero\n * chunks \u2014 a segment with data is self-evidently registered \u2014 so the check is a registry read exactly when\n * a combine was about to do something suspicious, and no reads at all otherwise. A source that cannot answer\n * `exists` (it has no notion of registration) skips the check rather than guessing. An operand is judged by\n * all its chunks, not those inside a range: one with nothing in range but data elsewhere is not suspicious.\n */\n private async refuseAbsentOperands(\n resolved: readonly Operand[],\n allow: boolean | undefined,\n op: string,\n ): Promise<void> {\n if (allow === true || this.storage.exists === undefined) return;\n const empty = resolved.filter((o) => o.chunkless);\n if (empty.length === 0) return;\n const checked = await Promise.all(\n empty.map(async (o) => ({ seg: o.seg, exists: await this.storage.exists!(o.seg) })),\n );\n const absent = checked.filter((c) => !c.exists).map((c) => c.seg);\n if (absent.length === 0) return;\n const named = absent\n .map((s) => (s.namespace === undefined ? `\"${s.segment}\"` : `\"${s.namespace}/${s.segment}\"`))\n .join(', ');\n throw new ValidationError(\n `${op}: operand ${named} names a segment that does not exist, so it would contribute nothing \u2014 ` +\n `an exclude would suppress no ids and an include would contribute none. Check the name and the ` +\n `namespace (a segment addressed without its \\`namespace\\` is a DIFFERENT segment). ` +\n `Pass \\`allowAbsentOperands: true\\` if you meant it.`,\n );\n }\n\n /**\n * Drop every piece of state this engine derived from `ref`, and tell the Storage source to do the same.\n *\n * The decoded-chunk cache is keyed by generation, which handles a *publish* (the new generation misses) but\n * not a *destruction*: a segment that was erased from, dropped, shredded or retired has no newer generation\n * whose key would miss, so its decoded chunks stay resident and keep answering. That is why the erasing\n * process itself could report `erased: true` and then still answer `true` for the same id, out of RAM, with\n * no storage read to intercept.\n *\n * Called by the destructive verbs on the facade. Synchronous and best-effort \u2014 forgetting cannot fail.\n */\n invalidate(ref: SegmentRef): void {\n const prefix = segmentPrefix(ref);\n this.cache?.deleteWhere((key) => key.startsWith(prefix));\n // A read already open was asked for before this call, so a caller after it must not join it.\n for (const key of this.openReads.keys()) if (key.startsWith(prefix)) this.openReads.delete(key);\n this.storage.invalidate?.(ref);\n }\n\n /** The segment's current generation, resolved once per op (`undefined` \u21D2 source can't report it). */\n private currentGen(seg: SegmentRef): Promise<number | null | undefined> {\n return this.storage.currentGeneration\n ? this.storage.currentGeneration(seg)\n : Promise.resolve(undefined);\n }\n\n /**\n * The cache-key component identifying **which bytes** this op will read: the source's `currentVersion` when\n * it has one, else the generation alone.\n *\n * The generation alone is not an identity. A load's generation number restarts at 0 once a row is purged\n * and the bucket emptied, so a retired-and-re-created name serves different data at the same `currentGen` \u2014 and a\n * decoded chunk cached under `(segment, chunk, 0)` is handed straight back to the new incarnation. That is an\n * erased id reappearing with no read to intercept it, which is why this is keyed on the version rather than\n * the number.\n */\n private async cacheVersion(seg: SegmentRef): Promise<string | number | null | undefined> {\n if (this.storage.currentVersion) return this.storage.currentVersion(seg);\n return this.currentGen(seg);\n }\n\n /**\n * Decode a Storage chunk. `gen` is the segment's current generation, resolved **once per op** by the caller (not\n * per chunk \u2014 that would put a registry re-resolve on every chunk of a count/intersect). The cache is\n * keyed by it, so a load that advances the generation misses the cache and re-reads the new bytes instead of\n * serving a stale decoded chunk (an erased id can't resurrect from a cached superseded chunk). `gen === null`\n * \u21D2 the source reports no current generation \u21D2 no storage bytes for any chunk, so skip the fetch entirely.\n * `gen === undefined` \u21D2 the source can't report a generation (it only ever serves one) \u21D2 the key stays\n * generation-free. Superseded-generation entries age out under the LRU ceiling \u2014 no active purge.\n *\n * Concurrent misses of one key share **one** request, with or without a cache: the first caller starts the read,\n * later ones await it, and the entry goes when the read settles, so a failure is every waiting caller's and the\n * next call reads again. The key carries the version exactly as the cache's does, so two versions of one chunk\n * are two reads, never one.\n *\n * The returned instance is **shared** (it may be the cached one, and every caller that joined a read gets the same\n * one): callers read it or clone it, never mutate it.\n */\n private async storageChunk(\n ref: ChunkRef,\n gen: string | number | null | undefined,\n ): Promise<CodecBitmap | null> {\n if (gen === null) return null;\n const cacheKey = gen === undefined ? chunkRefKey(ref) : chunkGenKey(ref, gen);\n if (this.cache) {\n const cached = this.cache.get(cacheKey);\n if (cached) {\n if (this.metricsOn) this.metrics.onEvent({ kind: 'cache', hit: true });\n return cached;\n }\n if (this.metricsOn) this.metrics.onEvent({ kind: 'cache', hit: false });\n }\n const open = this.openReads.get(cacheKey);\n if (open) return open.read;\n const token = {};\n const entry: OpenRead = { token, read: this.fetchChunk(ref, cacheKey, token) };\n this.openReads.set(cacheKey, entry);\n // Whether it resolves or rejects the entry goes, so a later caller reads again. An invalidation may have dropped\n // this entry and a newer read taken the key: leave that one.\n const settled = (): void => {\n if (this.openReads.get(cacheKey) === entry) this.openReads.delete(cacheKey);\n };\n entry.read.then(settled, settled);\n return entry.read;\n }\n\n /** One request for one chunk, decoded, range-checked and cached. */\n private async fetchChunk(\n ref: ChunkRef,\n cacheKey: string,\n token: object,\n ): Promise<CodecBitmap | null> {\n const startedAt = this.metricsOn ? this.clock.now() : 0;\n const bytes = await this.storage.getChunk(ref);\n if (this.metricsOn) {\n this.metrics.onEvent({\n kind: 'storage.get',\n namespace: ref.namespace,\n segment: ref.segment,\n bytes: bytes ? bytes.length : 0,\n ms: Math.max(0, this.clock.now() - startedAt),\n });\n }\n if (!bytes) return null;\n const bitmap = this.codec.safeDeserialize(bytes, this.maxBitmapBytes);\n this.assertChunkPayloadInRange(bitmap, ref.chunkKey);\n // A read whose entry an invalidation dropped is not cached: its bytes may be older than what a newer read cached.\n if (this.openReads.get(cacheKey)?.token === token) this.cache?.set(cacheKey, bitmap);\n return bitmap;\n }\n}\n", "/**\n * Bounded LRU cache with optional TTL \u2014 the cache tier's memory ceiling.\n *\n * Time comes from an injected `Clock` (never `Date.now()`) so the cache is deterministic\n * under simulation.\n *\n * The cache can be bounded by entry **count** (`maxEntries`, always) and, optionally, by an aggregate\n * **byte weight** (`maxBytes`). The byte bound exists for values whose footprint is large and uneven \u2014 the\n * storage reader cache, where one entry pins a fully-parsed `.crbm` index that can be ~6 MB for a wide segment\n * yet a few hundred bytes for a small one (a count-only bound lets 1024 wide indices blow a Lambda's\n * memory even though the count is \"in bounds\"). Weights arrive **after** insertion (a cached reader is a\n * pending promise; its size is known only once it resolves), so weight is reported via {@link setWeight} and\n * eviction re-runs then.\n */\nimport type { Clock } from './determinism';\nimport { ValidationError } from './errors';\n\nexport interface LruOptions {\n /** Hard ceiling on entries; the least-recently-used is evicted past it. */\n readonly maxEntries: number;\n /**\n * Optional aggregate byte ceiling across all entries' reported weights (see {@link BoundedLru.setWeight}).\n * When set, the least-recently-used entries are evicted until the total is within budget \u2014 in addition to\n * the `maxEntries` bound. Omit for a pure count-bounded cache (weights stay 0, no byte eviction).\n */\n readonly maxBytes?: number;\n /** Optional time-to-live in ms; an entry older than this is treated as absent. */\n readonly ttlMs?: number;\n /** Only `now()` is used (never `sleep`), so a count-only cache can pass a minimal time source. */\n readonly clock: Pick<Clock, 'now'>;\n}\n\ninterface Entry<V> {\n readonly value: V;\n readonly expiresAt: number; // Infinity when no TTL\n bytes: number; // reported weight (0 until setWeight); summed against maxBytes\n}\n\nexport class BoundedLru<K, V> {\n private readonly map = new Map<K, Entry<V>>();\n private readonly maxEntries: number;\n private readonly maxBytes: number | undefined;\n private readonly ttlMs: number | undefined;\n private readonly clock: Pick<Clock, 'now'>;\n private totalBytes = 0;\n\n constructor(options: LruOptions) {\n if (!Number.isInteger(options.maxEntries) || options.maxEntries < 1) {\n throw new ValidationError(`maxEntries must be a positive integer; got ${options.maxEntries}`);\n }\n if (\n options.maxBytes !== undefined &&\n (!(options.maxBytes > 0) || !Number.isFinite(options.maxBytes))\n ) {\n throw new ValidationError(\n `maxBytes must be a positive finite number; got ${options.maxBytes}`,\n );\n }\n if (options.ttlMs !== undefined && (!(options.ttlMs > 0) || !Number.isFinite(options.ttlMs))) {\n throw new ValidationError(`ttlMs must be a positive finite number; got ${options.ttlMs}`);\n }\n this.maxEntries = options.maxEntries;\n this.maxBytes = options.maxBytes;\n this.ttlMs = options.ttlMs;\n this.clock = options.clock;\n }\n\n get(key: K): V | undefined {\n const entry = this.map.get(key);\n if (entry === undefined) return undefined;\n if (entry.expiresAt <= this.clock.now()) {\n this.drop(key, entry);\n return undefined;\n }\n // Refresh recency: re-insert so it becomes most-recently-used.\n this.map.delete(key);\n this.map.set(key, entry);\n return entry.value;\n }\n\n /** Read a value **without** counting as an access (recency unchanged, no purge on expiry). */\n peek(key: K): V | undefined {\n return this.map.get(key)?.value;\n }\n\n set(key: K, value: V): void {\n const prev = this.map.get(key);\n if (prev !== undefined) this.totalBytes -= prev.bytes;\n this.map.delete(key);\n const expiresAt = this.ttlMs === undefined ? Infinity : this.clock.now() + this.ttlMs;\n this.map.set(key, { value, expiresAt, bytes: 0 });\n while (this.map.size > this.maxEntries) {\n const oldest = this.map.keys().next();\n if (oldest.done === true) break;\n this.dropKey(oldest.value);\n }\n this.evictToBudget(key);\n }\n\n /**\n * Report an entry's byte weight (typically once a cached promise resolves and its size is known). Updates\n * the running total and evicts the least-recently-used entries if the aggregate now exceeds `maxBytes`.\n * Recency is **not** changed. The `key` being weighted is protected from this eviction pass (we just\n * populated it) \u2014 so a single entry larger than the whole budget is kept rather than immediately dropped.\n */\n setWeight(key: K, bytes: number): void {\n if (!(bytes >= 0) || !Number.isFinite(bytes)) {\n // Guard the running total against a NaN/negative weight, which no removal path could ever restore.\n throw new ValidationError(`weight must be a non-negative finite number; got ${bytes}`);\n }\n const entry = this.map.get(key);\n if (entry === undefined) return;\n this.totalBytes += bytes - entry.bytes;\n entry.bytes = bytes;\n this.evictToBudget(key);\n }\n\n private evictToBudget(protectKey: K): void {\n if (this.maxBytes === undefined) return;\n while (this.totalBytes > this.maxBytes && this.map.size > 1) {\n const oldest = this.oldestExcept(protectKey);\n if (oldest === undefined) break; // only the protected key remains\n this.dropKey(oldest);\n }\n }\n\n /** The least-recently-used key that is not `protectKey`, or undefined if none other exists. */\n private oldestExcept(protectKey: K): K | undefined {\n for (const key of this.map.keys()) {\n if (key !== protectKey) return key;\n }\n return undefined;\n }\n\n private dropKey(key: K): void {\n const entry = this.map.get(key);\n if (entry !== undefined) this.drop(key, entry);\n }\n\n private drop(key: K, entry: Entry<V>): void {\n this.totalBytes -= entry.bytes;\n this.map.delete(key);\n }\n\n /** Membership test that does NOT count as an access (recency is unchanged). Purges if expired. */\n has(key: K): boolean {\n const entry = this.map.get(key);\n if (entry === undefined) return false;\n if (entry.expiresAt <= this.clock.now()) {\n this.drop(key, entry);\n return false;\n }\n return true;\n }\n\n delete(key: K): boolean {\n const entry = this.map.get(key);\n if (entry === undefined) return false;\n this.drop(key, entry);\n return true;\n }\n\n /**\n * Delete every entry whose key matches `pred`, returning how many went. Used to drop one segment's decoded\n * chunks when something destroys the generation they came from \u2014 a deletion the generation-keyed cache\n * cannot express on its own, because a *destroyed* segment has no newer generation whose key would miss.\n * Linear in the cache size; called on admin paths (erasure, drop, shred, retirement), never on a read.\n */\n deleteWhere(pred: (key: K) => boolean): number {\n let dropped = 0;\n // Snapshot the keys first: `drop` mutates the map, and deleting during its own iteration is fragile.\n for (const key of [...this.map.keys()]) {\n if (!pred(key)) continue;\n const entry = this.map.get(key);\n if (entry === undefined) continue;\n this.drop(key, entry);\n dropped += 1;\n }\n return dropped;\n }\n\n clear(): void {\n this.map.clear();\n this.totalBytes = 0;\n }\n\n /** Current entry count (may include not-yet-evicted expired entries). */\n get size(): number {\n return this.map.size;\n }\n\n /** Current aggregate reported weight (bytes) across all entries. */\n get weightBytes(): number {\n return this.totalBytes;\n }\n}\n", "/**\n * Generation bookkeeping for the loaded store: which generation a writer should take next, and which\n * superseded generations may be collected.\n *\n * Storage generations are write-once, generation-keyed objects (`<segment>.<gen>.crbm`) behind one registry pointer\n * (`currentGen`). Every write path in the library \u2014 a load, an `*Into` materialisation, a subject-erasure rewrite \u2014\n * writes a **new** object and then advances the pointer. Every load that finds a row fences its publish on the\n * row's token; a guarded load (the default, since the empty refusal needs the size of the current generation) also fences on\n * the pointer it judged (`expectFrom`), and one that found no row fences on that absence instead. Only an unguarded\n * load (`allowEmpty: true` and no `guard.minRetained`) onto a segment with no row publishes bare forward-only. An\n * `*Into` materialisation is a load, and publishes the same way. The rewrite is fenced on its source generation and\n * the row's token (see invariant 1). That leaves the superseded object in the bucket, still billed, so something has\n * to collect it: {@link gcOrphanGenerations}, or, for a load that numbered its generation with one existence check\n * and keeps at most one generation, {@link collectByName}. Pure orchestration over the driver ports \u2014 no I/O, time or\n * randomness of its own.\n */\nimport { ValidationError, WriteConflictError, isNotFoundError } from './errors';\nimport type { GenKey, IStorageDriver, IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\n\n/** The two ports generation bookkeeping needs: the objects, and the pointer that says which one is current. */\nexport interface GenerationDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n}\n\n/**\n * The generation number a writer should use for the segment's **next** object: one above the highest generation\n * the registry points at *or* that is present in Storage \u2014 whichever is higher. The erasure rewrite numbers this\n * way; a load numbers with {@link nextLoadGeneration}, which lists as this does when its existence check finds the\n * number taken or cannot answer.\n *\n * Both are consulted because they can disagree. A load that wrote its object and crashed before publishing leaves\n * an object *above* `currentGen`; a writer that consulted only the pointer would pick that same number and hit the\n * write-once conflict on every retry. Skipping past it costs one `list` and keeps the retry trivial \u2014 the crashed\n * object is an orphan {@link gcOrphanGenerations} collects once the pointer moves on. Reusing such an object\n * instead of skipping it (verify-and-publish) is `load()`'s job, not a numbering helper's.\n *\n * A segment with no row and no objects starts at 0. An admin/write-path helper, never on the read path.\n */\nexport async function nextGeneration(ref: SegmentRef, deps: GenerationDeps): Promise<number> {\n return aboveEverything(ref, deps, await deps.registry.get(ref));\n}\n\n/** One above `record`'s pointer and above every generation listed in Storage, whichever is higher. */\nasync function aboveEverything(\n ref: SegmentRef,\n deps: GenerationDeps,\n record: RegistryRecord | null,\n): Promise<number> {\n let highest = record?.currentGen ?? -1;\n for await (const key of deps.storage.list(ref)) {\n if (key.generation > highest) highest = key.generation;\n }\n return highest + 1;\n}\n\n/** The number a load takes, and whether one existence check proved it free (so nothing is above the pointer). */\nexport interface LoadNumber {\n readonly generation: number;\n /**\n * `true` when the number is `currentGen + 1` and the check found no object holding it. Collection by name rests\n * on it: the row named `generation - 1` as current, so every number the window evicts is below it, and nothing\n * the listing would have kept is a name the pass takes. `false` when the listing numbered the load.\n */\n readonly checked: boolean;\n}\n\n/**\n * The generation number a load takes, from the row it has already read: `currentGen + 1` (0 with no row or no\n * pointer yet) when no object holds that number, which one existence check proves (a zero-byte `getTail`, one\n * metadata request on every shipped driver); otherwise one above the pointer and above everything listed, as\n * {@link nextGeneration} numbers. The check finding the number taken (a crashed load's orphan, the object of a\n * load still in flight, or the generations a rollback left above the pointer) and the check failing in any way\n * other than \"not found\" both take the listing, so the listing stays the authority whenever the check cannot\n * prove the number free. A failed check falls back silently: nothing records it, and what it costs is the listing,\n * one more PUT-class request on S3, so a fault that makes every check fail shows only in the bill.\n *\n * A load can therefore take a number **below** an object already in the bucket: an orphan at `currentGen + 2`\n * with `currentGen + 1` free is numbered under, not past. That is as safe as a listing's number. A load never\n * takes a number an object holds, and a put that races onto one fails write-once, so the load reports\n * `superseded`. The orphan above stays unpublished; the load whose check meets it numbers past it by the listing,\n * and collection takes it once a generation above it is current. Like a listing's, the number can be one whose\n * object was deleted (an erasure removes the generations above a rolled-back pointer that held the id), which is\n * why nothing identifies a generation by its number alone: the reader cache, the chunk cache and a pin all key on\n * the number and the row's token, and the token has moved on.\n */\nexport async function nextLoadGeneration(\n ref: SegmentRef,\n deps: GenerationDeps,\n row: RegistryRecord | null,\n): Promise<LoadNumber> {\n const generation = (row?.currentGen ?? -1) + 1;\n try {\n await deps.storage.getTail({ namespace: ref.namespace, segment: ref.segment, generation }, 0);\n } catch (err) {\n if (isNotFoundError(err)) return { generation, checked: true };\n }\n return { generation: await aboveEverything(ref, deps, row), checked: false };\n}\n\n/**\n * How often a load that could collect by name lists instead: every generation divisible by this. A name-only pass\n * takes one generation a load and never looks at the rest, so what it misses (a refused load's object that stayed\n * because the row had changed, extras left by a `keep` that shrank, an object a rollback or an erasure left\n * stranded below the pointer) waits for a listing. The number is a generation's, not a clock's, so the choice needs\n * no state and no time, and a segment that loads cleanly lists once in this many loads at most. The bound is in\n * generation numbers: a rollback moves the pointer down, and the loads that then take the numbers above it count\n * from there.\n */\nexport const LIST_COLLECTION_CADENCE = 16;\n\n/**\n * Garbage-collect superseded Storage generations for a segment: everything strictly below `currentGen`, keeping\n * the most recent `keep` of them as a grace window, so a read still fetching from a just-superseded\n * generation need not re-resolve mid-call (**invariant 4**). It is a window, not a lock: a read whose\n * generation is swept anyway re-resolves and retries once rather than failing (see `withFreshSnapshot`), so\n * `keep` trades storage for round trips. Generations \u2265 `currentGen` are never touched. Returns the generations\n * deleted.\n *\n * **Except on a `destroyed` segment, where EVERY generation is garbage** and the grace window is meaningless.\n * A tombstoned segment resolves no generation for any reader, pinned or not \u2014 a pin cannot be taken on one, and\n * a pin taken while it was still active re-checks the status on every open \u2014 and nothing else in the library\n * would ever collect them, so without this those objects are billed forever.\n *\n * **Throws {@link WriteConflictError} when it cannot act on the row it read**: the row is gone when it is re-read,\n * after the listing or before a delete; on an active segment, the pointer has fallen below the cutoff before a\n * delete; on a `destroyed` segment, the row's token has changed. Not to be confused with the empty array it still\n * returns when there was genuinely nothing to collect (no row at all, or no pointer yet), which is why an empty\n * array is not a receipt. The row is read before the listing and acted on after it, so a name that was purged and\n * re-created in that window is a *different* segment wearing the same name, and its live object must not be\n * collected on the strength of the old row: on an active segment the lower of the two pointers is what keeps it\n * safe, and on a `destroyed` one the token. Refusing is safe \u2014 the objects are not going anywhere and the next\n * pass reads a consistent row \u2014 but it has to be **distinguishable** from \"there was nothing to collect\", which\n * an empty array is not: `eraseIdFromSegment` reads the returned list as the physical half of its erasure\n * receipt, and would otherwise report `erased: true` over bytes still in the bucket. Re-run it.\n *\n * That state is reachable in practice: a `dropSegment` whose Storage sweep threw part-way, or a load that had read\n * the row before the tombstone landed, wrote its object afterwards, and stopped before its refusal deleted it. `dropSegment` re-sweeps\n * and reports whatever it could not reclaim in `generationsRemaining`, but a drop that was never re-run leaves a\n * residual, and this is what eventually collects it from the retention sweep.\n *\n * `keep: 0` is how a subject-erasure rewrite makes a bit **physically** gone on return: the generation that held\n * it is collected the moment the rewrite is current. A live read still fetching from it heals forward to the\n * rewrite, and a pin of it (`seg.pin()`) fails with `NotFoundError` for any chunk it has yet to read.\n */\nexport async function gcOrphanGenerations(\n ref: SegmentRef,\n deps: GenerationDeps,\n options: { keep?: number } = {},\n): Promise<number[]> {\n const keep = options.keep ?? 1;\n // Refused, not clamped: `NaN` slices nothing off the end and would collect the whole grace window, and a\n // negative count that clamps to 0 collects it too, for a caller who wrote a typo.\n if (!Number.isInteger(keep) || keep < 0) {\n throw new ValidationError(`keep must be a non-negative integer; got ${String(keep)}`);\n }\n const record = await deps.registry.get(ref);\n if (record === null) return []; // no authoritative pointer \u2192 don't delete anything\n const current = record.currentGen;\n // A set, not an array: a listing that spans a purge-and-recreate can yield the same generation number\n // twice (the objects are re-created under the numbers just swept), and the grace window below keeps the\n // newest `keep` ENTRIES \u2014 so a duplicate would silently consume a keep slot and evict a live generation.\n const seen = new Set<number>();\n for await (const key of deps.storage.list(ref)) seen.add(key.generation);\n const gens = [...seen];\n // Everything above was read BEFORE the listing and is acted on after it, and the listing is paginated \u2014\n // seconds wide on a real object store. So re-read the row and reconcile, because BOTH branches can otherwise\n // delete an object the live pointer names, which is the forbidden `missing-storage-generation` state.\n //\n // A purged row refuses outright, on either branch: purged-and-idle is indistinguishable from\n // purged-and-being-recreated, and the top of this function already declines to act without an authoritative\n // pointer.\n const after = await deps.registry.get(ref);\n if (after === null)\n throw new WriteConflictError(`registry row for segment ${ref.segment} was purged mid-pass`);\n\n // On a tombstone, require the *same* row. A token is not reused (ABA-safe, 2^-128 per pair of incarnations), so an unchanged one proves the\n // segment was not purged and re-created underneath this pass \u2014 which matters here because this branch deletes\n // every object it enumerated, `currentGen` included, so a re-created segment would lose the generation its\n // new pointer names.\n if (record.status === 'destroyed' && after.token !== record.token) {\n throw new WriteConflictError(\n `segment ${ref.segment} changed incarnation while its generations were being listed`,\n );\n }\n\n // On the ordinary branch, take the LOWER of the two pointers. Within one incarnation the pointer only moves\n // forward, so a publish landing mid-listing leaves the cutoff exactly where it was and routine GC still\n // collects \u2014 refusing on any token change would make GC useless on a busy segment. But the pointer is only\n // monotonic *within* an incarnation: the numbering restarts at 0 once a row is purged and the bucket\n // emptied, so a name that was retired and re-created wears a LOWER `currentGen` than the one read before the\n // listing \u2014 and `rollbackSegment` lets an operator lower it deliberately \u2014 so `g < current` would then select\n // a live object. `Math.min` is what makes a\n // regressed pointer narrow the cutoff instead of widening it.\n const cutoff =\n current === null || after.currentGen === null ? null : Math.min(current, after.currentGen);\n\n /**\n * Re-prove that everything still queued for deletion is still collectable. Two different questions, because\n * the two branches delete under different licences:\n *\n * - a **tombstone** deletes at and above `currentGen`, so it needs incarnation IDENTITY \u2014 the same row,\n * by token;\n * - the **ordinary** branch deletes strictly below `cutoff`, so it needs only that the live pointer has not\n * fallen below `cutoff`. A forward publish moves it up and changes nothing, which is what keeps routine\n * collection working on a busy segment. A purge-and-recreate or a `rollbackSegment` can move it down, which\n * is why the pointer is re-proved before every delete (below).\n */\n const stillCollectable = async (): Promise<void> => {\n const still = await deps.registry.get(ref);\n if (still === null) {\n throw new WriteConflictError(`registry row for segment ${ref.segment} was purged mid-pass`);\n }\n const ok =\n record.status === 'destroyed'\n ? still.token === after.token\n : cutoff === null || (still.currentGen !== null && still.currentGen >= cutoff);\n if (!ok) {\n throw new WriteConflictError(\n `segment ${ref.segment} changed incarnation while its generations were being collected`,\n );\n }\n };\n\n const toDelete =\n record.status === 'destroyed'\n ? gens.sort((a, b) => a - b) // all of it: no reader can resolve a generation of a tombstoned segment\n : cutoff === null\n ? // No Storage pointer yet, so \"below current\" selects nothing and there is nothing safe to infer: an object\n // here is either a load about to publish or an orphan we cannot tell apart from it. Deleting would race\n // that publish into a dangling pointer. It is collected once a pointer exists.\n []\n : // Delete generations below the cutoff, except the newest `keep` of them (the grace window).\n gens\n .filter((g) => g < cutoff)\n .sort((a, b) => b - a) // newest-first\n .slice(keep);\n // The re-read above proves the segment was intact at ONE instant; the deletes below are one round trip each,\n // so the exposure is the whole loop, not that instant. The ordinary branch deletes newest-first, which puts a\n // restarted incarnation's generation 0 LAST \u2014 the worst ordering.\n //\n // Re-proved before EVERY delete, the first included. The re-read above does not cover the first: a rising\n // pointer only makes more things collectable, but `rollbackSegment` can move the pointer *down*, onto a\n // generation this pass has already queued, and a rollback landing between the re-read and the first delete\n // would leave the pass deleting the live generation before its second iteration noticed anything.\n //\n // Cost is one registry read per object actually deleted, on a path that is already one round trip per object\n // and is never on the read path.\n for (const generation of toDelete) {\n await stillCollectable();\n await deps.storage.delete({ namespace: ref.namespace, segment: ref.segment, generation });\n }\n return toDelete;\n}\n\n/**\n * Collect after a load's publish: by name when `byName` (the load numbered its generation with one existence check,\n * the object its row named was there when the guard looked, and its caller does not ask for a listing) and `keep` is\n * at most one, and by listing otherwise ({@link gcOrphanGenerations}). A load lists in three cases: `keep` of 2 or\n * more, which a name cannot serve because the window then counts generations that may be absent (a refused\n * neighbour's number is a gap, and by name would take one the window promised to keep); a number the listing chose,\n * so something sits above the pointer or the check could not answer, or a current object the load's check found gone; and\n * every {@link LIST_COLLECTION_CADENCE}th generation, which collects what a name-only pass leaves. It makes no\n * request when `keep` is at least the generation published, since no more generations than that exist below it. A\n * load onto a `destroyed` row is refused at its publish and never reaches here; a drop that lands after the publish\n * leaves this pass collecting the tombstone's generations, one by name or all of them by listing, and the drop's own\n * sweep takes the rest.\n */\nexport async function collectAfterLoad(\n ref: SegmentRef,\n deps: GenerationDeps,\n options: {\n generation: number;\n keep: number;\n byName: boolean;\n /**\n * The object the load's row named as current, which the caller did not open. Before it collects by name a generation\n * the window keeps (a `keep` of 1), the pass looks for this object with one zero-byte read, and lists instead\n * unless that finds it: a name-only pass is safe only while the object the row named is in the bucket.\n */\n proveCurrent?: GenKey;\n },\n): Promise<number[]> {\n const { generation, keep, byName, proveCurrent } = options;\n if (keep >= generation) return [];\n const periodic = generation % LIST_COLLECTION_CADENCE === 0;\n if (byName && keep <= 1 && !periodic) {\n // A `keep` of 0 deletes the generation it supersedes, which is the one in question: whether it is there or not, it is\n // the name to take, and nothing is lost by taking it.\n if (\n keep === 1 &&\n proveCurrent !== undefined &&\n !(await objectIsThere(deps.storage, proveCurrent))\n ) {\n return gcOrphanGenerations(ref, deps, { keep });\n }\n return collectByName(ref, deps, { generation, keep });\n }\n return gcOrphanGenerations(ref, deps, { keep });\n}\n\n/**\n * Whether an object is in the bucket, from one zero-byte read, a metadata request on every shipped driver. False when it\n * is not there and when the read fails in any way: a caller that collects by name on `true` needs the proof, and\n * lists when it does not have it.\n */\nasync function objectIsThere(storage: IStorageDriver, key: GenKey): Promise<boolean> {\n try {\n await storage.getTail(key, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Collect, with no listing, the one generation a publish of `generation` pushed out of a grace window of `keep`\n * (0 or 1): `generation - keep - 1`, when that is a number at all. Returns that name, whether or not an object was\n * there: a delete of an absent object succeeds on every backend and says nothing, so the name returned is the one\n * asked for, not one found, and a generation a concurrent pass, an erasure or a lifecycle rule had already taken is\n * listed all the same. A list is not a receipt (invariant 4), and neither is this.\n *\n * The caller has published `generation` having numbered it with one existence check, so the row named\n * `generation - 1` and every generation the window newly evicts is below it. With `keep` of 0 or 1 that name is\n * the one a listing pass would also take, or one it left to a later pass: never a generation the listing keeps.\n * That rests on the object the row named being in the bucket, and a fault can break it (a lifecycle rule or a\n * partial restore removing it, or an erasure deleting the object of a load whose publish then landed;\n * `checkConsistency` reports the state). The caller, which learns that from its guard's read of the current\n * generation or, when the guard took the size from the row's summary and opened nothing, from one zero-byte read of\n * it (see {@link collectAfterLoad}), lists instead; a load that made neither (`allowEmpty` without `minRetained`)\n * cannot tell, and deletes by name the older generation a listing would have kept as the window. `keep` of 2 or more is refused,\n * because there the window counts the generations that exist, which a name cannot know.\n *\n * Collection never touches the current generation, and the row is re-proved before the delete as every pass does.\n * A row that is gone, or a pointer that has fallen below `generation` (a rollback, or a name purged and re-created\n * with fewer loads), deletes nothing and returns `[]`: the publish already landed, nothing here protects it or\n * needs the result, and a throw would hand the caller a failure for a load that took effect. A pointer that has\n * moved above `generation` does not stop it: a publish landing in the meantime leaves routine collection working.\n * A fault, a registry read or a delete that throws, still propagates, with the pointer at `generation`. The bound\n * is `generation`, the pointer this load's own publish set, so the lower-of-two-pointers rule of a listing pass\n * holds with one observation in place of two. Nothing is deleted when `generation` has nothing below its window,\n * and no request is made.\n */\nexport async function collectByName(\n ref: SegmentRef,\n deps: GenerationDeps,\n options: { generation: number; keep: number },\n): Promise<number[]> {\n const { generation, keep } = options;\n if (!Number.isInteger(keep) || keep < 0 || keep > 1) {\n throw new ValidationError(`collecting by name needs a keep of 0 or 1; got ${String(keep)}`);\n }\n const evicted = generation - keep - 1;\n if (evicted < 0) return [];\n const still = await deps.registry.get(ref);\n if (still === null || still.currentGen === null || still.currentGen < generation) return [];\n await deps.storage.delete({\n namespace: ref.namespace,\n segment: ref.segment,\n generation: evicted,\n });\n return [evicted];\n}\n", "/**\n * The storage reader's defaults, stated once for the two modules that need them: the reader, which applies them,\n * and the cost model, which prices what they cause. The cost model once imported them from the reader itself,\n * which pulled the whole read path into a module that is otherwise arithmetic.\n */\n\n/** Default TTL (ms) for re-resolving a segment's `currentGen` \u2014 the bound on post-publish read staleness. */\nexport const DEFAULT_CURRENT_GEN_TTL_MS = 2000;\n\n/**\n * How long (ms) a reader keeps serving after a pointer refresh failed transiently, before the next read asks the\n * registry again. Short, so an outage ends the stale serving soon after the registry answers; fixed, so a registry\n * that is down is asked at most once per this interval per segment being read (the refresh is coalesced per\n * segment). Never longer than the TTL itself.\n */\nexport const REFRESH_RETRY_MS = 500;\n\n/** Default ceiling on cached segment readers (each holds a parsed `.crbm` index) \u2014 the steady-state count bound. */\nexport const DEFAULT_MAX_OPEN_SEGMENTS = 1024;\n\n/** Default aggregate ceiling (bytes) on resident parsed indices in the reader cache \u2014 the steady-state byte bound. */\nexport const DEFAULT_MAX_OPEN_INDEX_BYTES = 64 * 1024 * 1024;\n", "/**\n * Cost model & estimator \u2014 pure, `core/`-safe (no I/O, time, or SDK). Turns the verified economics of the\n * loaded store into a first-class library API:\n *\n * - {@link estimateCost} \u2014 **planning** mode: pure what-if from segment sizes + a workload (sizing/sales).\n * - `segment.costReport()` (wired in the facade) \u2014 **grounded** mode: the segment's real size from the `.crbm`\n * index (free + exact, no payload reads) + a supplied workload for request rates. (Whole-store aggregation is\n * a later phase.)\n *\n * Guiding split: **formulas are the spec; rates are a pluggable {@link PricingProfile}.** The report always emits\n * a {@link CostReport.verdict} that includes the lose-zone \u2014 it never hides where an always-on cache (Redis)\n * wins.\n *\n * What the model covers, and states in `assumptions.notes`: object-store GETs for point reads, for\n * intersections (each operand's pointer and index as well as its chunks) and for the pointer refresh a long-lived\n * reader pays; the requests of the retention sweep (a retirement and a purge, each priced from the registry requests\n * it makes); the requests of a load (the object's write, and the pointer reads and write, the checks of the next\n * generation number and of the current generation's object, and the listing of every 16th generation that\n * `store.load()` makes around it); and storage. Same-region egress is\n * treated as free and internet egress is not modeled; request cost is derived from the supplied workload rates\n * (deriving it from live metrics counters is a later refinement). There is no per-write term because the loaded\n * store has no per-id write: data arrives as generations, and a generation is a load.\n *\n * Every request count below is one the engine makes, and `tests/core/cost.test.ts` holds each to the engine by\n * counting what it sends: the model is only as honest as those counts, and they move when the engine does. They\n * are for a single-bucket store, where the pointer is an object beside the data, which is the topology that\n * ships. A load is more than the object's PUT, and an intersect more than its chunk reads: pricing either as the\n * one alone under-quotes a load by more than half and leaves out a pointer read and an index read for every\n * operand.\n *\n * The counts are S3's (and GCS's), and one reader process's. A pointer read is one request on every backend, and\n * {@link PricingProfile} carries it as `requestsPerPointerRead`. A segment's tail read needs the object's size, and\n * on Azure Blob, which takes no suffix range, it is two requests, the properties and then the bytes, which\n * {@link PricingProfile} carries as `requestsPerSizedRead`. A fleet of reader processes pays the pointer refresh\n * once per process, which {@link Workload.readerProcesses} carries. Where the model still quotes low is listed on\n * {@link Workload.hotSegments} and {@link Workload.chunksPerIntersect}.\n */\nimport { ValidationError } from './errors';\nimport { LIST_COLLECTION_CADENCE } from './generation-gc';\nimport { DEFAULT_CURRENT_GEN_TTL_MS, DEFAULT_MAX_OPEN_SEGMENTS } from './reader-defaults';\n\n/**\n * Freeze a constant and everything in it, so a caller that changes one \u2014 a `push` onto a catalogue's rows \u2014 cannot\n * change the default for every other caller in the process: the change throws in strict mode, as in every ES module,\n * and is ignored in a sloppy-mode script.\n */\nfunction deepFreeze<T>(value: T): Readonly<T> {\n if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const inner of Object.values(value)) deepFreeze(inner);\n }\n return value;\n}\n\n/** Pluggable rate card. Rates differ by cloud/region/term and drift over time; the formulas don't. */\nexport interface PricingProfile {\n readonly name: string;\n readonly storage: {\n /** Object GET (per **million** requests). A ranged GET bills as a full GET. */\n readonly getPerMillion: number;\n /** Object PUT (per million) \u2014 what a load pays, per request. */\n readonly putPerMillion: number;\n readonly storagePerGiBMonth: number;\n /**\n * Requests one pointer read costs: a read of a segment's registry row, whose version comes back with its bytes.\n * Default **1**, and 1 on S3, GCS and Azure Blob, each of which answers it with one GET. Charged for each operand\n * of an intersection, for the pointer reads a load makes, and for each pointer refresh. Set it when a\n * registry of your own takes more than one request to read a row.\n */\n readonly requestsPerPointerRead?: number;\n /**\n * Requests one tail read costs: the read of a segment's index from the end of its generation, which needs the\n * object's size. Default **1**, S3's, whose suffix-range GET returns the size with the bytes, and GCS's, the same.\n * **2** on Azure Blob, which takes no suffix range and reads the properties and then the bytes. Charged for each\n * operand of an intersection. A load reads no index of its own: a row that carries a summary of its current\n * generation gives the load the size it needs, and the checks it makes are one metadata request on every backend.\n * A chunk read knows its range, and is one request everywhere.\n */\n readonly requestsPerSizedRead?: number;\n };\n /**\n * The always-on Redis the verdict compares against: exactly one of the two. `sizedToData`, the default's, prices\n * the cheapest cluster that holds the report's stored bytes; `monthlyUSD` is one cluster you name, whatever the\n * data size. A profile carrying both is refused rather than read one way.\n */\n readonly redis:\n | { readonly monthlyUSD: number; readonly sizedToData?: never }\n | { readonly sizedToData: RedisSizing; readonly monthlyUSD?: never };\n}\n\n/** One node type a Redis cluster can be built from, as its cloud prices it. */\nexport interface RedisNodeType {\n readonly name: string;\n /** Memory, in GiB, as the cloud publishes it for the node type. */\n readonly memoryGiB: number;\n /**\n * SSD, in GiB, on a data-tiering node (ElastiCache's `r6gd`): keys stay in memory, and the values read least\n * recently move to the SSD. Counted in full beside the memory, as AWS counts a data-tiering node's capacity.\n */\n readonly ssdGiB?: number;\n /** Price per node-hour, in USD. */\n readonly hourlyUSD: number;\n /**\n * The most shards a cluster of this type is priced at. The default catalogue prices its burstable nodes as one\n * shard: a cluster sharded across them is not how data anyone queries is held. Default: no limit.\n */\n readonly maxShards?: number;\n}\n\n/**\n * Redis sized to hold the data: enough shards for the bytes, each a primary and its replicas, on whichever node\n * type in `nodeTypes` makes that cheapest. Prices within a millionth of a dollar of the cheapest are a tie, which\n * fewer nodes win, then the lower price, then the name, so the answer never depends on the order of the rows. The data is held at its stored,\n * compressed size, which is a floor on the memory Redis needs for it: a native Redis bitmap is sized by its highest\n * id, not by how many ids it holds, so sparse ids need more.\n */\nexport interface RedisSizing {\n /** Where the prices come from, and when; the report's notes quote it. */\n readonly source: string;\n readonly nodeTypes: readonly RedisNodeType[];\n /** Replicas per shard. AWS's best practice is 2; Multi-AZ needs at least 1. */\n readonly replicasPerShard: number;\n /** The share of each node's memory kept back from data: ElastiCache's `reserved-memory-percent`, 25% by default. */\n readonly reservedMemoryFraction: number;\n}\n\n/**\n * **ElastiCache for Redis OSS in us-east-1, on-demand**: node prices from AWS's public price list, version\n * 20260914063714 (published 2026-09-14, effective 2026-09-01,\n * https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonElastiCache/20260914063714/us-east-1/index.json), and the\n * memory AWS publishes for each node type. Every shard is a primary and two replicas, AWS's best practice, with\n * ElastiCache's default 25% of each node's memory reserved, and the burstable `t4g` nodes are priced as one shard.\n *\n * It lists only rows that are the cheapest fit for some size of data: the data-tiering `r6gd` rows above about\n * 30 GiB, and the larger in-memory `r6g` rows, `2xlarge` and up, when those are left out. The current `m7g` and `r7g` nodes are not here,\n * because an `m6g` or `r6g` with the same memory costs less. It is the cheapest cluster of THIS kind; Redis bought\n * another way costs less \u2014 one replica a shard a third less, ElastiCache for Valkey 20% less a node, reserved nodes\n * less again \u2014 so against those the saving the verdict reports is smaller. Pass their prices to compare with them.\n */\nexport const ELASTICACHE_REDIS_US_EAST_1_ONDEMAND: RedisSizing = deepFreeze({\n source: 'ElastiCache for Redis OSS, us-east-1 on-demand, AWS price list 20260914063714',\n replicasPerShard: 2,\n reservedMemoryFraction: 0.25,\n nodeTypes: [\n { name: 'cache.t4g.micro', memoryGiB: 0.5, hourlyUSD: 0.016, maxShards: 1 },\n { name: 'cache.t4g.small', memoryGiB: 1.37, hourlyUSD: 0.032, maxShards: 1 },\n { name: 'cache.t4g.medium', memoryGiB: 3.09, hourlyUSD: 0.065, maxShards: 1 },\n { name: 'cache.m6g.large', memoryGiB: 6.38, hourlyUSD: 0.149 },\n { name: 'cache.r6g.large', memoryGiB: 13.07, hourlyUSD: 0.206 },\n { name: 'cache.r6g.xlarge', memoryGiB: 26.32, hourlyUSD: 0.411 },\n { name: 'cache.r6g.2xlarge', memoryGiB: 52.82, hourlyUSD: 0.821 },\n { name: 'cache.r6g.4xlarge', memoryGiB: 105.81, hourlyUSD: 1.642 },\n { name: 'cache.r6g.8xlarge', memoryGiB: 209.55, hourlyUSD: 3.284 },\n { name: 'cache.r6g.12xlarge', memoryGiB: 317.77, hourlyUSD: 4.925 },\n { name: 'cache.r6g.16xlarge', memoryGiB: 419.09, hourlyUSD: 6.567 },\n { name: 'cache.r6gd.xlarge', memoryGiB: 26.32, ssdGiB: 99.33, hourlyUSD: 0.781 },\n { name: 'cache.r6gd.2xlarge', memoryGiB: 52.82, ssdGiB: 199.07, hourlyUSD: 1.56 },\n { name: 'cache.r6gd.4xlarge', memoryGiB: 105.81, ssdGiB: 398.14, hourlyUSD: 3.12 },\n { name: 'cache.r6gd.8xlarge', memoryGiB: 209.55, ssdGiB: 796.28, hourlyUSD: 6.24 },\n { name: 'cache.r6gd.12xlarge', memoryGiB: 317.77, ssdGiB: 1194.42, hourlyUSD: 9.358 },\n { name: 'cache.r6gd.16xlarge', memoryGiB: 419.09, ssdGiB: 1592.56, hourlyUSD: 12.477 },\n ],\n});\n\n/**\n * One ElastiCache HA cluster, a primary and two replicas of cache.m7g.large \u2014 the m7g family's smallest node \u2014\n * at $0.158 an hour each: 3 \u00D7 730 h \u00D7 $0.158 = $346.02, published to the dollar. The per-request crossovers on the\n * benchmarks page are drawn against this one cluster, whatever the data size; pass it as `pricing.redis` to compare\n * with it. It is not the cheapest cluster for any size of data: three cache.m6g.large hold the same memory for less.\n */\n// The trailing comment below is parsed by scripts/site-figures.cjs, and the literal by bench/lib/calibration-figures.cjs.\nexport const ONE_REDIS_HA_CLUSTER = deepFreeze({ monthlyUSD: 346 }); // ElastiCache HA: 1 primary + 2 replicas (cache.m7g.large); ~$115 single-node\n\n/**\n * Default profile \u2014 **AWS us-east-1, on-demand**, mid-2026, from the fact-checked published pricing rather than\n * copied from a blog post, with Redis sized to the data. Override it for your region, cloud, or committed term.\n */\nexport const AWS_US_EAST_1_ONDEMAND: PricingProfile = deepFreeze({\n name: 'aws-us-east-1-ondemand',\n storage: { getPerMillion: 0.4, putPerMillion: 5.0, storagePerGiBMonth: 0.023 },\n redis: { sizedToData: ELASTICACHE_REDIS_US_EAST_1_ONDEMAND },\n});\n\n/** Sustained access pattern. All rates default to 0; unspecified \u21D2 that op contributes nothing. */\nexport interface Workload {\n /**\n * Point reads (`has`) per second. Each cache miss is one object GET, once the segment's pointer and index are\n * read.\n */\n readonly readsPerSec?: number;\n readonly intersectsPerSec?: number;\n /** Cache hit rate in `[0, 1]` \u2014 hits are free; only misses cost. Default 0. */\n readonly cacheHitRate?: number;\n /**\n * Storage chunks one intersection fetches, summed over its operands: the chunk-skipping survivors. Default 1.\n * Two segments sharing `k` chunks fetch `2k`. The model adds each operand's pointer and index reads itself (see\n * {@link Workload.operandsPerIntersect}), so count chunks only. One more GET per operand whose index outgrows\n * the reader's tail read (256 KiB by default), which then reads the index whole, belongs here too, and so does a\n * pointer re-read by an intersect slow enough to outlive {@link Workload.genTtlMs}.\n */\n readonly chunksPerIntersect?: number;\n /**\n * Segments each intersection reads, `exclude` operands included; at least 1. Default 2. An intersection is priced\n * **cold**: before its chunks, each operand's pointer is read, then its index, in one read of the object's tail \u2014\n * 2 GETs an operand on S3 and GCS, so a cold intersect of two segments sharing `k` chunks is `4 + 2k` GETs there,\n * and 3 on Azure Blob, whose tail read is two requests (see {@link PricingProfile}). `cacheHitRate`\n * does not apply to intersections, so a long-lived reader that answers a repeat from its cache pays less. Other\n * combines read their operands the same way and can be priced here too, with the chunks they fetch.\n */\n readonly operandsPerIntersect?: number;\n /**\n * Generations published per month across the modeled data \u2014 the write side of a loaded store. Default **0**\n * \u21D2 loads are not modeled and the report *discloses* the omission rather than silently under-reporting.\n */\n readonly loadsPerMonth?: number;\n /**\n * PUT-class requests one load's object write issues, each priced at the PUT rate. Default **1** (a single-object\n * PUT). A multipart write of `P` parts bills `P + 2` (initiate, the parts, complete) \u2014 set it when you know your\n * object sizes. The model adds what `store.load()` does around the write, at the default `keep` of 1: the pointer's\n * write, PUT-class on S3, and five GETs: the pointer read three times, one check that the next generation number is\n * free, and one that the current generation's object is there, which tells the collection that deletes by name it\n * may. The current generation's size comes from the row's summary of it, so the load reads no index. Collection\n * deletes the generation the window pushed out by name, so it lists only on every 16th generation, which adds a\n * PUT-class request and two pointer reads there and makes no check that the current generation is there, a\n * sixteenth of each on average. That is a segment with two\n * generations behind it, whose row carries a summary; its first two loads make fewer requests and collect nothing,\n * and the first load of a row written before rows carried a summary reads the current generation's index, one tail\n * read, in place of the check that its object is there. A publish that loses a race to another writer\n * reads the pointer again, and a load whose check finds the number taken (a crashed load's object, or the\n * generations a rollback left above the pointer) lists the segment's objects to number past them and to collect,\n * two PUT-class requests on S3 and two more pointer reads. So does every load that keeps two or more generations,\n * which lists to collect: one more PUT-class request and two more pointer reads than the model counts. The counts\n * are a cleartext segment's: an encrypted segment's load reads its row once more, after its ids and before it\n * unwraps the key, one more GET ($0.40 per million at the default prices) that the model leaves out, beside the\n * key-management calls it does not price either.\n */\n readonly requestsPerLoad?: number;\n /**\n * Segments each long-lived reader process keeps reading. A reader re-reads a segment's pointer when it reads the\n * segment after {@link Workload.genTtlMs} has passed, so each costs at most one GET per `genTtlMs` in each process\n * that reads it \u2014 1,314,000 GETs a month at the default 2 s \u2014 and the whole term at most one GET per point read\n * (`readsPerSec`, across the fleet). Intersections are priced cold and pay their own pointer reads. Default **0**\n * \u21D2 the refresh is not modeled, and the report *discloses* the omission when there are reads to refresh for.\n *\n * It assumes each hot segment stays open in the reader's cache (`cache.readerMax`, 1,024 segments by default,\n * and `cache.readerMaxBytes`, 64 MiB of parsed index). A read of a segment the cache evicted opens it again, a\n * pointer read and a tail read, which is not priced here, and neither is the re-open every reader makes after\n * each load, for the new generation's index. Size those caches to keep the hot set open; the report says so when\n * `hotSegments` is more than a store keeps open by default.\n */\n readonly hotSegments?: number;\n /**\n * Long-lived reader processes, each keeping its own {@link Workload.hotSegments} open and refreshing their\n * pointers on its own: ten processes reading the same hundred segments pay ten times one process's refresh.\n * At least 1. Default 1.\n */\n readonly readerProcesses?: number;\n /**\n * How long the reader trusts a pointer, in ms: the store's `cache.genTtlMs`. Default 2000, the store's own\n * default; `segment.costReport()` uses the store's. `0` turns the timed refresh off, and the model bills none: a\n * reader then re-reads a pointer only when its cache evicts the segment, a read finds its generation swept, or a\n * write invalidates it, none of which this term prices.\n */\n readonly genTtlMs?: number;\n /**\n * Segments the retention sweep (`retireExpired`) retires a month. Each costs 9 registry reads, 3 writes and a\n * delete with {@link Workload.conditionalDelete} on, and 8 reads and 3 writes with it off, priced at the GET and PUT\n * rates, a delete unbilled, as S3 leaves it. Default\n * **0**.\n */\n readonly retirementsPerMonth?: number;\n /**\n * Tombstones the sweep purges a month: at a steady state, as many as it retires a month, a `tombstoneGraceMs` later.\n * A purge costs 4 reads and 2 deletes with {@link Workload.conditionalDelete} on, and 3 reads and a write with it\n * off. Default **0**.\n */\n readonly purgesPerMonth?: number;\n /**\n * Whether the registry's deletes remove a row for good (`RegCaps.conditionalDelete`), which the shipped registries\n * do where the backend applies a delete precondition. Default **true**. `false` prices the sweep of a registry that\n * only tombstones: a purge rewrites the row instead of deleting it, and every later full sweep reads each tombstone\n * left (two reads per purged segment), which this term does not price.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/** One (group of) segment(s) for planning. `count` = how many like this (default 1). */\nexport interface SegmentSizing {\n readonly sizeBytes?: number;\n readonly cardinality?: number;\n /** Number of segments with these characteristics. Default 1. */\n readonly count?: number;\n}\n\nexport interface EstimateInput {\n readonly segments: readonly SegmentSizing[];\n readonly workload?: Workload;\n readonly pricing?: PricingProfile;\n}\n\nexport interface CostReport {\n readonly monthlyUSD: {\n readonly byOp: {\n readonly reads: number;\n readonly intersects: number;\n readonly storage: number;\n /**\n * Loads: each its object's `requestsPerLoad` PUT-class requests plus what `store.load()` adds around them.\n * 0 unless `loadsPerMonth` is set.\n */\n readonly loads: number;\n /** The pointer refresh of {@link Workload.hotSegments}. 0 unless `hotSegments` is set. */\n readonly pointerRefresh: number;\n /**\n * The retention sweep's retirements and purges: each's registry reads at the GET rate and its writes at the PUT\n * rate. 0 unless `retirementsPerMonth` or `purgesPerMonth` is set.\n */\n readonly retention: number;\n };\n readonly total: number;\n };\n /**\n * The Redis the verdict compares against. Sized, the default, it is the cheapest cluster in\n * `pricing.redis.sizedToData` that holds this report's stored bytes; fixed, it is `pricing.redis.monthlyUSD` as\n * given.\n *\n * **It is not additive across reports.** Each report sizes Redis to its own bytes, so a per-segment report\n * (`segment.costReport()`) compares with a cluster holding that one segment alone, and the baselines of a store's\n * segments do not sum to the store's. To judge a store, price all its segments in one `estimateCost`. To alarm on\n * one, sum `monthlyUSD.total` over its segments and compare the sum with the Redis you would run for it: a\n * per-segment verdict against that whole price fires only when one segment alone costs more than all of it.\n */\n readonly redisBaseline:\n | { readonly basis: 'fixed'; readonly monthlyUSD: number }\n | {\n readonly basis: 'sized-to-data';\n readonly monthlyUSD: number;\n /** The node type, the shards, the nodes (every shard's primary and replicas), and whether they tier to SSD. */\n readonly cluster: {\n readonly nodeType: string;\n readonly shards: number;\n readonly nodes: number;\n readonly dataTiering: boolean;\n };\n };\n /**\n * Sustained read rate at which the pay-per-use model's cost passes {@link CostReport.redisBaseline}, **evaluated\n * at this report's `cacheHitRate`** \u2014 so a higher cache-hit rate raises it (cache hits are free) \u2014 with the other\n * request axes at 0, and with what keeping the data readable costs taken out of the baseline first: storage, and\n * this report's pointer refresh. Loads, the write side, are left out of it. `Infinity` means it never crosses (a\n * 100% cache-hit rate). The published anchor (~329 reads/s) is against {@link ONE_REDIS_HA_CLUSTER}, at\n * `cacheHitRate: 0` with no refresh modeled.\n */\n readonly redisCrossover: { readonly readsPerSec: number };\n readonly verdict: 'win-big' | 'win' | 'lose-zone';\n readonly rationale: string;\n readonly assumptions: {\n readonly cacheHitRate: number;\n readonly pricingName: string;\n /** True when segment **sizes** were real (grounded `costReport`), false for a pure `estimateCost`. */\n readonly grounded: boolean;\n /** What the model assumed and what it left out, in words. The last says how the Redis was priced. */\n readonly notes: readonly string[];\n };\n}\n\nconst HOURS_PER_MONTH = 730; // AWS's own convention: 365 days \u00D7 24 hours \u00F7 12 months\nconst SECONDS_PER_MONTH = HOURS_PER_MONTH * 3600; // 2,628,000\nconst GIB = 1024 ** 3;\n\n/**\n * What `store.load()` adds to its object's write, as the engine makes the requests on a segment with two\n * generations behind it, at the default `keep` of 1 with nothing above the pointer. Collection deletes the one\n * generation the window pushed out by name, so the pointer is read three times (the load's one read before its\n * publish, the compare-and-swap's read of the row's version, and the re-read before the delete) and nothing is\n * listed, except on every {@link LIST_COLLECTION_CADENCE}th generation, where collection lists instead: one more\n * PUT-class request and two more pointer reads (before and after the listing). Those two are averaged over the\n * cadence, so a count of `n` loads is exact for `n` consecutive generations of the cadence. PUT-class: the\n * pointer's write and the averaged listing. Pointer reads each cost `requestsPerPointerRead` requests. The load reads\n * no index: the row's summary of the current generation gives it the size the guard needs. It makes two checks, each\n * a single metadata request on every backend (S3's `HeadObject`, GCS's object metadata, Azure Blob's properties), so\n * `requestsPerSizedRead` applies to neither: that the next generation number is free, on every load, and that the\n * current generation's object is there, before the collection deletes by name the generation it pushed out of the\n * window, so on every load but the listing one. `tests/core/cost.test.ts` holds these to the engine over sixteen\n * consecutive loads. A load that keeps two or more generations, or whose check meets an object, lists on every load:\n * one more PUT-class request and two more pointer reads than these, and one check fewer.\n */\nconst STORE_LOAD_PUT_CLASS = 1 + 1 / LIST_COLLECTION_CADENCE;\nconst STORE_LOAD_POINTER_READS = 3 + 2 / LIST_COLLECTION_CADENCE;\nconst STORE_LOAD_EXISTENCE_CHECKS = 2 - 1 / LIST_COLLECTION_CADENCE;\n\n/** The requests one segment's retirement or purge makes: reads (GET-class), writes (PUT-class) and deletes. */\ninterface SweepRequests {\n readonly reads: number;\n readonly writes: number;\n readonly deletes: number;\n}\n\n/**\n * What the retention sweep makes per segment, counted with a store that counts its requests\n * (`tests/core/retention-hard-purge.test.ts` holds each figure to the engine, and `tests/core/cost.test.ts` holds the\n * estimator to these). With the registry's `conditionalDelete` on, a retirement files a due-index pointer to the\n * tombstone and a purge removes the row and every pointer: a later sweep reads nothing of a purged segment. With it\n * off, a retirement files no pointer, a purge rewrites the row as a tombstone, and every later full sweep reads what\n * is left, two objects per purged segment.\n */\nexport const RETENTION_SWEEP_REQUESTS = deepFreeze({\n conditionalDelete: {\n retirement: { reads: 9, writes: 3, deletes: 1 },\n purge: { reads: 4, writes: 0, deletes: 2 },\n },\n tombstoning: {\n retirement: { reads: 8, writes: 3, deletes: 0 },\n purge: { reads: 3, writes: 1, deletes: 0 },\n },\n}) satisfies Readonly<Record<string, Readonly<Record<string, SweepRequests>>>>;\n\n/** A count of requests for a note: at most two decimals, none when it is whole. */\nconst shown = (n: number): string => String(Number(n.toFixed(2)));\n\n/** Fail-fast at the boundary: reject non-finite / negative inputs rather than leak NaN into the report. */\nfunction requireFiniteNonNeg(n: number | undefined, field: string): number {\n if (n === undefined || !Number.isFinite(n) || n < 0) {\n throw new ValidationError(`${field} must be a finite number >= 0; got ${n}`);\n }\n return n;\n}\n\nfunction clamp01(n: number): number {\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n\n/** Two cluster prices closer than this are the same price. */\nconst TIE_USD = 1e-6;\n\ninterface SizedCluster {\n readonly nodeType: RedisNodeType;\n readonly shards: number;\n readonly nodes: number;\n readonly monthlyUSD: number;\n}\n\n/** The node's share of the data: its memory less the reserve, and all of a data-tiering node's SSD. */\nfunction usableGiB(sizing: RedisSizing, nodeType: RedisNodeType): number {\n return nodeType.memoryGiB * (1 - sizing.reservedMemoryFraction) + (nodeType.ssdGiB ?? 0);\n}\n\n/** Fail-fast on a caller-supplied catalogue: a bad row would otherwise win on NaN, or never fit anything. */\nfunction validateSizing(sizing: RedisSizing | undefined): asserts sizing is RedisSizing {\n const at = 'pricing.redis.sizedToData';\n if (sizing === null || typeof sizing !== 'object') {\n throw new ValidationError(`${at} must be a RedisSizing`);\n }\n if (typeof sizing.source !== 'string' || sizing.source === '') {\n throw new ValidationError(`${at}.source must say where the prices come from`);\n }\n if (!Array.isArray(sizing.nodeTypes) || sizing.nodeTypes.length === 0) {\n throw new ValidationError(`${at}.nodeTypes must list at least one node type`);\n }\n if (!Number.isInteger(sizing.replicasPerShard) || sizing.replicasPerShard < 0) {\n throw new ValidationError(\n `${at}.replicasPerShard must be an integer >= 0; got ${sizing.replicasPerShard}`,\n );\n }\n const reserved = requireFiniteNonNeg(\n sizing.reservedMemoryFraction,\n `${at}.reservedMemoryFraction`,\n );\n if (reserved >= 1) {\n throw new ValidationError(`${at}.reservedMemoryFraction must be below 1; got ${reserved}`);\n }\n const names = new Set<string>();\n for (const n of sizing.nodeTypes) {\n if (n === null || typeof n !== 'object' || typeof n.name !== 'string' || n.name === '') {\n throw new ValidationError(`${at}.nodeTypes: every node type needs a name`);\n }\n const row = `${at}.nodeTypes[${JSON.stringify(n.name)}]`;\n if (names.has(n.name)) throw new ValidationError(`${row} is listed twice`);\n names.add(n.name);\n if (!(requireFiniteNonNeg(n.memoryGiB, `${row}.memoryGiB`) > 0)) {\n throw new ValidationError(`${row}.memoryGiB must be above 0`);\n }\n requireFiniteNonNeg(n.ssdGiB ?? 0, `${row}.ssdGiB`);\n requireFiniteNonNeg(n.hourlyUSD, `${row}.hourlyUSD`);\n if (n.maxShards !== undefined && (!Number.isInteger(n.maxShards) || n.maxShards < 1)) {\n throw new ValidationError(`${row}.maxShards must be an integer >= 1; got ${n.maxShards}`);\n }\n // A memory so small that the reserve leaves nothing would size any data at infinite shards.\n if (!(usableGiB(sizing, n) > 0)) {\n throw new ValidationError(\n `${row} leaves no memory for data once ${reserved} of it is reserved`,\n );\n }\n }\n}\n\n/**\n * The cheapest cluster in `sizing` that holds `dataGiB`: for each node type, enough shards for the data in what each\n * node leaves free, every shard a primary and its replicas. See {@link RedisSizing} for how a tie is broken.\n */\nfunction cheapestCluster(sizing: RedisSizing, dataGiB: number): SizedCluster {\n const fits: SizedCluster[] = [];\n for (const nodeType of sizing.nodeTypes) {\n const usable = usableGiB(sizing, nodeType);\n let shards = Math.max(1, Math.ceil(dataGiB / usable));\n // The division can round an exact multiple up by one: a shard fewer that still holds the data is the answer.\n if (shards > 1 && (shards - 1) * usable >= dataGiB) shards -= 1;\n if (nodeType.maxShards !== undefined && shards > nodeType.maxShards) continue;\n const nodes = shards * (1 + sizing.replicasPerShard);\n fits.push({\n nodeType,\n shards,\n nodes,\n monthlyUSD: nodes * nodeType.hourlyUSD * HOURS_PER_MONTH,\n });\n }\n const size = bytesWords(dataGiB * GIB);\n if (fits.length === 0) {\n throw new ValidationError(\n `no node type in pricing.redis.sizedToData.nodeTypes holds ${size} within its maxShards`,\n );\n }\n // Prices within TIE_USD of the cheapest are one price reached by different roundings. Chosen among them by a rule\n // that never looks at where a row sits in the list: fewer nodes, then the lower price, then the name.\n const cheapest = fits.reduce((m, c) => Math.min(m, c.monthlyUSD), Infinity);\n const [best] = fits\n .filter((c) => c.monthlyUSD <= cheapest + TIE_USD)\n .sort(\n (a, b) =>\n a.nodes - b.nodes ||\n a.monthlyUSD - b.monthlyUSD ||\n (a.nodeType.name < b.nodeType.name ? -1 : a.nodeType.name > b.nodeType.name ? 1 : 0),\n );\n if (best === undefined || !Number.isFinite(best.monthlyUSD)) {\n throw new ValidationError(\n `pricing.redis.sizedToData prices ${size} at no finite cost; check its node types`,\n );\n }\n return best;\n}\n\n/**\n * A plain copy of a caller's sizing, each value read once, so what is validated is what is priced: a getter or a\n * Proxy that answered differently the second time could otherwise price a row it never validated. Anything not\n * shaped like a sizing is passed through for {@link validateSizing} to refuse.\n */\nfunction snapshotSizing(sizing: RedisSizing | undefined): RedisSizing | undefined {\n if (sizing === null || typeof sizing !== 'object') return sizing;\n const { source, nodeTypes, replicasPerShard, reservedMemoryFraction } = sizing;\n const row = (n: RedisNodeType): RedisNodeType => {\n if (n === null || typeof n !== 'object') return n;\n const { name, memoryGiB, ssdGiB, hourlyUSD, maxShards } = n;\n return {\n name,\n memoryGiB,\n hourlyUSD,\n ...(ssdGiB === undefined ? {} : { ssdGiB }),\n ...(maxShards === undefined ? {} : { maxShards }),\n };\n };\n return {\n source,\n replicasPerShard,\n reservedMemoryFraction,\n nodeTypes: Array.isArray(nodeTypes) ? Array.from(nodeTypes, row) : nodeTypes,\n };\n}\n\n/** What a report compares against: a fixed price, or the cluster sized to its bytes, and the sizing that chose it. */\ninterface Baseline {\n readonly monthlyUSD: number;\n readonly sized?: { readonly sizing: RedisSizing; readonly cluster: SizedCluster };\n}\n\n/** Read `pricing.redis`, which must be exactly one of the two shapes, and price it for `storedGiB`. */\nfunction resolveBaseline(redis: PricingProfile['redis'], storedGiB: number): Baseline {\n if (redis === null || typeof redis !== 'object') {\n throw new ValidationError('pricing.redis must be { monthlyUSD } or { sizedToData }');\n }\n // A shape is given when its key has a value, however the value is reached: an inherited price, or one behind a\n // getter, is as given as an own property, and a key set to undefined gives nothing. Each is read once.\n const { monthlyUSD, sizedToData } = redis;\n const fixed = monthlyUSD !== undefined;\n const sized = sizedToData !== undefined;\n if (fixed === sized) {\n throw new ValidationError(\n 'pricing.redis must be exactly one of { monthlyUSD } and { sizedToData }' +\n (fixed\n ? ', not both: spreading AWS_US_EAST_1_ONDEMAND.redis keeps its sizedToData, so pass redis: { monthlyUSD } alone'\n : ''),\n );\n }\n if (sized) {\n const sizing = snapshotSizing(sizedToData);\n validateSizing(sizing);\n const cluster = cheapestCluster(sizing, storedGiB);\n return { monthlyUSD: cluster.monthlyUSD, sized: { sizing, cluster } };\n }\n return { monthlyUSD: requireFiniteNonNeg(monthlyUSD, 'pricing.redis.monthlyUSD') };\n}\n\n/**\n * A byte count in the largest binary unit it fills \u2014 \"18.63 GiB\", \"190.73 MiB\", \"20 bytes\" \u2014 since a report on one\n * segment is usually far under a GiB, and \"0.00 GiB\" would read as nothing at all.\n */\nfunction bytesWords(bytes: number): string {\n const units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];\n let value = bytes;\n let unit = 0;\n // Rounded before the unit is chosen, so 1,048,575 bytes reads \"1.00 MiB\", not \"1024.00 KiB\".\n const shown = (v: number, u: number): number => (u === 0 ? Math.round(v) : Number(v.toFixed(2)));\n while (unit < units.length - 1 && shown(value, unit) >= 1024) {\n value /= 1024;\n unit += 1;\n }\n if (unit > 0) return `${value.toFixed(2)} ${units[unit]}`;\n const n = Math.round(value);\n return `${n} byte${n === 1 ? '' : 's'}`;\n}\n\n/** \"1 shard of 3 cache.r6g.xlarge nodes\", or \"95 shards, 285 cache.r6g.xlarge nodes\". */\nfunction clusterWords(c: SizedCluster): string {\n const count = (n: number): string => n.toLocaleString('en-US');\n const nodes = `${count(c.nodes)} ${c.nodeType.name} node${c.nodes === 1 ? '' : 's'}`;\n return c.shards === 1 ? `1 shard of ${nodes}` : `${count(c.shards)} shards, ${nodes}`;\n}\n\n/** Rough bytes for a segment when only cardinality is known: 2 B/value (array-container upper bound). */\nfunction sizingBytes(spec: SegmentSizing): number {\n if (spec.sizeBytes !== undefined) return requireFiniteNonNeg(spec.sizeBytes, 'segment.sizeBytes');\n if (spec.cardinality !== undefined) {\n return requireFiniteNonNeg(spec.cardinality, 'segment.cardinality') * 2;\n }\n return 0;\n}\n\n/** Core report builder shared by planning + grounded modes. `storageBytes` is total across all segments. */\nfunction buildReport(input: {\n readonly storageBytes: number;\n readonly workload: Workload;\n readonly pricing: PricingProfile;\n readonly grounded: boolean;\n readonly extraNotes?: readonly string[];\n /** Some sizes were derived from cardinality, an upper bound: a Redis sized to them may be larger than needed. */\n readonly sizesFromCardinality?: boolean;\n}): CostReport {\n const { pricing, grounded } = input;\n const { storage, redis } = pricing;\n const S = SECONDS_PER_MONTH;\n\n // The pricing profile is a public, caller-supplied boundary input too \u2014 validate every rate that feeds\n // the report so a malformed profile fails fast rather than leaking NaN/Infinity dollars + a bogus verdict.\n requireFiniteNonNeg(storage.getPerMillion, 'pricing.storage.getPerMillion');\n requireFiniteNonNeg(storage.putPerMillion, 'pricing.storage.putPerMillion');\n requireFiniteNonNeg(storage.storagePerGiBMonth, 'pricing.storage.storagePerGiBMonth');\n const pointerRead = requireFiniteNonNeg(\n storage.requestsPerPointerRead ?? 1,\n 'pricing.storage.requestsPerPointerRead',\n );\n const sizedRead = requireFiniteNonNeg(\n storage.requestsPerSizedRead ?? 1,\n 'pricing.storage.requestsPerSizedRead',\n );\n\n const storageBytes = requireFiniteNonNeg(input.storageBytes, 'storageBytes');\n const storedGiB = storageBytes / GIB;\n\n // The Redis to compare against: the cheapest cluster that holds the stored bytes, or the one cluster named.\n const baseline = resolveBaseline(redis, storedGiB);\n const baselineUSD = baseline.monthlyUSD;\n\n const cacheHitRate = clamp01(\n requireFiniteNonNeg(input.workload.cacheHitRate ?? 0, 'cacheHitRate'),\n );\n const readsPerSec = requireFiniteNonNeg(input.workload.readsPerSec ?? 0, 'readsPerSec');\n const intersectsPerSec = requireFiniteNonNeg(\n input.workload.intersectsPerSec ?? 0,\n 'intersectsPerSec',\n );\n const chunksPerIntersect = requireFiniteNonNeg(\n input.workload.chunksPerIntersect ?? 1,\n 'chunksPerIntersect',\n );\n const loadsPerMonth = requireFiniteNonNeg(input.workload.loadsPerMonth ?? 0, 'loadsPerMonth');\n const requestsPerLoad = requireFiniteNonNeg(\n input.workload.requestsPerLoad ?? 1,\n 'requestsPerLoad',\n );\n const operandsPerIntersect = requireFiniteNonNeg(\n input.workload.operandsPerIntersect ?? 2,\n 'operandsPerIntersect',\n );\n if (operandsPerIntersect < 1) {\n throw new ValidationError(\n `operandsPerIntersect must be at least 1, since an intersection reads its operands; got ${operandsPerIntersect}`,\n );\n }\n const hotSegments = requireFiniteNonNeg(input.workload.hotSegments ?? 0, 'hotSegments');\n const readerProcesses = requireFiniteNonNeg(\n input.workload.readerProcesses ?? 1,\n 'readerProcesses',\n );\n if (readerProcesses < 1) {\n throw new ValidationError(`readerProcesses must be at least 1; got ${readerProcesses}`);\n }\n const genTtlMs = requireFiniteNonNeg(\n input.workload.genTtlMs ?? DEFAULT_CURRENT_GEN_TTL_MS,\n 'genTtlMs',\n );\n const retirementsPerMonth = requireFiniteNonNeg(\n input.workload.retirementsPerMonth ?? 0,\n 'retirementsPerMonth',\n );\n const purgesPerMonth = requireFiniteNonNeg(input.workload.purgesPerMonth ?? 0, 'purgesPerMonth');\n const conditionalDelete = input.workload.conditionalDelete ?? true;\n if (typeof conditionalDelete !== 'boolean') {\n throw new ValidationError(`conditionalDelete must be a boolean; got ${conditionalDelete}`);\n }\n\n // Per-request unit costs (USD). Same-region egress is free; internet egress not modeled.\n const storageGetUSD = storage.getPerMillion / 1e6;\n const putUSD = storage.putPerMillion / 1e6;\n\n // Monthly volumes.\n const missFraction = 1 - cacheHitRate;\n const readMisses = readsPerSec * S * missFraction;\n const intersects = intersectsPerSec * S;\n\n // A reader re-reads a pointer only when it reads the segment after the TTL has lapsed, so the refresh is at most\n // one per hot segment per TTL, and at most one per point read. With no timed refresh (`genTtlMs: 0`) it is none.\n const refreshes =\n genTtlMs > 0\n ? Math.min((readerProcesses * hotSegments * S * 1000) / genTtlMs, readsPerSec * S)\n : 0;\n\n const storageUSD = storedGiB * storage.storagePerGiBMonth;\n const readsUSD = readMisses * storageGetUSD;\n // A cold intersection reads each operand's pointer, then its index in one tail read, before its chunks.\n const getsPerColdOperand = pointerRead + sizedRead;\n const intersectGets = chunksPerIntersect + getsPerColdOperand * operandsPerIntersect;\n const intersectsUSD = intersects * intersectGets * storageGetUSD;\n const loadGets = STORE_LOAD_POINTER_READS * pointerRead + STORE_LOAD_EXISTENCE_CHECKS;\n const loadsUSD =\n loadsPerMonth * ((requestsPerLoad + STORE_LOAD_PUT_CLASS) * putUSD + loadGets * storageGetUSD);\n const refreshUSD = refreshes * pointerRead * storageGetUSD;\n const sweep = RETENTION_SWEEP_REQUESTS[conditionalDelete ? 'conditionalDelete' : 'tombstoning'];\n const retentionUSD =\n retirementsPerMonth *\n (sweep.retirement.reads * storageGetUSD + sweep.retirement.writes * putUSD) +\n purgesPerMonth * (sweep.purge.reads * storageGetUSD + sweep.purge.writes * putUSD);\n const total = readsUSD + intersectsUSD + storageUSD + loadsUSD + refreshUSD + retentionUSD;\n\n // Crossover: the sustained read rate (other axes 0) where request cost alone passes the baseline less the fixed\n // monthly costs (storage and the pointer refresh), evaluated at this report's cache posture (misses).\n const headroom = Math.max(0, baselineUSD - storageUSD - refreshUSD);\n const readsCross =\n storageGetUSD > 0 && missFraction > 0\n ? headroom / (S * storageGetUSD * missFraction)\n : Infinity;\n\n const verdict: CostReport['verdict'] =\n total <= baselineUSD * 0.1 ? 'win-big' : total < baselineUSD ? 'win' : 'lose-zone';\n\n const dominant = Math.max(\n readsUSD,\n intersectsUSD,\n storageUSD,\n loadsUSD,\n refreshUSD,\n retentionUSD,\n );\n let driver = 'storage';\n if (dominant === readsUSD && readsUSD > 0) driver = 'point reads (object GETs)';\n else if (dominant === intersectsUSD && intersectsUSD > 0) {\n driver = 'intersections (pointer, index and chunk GETs)';\n } else if (dominant === loadsUSD && loadsUSD > 0)\n driver = 'loads (object, listing and pointer requests)';\n else if (dominant === refreshUSD && refreshUSD > 0) driver = 'the pointer refresh (object GETs)';\n else if (dominant === retentionUSD && retentionUSD > 0)\n driver = 'the retention sweep (registry reads and writes)';\n // A fixed baseline keeps the words it always had \u2014 \"flat baseline\" in a lose-zone, \"baseline\" otherwise \u2014 and a\n // sized one says what it priced.\n const sized = baseline.sized;\n // No bytes counted \u2014 nothing stored, or a source that cannot measure \u2014 sizes Redis to nothing: the rationale says\n // so, rather than naming a cluster that \"would hold 0 bytes\" as if that were a measurement.\n const noBytes = Math.round(storageBytes) === 0;\n const redisWords =\n sized === undefined\n ? undefined\n : noBytes\n ? `$${baselineUSD.toFixed(2)}/mo cheapest Redis cluster in the catalogue, as no bytes are counted ` +\n `(${clusterWords(sized.cluster)})`\n : `$${baselineUSD.toFixed(2)}/mo Redis that would hold ${bytesWords(storageBytes)} ` +\n `(${clusterWords(sized.cluster)})`;\n const rationale =\n verdict === 'lose-zone'\n ? `pay-per-use total $${total.toFixed(2)}/mo exceeds the ${redisWords ?? `$${baselineUSD}/mo flat baseline`}; dominated by ${driver}`\n : verdict === 'win-big'\n ? `$${total.toFixed(2)}/mo \u2014 \u226410% of the ${redisWords ?? `$${baselineUSD}/mo baseline`}; dominated by ${driver}`\n : `$${total.toFixed(2)}/mo, under the ${redisWords ?? `$${baselineUSD}/mo baseline`}; dominated by ${driver}`;\n\n const dataTiering = sized !== undefined && (sized.cluster.nodeType.ssdGiB ?? 0) > 0;\n const redisNote =\n sized === undefined\n ? `Redis priced at the fixed $${baselineUSD}/mo given, whatever the data size.`\n : (noBytes\n ? 'Redis priced at the cheapest cluster in the catalogue, as no bytes are counted, because nothing ' +\n 'is stored or nothing was measured'\n : `Redis priced at the cheapest cluster in the catalogue that holds the ${bytesWords(storageBytes)} stored`) +\n `: ${clusterWords(sized.cluster)}, each shard a primary and ${sized.sizing.replicasPerShard} ` +\n `replica(s), ${Number((sized.sizing.reservedMemoryFraction * 100).toFixed(1))}% of memory reserved ` +\n `(${sized.sizing.source}).` +\n (noBytes\n ? ' Pass pricing.redis: { monthlyUSD } to compare against a Redis of your own.'\n : ' Compressed bytes are a floor on the memory Redis needs: a native Redis bitmap is sized ' +\n 'by its highest id, so sparse ids need more.') +\n ' Redis bought another way \u2014 fewer replicas, another engine, reserved nodes \u2014 can cost less than the ' +\n 'catalogue prices it.' +\n (dataTiering\n ? ` ${sized.cluster.nodeType.name} is a data-tiering node, which keeps the values read least recently ` +\n 'on its SSD. It is priced as if the data read often fits in its memory: AWS recommends data tiering ' +\n 'for workloads that regularly read up to 20% of their data.'\n : '') +\n (input.sizesFromCardinality\n ? ' Some segment sizes came from cardinality, at 2 bytes an id, which is more than a dense bitmap ' +\n 'needs, so this Redis may be larger than the data calls for: pass sizeBytes to size it to the ' +\n 'compressed bytes.'\n : '');\n\n const notes = [\n 'Same-region egress treated as free; internet egress is not modeled.',\n 'Request cost is from the supplied workload rates (live-metrics-derived request cost is a later phase).',\n loadsPerMonth > 0\n ? `Loads modeled: ${loadsPerMonth}/mo, each ${requestsPerLoad} PUT-class request(s) for the object plus ` +\n `${shown(STORE_LOAD_PUT_CLASS)} PUT-class and ${shown(loadGets)} GETs that store.load() adds (the pointer, ` +\n 'a check that the next generation number is free, a check that the current generation is there, and a listing every ' +\n `${LIST_COLLECTION_CADENCE}th load).`\n : 'Loads are NOT modeled \u2014 set workload.loadsPerMonth (+ requestsPerLoad for multipart) to include them.',\n ...(intersects > 0\n ? [\n `Intersections priced cold: ${intersectGets} GETs each, ` +\n `${getsPerColdOperand} for each of ${operandsPerIntersect} operand(s) plus ` +\n `${chunksPerIntersect} chunk read(s); cacheHitRate does not apply.`,\n ]\n : []),\n hotSegments > 0\n ? genTtlMs > 0\n ? `Pointer refresh modeled: ${hotSegments} hot segment(s) in each of ${readerProcesses} reader ` +\n `process(es), each re-reading its pointer at most every ${genTtlMs} ms, and at most once a point read.`\n : 'Pointer refresh: none \u2014 genTtlMs 0 turns the timed refresh off. A reader still re-reads a pointer on an ' +\n 'eviction, a swept generation or an invalidation, which this does not price.'\n : readsPerSec > 0\n ? 'The pointer refresh is NOT modeled \u2014 set workload.hotSegments to the segments each long-lived reader ' +\n 'keeps reading, and workload.readerProcesses to how many readers there are.'\n : null,\n hotSegments > DEFAULT_MAX_OPEN_SEGMENTS\n ? `More hot segments in a reader than a store keeps open by default (${DEFAULT_MAX_OPEN_SEGMENTS}): a read of a segment ` +\n 'the reader evicted opens it again, a pointer and a tail read, which this does not price. Raise ' +\n 'cache.readerMax, and cache.readerMaxBytes, to keep them open.'\n : null,\n ...(retirementsPerMonth > 0 || purgesPerMonth > 0\n ? [\n `Retention sweep modeled: ${retirementsPerMonth}/mo retirements at ${sweep.retirement.reads} reads, ` +\n `${sweep.retirement.writes} writes and ${sweep.retirement.deletes} delete(s) each, ${purgesPerMonth}/mo ` +\n `purges at ${sweep.purge.reads} reads, ${sweep.purge.writes} writes and ${sweep.purge.deletes} ` +\n 'delete(s) each; reads are priced at the GET rate and writes at the PUT rate, deletes at nothing, as S3 ' +\n 'bills none' +\n (conditionalDelete\n ? '.'\n : '; a registry that only tombstones is read again, two reads per purged segment, by every later ' +\n 'full sweep, which is not priced.'),\n ]\n : []),\n ...(input.extraNotes ?? []),\n // Last, so the notes a report already carried keep their places.\n redisNote,\n ].filter((n): n is string => n !== null);\n\n return {\n monthlyUSD: {\n byOp: {\n reads: readsUSD,\n intersects: intersectsUSD,\n storage: storageUSD,\n loads: loadsUSD,\n pointerRefresh: refreshUSD,\n retention: retentionUSD,\n },\n total,\n },\n redisBaseline:\n sized === undefined\n ? { basis: 'fixed', monthlyUSD: baselineUSD }\n : {\n basis: 'sized-to-data',\n monthlyUSD: baselineUSD,\n cluster: {\n nodeType: sized.cluster.nodeType.name,\n shards: sized.cluster.shards,\n nodes: sized.cluster.nodes,\n dataTiering,\n },\n },\n redisCrossover: { readsPerSec: readsCross },\n verdict,\n rationale,\n assumptions: { cacheHitRate, pricingName: pricing.name, grounded, notes },\n };\n}\n\n/**\n * **Planning** cost estimate \u2014 pure, no instance or live data needed (sizing, sales, what-if). Segment sizes\n * are taken as given (or roughly derived from cardinality); use the grounded `segment.costReport()` for\n * exact, real sizes. See {@link CostReport}.\n */\nexport function estimateCost(input: EstimateInput): CostReport {\n const pricing = input.pricing ?? AWS_US_EAST_1_ONDEMAND;\n const workload = input.workload ?? {};\n let storageBytes = 0;\n let sizesFromCardinality = false;\n for (const spec of input.segments) {\n const count = Math.floor(requireFiniteNonNeg(spec.count ?? 1, 'segment.count'));\n storageBytes += sizingBytes(spec) * count;\n if (\n spec.sizeBytes === undefined &&\n spec.cardinality !== undefined &&\n sizingBytes(spec) * count > 0\n ) {\n sizesFromCardinality = true;\n }\n }\n return buildReport({ storageBytes, workload, pricing, grounded: false, sizesFromCardinality });\n}\n\n/**\n * **Grounded** report from a real segment byte total (from the `.crbm` index) + a supplied workload. Used by\n * `Segment.costReport()` in the facade. `grounded` defaults to true (the size is exact, not estimated); the\n * caller passes `grounded: false` + a note when the Storage source can't measure size.\n */\nexport function groundedReport(input: {\n readonly storageBytes: number;\n readonly grounded?: boolean;\n readonly workload?: Workload;\n readonly pricing?: PricingProfile;\n readonly extraNotes?: readonly string[];\n}): CostReport {\n return buildReport({\n storageBytes: input.storageBytes,\n workload: input.workload ?? {},\n pricing: input.pricing ?? AWS_US_EAST_1_ONDEMAND,\n grounded: input.grounded ?? true,\n extraNotes: input.extraNotes,\n });\n}\n", "/**\n * The **due index** \u2014 a time-bucketed set of the segments that carry an expiry, so a retention cycle costs what\n * is *expiring* rather than what the fleet *holds*.\n *\n * Without it, every sweep drains `registry.list()` and filters: `O(fleet)` reads every cycle, forever, even when\n * nothing expires. At 500 segments that is free; at 100,000 on a 15-minute cadence it is a standing bill for\n * finding nothing. Redis solves the same problem with a separate `expires` dict, and Elasticsearch ILM with a\n * scheduler; this is that structure, built out of registry rows so it needs **no driver change** and works on\n * every backend.\n *\n * ## Why a namespace per bucket\n *\n * `IRegistryDriver.list()` filters by **namespace and nothing else** \u2014 there is no cursor, no `startAfter`, no\n * key range. That single fact drives the whole design: the only way to read a *subset* of rows is to make the\n * subset a namespace. So the day a segment expires becomes the namespace, and listing one due day yields exactly\n * the segments due that day.\n *\n * ```\n * cbm.due.20356 \u2500\u25BA the segments whose expiry falls on day 20,356\n * cbm.due.20357 \u2500\u25BA \u2026 day 20,357\n * ```\n *\n * The prefix `cbm.due.` is declared once, as `RESERVED_NAMESPACE_PREFIX` in `validate.ts`: the validator refuses it in\n * any name an application passes in, and this module writes and recognises its rows by it.\n *\n * A day index rather than a formatted date, because `core/` reads no ambient time and formatting a date would\n * drag in a calendar (and a timezone question) for no benefit. `Math.floor(expiresAt / 86_400_000)` is total,\n * reversible and has no edge cases.\n *\n * ## The index is a FAST PATH, never the source of truth\n *\n * The segment's own registry row holds the policy; an index row is a pointer that may be stale, missing, or\n * left behind. That is deliberate, and it is what makes a second index safe here \u2014 the failure family this\n * project has hit fourteen times is *a check that cannot fire*, and the answer is that **nothing is only known\n * by the index**:\n *\n * - **A stale pointer cannot retire anything.** The sweep re-reads the live segment row before acting (it\n * already does this \u2014 the `policy-changed` skip), so an index row whose policy has since been cleared or\n * moved is a wasted read and nothing worse.\n * - **A missing pointer cannot lose data.** The full `registry.list()` scan runs as a periodic **repair**\n * pass. Anything the index never learned about \u2014 a policy whose pointer write failed, or a segment whose\n * name is too long to encode (see {@link canIndex}) \u2014 is retired by that pass instead. Slower, never wrong.\n *\n * So the index can only make the sweep *cheaper*, never *wronger*, and both drift directions are bounded by\n * machinery that already exists.\n */\nimport { ValidationError } from './errors';\nimport {\n MAX_NAME_LENGTH,\n RESERVED_NAMESPACE_PREFIX,\n encodedNameLength,\n isReservedNamespace,\n} from './validate';\nimport type { RegistryRecord, SegmentRef } from './ports';\n\n/** Bucket width. One day: small enough that a cycle reads little, coarse enough that the index stays tiny. */\nexport const DUE_BUCKET_MS = 86_400_000;\n\n/**\n * The name-length cap, and an index row encodes **both** of the original ref's parts into one name \u2014 so a ref\n * whose encoding would exceed it is simply not indexed (see {@link canIndex}).\n *\n * Re-exported from the validator rather than restated. The two were separate constants with the same value\n * until the validator started measuring the **encoded** length; `canIndex` went on counting raw characters and\n * began answering `true` for refs the registry then refused, turning a documented graceful degradation into a\n * guaranteed throw on the retention fast path.\n */\nexport { MAX_NAME_LENGTH };\n\n/** Which bucket an expiry falls in. Total and reversible; no calendar, no timezone, no ambient time. */\nexport function dueBucket(expiresAt: number): number {\n return Math.floor(expiresAt / DUE_BUCKET_MS);\n}\n\n/** The namespace holding one bucket's pointers. */\nexport function dueNamespace(bucket: number): string {\n return `${RESERVED_NAMESPACE_PREFIX}${bucket}`;\n}\n\n/** Is this record a due-index pointer rather than a segment? */\nexport function isDueIndexRow(record: Pick<RegistryRecord, 'namespace'>): boolean {\n return isReservedNamespace(record.namespace);\n}\n\n/**\n * Encode a ref into one index-row name, unambiguously.\n *\n * `${namespaceLength}.${namespace}${segment}` \u2014 a decimal length, a dot, then the two parts concatenated. The\n * length prefix is what makes it reversible: a name may contain any character, the dot included, so no\n * separator character could ever be unambiguous on its own. Reading the digits\n * up to the first dot tells the parser exactly where the namespace ends.\n */\nexport function encodeDueName(ref: SegmentRef): string {\n const ns = ref.namespace ?? '';\n return `${ns.length}.${ns}${ref.segment}`;\n}\n\n/**\n * Can this ref be indexed at all? False when the encoded name would exceed the 256-character name cap \u2014\n * possible only for a ref whose namespace and segment are together near the limit.\n *\n * **Not indexable is not \"not retired\".** The repair scan still sees the segment's own row, so the consequence\n * is that it expires on the repair cadence instead of the fast one. Callers must not treat `false` as an error.\n */\nexport function canIndex(ref: SegmentRef): boolean {\n // Measured the way the registry will measure it. Counting raw characters here made this guard lie: a ref of\n // 80 `#`s in each part encodes to 483 and was reported indexable, then threw on the write.\n return encodedNameLength(encodeDueName(ref)) <= MAX_NAME_LENGTH;\n}\n\n/** The registry ref of the pointer for `ref` in `bucket`. Throws if the ref cannot be encoded \u2014 check first. */\nexport function dueIndexRef(bucket: number, ref: SegmentRef): SegmentRef {\n const segment = encodeDueName(ref);\n if (segment.length > MAX_NAME_LENGTH) {\n throw new ValidationError(\n `due index: ${JSON.stringify(ref)} encodes to ${segment.length} characters, over the ` +\n `${MAX_NAME_LENGTH}-character name limit \u2014 check canIndex() first; such a segment is retired by the ` +\n `repair scan instead`,\n );\n }\n return { namespace: dueNamespace(bucket), segment };\n}\n\n/**\n * Recover the original ref from an index row's name. `null` for anything we did not write \u2014 a foreign row in\n * the reserved namespace is ignored rather than acted upon.\n */\nexport function decodeDueName(name: string): SegmentRef | null {\n const dot = name.indexOf('.');\n if (dot <= 0) return null;\n const lengthPart = name.slice(0, dot);\n if (!/^(0|[1-9][0-9]*)$/.test(lengthPart)) return null;\n const nsLength = Number(lengthPart);\n const rest = name.slice(dot + 1);\n const namespace = rest.slice(0, nsLength);\n const segment = rest.slice(nsLength);\n // A pointer to a segment with no name is meaningless \u2014 and would round-trip to a ref the validator rejects.\n // This also covers a length prefix larger than the remainder: the slice then consumes everything and leaves\n // the segment empty, so a separate overflow guard would be unreachable.\n if (segment.length === 0) return null;\n return namespace.length === 0 ? { segment } : { namespace, segment };\n}\n\n/**\n * Which buckets a cycle must read at `now`: everything from `since` up to and including the current one.\n *\n * Past buckets are included because a sweep that did not run \u2014 a scaled-to-zero worker, a failed deploy, a\n * paused schedule \u2014 must not leave its buckets stranded. `lookbackBuckets` bounds how far back a first run\n * reaches, so a long outage costs a bounded number of list calls per cycle rather than one per day since the\n * epoch; anything older is the repair scan's job.\n */\nexport function dueBucketsAt(now: number, lookbackBuckets: number): number[] {\n if (!Number.isSafeInteger(lookbackBuckets) || lookbackBuckets < 0) {\n throw new ValidationError(\n `due index: lookbackBuckets must be a non-negative integer; got ${lookbackBuckets}`,\n );\n }\n const current = dueBucket(now);\n const buckets: number[] = [];\n for (let b = current - lookbackBuckets; b <= current; b++) buckets.push(b);\n return buckets;\n}\n", "/**\n * The one bounded drain of `registry.list()`.\n *\n * `runConsistencyCheck`, `retireExpired` and `eraseNamespace` drain through it.\n * One copy of the loop, the ceiling constant and the option validation keeps them from drifting apart, and a\n * fleet-wide enumeration is exactly the place where drift between callers costs memory rather than tidiness.\n *\n * **Why drain at all rather than stream.** Every caller mutates rows as it goes (a CAS, a delete), and iterating a\n * live listing while doing so is driver-dependent \u2014 a paged LIST may or may not observe its own writes. A\n * snapshot also makes per-cycle counters and limits mean something. The cost is resident memory proportional to\n * the fleet, which is why the ceiling is not optional: at the default of 250,000 rows the snapshot is tens of MB,\n * comfortably more than the 128\u2013256 MB Lambda the guide suggests starting with, so it fails loudly instead.\n */\nimport { BudgetExceededError, ValidationError } from './errors';\nimport { isDueIndexRow } from './due-index';\nimport type { IRegistryDriver, RegistryRecord } from './ports';\n\n/**\n * Ceiling on registry records one fleet-wide scan holds resident. Raise it via the caller's `maxScanSegments` when\n * the fleet really is that large *and* the memory is there; narrow the scan with a `namespace` otherwise.\n */\nexport const DEFAULT_MAX_SCAN_SEGMENTS = 250_000;\n\n/** Fail fast on a bad ceiling BEFORE the (possibly huge) scan, not after. */\nexport function validateMaxScanSegments(value: number, op: string): void {\n if (!Number.isFinite(value) || value < 1) {\n throw new ValidationError(`${op}: maxScanSegments must be a finite number >= 1; got ${value}`);\n }\n}\n\n/**\n * Drain every registry record (optionally one namespace) into an array, refusing past `maxScanSegments`.\n *\n * The bound is checked **before** each push, so the array never exceeds the ceiling the caller agreed to. Pushing\n * first and then throwing would leave the very row that broke the budget resident, and a caller sizing a container\n * against `maxScanSegments` off by one row at the worst possible moment.\n */\nexport async function drainRegistry(\n registry: IRegistryDriver,\n options: {\n namespace?: string;\n maxScanSegments: number;\n op: string;\n /**\n * How a caller of `op` raises the ceiling, completing \"Narrow it with `namespace`, or \u2026\" in the refusal.\n * Defaults to raising `maxScanSegments`, which is right wherever `op` takes that option itself.\n */\n raise?: string;\n /**\n * Whether the caller could scope the scan down to a `namespace`. Defaults to true; `eraseNamespace` is already\n * one namespace, so it passes false and the refusal does not tell it to narrow what it cannot.\n */\n narrowable?: boolean;\n /**\n * Told of each bookkeeping row an unscoped scan skips, which the listing has already paid to read. Only the\n * retention sweep uses it, to find the due-index pointers whose segment has no row. It is not counted against\n * `maxScanSegments`, and a caller that holds the rows it is given bounds them itself.\n */\n onReserved?: (record: RegistryRecord) => void;\n },\n): Promise<RegistryRecord[]> {\n const { maxScanSegments, op } = options;\n validateMaxScanSegments(maxScanSegments, op);\n const raise = options.raise ?? 'raise `maxScanSegments`';\n const advice =\n options.narrowable === false\n ? `${raise[0]!.toUpperCase()}${raise.slice(1)} if the namespace really is that large and the memory is `\n : `Narrow it with \\`namespace\\`, or ${raise} if the fleet really is that large and the memory is `;\n const rows: RegistryRecord[] = [];\n for await (const rec of registry.list(options.namespace)) {\n // A due-index pointer is bookkeeping, not a segment. It lives in a reserved namespace, so an unscoped fleet\n // scan would otherwise pay a strong `get` per pointer row in `checkConsistency`, hand it to a retention\n // sweep, and inflate every fleet-wide count. A scope that names the reserved namespace never gets here: the\n // callers refuse it before they scan.\n if (options.namespace === undefined && isReservedRow(rec)) {\n options.onReserved?.(rec);\n continue;\n }\n if (rows.length >= maxScanSegments) {\n throw new BudgetExceededError(\n `${op} would enumerate more than ${maxScanSegments} segments \u2014 the scan was abandoned there rather than ` +\n `completed. ${advice}available (a record is a few hundred bytes resident).`,\n );\n }\n rows.push(rec);\n }\n return rows;\n}\n\n/**\n * Is this record **bookkeeping** rather than a segment? Covers every reserved family \u2014 today the due-index\n * pointers. **Every unscoped fleet-wide enumeration must skip these**: the bounded drain,\n * the export/eject scan, and the all-namespaces GDPR paths.\n *\n * This is the ONE place a reserved family is declared, and a new one belongs here rather than at the call\n * sites. A comparison inlined per site is easy to miss at one of them \u2014 at `subjectReport`, the rows would consume\n * an Art. 15 request's per-op budget. A filter you have to remember at each site is a check that cannot fire.\n */\nexport function isReservedRow(record: Pick<RegistryRecord, 'namespace'>): boolean {\n return isDueIndexRow(record);\n}\n\n/**\n * Filter bookkeeping rows out of a registry listing **before** anything downstream pays for them. Needed where\n * the enumeration is consumed inside a budgeted collector rather than a plain loop: charging a GDPR Art. 15\n * request's per-op budget for pointer rows can refuse a subject report for a reason that has nothing to do with\n * the subject.\n */\nexport async function* excludingReservedRows(\n source: AsyncIterable<RegistryRecord>,\n): AsyncIterable<RegistryRecord> {\n for await (const record of source) {\n if (!isReservedRow(record)) yield record;\n }\n}\n", "/**\n * The **bitmap-codec seam**.\n *\n * `core/` is **codec-agnostic**: `SegmentEngine`, the erasure rewrite, and the `.crbm` read/write helpers only ever\n * construct and combine bitmaps through the {@link CodecInterface} factory + the {@link CodecBitmap} value type\n * defined here \u2014 never a concrete implementation. The flagship codec is roaring (in\n * `@cloudbitmaps/roaring`), and another codec would plug in behind the same seam with zero engine or driver\n * changes. That is the seam's value even with one codec shipped: a plain-bitset flavor was measured and\n * **decided against** (above ~6% density a Roaring chunk already *is* an uncompressed bitset), so the seam is\n * what let that question be answered by measurement rather than by a rewrite.\n *\n * **Homogeneity contract:** a single store uses a single codec, so every {@link CodecBitmap} an operation sees\n * was produced by the same {@link CodecInterface}. The binary set ops ({@link CodecBitmap.orInPlace} etc.) may\n * therefore assume `other` is the same concrete type and are not required to interoperate across codecs.\n * (Corollary: a decoded-chunk cache is codec-specific \u2014 never share one `cache` across engines built with\n * different codecs, or a cached bitmap from codec A could reach codec B's in-place op. The `CloudRoaring` facade\n * mints the cache per store, so this cannot arise in normal use.)\n *\n * **Interface surface = exactly what the engine needs**, kept general enough for the known fast-follow codecs\n * (positional/rank-select access + raw-bitset interop that `@cloudbitmaps/bitset` will add live on that\n * package's own extended value type, not here \u2014 the engine never calls them). The one codec-linked concern\n * this seam intentionally leaves in the format layer is the `.crbm` **payload codec id**\n * (`PAYLOAD_CODEC_ROARING_PORTABLE` / `KNOWN_PAYLOAD_CODEC_IDS` in `crbm/format.ts`): it is stamped in the footer\n * and validated on read, as membership of a registry rather than equality with one constant, so a codec-specific\n * name is not frozen into the format at `1.0` and a future codec is a one-line registration rather than a major\n * format version. Still deliberately outside this interface: a codec declares\n * its bytes, the seam does not.\n */\n\nimport { ValidationError } from './errors';\n\n/**\n * The value type a codec produces \u2014 a mutable set of `u32` with set algebra and portable (de)serialization.\n * This is the shape the roaring flavor's bitmap already has; the engine holds these, caches them, and merges tiers with them.\n */\nexport interface CodecBitmap {\n /** Serialize with the codec's **stable, portable** format (never a frozen/unsafe variant). */\n serialize(): Uint8Array;\n add(value: number): void;\n addMany(values: Iterable<number>): void;\n remove(value: number): void;\n removeMany(values: Iterable<number>): void;\n has(value: number): boolean;\n /** Cardinality. */\n readonly size: number;\n readonly isEmpty: boolean;\n /** A deep copy \u2014 mutating the clone must not touch the original (the cache relies on this). */\n clone(): CodecBitmap;\n /** In-place union `this = this \u222A other`. `other` is from the same codec (see the homogeneity contract). */\n orInPlace(other: CodecBitmap): void;\n /** In-place difference `this = this \\ other`. */\n andNotInPlace(other: CodecBitmap): void;\n /** In-place intersection `this = this \u2229 other`. */\n andInPlace(other: CodecBitmap): void;\n /** Ascending iterator over the set values. */\n [Symbol.iterator](): IterableIterator<number>;\n toArray(): number[];\n /**\n * The largest value in the set, or `undefined` when empty.\n *\n * **Optional, and deliberately so.** The engine uses it for one thing: asserting that a payload it is about\n * to interpret **as a chunk** holds only 16-bit remainders (see {@link assertChunkPayload}). A codec that\n * cannot answer this in better than O(n) should simply omit it \u2014 the engine then skips the check rather\n * than walking every value on the read path, which is the one thing this must never cost.\n *\n * Roaring answers it in O(1) from its container index, so the flagship codec implements it. The check is only\n * as good as the answer: a codec that implements this must return the true largest value of whatever\n * {@link CodecInterface.safeDeserialize} decoded, which for roaring holds only because that call refuses bytes\n * whose containers or values are out of order.\n */\n maximum?(): number | undefined;\n /**\n * Re-encode for size, in place, immediately before a **storage** write. Representation only \u2014 this must never\n * change membership, and `serialize()` afterwards must decode back to exactly the same set.\n *\n * **Canonical:** afterwards `serialize()` depends on membership alone, never on how the bitmap was built. A load\n * relies on it: a chunk built from ids and the same chunk cut from a caller's bitmap must store the same bytes.\n *\n * **Optional, like {@link maximum}.** A codec whose encoding has no size decision to make (a plain bitset has\n * one representation and nothing to choose) simply omits it, and the engine skips the call.\n *\n * WHY THIS EXISTS. Roaring picks per container between an array, a bitset and a RUN \u2014 but the run choice is\n * not automatic in any implementation: it is a `runOptimize()` pass you have to ask for, and nothing here was\n * asking. So two of the three container types were ever used, and run-shaped data paid list or bitmap prices\n * for a run. Measured on the shipped codec: a contiguous 1,000,000-id range serialized to 128.1 KiB where\n * run-encoding needs **0.2 KiB** (570\u00D7), and a 2,000-run shape 536.5 KiB against **8.5 KiB** (63\u00D7). Sparse\n * data is unchanged, because there are no runs to find \u2014 the pass is not a gamble.\n *\n * WHY ONLY AT WRITE TIME. This is called where a whole immutable generation is written, so its cost amortizes\n * over a write that is already serializing and checksumming every chunk \u2014 and a generation is read many times\n * after. It is deliberately NOT called on the read path (a clone made for a combine is transient): the hot path\n * must not pay for a win that only the stored bytes collect, per KISS/YAGNI.\n */\n optimize?(): void;\n /**\n * This set as storage chunks: ascending by chunk key, empty chunks left out, each payload exactly the bytes that\n * {@link CodecInterface.fromValues} of that chunk's low 16 bits, then {@link optimize}, then {@link serialize}\n * would give. So a generation written from these is byte for byte the one the same ids write. It may re-encode\n * this bitmap in place first, as `optimize()` does; membership is untouched.\n *\n * **Flavor-author surface, and optional.** A load from a bitmap or from serialized bytes writes these, never\n * touching an id in JavaScript. A codec without it still loads those inputs, through the bitmap's ids.\n */\n encodeChunks?(): Iterable<EncodedChunk>;\n}\n\n/**\n * One chunk of a set, encoded for storage: what {@link CodecBitmap.encodeChunks} yields, and what a `.crbm`\n * generation stores per chunk. Flavor-author surface.\n */\nexport interface EncodedChunk {\n /** The high 16 bits every id in the chunk shares. */\n readonly chunkKey: number;\n /** The stored bytes: the codec's serialization of a bitmap holding only the chunk's low 16 bits. */\n readonly payload: Uint8Array;\n /** How many ids the chunk holds, 1 to 65,536. */\n readonly cardinality: number;\n}\n\n/**\n * A pluggable bitmap codec \u2014 the factory the codec-agnostic engine constructs {@link CodecBitmap}s through.\n * Implementations **must** size-cap before handing untrusted bytes to any native decoder and use a safe\n * (never a trusting/frozen) deserializer \u2014 hard invariant 5.\n */\nexport interface CodecInterface {\n /** An empty set. */\n empty(): CodecBitmap;\n /** A set seeded from an iterable of `u32` values. */\n fromValues(values: Iterable<number>): CodecBitmap;\n /**\n * Size-cap (`bytes.length <= maxBytes`) then portable-deserialize. Throws `IntegrityError` when the input\n * exceeds the cap or fails to decode \u2014 the native decoder is never handed unbounded or unsafe-format input.\n *\n * \"Fails to decode\" includes bytes that are the right length and the wrong shape. A bitmap this returns must\n * satisfy every invariant its own operations assume (for roaring: containers and values in order, runs\n * disjoint, cardinalities matching the bits), because the engine trusts `has`, `size`, iteration order and\n * {@link CodecBitmap.maximum} on what comes back, and a decoder that only bounds its reads guarantees none of\n * them.\n *\n * `whole: true` means the bytes must be exactly one bitmap: anything after its end is refused too. A load passes\n * it for a caller's bytes, so two serializations back to back are refused rather than loaded as the first. A\n * stored chunk is read without it. A codec must honour it: core cannot read the format, so it relies on the codec\n * for this refusal, and a codec that ignores the option loads two concatenated bitmaps as the first of them.\n */\n safeDeserialize(\n bytes: Uint8Array,\n maxBytes: number,\n options?: { readonly whole?: boolean },\n ): CodecBitmap;\n}\n\n/**\n * Resolve a codec that a **public core entry point** was given, failing fast when it is missing.\n *\n * Why these entry points take `codec?` rather than a required field: `loadSegment` / `eraseIdFromSegment` /\n * `runExport` are call-compatible public API, and core cannot supply a default (the concrete\n * codec lives in a *flavor* package that depends on core \u2014 a default here would invert that arrow). A **flavor**\n * package binds the codec for its users (`@cloudbitmaps/roaring`'s store passes its codec to each of these), so an\n * application never reaches this throw; only someone calling `@cloudbitmaps/core` directly \u2014 i.e. a flavor or\n * driver author \u2014 can, and for them the typed error names exactly what to pass.\n */\nexport function requireCodec(codec: CodecInterface | undefined, api: string): CodecInterface {\n if (codec === undefined) {\n throw new ValidationError(\n `${api} needs a bitmap codec: pass \\`codec\\`. ` +\n `@cloudbitmaps/core is codec-agnostic and has no default. A flavor's store passes its own codec, ` +\n `so an application calls the store's method instead of this function.`,\n );\n }\n return codec;\n}\n", "/**\n * Segment **export (\"eject\")** \u2014 dump every registered segment's current *effective* set to a portable file,\n * using only public read APIs, so your data is readable **without CloudRoaring** (a first-class exit path;\n * see the README \"Your data stays yours\" section).\n *\n * Two formats:\n * - `'roaring'` (default) \u2014 one **portable RoaringBitmap32** per segment (the cross-language CRoaring format,\n * loadable by any roaring library in Java/Go/Python/Rust/C++/\u2026). Compact; builds one bitmap in memory per\n * segment.\n * - `'ndjson'` \u2014 newline-delimited ids, streamed (constant memory, any segment size); zero dependencies to read.\n *\n * The engine is **storage-agnostic and I/O-free**: it enumerates via an {@link IRegistryDriver}, reads each\n * segment's current generation via a store's `segment(...).iterate()`, and writes bytes through an injected\n * {@link ExportSink} \u2014 so it never imports `node:fs` (the `export-segments` CLI supplies a filesystem sink; a\n * test supplies an in-memory one; you could supply an S3 uploader). Crypto-shredded (`destroyed`) segments are\n * skipped (their bytes are unrecoverable); an encrypted segment is decrypted transparently **iff** the store was\n * wired with its keystore \u2014 so the export is **cleartext**, and protecting it is on you.\n *\n * **Enumeration** = the registry's known set. Every loaded segment has a registry row (the publish writes it),\n * so the registry is complete by construction; a segment loaded without a registry is not exportable here \u2014 wire\n * the registry the store was loaded with.\n *\n * **Per-segment fault isolation:** a segment that can't be read (a corrupt storage object, or an encrypted segment\n * when the store has no keystore) is recorded in the manifest's {@link ExportManifest.failed} list and the export\n * **continues** \u2014 one bad segment never blocks the rest. Its partial output is aborted, so a truncated file never\n * masquerades as complete. Therefore \"a manifest exists\" means *the run finished*, not that every segment\n * succeeded \u2014 check `failed` (the CLI also exits non-zero when it's non-empty).\n */\nimport { isReservedRow } from '../core/registry-scan';\nimport type { CodecInterface } from '../core/codec';\nimport { requireCodec } from '../core/codec';\nimport type { IRegistryDriver, SegmentRef } from '../core/ports';\nimport { validateUserNamespace, validateSegmentRef } from '../core/validate';\n\n/** Output format for {@link runExport} / `CloudRoaring.exportSegments`. */\nexport type ExportFormat = 'roaring' | 'ndjson';\n\n/** A writer for one segment's output \u2014 inject a filesystem file, an S3 upload, stdout, or a test buffer. */\nexport interface ExportWriter {\n /** Append bytes (called one or more times; for `ndjson`, batched line chunks; for `roaring`, once). */\n write(bytes: Uint8Array): Promise<void> | void;\n /** **Commit** the output \u2014 called once, only on success (all bytes written). Finalize/flush/rename here. */\n close(): Promise<void> | void;\n /**\n * **Discard** a partial output \u2014 called instead of `close()` if a fault occurs mid-segment, so a truncated file\n * never masquerades as complete (e.g. delete the temp file). Optional; sinks with nothing to undo can omit it.\n */\n abort?(): Promise<void> | void;\n}\n\n/** A destination the export writes each segment to. `ext` is `.roaring` or `.ndjson`. */\nexport interface ExportSink {\n open(ref: SegmentRef, ext: string): Promise<ExportWriter> | ExportWriter;\n}\n\nexport interface ExportOptions {\n /** Output format; defaults to `'roaring'`. */\n readonly format?: ExportFormat;\n /**\n * Bitmap codec that builds the exported bitmap for the `'roaring'` format. Optional in the type; a **flavor**'s\n * store passes its own (see `requireCodec`). Not needed for `'ndjson'`, which streams plain ids.\n */\n readonly codec?: CodecInterface;\n /** Scope the export to one namespace; omit to export every registered segment. */\n readonly namespace?: string;\n /** `ndjson` flush threshold in ~bytes (ids are ASCII, so \u2248 chars); defaults to 64 KiB. */\n readonly ndjsonBatchBytes?: number;\n}\n\n/** Per-segment result recorded in the {@link ExportManifest}. */\nexport interface ExportedSegment {\n readonly segment: string;\n readonly namespace?: string;\n /** Ids written (the segment's effective cardinality at export time). */\n readonly count: number;\n /** Bytes written to the sink for this segment. */\n readonly bytes: number;\n}\n\n/**\n * A segment that could **not** be exported \u2014 recorded rather than aborting the whole run (fault isolation), so a\n * single corrupt/undecryptable segment never blocks the rest of the dump.\n */\nexport interface ExportFailure {\n readonly segment: string;\n readonly namespace?: string;\n /** Why it failed (an error message) \u2014 e.g. a corrupt storage object, or an encrypted segment with no keystore. */\n readonly error: string;\n}\n\n/** What {@link runExport} produced \u2014 a self-describing summary the CLI persists as `manifest.json`. */\nexport interface ExportManifest {\n /** Schema version of this manifest shape (currently `1`) \u2014 insurance for evolving the format post-1.0. */\n readonly version: number;\n readonly format: ExportFormat;\n readonly totalSegments: number;\n readonly totalIds: number;\n readonly segments: readonly ExportedSegment[];\n /**\n * Segments that could not be read (empty when everything succeeded). Its presence in the manifest is the point:\n * \"a manifest exists\" \u21D2 the run finished, **not** that every segment succeeded \u2014 always check this list.\n */\n readonly failed: readonly ExportFailure[];\n}\n\n/** The minimal read surface the export needs from a store \u2014 a `CloudRoaring` satisfies this structurally. */\nexport interface SegmentReader {\n segment(name: string, options?: { namespace?: string }): { iterate(): AsyncIterable<number> };\n}\n\nconst DEFAULT_NDJSON_BATCH_BYTES = 64 * 1024;\n\nconst errMessage = (err: unknown): string => (err instanceof Error ? err.message : String(err));\n\n/**\n * Export every registered segment's current generation through `sink`. Enumerates via `registry`, reads via\n * `reader` (the store). Returns a manifest. `CloudRoaring.exportSegments` is the\n * public entry \u2014 it calls this with its own store + registry (so enumeration and the read path provably share one\n * registry).\n *\n * **Fault isolation:** a per-segment read/write fault aborts that segment's partial (so a truncated file never\n * looks complete), records it in `failed`, and the run continues \u2014 one bad segment never blocks the rest. A\n * `close()` fault is likewise recorded (never triggering `abort()` \u2014 they're mutually exclusive).\n *\n * Re-running overwrites the segments it re-exports but does **not** prune files for segments that have since\n * disappeared \u2014 export to a fresh directory for a clean dump. For a *current* image, run against a\n * freshly-constructed store: a store's storage source re-resolves each segment's generation on a short TTL, or, with\n * no registry, with `cache.genTtlMs: 0`, or built without a clock, only when an eviction, a sweep or an invalidation\n * makes it, so a long-lived store may export a view one load behind, or more. Each segment is read live, not pinned:\n * a publish while a long segment exports can re-resolve it part-way, and its file then holds chunks of two\n * generations, each whole and verified. For one generation per file, export from a quiet window.\n */\nexport async function runExport(\n reader: SegmentReader,\n registry: IRegistryDriver,\n sink: ExportSink,\n options: ExportOptions = {},\n): Promise<ExportManifest> {\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const format = options.format ?? 'roaring';\n const ext = format === 'roaring' ? '.roaring' : '.ndjson';\n const batchCap = options.ndjsonBatchBytes ?? DEFAULT_NDJSON_BATCH_BYTES;\n const segments: ExportedSegment[] = [];\n const failed: ExportFailure[] = [];\n let totalIds = 0;\n\n const exportRef = async (ref: SegmentRef): Promise<void> => {\n let writer: ExportWriter | undefined;\n let count = 0;\n let bytes = 0;\n try {\n validateSegmentRef(ref); // defense-in-depth: registry rows are untrusted bytes here\n const ids = reader.segment(ref.segment, { namespace: ref.namespace }).iterate();\n writer = await sink.open(ref, ext);\n if (format === 'roaring') {\n const bm = requireCodec(options.codec, \"runExport({ format: 'roaring' })\").empty();\n for await (const id of ids) {\n bm.add(id);\n count += 1;\n }\n const out = bm.serialize(); // portable RoaringBitmap32 \u2014 the cross-language CRoaring format\n await writer.write(out);\n bytes = out.length;\n } else {\n let buf = '';\n for await (const id of ids) {\n buf += `${id}\\n`;\n count += 1;\n if (buf.length >= batchCap) {\n const b = Buffer.from(buf, 'utf8');\n await writer.write(b);\n bytes += b.length;\n buf = '';\n }\n }\n if (buf.length > 0) {\n const b = Buffer.from(buf, 'utf8');\n await writer.write(b);\n bytes += b.length;\n }\n }\n } catch (err) {\n // Read/write fault: discard the partial (best-effort, so a failing cleanup can't mask the real fault),\n // record it, and move on \u2014 one bad segment never blocks the rest.\n if (writer !== undefined) {\n try {\n await writer.abort?.();\n } catch {\n /* best-effort cleanup */\n }\n }\n failed.push({ segment: ref.segment, namespace: ref.namespace, error: errMessage(err) });\n return;\n }\n // Commit outside the write try: a `close()` failure must NOT also trigger `abort()` (they're mutually\n // exclusive \u2014 abort on a write fault, close on success \u2014 so a custom sink never double-finalizes). A close\n // fault is still isolated: recorded, not thrown.\n try {\n await writer.close();\n } catch (err) {\n failed.push({ segment: ref.segment, namespace: ref.namespace, error: errMessage(err) });\n return;\n }\n segments.push({ segment: ref.segment, namespace: ref.namespace, count, bytes });\n totalIds += count;\n };\n\n for await (const rec of registry.list(options.namespace)) {\n // A due-index pointer row is not a segment: an unscoped eject would otherwise write one empty file per pointer\n // into the portability dump \u2014 the artifact whose whole value is being a faithful copy of the user's data.\n if (options.namespace === undefined && isReservedRow(rec)) continue;\n if (rec.status === 'destroyed') continue; // crypto-shredded \u2192 bytes unrecoverable; nothing to export\n await exportRef({ segment: rec.segment, namespace: rec.namespace });\n }\n return { version: 1, format, totalSegments: segments.length, totalIds, segments, failed };\n}\n", "/**\n * `segmentExists` and `listSegments` \u2014 ask the registry what is there.\n *\n * The registry has always known which segments exist; it is what `checkConsistency`, the retention sweep and\n * `exportSegments` each enumerate. It was simply never exposed, so a user asking \"do I already have this?\" had\n * to infer it: `count()` returns `0` for a segment that was never loaded AND for one loaded with no ids, and\n * `generations()` costs a bucket listing to answer a question the pointer alone settles.\n *\n * Inferring it is the expensive kind of wrong. The alternative a user reaches for is keeping their own list of\n * segment names alongside the store \u2014 a second source of truth, updated by hand, drifting from the first the\n * moment a load fails halfway. The registry is already the list; these two functions read it.\n */\nimport type { IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\nimport { excludingReservedRows } from './registry-scan';\nimport { validateUserNamespace, validateUserRef } from './validate';\n\n/** One segment the registry knows about. */\nexport interface SegmentInfo extends SegmentRef {\n /**\n * The generation the **pointer names** \u2014 not what a read would resolve. On a `destroyed` tombstone this is\n * still a number while reads answer empty, so read it together with {@link SegmentInfo.status} rather than\n * alone. `null` means the row exists with no Storage generation yet.\n */\n readonly currentGen: number | null;\n /**\n * `destroyed` is a tombstone: the row stays so sweeps can still find it, and the generations are gone \u2014 on an\n * encrypted segment the DEK wrappings went with them, which is what makes the bytes unrecoverable rather than\n * merely deleted.\n */\n readonly status: RegistryRecord['status'];\n}\n\n/**\n * Whether this segment's pointer resolves a generation \u2014 the question behind \"does it already exist?\".\n *\n * One registry point read, on every backend. Deliberately narrower than \"is there a row\": it is **false** for a\n * row that exists with `currentGen: null` (a retention policy recorded ahead of the first load) and for a\n * `destroyed` tombstone, because a read answers empty in both. The predicate mirrors generation resolution\n * clause for clause, which is what keeps the two from disagreeing.\n *\n * It is NOT the same question as `count() > 0`. A segment loaded with no ids exists and counts zero \u2014 the\n * distinction between \"never loaded\" and \"loaded, and genuinely empty\" is the one `count()` cannot make and\n * the main reason this function is worth having.\n *\n * Two states answer `true` where a read still gives you nothing, and neither is this call's job to detect: a\n * **torn restore** (`missing-storage-generation` \u2014 a live pointer whose object was deleted) makes reads *throw*\n * `NotFoundError` rather than answer empty, and `runConsistencyCheck` is the call that looks for it; and a\n * handle carrying an expired `expiresAt` reads as empty by a rule that lives on the handle, not on the row.\n *\n * Not a lock. A segment can be created or dropped between this call and whatever you do next; if the answer\n * has to hold, use the fences that exist for it \u2014 `load`'s guard, or `expectFrom`/`expectToken` on a publish.\n */\nexport async function segmentExists(ref: SegmentRef, registry: IRegistryDriver): Promise<boolean> {\n validateUserRef(ref);\n const record = await registry.get(ref);\n return record !== null && record.status !== 'destroyed' && record.currentGen !== null;\n}\n\n/**\n * Every segment the registry holds, streamed \u2014 optionally scoped to one namespace.\n *\n * **This is an admin/discovery call, not a request-path one.** It is the registry's own enumeration \u2014 a paged\n * LIST over the `registry/` prefix \u2014 so cost grows with the fleet, not with what you are looking for. The\n * fleet-wide sweeps that *drain* it bound it (`maxScanSegments`); this one streams, so the bound is yours.\n *\n * **Scoping to a namespace narrows the LIST prefix**, so it really is the difference between reading one\n * tenant and reading all of them.\n *\n * Streams, so a large fleet need not be held at once, and stopping the iteration stops the scan \u2014 as long as the\n * registry driver's own `list` streams. One that collects its whole enumeration before yielding defeats both\n * properties, since the scan is then paid for and resident before the first row reaches you, so a custom driver's\n * `list` should yield each row as it reads it. The registry drivers this library ships all stream; on S3, GCS and\n * Azure Blob the granularity is a page, so one LIST page and its in-flight row reads complete regardless. Nothing\n * here retries the scan: a transient fault part-way through ends the iteration with that error, and calling\n * `listSegments` again scans from the start, since the iterable one call returns is used up.\n *\n * Yields `destroyed` tombstones, and rows whose `currentGen` is `null`, because hiding either would make this\n * disagree with the registry it reports on \u2014 a filtered enumeration that looks complete is how a retention\n * sweep ends up permanently skipping rows nobody can see. Filter on `status`/`currentGen` yourself, or use\n * {@link segmentExists} for the \"does the pointer resolve\" question. Internal bookkeeping rows are the one\n * exclusion, and only on an unscoped scan: they are not segments, and a caller naming their namespace still\n * sees them.\n */\nexport async function* listSegments(\n registry: IRegistryDriver,\n options: { namespace?: string } = {},\n): AsyncIterable<SegmentInfo> {\n // Validated here rather than only at the facade: this is a public export of `@cloudbitmaps/core`, and its\n // sibling `segmentExists` validates. Without it a typo'd or externally-supplied tenant id reads as \"this\n // tenant has no segments\" \u2014 an empty result is the most dangerous possible answer to a malformed question.\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const raw = registry.list(options.namespace);\n const source = options.namespace === undefined ? excludingReservedRows(raw) : raw;\n for await (const record of source) {\n yield {\n ...(record.namespace === undefined ? {} : { namespace: record.namespace }),\n segment: record.segment,\n currentGen: record.currentGen,\n status: record.status,\n };\n }\n}\n", "/**\n * The two SDK-free backends, each as one object: `MemoryStorage` and `LocalFsStorage`.\n *\n * These live in the main entry because neither needs a cloud SDK \u2014 there is nothing to keep out of the\n * install, and it keeps the first five minutes to a single import. Their cloud siblings (`S3Storage`,\n * `GcsStorage`, `AzureBlobStorage`) ship in `@cloudbitmaps/s3`, `/gcs` and `/azure-blob` for the opposite\n * reason: each carries an SDK nobody should install for a service they do not use.\n *\n * Each one exists to state a location **once**. See {@link StorageBackend} for why that is worth a class.\n */\nimport { join } from 'node:path';\nimport { brandAsBackend, STORAGE_BACKEND } from '@/core/ports';\nimport type { IRegistryDriver, IStorageDriver, StorageBackend } from '@/core/ports';\nimport { MemoryRegistryDriver, MemoryStorageDriver } from './memory';\nimport { LocalFsStorageDriver } from './localfs/storage';\nimport { LocalFsRegistryDriver } from './localfs/registry';\n\nexport interface MemoryStorageOptions {\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n}\n\n/**\n * An in-process backend \u2014 nothing to install, nothing to clean up, and it disappears with the process.\n *\n * This is the one to reach for in tests and in the first five minutes. It is **not** a cache in front of\n * anything: it is the whole store, held in RAM, so a restart is a data loss.\n */\nexport class MemoryStorage implements StorageBackend {\n /** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */\n declare readonly [STORAGE_BACKEND]: true;\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n\n constructor(options: MemoryStorageOptions = {}) {\n this.storage = new MemoryStorageDriver();\n this.registry = new MemoryRegistryDriver(options.now === undefined ? {} : { now: options.now });\n brandAsBackend(this);\n }\n}\n\nexport interface LocalFsStorageOptions {\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n}\n\n/**\n * A local-filesystem backend rooted at one directory: generations under `<root>/storage`, pointers under\n * `<root>/registry`.\n *\n * The layout is stated here rather than by the caller, which is the point \u2014 two paths a caller wrote out\n * separately could name different roots, and nothing would stop them. It is also the layout\n * the `export-segments` CLI expects, so a store built this way can be ejected without being told where to look.\n *\n * A root is for **one process**. Every instance in a process that names the same root shares one lock per\n * registry row, however the root is spelled (relative path, symlink), so they cannot both advance a row from\n * one token. Two processes on one root are **not** fenced from each other.\n */\nexport class LocalFsStorage implements StorageBackend {\n /** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */\n declare readonly [STORAGE_BACKEND]: true;\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n\n constructor(\n readonly root: string,\n options: LocalFsStorageOptions = {},\n ) {\n this.storage = new LocalFsStorageDriver(join(root, 'storage'));\n this.registry = new LocalFsRegistryDriver(\n join(root, 'registry'),\n options.now === undefined ? {} : { now: options.now },\n );\n brandAsBackend(this);\n }\n}\n", "/**\n * In-memory drivers \u2014 the reference backing for tests and quick experiments.\n * They store **serialized bytes + an OCC token** (not live objects), so the engine's\n * safe-deserialize / size-cap paths and the registry's OCC paths are genuinely exercised.\n *\n * Not in `core/` (this is a driver), so it may hold concrete state.\n */\nimport { createHash } from 'node:crypto';\nimport { BufferSink } from '../core/blob';\nimport type { BlobSink } from '../core/blob';\nimport type { Entropy } from '../core/determinism';\nimport { NotFoundError, ValidationError, WriteConflictError } from '../core/errors';\nimport { segmentKey } from '../core/keys';\nimport { validateSegmentRef } from '../core/validate';\nimport type {\n StorageCaps,\n GenKey,\n IStorageDriver,\n IRegistryDriver,\n NewRegistryRecord,\n RegCaps,\n RegistryPatch,\n RegistryRecord,\n SegmentRef,\n Token,\n} from '../core/ports';\nimport {\n applyRegistryPatch,\n drawIncarnation,\n drawWrite,\n incarnationOf,\n recordFromNew,\n validateNewRegistryRecord,\n validateRegistryPatch,\n} from './_shared/registry';\nimport { entropyIsAvailable, webCryptoEntropy } from './_shared/entropy';\n\nexport interface MemoryRegistryDriverOptions {\n /** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now` (drivers may use ambient time). */\n readonly now?: () => number;\n /** Draws every token's random parts; defaults to Web Crypto. Inject one only to make a test replayable. */\n readonly entropy?: Entropy;\n}\n\n/**\n * In-memory {@link IRegistryDriver} \u2014 one record per segment under OCC. The token has the form every shipped driver\n * gives it, an incarnation id drawn at create, a counter and a per-write random part, but its counter is global to the\n * driver: monotonic and never reused, so a record recreated after `delete` never meets an earlier token, by\n * construction, even though `delete` removes the row physically.\n */\nexport class MemoryRegistryDriver implements IRegistryDriver {\n private readonly rows = new Map<string, RegistryRecord>();\n private readonly now: () => number;\n private readonly entropy: Entropy;\n private seq = 0;\n\n constructor(options: MemoryRegistryDriverOptions = {}) {\n this.now = options.now ?? (() => Date.now());\n this.entropy = options.entropy ?? webCryptoEntropy;\n }\n\n private nextToken(incarnation: string): Token {\n this.seq += 1;\n return `${incarnation}.${this.seq}.${drawWrite(this.entropy)}`;\n }\n\n /** `conditionalDelete`: a delete checks the token and removes the row in one step, with no await between. */\n capabilities(): RegCaps {\n return entropyIsAvailable(this.entropy)\n ? { strongRead: true, conditionalDelete: true }\n : { strongRead: true, canWrite: false, conditionalDelete: true };\n }\n\n async get(ref: SegmentRef): Promise<RegistryRecord | null> {\n validateSegmentRef(ref);\n const row = this.rows.get(segmentKey(ref));\n return row ? structuredClone(row) : null; // clone out: callers can't mutate stored (nested) state\n }\n\n async create(ref: SegmentRef, record: NewRegistryRecord): Promise<{ token: Token }> {\n validateSegmentRef(ref);\n const checked = validateNewRegistryRecord(record);\n const key = segmentKey(ref);\n if (this.rows.has(key)) {\n throw new WriteConflictError(`registry row already exists for segment ${ref.segment}`);\n }\n const token = this.nextToken(drawIncarnation(this.entropy));\n // clone in: the caller's (nested) retention/residency can't alias stored state (value semantics, parity\n // with the serialize-based persistent drivers).\n this.rows.set(key, structuredClone(recordFromNew(ref, checked, this.now(), token)));\n return { token };\n }\n\n async compareAndSwap(\n ref: SegmentRef,\n expected: Token,\n patch: RegistryPatch,\n ): Promise<{ token: Token }> {\n validateSegmentRef(ref);\n const checked = validateRegistryPatch(patch);\n const key = segmentKey(ref);\n const existing = this.rows.get(key);\n if (!existing || existing.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n const token = this.nextToken(incarnationOf(existing.token) ?? drawIncarnation(this.entropy));\n this.rows.set(key, structuredClone(applyRegistryPatch(existing, checked, this.now(), token)));\n return { token };\n }\n\n async *list(namespace?: string): AsyncIterable<RegistryRecord> {\n for (const row of this.rows.values()) {\n if (namespace === undefined || row.namespace === namespace) yield structuredClone(row);\n }\n }\n\n async delete(ref: SegmentRef, expected?: Token): Promise<void> {\n validateSegmentRef(ref);\n const key = segmentKey(ref);\n if (expected !== undefined && this.rows.get(key)?.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n this.rows.delete(key); // idempotent; token uniqueness is global so a recreate is ABA-safe\n }\n}\n\n/**\n * In-memory {@link IStorageDriver} \u2014 write-once immutable generation objects as opaque bytes. A \"dumb byte\n * mover\" (understands neither roaring nor `.crbm`), so it's a faithful storage backend for tests and a zero-setup\n * local storage tier. Mirrors the LocalFs/S3 contract (write-once, range/tail reads).\n */\nexport class MemoryStorageDriver implements IStorageDriver {\n private readonly objects = new Map<string, Uint8Array>();\n\n capabilities(): StorageCaps {\n return { rangeRead: true, maxObjectBytes: Number.MAX_SAFE_INTEGER, conditionalPut: true };\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const k = genObjectKey(key); // validates ref + generation\n const sink = new BufferSink();\n await write(sink);\n const body = sink.bytes();\n if (this.objects.has(k)) {\n throw new WriteConflictError(\n `generation already exists (write-once): ${key.segment}.${key.generation}`,\n );\n }\n this.objects.set(k, body);\n return { size: body.length, sha256: createHash('sha256').update(body).digest('hex') };\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n const body = this.require(key);\n if (offset + length > body.length) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${body.length}B`,\n );\n }\n return body.slice(offset, offset + length);\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const body = this.require(key);\n const take = Math.min(Math.max(maxBytes, 0), body.length);\n return { bytes: body.slice(body.length - take), size: body.length };\n }\n\n async delete(key: GenKey): Promise<void> {\n this.objects.delete(genObjectKey(key)); // idempotent\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n validateSegmentRef(ref);\n const prefix = `${segmentKey(ref)} `;\n for (const k of this.objects.keys()) {\n if (k.startsWith(prefix)) {\n yield {\n namespace: ref.namespace,\n segment: ref.segment,\n generation: Number(k.slice(prefix.length)),\n };\n }\n }\n }\n\n private require(key: GenKey): Uint8Array {\n const body = this.objects.get(genObjectKey(key));\n if (body === undefined) {\n throw new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);\n }\n return body;\n }\n}\n\n/** Collision-proof object key for a generation: `<segmentKey> <generation>` (the name parts are encoded, so a space cannot appear inside one). */\nfunction genObjectKey(key: GenKey): string {\n validateSegmentRef(key);\n if (!Number.isInteger(key.generation) || key.generation < 0) {\n throw new ValidationError(`generation must be a non-negative integer; got ${key.generation}`);\n }\n return `${segmentKey(key)} ${key.generation}`;\n}\n", "/**\n * The byte-IO seam the `.crbm` codec reads/writes through.\n *\n * Deliberately tiny and driver-independent: a `BlobSink` is a streaming append target (the writer needs\n * no random access), a `BlobReader` supports the speculative tail read + bounded range reads. An\n * `IStorageDriver` adapts a `GenKey` to these; the in-memory impls below let the format be\n * golden-tested and let the in-memory storage tier hold a real `.crbm` with no filesystem.\n */\nimport { ValidationError } from './errors';\n\n/** Streaming append target for a single object being written. */\nexport interface BlobSink {\n write(bytes: Uint8Array): Promise<void>;\n}\n\n/**\n * Random/tail read access to a single finished object. The total size comes back from `getTail` (the\n * codec's entry point), so there's no separate `size()` \u2014 keeping the seam minimal for driver authors.\n */\nexport interface BlobReader {\n /** Exactly `length` bytes starting at `offset`. Out-of-range is a `ValidationError`, never a short read. */\n getRange(offset: number, length: number): Promise<Uint8Array>;\n /** The last `min(maxBytes, size)` bytes plus the total size \u2014 the one-GET footer+index path. */\n getTail(maxBytes: number): Promise<{ bytes: Uint8Array; size: number }>;\n}\n\n/** In-memory `BlobSink` that accumulates written bytes; `bytes()` returns the concatenation. */\nexport class BufferSink implements BlobSink {\n private readonly chunks: Uint8Array[] = [];\n private length = 0;\n\n write(bytes: Uint8Array): Promise<void> {\n // Copy: callers may reuse their buffer after write() resolves.\n this.chunks.push(bytes.slice());\n this.length += bytes.length;\n return Promise.resolve();\n }\n\n bytes(): Uint8Array {\n const out = new Uint8Array(this.length);\n let offset = 0;\n for (const chunk of this.chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n }\n}\n\n/** In-memory `BlobReader` over a fixed byte buffer. */\nexport class BufferReader implements BlobReader {\n constructor(private readonly buffer: Uint8Array) {}\n\n getRange(offset: number, length: number): Promise<Uint8Array> {\n if (\n !Number.isInteger(offset) ||\n !Number.isInteger(length) ||\n offset < 0 ||\n length < 0 ||\n offset + length > this.buffer.length\n ) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${this.buffer.length}-byte blob`,\n );\n }\n return Promise.resolve(this.buffer.subarray(offset, offset + length));\n }\n\n getTail(maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const size = this.buffer.length;\n const take = Math.min(Math.max(maxBytes, 0), size);\n return Promise.resolve({ bytes: this.buffer.subarray(size - take), size });\n }\n}\n", "/**\n * `LocalFsStorageDriver` \u2014 a zero-cloud {@link IStorageDriver} backed by the local filesystem.\n *\n * Generations are write-once immutable files. A new object is streamed to a temp file (with its content\n * hashed in-flight), `fsync`-ed, then published with an atomic `link` that fails if the destination\n * already exists \u2014 so a generation can never be silently overwritten (hard invariant 2), and a crash\n * leaves only an orphan temp file, never a torn object. Drivers do filesystem I/O and may use `node:crypto`; only `core/`\n * is bound by the determinism lint.\n */\nimport { constants as FS } from 'node:fs';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { link, mkdir, open, readdir, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { NotFoundError, ValidationError, WriteConflictError } from '@/core/errors';\nimport type { BlobSink } from '@/core/blob';\nimport type { StorageCaps, GenKey, IStorageDriver, SegmentRef } from '@/core/ports';\nimport { storageObjectPath, parseGeneration, segmentsDir } from './paths';\nimport { O_NOFOLLOW, fsyncDir, isCode, mapFsError } from './fs-util';\n\nexport class LocalFsStorageDriver implements IStorageDriver {\n constructor(private readonly root: string) {}\n\n capabilities(): StorageCaps {\n return { rangeRead: true, maxObjectBytes: Number.MAX_SAFE_INTEGER, conditionalPut: false };\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const finalPath = storageObjectPath(this.root, key);\n await mkdir(dirname(finalPath), { recursive: true });\n\n const tmpPath = `${finalPath}.${randomUUID()}.tmp`;\n // 'wx' = O_CREAT|O_EXCL|O_WRONLY; add O_NOFOLLOW so a pre-planted symlink can't redirect the write.\n const handle = await open(tmpPath, FS.O_CREAT | FS.O_EXCL | FS.O_WRONLY | O_NOFOLLOW);\n const hash = createHash('sha256');\n let size = 0;\n const sink: BlobSink = {\n async write(bytes) {\n await handle.write(bytes);\n hash.update(bytes);\n size += bytes.length;\n },\n };\n\n try {\n await write(sink);\n await handle.sync();\n } catch (err) {\n await handle.close().catch(() => {});\n await unlink(tmpPath).catch(() => {});\n throw mapFsError(err);\n }\n await handle.close();\n\n // Atomic write-once publish: link fails with EEXIST if the generation already exists.\n try {\n await link(tmpPath, finalPath);\n } catch (err) {\n await unlink(tmpPath).catch(() => {});\n if (isCode(err, 'EEXIST')) {\n // A generation is write-once; a collision is a concurrency/state condition, not bad input.\n // Identify it by the logical key only \u2014 never leak the absolute filesystem path.\n throw new WriteConflictError(\n `generation already exists (write-once): ${key.segment}.${key.generation}`,\n );\n }\n throw mapFsError(err);\n }\n await unlink(tmpPath).catch(() => {});\n await fsyncDir(dirname(finalPath));\n return { size, sha256: hash.digest('hex') };\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n const handle = await this.openRead(key);\n try {\n const { size } = await handle.stat();\n if (offset + length > size) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds for ${size}B`,\n );\n }\n const buf = Buffer.alloc(length);\n if (length > 0) await handle.read(buf, 0, length, offset);\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n } finally {\n await handle.close();\n }\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const handle = await this.openRead(key);\n try {\n const { size } = await handle.stat();\n const take = Math.min(Math.max(maxBytes, 0), size);\n const buf = Buffer.alloc(take);\n if (take > 0) await handle.read(buf, 0, take, size - take);\n return { bytes: new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength), size };\n } finally {\n await handle.close();\n }\n }\n\n async delete(key: GenKey): Promise<void> {\n // Idempotent: deleting an absent generation is a no-op (GC may race / retry).\n await unlink(storageObjectPath(this.root, key)).catch((err) => {\n if (!isCode(err, 'ENOENT')) throw mapFsError(err);\n });\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n const dir = segmentsDir(this.root, ref);\n let names: string[];\n try {\n names = await readdir(dir);\n } catch (err) {\n if (isCode(err, 'ENOENT')) return; // no generations yet\n throw mapFsError(err);\n }\n for (const name of names) {\n const generation = parseGeneration(ref.segment, name);\n if (generation !== null) {\n yield { namespace: ref.namespace, segment: ref.segment, generation };\n }\n }\n }\n\n private async openRead(key: GenKey): Promise<Awaited<ReturnType<typeof open>>> {\n try {\n return await open(storageObjectPath(this.root, key), FS.O_RDONLY | O_NOFOLLOW);\n } catch (err) {\n if (isCode(err, 'ENOENT')) {\n throw new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);\n }\n // A symlink at the object path is rejected (ELOOP) \u2014 treat as not-found, don't follow it.\n if (isCode(err, 'ELOOP')) {\n throw new NotFoundError(\n `generation path is a symlink, refusing: ${key.segment}.${key.generation}`,\n );\n }\n throw mapFsError(err);\n }\n }\n}\n", "/**\n * Logical-ref \u2192 filesystem-path mapping for the LocalFs drivers.\n *\n * Names are re-validated here at the driver boundary \u2014 defense in depth, because a driver can be\n * constructed and driven directly rather than through the engine that would otherwise have validated for\n * it, and a path built from an unvalidated name is a traversal or injection. The default\n * (absent) namespace maps to `_default`, which cannot collide with a real namespace because a\n * caller's `_default` encodes to `%5Fdefault` while this sentinel is emitted literally.\n */\nimport { join } from 'node:path';\nimport { ValidationError } from '@/core/errors';\nimport { validateSegmentRef } from '@/core/validate';\nimport type { GenKey, SegmentRef } from '@/core/ports';\nimport {\n DEFAULT_NAMESPACE,\n decodeNameFromPath,\n encodeNameForPath,\n namespacePathPart,\n} from '../_shared/keys';\n\n/** Directory holding all of a namespace's segment objects. */\nexport function segmentsDir(root: string, ref: SegmentRef): string {\n validateSegmentRef(ref);\n return join(root, namespacePathPart(ref.namespace), 'segments');\n}\n\n/** Absolute path of one `.crbm` generation object. */\nexport function storageObjectPath(root: string, key: GenKey): string {\n validateSegmentRef(key);\n if (!Number.isInteger(key.generation) || key.generation < 0) {\n throw new ValidationError(`generation must be a non-negative integer; got ${key.generation}`);\n }\n return join(segmentsDir(root, key), storageObjectFilename(key.segment, key.generation));\n}\n\n/** Filename pattern for a segment's generations: `<segment>.<gen>.crbm`. */\nexport function storageObjectFilename(segment: string, generation: number): string {\n return `${encodeNameForPath(segment)}.${generation}.crbm`;\n}\n\n/** Parse a generation number out of a `<segment>.<gen>.crbm` filename, or `null` if it doesn't match. */\nexport function parseGeneration(segment: string, filename: string): number | null {\n const prefix = `${encodeNameForPath(segment)}.`;\n const suffix = '.crbm';\n if (!filename.startsWith(prefix) || !filename.endsWith(suffix)) return null;\n const middle = filename.slice(prefix.length, filename.length - suffix.length);\n // Canonical decimal only: no leading zeros (so `s.07.crbm` can't alias `s.7.crbm`), and within\n // safe-integer range. The writer only ever emits the canonical form.\n if (!/^(0|[1-9]\\d*)$/.test(middle)) return null;\n const gen = Number(middle);\n return Number.isSafeInteger(gen) ? gen : null;\n}\n\nconst REGISTRY_SUFFIX = '.reg';\n\n/** Directory holding a namespace's registry rows (one file per segment). */\nexport function registryDir(root: string, namespace: string | undefined): string {\n // Validated like every sibling builder here. It is the caller-facing entry for a namespace-scoped\n // `list()`, so without this a namespace of `../..` reaches `readdir` outside the storage root \u2014 no content\n // escapes (iteration aborts before yielding) but it answers \"does this directory exist?\", and the whole\n // point of re-validating at the driver boundary is that a driver driven directly must be safe on its own.\n if (namespace !== undefined) validateSegmentRef({ segment: 'x', namespace });\n return join(root, namespacePathPart(namespace), 'registry');\n}\n\n/** Absolute path of one segment's registry row file: `<ns>/registry/<segment>.reg`. */\nexport function registryRowPath(root: string, ref: SegmentRef): string {\n validateSegmentRef(ref);\n return join(\n registryDir(root, ref.namespace),\n `${encodeNameForPath(ref.segment)}${REGISTRY_SUFFIX}`,\n );\n}\n\n/**\n * Parse a segment name out of a `<segment>.reg` filename, or `null` if it doesn't match \u2014 including any\n * file whose stem isn't a valid segment name. A strict parser means a stray or planted `.reg` file makes\n * `list()` *skip* it, never abort the whole enumeration on a boundary throw.\n */\nexport function parseRegistryRow(filename: string): string | null {\n if (!filename.endsWith(REGISTRY_SUFFIX)) return null;\n const stem = filename.slice(0, filename.length - REGISTRY_SUFFIX.length);\n const segment = decodeNameFromPath(stem);\n // The encoding must ROUND-TRIP, not merely decode. POSIX will happily hold a literal `dedup:foo.reg`\n // alongside the driver's own `dedup%3Afoo.reg`; both decode to `dedup:foo`, and reporting that name twice\n // would hand `list()` a segment whose row path resolves to only one of them. The driver never writes the\n // literal form, so anything that does not re-encode to exactly this filename was not written by us.\n if (encodeNameForPath(segment) !== stem) return null;\n try {\n validateSegmentRef({ segment });\n } catch {\n return null;\n }\n return segment;\n}\n\n/**\n * Parse a namespace out of a directory name under the storage root, or `null` if it was not written by us.\n *\n * The inverse of the `namespacePathPart(ns)` that {@link segmentsDir} and {@link registryDir}\n * write. A fleet-wide scan enumerates these directories, so getting it wrong does not fail one segment \u2014 it\n * aborts the whole enumeration, taking the consistency check, the retention sweep and subject erasure with it.\n *\n * Like {@link parseRegistryRow} this requires the encoding to **round-trip**, so a planted literal `tenant:acme`\n * directory sitting next to the driver's own `tenant%3Aacme` is skipped rather than reported as a namespace\n * whose rows then resolve to the other directory. `_default` maps back to \"no namespace\".\n */\nexport function parseNamespaceDir(entry: string): { namespace: string | undefined } | null {\n if (entry === DEFAULT_NAMESPACE) return { namespace: undefined };\n const namespace = decodeNameFromPath(entry);\n if (encodeNameForPath(namespace) !== entry) return null;\n try {\n validateSegmentRef({ segment: 'x', namespace });\n } catch {\n return null;\n }\n return { namespace };\n}\n", "/**\n * Shared filesystem helpers for the LocalFs drivers (storage and registry) \u2014 one source\n * of truth for error-code matching, the symlink-refusal flag, and directory durability.\n */\nimport { constants as FS } from 'node:fs';\nimport { open } from 'node:fs/promises';\nimport { TransientError } from '@/core/errors';\n\ninterface NodeError extends Error {\n code?: string;\n}\n\n/** True when `err` is a Node system error with the given `code` (e.g. `'ENOENT'`, `'EEXIST'`). */\nexport const isCode = (err: unknown, code: string): boolean =>\n err instanceof Error && (err as NodeError).code === code;\n\n/**\n * Filesystem error codes that are transient \u2014 a retry after a brief backoff often succeeds: the resource is\n * busy (`EBUSY`), the OS asked us to retry (`EAGAIN`), or we hit the open-file-descriptor ceiling\n * (`EMFILE`/`ENFILE`, common under heavy concurrency once earlier handles close). A networked FS may also\n * surface `ETIMEDOUT`.\n */\nconst TRANSIENT_FS_CODES = new Set(['EBUSY', 'EAGAIN', 'EMFILE', 'ENFILE', 'ETIMEDOUT']);\n\nexport const isFsTransient = (err: unknown): boolean =>\n err instanceof Error && TRANSIENT_FS_CODES.has((err as NodeError).code ?? '');\n\n/**\n * Reclassify a transient filesystem fault as a retryable {@link TransientError}, so the store's read retry can\n * ride it out and a write's caller can tell it from a deterministic failure; everything else \u2014 including the\n * driver's own typed errors \u2014 propagates unchanged.\n */\nexport const mapFsError = (err: unknown): unknown =>\n isFsTransient(err)\n ? new TransientError(`transient filesystem fault: ${(err as NodeError).code}`, { cause: err })\n : err;\n\n/**\n * `O_NOFOLLOW` where the platform supports it (absent on Windows \u2192 0). OR it into an `open` flag set so a\n * symlink **at the final path component** is refused rather than followed outside the storage root.\n * Containment of symlinked *directory* components assumes the root itself is not attacker-writable.\n */\nexport const O_NOFOLLOW = FS.O_NOFOLLOW ?? 0;\n\n/**\n * Best-effort parent-directory fsync, so a just-published `rename`/`link` survives a crash. Without it the\n * directory entry can still be in the page cache when power goes: the generation's bytes are durable and\n * the name pointing at them is not, which is the one way a published generation can come back missing.\n */\nexport async function fsyncDir(dir: string): Promise<void> {\n try {\n const handle = await open(dir, 'r');\n try {\n await handle.sync();\n } finally {\n await handle.close();\n }\n } catch {\n // Some platforms reject directory fsync; durability of the entry is then best-effort.\n }\n}\n", "/**\n * `LocalFsRegistryDriver` \u2014 a zero-cloud, persistent {@link IRegistryDriver}.\n *\n * One JSON file per segment at `<root>/<namespace>/registry/<segment>.reg`, holding `{ deleted, record }`.\n * OCC: the token is a random incarnation id drawn when the row is created, a counter advanced on every mutation, and a\n * random part drawn for every write, so with overwhelming probability a deleted-then-recreated row, or one restored from a backup, never\n * re-issues an old token (ABA-safe). A `delete` unlinks a row born with an incarnation id, and **tombstones** one a release before\n * 0.12 wrote, whose bare counter a re-create carries on. Every write is temp \u2192 fsync(file) \u2192 atomic rename \u2192 fsync(dir), and\n * read-modify-write is serialized per row across the whole process (the lock is keyed by the row's resolved\n * path, so every instance on one root shares it). A root is for one process: two processes on one root are not\n * fenced. Drivers do I/O; only `core/` is bound by determinism.\n */\nimport { constants as FS } from 'node:fs';\nimport { randomUUID } from 'node:crypto';\nimport { mkdir, open, readdir, realpath, rename, unlink } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { IntegrityError, ValidationError, WriteConflictError } from '@/core/errors';\nimport type { Entropy } from '@/core/determinism';\nimport type {\n IRegistryDriver,\n NewRegistryRecord,\n RegCaps,\n RegistryPatch,\n RegistryRecord,\n SegmentRef,\n Token,\n} from '@/core/ports';\nimport {\n applyRegistryPatch,\n incarnationOf,\n newIncarnationToken,\n nextRegistryToken,\n parseRegistryEnvelope,\n recordFromNew,\n serializeRegistryEnvelope,\n validateNewRegistryRecord,\n validateRegistryPatch,\n type RegistryEnvelope,\n} from '../_shared/registry';\nimport { entropyIsAvailable, webCryptoEntropy } from '../_shared/entropy';\nimport { registryDir, registryRowPath, parseNamespaceDir, parseRegistryRow } from './paths';\nimport { O_NOFOLLOW, fsyncDir, isCode, mapFsError } from './fs-util';\n\n/** Defensive cap on a single registry file read from storage, before allocation. */\nconst DEFAULT_MAX_ROW_BYTES = 1 * 1024 * 1024;\n\n/**\n * Per-row promise chains, shared by every driver instance in the process and keyed by the row's resolved path,\n * so two instances on one root (or one root reached through a symlink or a relative path) take one lock. An\n * entry is deleted when its chain drains, so the map holds only rows with an operation in flight.\n */\nconst rowChains = new Map<string, Promise<unknown>>();\n\n/** Entries in the process-wide row-lock map; the map is empty whenever no registry operation is in flight. */\nexport function localFsRowLockCount(): number {\n return rowChains.size;\n}\n\n/**\n * The row's identity for locking: the real path of the nearest directory that exists, plus the not-yet-created\n * tail. Resolving symlinks and relative roots makes every spelling of one row the same key.\n */\nasync function rowLockKey(path: string): Promise<string> {\n const abs = resolve(path);\n const tail: string[] = [basename(abs)];\n let dir = dirname(abs);\n for (;;) {\n try {\n return join(await realpath(dir), ...tail);\n } catch (err) {\n const parent = dirname(dir);\n if (!isCode(err, 'ENOENT') || parent === dir) throw mapFsError(err);\n tail.unshift(basename(dir));\n dir = parent;\n }\n }\n}\n\nexport interface LocalFsRegistryDriverOptions {\n /** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /** Draws every token's random parts; defaults to Web Crypto. Inject one only to make a test replayable. */\n readonly entropy?: Entropy;\n}\n\nexport class LocalFsRegistryDriver implements IRegistryDriver {\n private readonly now: () => number;\n private readonly entropy: Entropy;\n\n constructor(\n private readonly root: string,\n options: LocalFsRegistryDriverOptions = {},\n ) {\n this.now = options.now ?? (() => Date.now());\n this.entropy = options.entropy ?? webCryptoEntropy;\n }\n\n /**\n * `conditionalDelete`: a delete reads the row, checks it, and unlinks it under the row's lock, which every writer on\n * the root in this process takes, so no write can land between the check and the unlink.\n */\n capabilities(): RegCaps {\n return entropyIsAvailable(this.entropy)\n ? { strongRead: true, conditionalDelete: true }\n : { strongRead: true, canWrite: false, conditionalDelete: true };\n }\n\n async get(ref: SegmentRef): Promise<RegistryRecord | null> {\n const env = await this.readRow(registryRowPath(this.root, ref));\n return env && !env.deleted ? env.record : null;\n }\n\n async create(ref: SegmentRef, record: NewRegistryRecord): Promise<{ token: Token }> {\n const checked = validateNewRegistryRecord(record);\n const path = registryRowPath(this.root, ref);\n return this.withRowLock(path, async () => {\n const current = await this.readRow(path);\n if (current !== null && !current.deleted) {\n throw new WriteConflictError(`registry row already exists for segment ${ref.segment}`);\n }\n // A new incarnation, whose counter continues across a tombstone (ABA-safe).\n const token = newIncarnationToken(this.entropy, current?.record);\n await this.writeRow(path, false, recordFromNew(ref, checked, this.now(), token));\n return { token };\n });\n }\n\n async compareAndSwap(\n ref: SegmentRef,\n expected: Token,\n patch: RegistryPatch,\n ): Promise<{ token: Token }> {\n const checked = validateRegistryPatch(patch);\n const path = registryRowPath(this.root, ref);\n return this.withRowLock(path, async () => {\n const current = await this.readRow(path);\n if (current === null || current.deleted || current.record.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n const token = nextRegistryToken(current.record, this.entropy);\n await this.writeRow(\n path,\n false,\n applyRegistryPatch(current.record, checked, this.now(), token),\n );\n return { token };\n });\n }\n\n async *list(namespace?: string): AsyncIterable<RegistryRecord> {\n for (const ns of await this.namespaceDirs(namespace)) {\n const dir = registryDir(this.root, ns);\n let names: string[];\n try {\n names = await readdir(dir);\n } catch (err) {\n if (isCode(err, 'ENOENT')) continue; // no registry rows in this namespace yet\n throw mapFsError(err);\n }\n for (const name of names) {\n const segment = parseRegistryRow(name);\n if (segment === null) continue;\n const env = await this.readRow(registryRowPath(this.root, { namespace: ns, segment }));\n if (env && !env.deleted) yield env.record;\n }\n }\n }\n\n async delete(ref: SegmentRef, expected?: Token): Promise<void> {\n const path = registryRowPath(this.root, ref);\n return this.withRowLock(path, async () => {\n const current = await this.readRow(path);\n if (expected !== undefined) {\n if (current === null || current.deleted || current.record.token !== expected) {\n throw new WriteConflictError(`OCC token mismatch for registry row ${ref.segment}`);\n }\n } else if (current === null || current.deleted) {\n return; // idempotent\n }\n if (incarnationOf(current.record.token) !== undefined) {\n // Born with an incarnation id: a re-create draws a new one, so the file can go.\n await this.unlinkRow(path);\n return;\n }\n // A row a release before 0.12 wrote: tombstone it (advance the counter) rather than unlink, so a re-create\n // carries the counter on, and a process still on that release cannot re-issue its tokens from 0.\n const token = nextRegistryToken(current.record, this.entropy);\n await this.writeRow(path, true, { ...current.record, token, updatedAt: this.now() });\n });\n }\n\n /** Remove a row's file and make the removal durable (fsync the directory). */\n private async unlinkRow(path: string): Promise<void> {\n try {\n await unlink(path);\n } catch (err) {\n if (!isCode(err, 'ENOENT')) throw mapFsError(err);\n }\n await fsyncDir(dirname(path));\n }\n\n /** Namespaces to scan: just the one requested, or every namespace dir under the root. */\n private async namespaceDirs(namespace: string | undefined): Promise<Array<string | undefined>> {\n if (namespace !== undefined) return [namespace];\n let entries: string[];\n try {\n entries = await readdir(this.root);\n } catch (err) {\n if (isCode(err, 'ENOENT')) return [];\n throw mapFsError(err);\n }\n // A namespace dir name is the namespace ENCODED for a path, so it has to be parsed back rather than used\n // as-is \u2014 `tenant:acme` lives in `tenant%3Aacme`, and `%` only ever appears in an escape we wrote, so an un-decoded directory name fails the round-trip check. Anything that does\n // not parse is skipped, never thrown on: this enumeration is fleet-wide, so one unrecognised directory\n // must not take the consistency check, the retention sweep and subject erasure down with it.\n const namespaces: Array<string | undefined> = [];\n for (const entry of entries) {\n const parsed = parseNamespaceDir(entry);\n if (parsed !== null) namespaces.push(parsed.namespace);\n }\n return namespaces;\n }\n\n private async readRow(path: string): Promise<RegistryEnvelope | null> {\n let handle;\n try {\n handle = await open(path, FS.O_RDONLY | O_NOFOLLOW);\n } catch (err) {\n if (isCode(err, 'ENOENT') || isCode(err, 'ELOOP')) return null;\n throw mapFsError(err);\n }\n try {\n const { size } = await handle.stat();\n if (size > DEFAULT_MAX_ROW_BYTES) {\n throw new IntegrityError(`registry row ${size}B exceeds cap ${DEFAULT_MAX_ROW_BYTES}B`);\n }\n const text = (await handle.readFile()).toString('utf8');\n return parseRegistryEnvelope(text, path);\n } finally {\n await handle.close();\n }\n }\n\n private async writeRow(path: string, deleted: boolean, record: RegistryRecord): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const out = Buffer.from(serializeRegistryEnvelope({ deleted, record }), 'utf8');\n // Cap on the write path too (the read path caps at the same size): never produce a row that would later\n // be unreadable. The governance fields are already capped by validate*, so this is a belt-and-braces guard.\n if (out.length > DEFAULT_MAX_ROW_BYTES) {\n throw new ValidationError(\n `registry row ${out.length}B exceeds cap ${DEFAULT_MAX_ROW_BYTES}B`,\n );\n }\n\n const tmp = `${path}.${randomUUID()}.tmp`;\n let handle;\n try {\n handle = await open(tmp, FS.O_CREAT | FS.O_EXCL | FS.O_WRONLY | O_NOFOLLOW);\n } catch (err) {\n throw mapFsError(err);\n }\n try {\n await handle.write(out);\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(tmp, path).catch(async (err) => {\n await unlink(tmp).catch(() => {});\n throw mapFsError(err);\n });\n await fsyncDir(dirname(path));\n }\n\n /** Serialize callbacks for a row so read-modify-write is atomic across every instance in the process. */\n private async withRowLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const key = await rowLockKey(path);\n const prev = rowChains.get(key) ?? Promise.resolve();\n const result = prev.then(fn, fn);\n const tail = result.then(\n () => undefined,\n () => undefined,\n );\n rowChains.set(key, tail);\n void tail.then(() => {\n if (rowChains.get(key) === tail) rowChains.delete(key);\n });\n return result;\n }\n}\n", "/**\n * Audit sink \u2014 an injected, no-op-by-default seam for **security/compliance** events, distinct\n * from the metrics sink (`IMetricsSink`). Different audience (an audit log / SIEM, not a dashboard), different\n * retention, and only the compliance-relevant *state changes* \u2014 never routine reads/writes (that's the metrics\n * sink). It doubles as the GDPR Art. 30 \"record of processing\" surface: publishes, refused loads, rollbacks,\n * rewrites, erasures and disposals.\n *\n * Like `Clock`/`Rng`/`IMetricsSink`, it's injected (into the operations that emit \u2014 a load, the `*Into`\n * verbs, a rollback, the erasure rewrite, crypto-shred, disposal and the retention sweep) and wrapped\n * exception-safe, so a buggy sink can never break the operation it observes.\n * Events are vendor-neutral and carry no timestamp/actor \u2014 the sink runs synchronously at the event, so it\n * stamps its own time / attaches the caller identity (keeps `core/` free of ambient time). As with metrics,\n * `segment`/`namespace` are caller-controlled strings that may be PII \u2014 treat them accordingly when routing.\n *\n * Not (yet) emitted: **KEK rotation** \u2014 rotation here is operator-side keystore reconfiguration (key-id-tagged\n * wrappings, no data re-encryption), so there is no library call to hook. A future `rewrapSegment()` op would\n * add a `kek.rotate` variant; until then, audit key changes at your keystore-config layer.\n */\n\n/** A security/compliance-relevant state change. Vendor-neutral; the sink adds its own timestamp/actor. */\nexport type AuditEvent =\n | {\n /** A new immutable Storage generation *became the segment's current generation*, by a load's publish. */\n readonly kind: 'segment.publish';\n readonly namespace?: string;\n readonly segment: string;\n readonly generation: number;\n }\n | {\n /**\n * A segment's pointer was moved by a rollback to a generation still in the bucket that the caller named:\n * **backwards**, or forward with `allowForward`, which undoes an earlier rollback.\n *\n * The only non-forward-only pointer move in the library, and the only one no automatic path can perform \u2014\n * a human decided the current generation was wrong and named the one they wanted. Every other pointer move\n * can be reconstructed from \"a load happened\"; this one is someone overriding the ordering rule the rest of\n * the system relies on, which is precisely what an incident review or an Art. 30 record wants to find.\n *\n * `fromGeneration` is `null` when the segment had a row but no current generation.\n */\n readonly kind: 'segment.rollback';\n readonly namespace?: string;\n readonly segment: string;\n readonly fromGeneration: number | null;\n readonly generation: number;\n }\n | {\n /**\n * A load was **refused**: it did not publish, because its result failed a guard or another writer got there\n * first. The security-relevant fact is that a replacement the caller asked for did NOT happen, which a\n * downstream system reconciling \"the segment should now contain X\" needs as much as it needs the publish.\n *\n * `reason` is `'empty'` (an empty result over a non-empty segment, with no `allowEmpty`),\n * `'min-cardinality'`, `'min-retained'`, or `'superseded'` (another load took the generation number, or the\n * segment's registry row changed while the load was writing). `cardinality` is what the refused generation\n * would have contained, and `0` for a load that lost its generation number and wrote nothing.\n *\n * `unanswered` is present, and `true`, on a `'superseded'` refusal whose registry write ended without an answer\n * before the row was found to have moved on: that write may have landed, so the generation may have been the\n * segment's current one for a while before another writer replaced it. The refusal says only that it is not\n * current now. Absent when every write the load made was answered.\n */\n readonly kind: 'segment.load-refused';\n readonly namespace?: string;\n readonly segment: string;\n readonly generation: number;\n readonly reason: 'empty' | 'min-cardinality' | 'min-retained' | 'superseded';\n readonly cardinality: number;\n readonly unanswered?: true;\n }\n | {\n /**\n * A generation was **rewritten**: a new generation derived from `fromGeneration` became current in its\n * place. Today the one emitter is `eraseIdFromSegment` (a subject erasure clearing one id), and the event is\n * emitted at the publish \u2014 before the superseded generation is collected \u2014 so the record exists the moment\n * the generation without the id is authoritative. A `segment.publish` is NOT also emitted for a rewrite: a\n * rewrite derives its content from the segment itself, a publish brings content in from outside.\n */\n readonly kind: 'segment.rewrite';\n readonly namespace?: string;\n readonly segment: string;\n readonly fromGeneration: number;\n readonly generation: number;\n }\n | {\n /**\n * A segment was **crypto-shredded** \u2014 its wrapped DEK(s) are gone, so its at-rest Storage bytes are now\n * permanently unreadable. Emitted only for a genuine key shred, never for a cleartext tombstone (which\n * leaves the Storage bytes readable) or an idempotent re-run.\n */\n readonly kind: 'segment.erase';\n readonly namespace?: string;\n readonly segment: string;\n }\n | {\n /**\n * A segment was **disposed of** \u2014 tombstoned and its storage reclaimed (its Storage generations deleted) by\n * `dropSegment`, *without* a key shred.\n *\n * Deliberately a separate kind from {@link AuditEvent} `segment.erase`, and the distinction is the point.\n * `segment.erase` attests that bytes are unreadable **everywhere, backups included** \u2014 the only claim that\n * survives WORM. Deleting an object is strictly weaker: a noncurrent version, a cross-region replica or a\n * PITR snapshot can still hold the cleartext. Emitting one kind for both would make a compliance dashboard\n * over-attest, so a cleartext drop gets this instead.\n *\n * An **encrypted** segment dropped via `dropSegment` emits **both** \u2014 `segment.erase` for the key shred and\n * this for the storage reclamation \u2014 because both things genuinely happened.\n *\n * `generationsDeleted` is how many Storage generations went. It can be 0 (a segment whose bytes were already\n * gone), and it does not promise the storage is now fully reclaimed: check `DropResult.generationsRemaining`\n * for that.\n */\n readonly kind: 'segment.dispose';\n readonly namespace?: string;\n readonly segment: string;\n readonly generationsDeleted: number;\n }\n | {\n /** A namespace erasure was executed. `segmentsShredded` is the count actually crypto-shredded (may be 0). */\n readonly kind: 'namespace.erase';\n readonly namespace: string;\n readonly segmentsShredded: number;\n };\n\n/** Sink for {@link AuditEvent}s. Injected via the lifecycle options; omit it and nothing is recorded. */\nexport interface IAuditSink {\n onEvent(event: AuditEvent): void;\n}\n\n/** The default sink: records nothing. */\nexport const NOOP_AUDIT: IAuditSink = {\n onEvent(): void {\n /* discard */\n },\n};\n\n/**\n * Wrap a sink so a throwing/buggy `onEvent` can never break the lifecycle operation it observes \u2014 audit is\n * strictly observation. Returns {@link NOOP_AUDIT} unchanged (so the no-op case skips even the try/catch alloc).\n */\nexport function safeAudit(sink: IAuditSink): IAuditSink {\n if (sink === NOOP_AUDIT) return sink;\n return {\n onEvent(event: AuditEvent): void {\n try {\n sink.onEvent(event);\n } catch {\n /* swallow \u2014 an audit sink must never break the operation it observes */\n }\n },\n };\n}\n\n/** A ready-made sink that records events into an in-memory list \u2014 handy for tests + simple audit trails. */\nexport class RecordingAuditSink implements IAuditSink {\n private readonly recorded: AuditEvent[] = [];\n\n onEvent(event: AuditEvent): void {\n this.recorded.push(event);\n }\n\n /** An independent copy of the recorded events, in emission order. */\n snapshot(): AuditEvent[] {\n return this.recorded.map((e) => ({ ...e }));\n }\n\n reset(): void {\n this.recorded.length = 0;\n }\n}\n", "/**\n * Cooperative yielding for the long CPU-bound loops.\n *\n * Everything this library does on the hot read path is small. Bulk-load is the exception: it is the one place\n * where a single call legitimately occupies the CPU for hundreds of milliseconds, and Node has exactly one thread\n * to occupy. Measured on a 1M-id load of 65,536 chunks onto in-memory storage, on a loaded Apple M3 Pro: **518 ms\n * wall, and 519 ms during which the event loop did not turn at all** (medians of 20 fresh-process runs). Wire that to a request handler and every other in-flight request on the instance \u2014 health\n * checks included \u2014 waits out the whole load.\n *\n * The fix is not to make the work asynchronous. That was measured too, and it is a 7x *regression*: handing each\n * chunk's insert to the threadpool costs ~9 \u00B5s of dispatch against ~1.5 \u00B5s of actual work once ids are spread\n * across ~62,000 chunks (636 ms versus 92 ms). The work is genuinely CPU-bound and genuinely small per chunk;\n * the only thing wrong with it is that it never stops to let anyone else go. So the fix is to keep the work\n * synchronous and periodically hand the loop back.\n *\n * TWO TRAPS, both of which make a plausible implementation measure as no change at all:\n *\n * 1. **`await Promise.resolve()` \u2014 and equally `Clock.sleep(0)` \u2014 do nothing.** Both resolve on a microtask, and\n * the microtask queue drains to empty before the event loop advances a single phase. A loop awaiting them\n * yields to other *microtasks* and to nothing else; no pending I/O callback runs. Measured: 555 ms of\n * starvation against a 568 ms unyielded baseline **on the synthetic yield-primitive benchmark below** \u2014\n * a different, smaller workload than the 442 ms end-to-end load above, which is why the two baselines\n * differ. The yield must be a **macrotask** \u2014 hence\n * `Clock.yieldNow`, backed by `setImmediate` in production wiring.\n * 2. **Yielding per unit of work costs more than the work.** The yield has to be periodic, and `everyN` has to be\n * large enough that its cost disappears into the batch it interrupts.\n *\n * At `everyN = 1024` over that same synthetic workload the trade is close to free: 569 ms wall against a 568 ms\n * baseline, with the worst event-loop gap down from 568 ms to 13.8 ms.\n *\n * **Three experiments, three sets of numbers \u2014 do not mix them.** (a) The end-to-end 1M-id bulk load:\n * 518 ms wall / 519 ms blocked unyielded, 526 ms / 24 ms yielded. (b) This synthetic yield-primitive comparison:\n * 568 ms / 568 ms unyielded, 569 ms / 13.8 ms with `setImmediate`. (c) A per-chunk insert microbenchmark:\n * 92 ms sync versus 636 ms via the threadpool. Quoting a baseline from one against a result from another describes\n * work none of them did, such as a 92 ms operation with 450 ms of starvation inside it.\n */\n/**\n * The only part of a `Clock` that yielding actually needs.\n *\n * Deliberately looser than `Clock` so a write path can opt in with whatever it has: a bulk load or a rewrite\n * yields when its caller supplies a clock that can, and simply runs unbroken when handed nothing (or a bare\n * `{ now }`). Requiring a full `Clock` would make every out-of-process caller wire one just to write a\n * generation.\n */\nexport interface Yielder {\n yieldNow?(): Promise<void>;\n sleep?(ms: number): Promise<void>;\n}\n\n/**\n * How many units of work pass between yields. Sized so the yield is unmeasurable against the batch it interrupts\n * (~60 yields across a 1M-id load) while still capping any single blocking stretch in the low tens of ms.\n */\nexport const YIELD_EVERY = 1024;\n\n/**\n * Build a periodic yield.\n *\n * Call the returned function **once per unit of work**. It returns `null` on the vast majority of calls \u2014 nothing\n * to await, nothing allocated \u2014 and a promise only on the `everyN`th, so the caller pays for a yield only when\n * one actually happens:\n *\n * ```ts\n * const tick = yieldEvery(clock);\n * for (const item of millions) {\n * work(item);\n * const pause = tick();\n * if (pause !== null) await pause;\n * }\n * ```\n *\n * The two-line shape is deliberate. `await tick()` would read better and would force a microtask turn on every\n * single iteration \u2014 an unconditional cost on the very loop this exists to keep fast.\n *\n * With no `clock` the result always returns `null`: the loop runs unbroken, so adding a yield point to a code\n * path can never change behaviour for a caller who did not opt in. `core/` cannot supply a clock itself \u2014 it is\n * timer-free by lint, which is the whole reason scheduling goes through the seam \u2014 so `@cloudbitmaps/roaring`\n * pre-binds a real one and flavor users get this with no wiring at all.\n */\nexport function yieldEvery(\n clock: Yielder | undefined,\n everyN: number = YIELD_EVERY,\n): () => Promise<void> | null {\n if (clock === undefined) return () => null;\n // Resolved once here rather than re-tested every yield.\n //\n // A clock without `yieldNow` falls back to `sleep(1)`, which yields on a *timer-backed* clock at ~1 ms of\n // dead wall-clock apiece (measured: +10% on the load above). It does NOT yield on a clock whose `sleep`\n // ignores its argument and resolves on a microtask \u2014 `InstantClock` and `SimClock` both do, deliberately.\n // Those are non-production wirings where yielding is meaningless, and both say so by implementing\n // `yieldNow` as an explicit no-op rather than arriving here by accident.\n const sleep = clock.sleep;\n const pause =\n clock.yieldNow !== undefined\n ? (): Promise<void> => clock.yieldNow!()\n : sleep !== undefined\n ? (): Promise<void> => sleep.call(clock, 1)\n : undefined;\n if (pause === undefined) return () => null; // a clock that can neither yield nor sleep: nothing to do\n let n = 0;\n return () => {\n if (++n < everyN) return null;\n n = 0;\n return pause();\n };\n}\n", "/**\n * Encryption seams \u2014 the **pure** interfaces `core/` depends on so the codec can encrypt/decrypt without\n * importing `node:crypto` (lint-banned here, like `Clock`/`Rng`/drivers). Concrete AES-256-GCM lives outside\n * `core/` (`src/drivers/crypto.ts`) and is injected. The envelope scheme: a per-segment DEK wrapped under an\n * operator-held KEK, so discarding the DEK crypto-shreds the segment. Per-chunk AEAD over the `.crbm` payloads + index; a per-segment **DEK** wrapped (envelope\n * encryption) under one or more **KEK**s by an {@link IKeystore}; the wrapped DEK(s) live in the registry, so\n * crypto-shred = delete them.\n */\n\n/** One AEAD result: a fresh per-message nonce, the ciphertext, and the authentication tag. */\nexport interface AeadSealed {\n readonly nonce: Uint8Array;\n readonly ciphertext: Uint8Array;\n readonly tag: Uint8Array;\n}\n\n/**\n * Authenticated encryption bound to a single key (a segment's DEK). `aad` (associated data) is authenticated\n * but not encrypted \u2014 the codec passes each chunk's `(namespace, segment, generation, chunkKey)` identity\n * (the library derives it; an `Aead` implementation receives it), so a chunk's ciphertext can't be silently\n * relocated to another segment/generation/chunk or\n * the index, even by someone who can rewrite Storage objects. The seam is symmetric and self-framing-agnostic:\n * the caller decides where the nonce/tag go (inline with a chunk, or in the footer for the index).\n */\nexport interface Aead {\n /** Encrypt `plaintext`, authenticating `aad`. Returns a fresh nonce + ciphertext + tag. */\n seal(plaintext: Uint8Array, aad: Uint8Array): AeadSealed;\n /**\n * Decrypt + verify. Throws {@link IntegrityError} on **any** mismatch (wrong key, tampered ciphertext/tag,\n * or `aad` that doesn't match what was sealed) \u2014 never returns wrong-but-plausible plaintext.\n */\n open(sealed: AeadSealed, aad: Uint8Array): Uint8Array;\n}\n\n/**\n * A DEK wrapped under one KEK. A segment stores a **list** of these (one per KEK it was wrapped under \u2014 e.g.\n * an active KEK plus an offline recovery KEK), so losing one KEK isn't fatal and KEK rotation needs no\n * data re-encryption. `keyId` names which KEK; `wrapped` is the opaque wrapped-key blob (base64). Carries no\n * plaintext key material \u2014 safe to store in the registry next to the (encrypted) data.\n */\nexport interface WrappedDek {\n readonly keyId: string;\n readonly wrapped: string;\n}\n\n/**\n * Envelope key management \u2014 the injected seam between the codec and however the operator holds key material\n * (in-process BYOK by default; KMS/Vault as optional adapters later). Async so a remote keystore (KMS) fits\n * the same interface. Never exposes raw key bytes to `core/`: it returns an {@link Aead} bound to the DEK.\n */\nexport interface IKeystore {\n /**\n * Mint a fresh per-segment DEK, wrapped under the active KEK (and any recovery KEK). Returns the wrapping(s)\n * to persist in the registry plus an {@link Aead} to encrypt this segment's chunks with.\n */\n createDek(): Promise<{ wrapped: WrappedDek[]; aead: Aead }>;\n /**\n * Reconstruct the {@link Aead} from a segment's stored wrappings by unwrapping with any KEK currently held.\n * Throws {@link KeyUnavailableError} if it holds none of the referenced KEKs (lost key, or a shredded\n * segment whose wrappings were deleted).\n */\n openDek(wrapped: readonly WrappedDek[]): Promise<Aead>;\n}\n\n/**\n * What the encrypting codec needs: the {@link Aead} for this segment's DEK plus a way to build the AAD for a\n * given scope (a chunkKey, `'index'`, or `'metadata'` for the generation's metadata section). Built per\n * (segment, generation) by the storage-source bridge so the codec itself stays segment-agnostic.\n */\nexport interface CrbmCrypto {\n readonly aead: Aead;\n aadFor(scope: number | 'index' | 'metadata'): Uint8Array;\n}\n\nconst AAD_VERSION = 1;\nconst SCOPE_CHUNK = 0;\nconst SCOPE_INDEX = 1;\nconst SCOPE_METADATA = 2;\nconst SCOPE_SUMMARY = 3;\n\n/**\n * Build the AEAD associated-data binding a chunk, the index, the metadata section or a registry row's sealed summary\n * to its exact location:\n * `v1 \u2016 len(namespace) \u2016 namespace \u2016 len(segment) \u2016 segment \u2016 generation \u2016 scope \u2016 chunkKey`, where scope is\n * `0` for a chunk, `1` for the index, `2` for the metadata and `3` for the row's summary, and chunkKey is `0` for\n * the last three.\n * Length-prefixed so distinct `(namespace, segment)` pairs can never collide. Pure (no crypto) \u2014 just the\n * authenticated label.\n */\nexport function aadFor(\n ref: { readonly namespace?: string; readonly segment: string },\n generation: number,\n scope: number | 'index' | 'metadata' | 'summary',\n): Uint8Array {\n const enc = new TextEncoder();\n const ns = enc.encode(ref.namespace ?? '');\n const seg = enc.encode(ref.segment);\n const out = new Uint8Array(1 + 2 + ns.length + 2 + seg.length + 8 + 1 + 4);\n const view = new DataView(out.buffer);\n let o = 0;\n out[o] = AAD_VERSION;\n o += 1;\n view.setUint16(o, ns.length, true);\n o += 2;\n out.set(ns, o);\n o += ns.length;\n view.setUint16(o, seg.length, true);\n o += 2;\n out.set(seg, o);\n o += seg.length;\n view.setBigUint64(o, BigInt(generation), true);\n o += 8;\n out[o] =\n scope === 'index'\n ? SCOPE_INDEX\n : scope === 'metadata'\n ? SCOPE_METADATA\n : scope === 'summary'\n ? SCOPE_SUMMARY\n : SCOPE_CHUNK;\n o += 1;\n view.setUint32(o, typeof scope === 'number' ? scope : 0, true);\n return out;\n}\n", "/**\n * CRC32C (Castagnoli, polynomial 0x1EDC6F41) \u2014 the pre-deserialize integrity screen for `.crbm`\n * payloads, index, and footer.\n *\n * Pure TypeScript, table-driven: `core/` may not import `crypto`/`node:crypto` (the determinism lint\n * enforces it), and a from-scratch implementation is what the cross-language golden corpus will be pinned\n * against. CRC32C (not CRC32) is the locked choice \u2014 it matches the hardware-accelerated checksum used\n * by SSE/iSCSI/Btrfs, and it is the algorithm the `.crbm` format locks in.\n */\n\n/** Reflected CRC32C lookup table (256 entries), built once from the reflected polynomial. */\n/**\n * Slicing-by-8 tables. `TABLES[0]` is the classic byte-at-a-time table; each subsequent table is the previous\n * one advanced by another byte position, which is what lets the loop below consume 8 bytes per iteration\n * instead of one.\n */\nconst TABLES = ((): Uint32Array[] => {\n const POLY = 0x82f63b78; // bit-reflected 0x1EDC6F41\n const t0 = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? POLY ^ (c >>> 1) : c >>> 1;\n t0[n] = c >>> 0;\n }\n const tables = [t0];\n for (let k = 1; k < 8; k++) {\n const prev = tables[k - 1] as Uint32Array;\n const next = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n const v = prev[n] as number;\n next[n] = ((v >>> 8) ^ (t0[v & 0xff] as number)) >>> 0;\n }\n tables.push(next);\n }\n return tables;\n})();\nconst T0 = TABLES[0] as Uint32Array;\nconst T1 = TABLES[1] as Uint32Array;\nconst T2 = TABLES[2] as Uint32Array;\nconst T3 = TABLES[3] as Uint32Array;\nconst T4 = TABLES[4] as Uint32Array;\nconst T5 = TABLES[5] as Uint32Array;\nconst T6 = TABLES[6] as Uint32Array;\nconst T7 = TABLES[7] as Uint32Array;\n\nexport function crc32c(bytes: Uint8Array, seed = 0): number {\n let crc = (seed ^ 0xffffffff) >>> 0;\n let i = 0;\n // Eight bytes per iteration. Identical output to the byte-at-a-time form \u2014 this is the standard\n // slicing-by-8 arrangement of the same polynomial, not a different checksum \u2014 but it amortises the table\n // lookups and the loop overhead across eight bytes. That matters because this runs over the whole `.crbm`\n // index on every reader open, where the byte-at-a-time version measured ~3.3 ms/MiB and up to ~27 ms for a\n // maximally-sized index, all of it a synchronous stall on Node's only thread.\n const n8 = bytes.length - (bytes.length % 8);\n for (; i < n8; i += 8) {\n crc ^=\n (bytes[i] as number) |\n ((bytes[i + 1] as number) << 8) |\n ((bytes[i + 2] as number) << 16) |\n ((bytes[i + 3] as number) << 24);\n crc =\n ((T7[crc & 0xff] as number) ^\n (T6[(crc >>> 8) & 0xff] as number) ^\n (T5[(crc >>> 16) & 0xff] as number) ^\n (T4[(crc >>> 24) & 0xff] as number) ^\n (T3[bytes[i + 4] as number] as number) ^\n (T2[bytes[i + 5] as number] as number) ^\n (T1[bytes[i + 6] as number] as number) ^\n (T0[bytes[i + 7] as number] as number)) >>>\n 0;\n }\n // Tail: whatever did not divide by 8, byte-at-a-time.\n for (; i < bytes.length; i++) {\n crc = ((T0[(crc ^ (bytes[i] as number)) & 0xff] as number) ^ (crc >>> 8)) >>> 0;\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n", "/**\n * LEB128 unsigned varint codec for the `.crbm` chunk index.\n *\n * The index is delta+varint encoded so a full 16-bit span stays ~400\u2013650 KB instead of ~900 KB of\n * fixed triplets. All index varint values are small (chunk-key deltas \u2264 65535,\n * cardinalities \u2264 65536, payload lengths bounded by a size cap) \u2014 well within 32 bits \u2014 so this codec\n * accepts unsigned integers in `[0, 0xffffffff]` and rejects anything wider as a programming error.\n */\nimport { IntegrityError } from '../errors';\n\nconst U32_CEIL = 0x1_0000_0000; // 2^32\n\n/** Append the LEB128 encoding of a u32 `value` to `out`. */\nexport function writeVarint(out: number[], value: number): void {\n if (!Number.isInteger(value) || value < 0 || value >= U32_CEIL) {\n throw new RangeError(`varint value must be a u32; got ${value}`);\n }\n let v = value;\n while (v > 0x7f) {\n out.push((v & 0x7f) | 0x80);\n v >>>= 7;\n }\n out.push(v);\n}\n\nexport interface VarintRead {\n readonly value: number;\n /** Offset just past the bytes consumed. */\n readonly next: number;\n}\n\n/**\n * Decode a LEB128 varint from `bytes` starting at `offset`. Throws `IntegrityError` on a truncated or\n * over-long (> 5-byte / > u32) encoding \u2014 the bytes are untrusted, so a malformed index must fail\n * cleanly, never loop or overflow.\n */\nexport function readVarint(bytes: Uint8Array, offset: number): VarintRead {\n let result = 0;\n let shift = 0;\n let pos = offset;\n for (let i = 0; i < 5; i++) {\n if (pos >= bytes.length) {\n throw new IntegrityError('varint truncated past end of buffer');\n }\n const byte = bytes[pos]!;\n pos += 1;\n // The 5th byte may contribute at most 4 bits (32 total); reject wider.\n if (i === 4 && byte > 0x0f) {\n throw new IntegrityError('varint exceeds u32 range');\n }\n // Integer arithmetic: the value stays a small integer where it fits, rather than a heap-boxed double.\n result = (result | ((byte & 0x7f) << shift)) >>> 0;\n if ((byte & 0x80) === 0) {\n return { value: result, next: pos };\n }\n shift += 7;\n }\n throw new IntegrityError('varint exceeds u32 range');\n}\n", "/**\n * `CrbmReader` \u2014 speculative-tail-read reader for one `.crbm` generation.\n *\n * `open()` fetches the object's tail in **one GET** (footer + usually the whole index), verifies the\n * footer/index CRCs, and parses the delta+varint index into parallel typed arrays. `getChunk()` then range-GETs a single\n * payload and verifies its CRC32C **before** any native deserialize \u2014 every byte from storage is\n * untrusted.\n *\n * **Encryption.** When the footer's `FLAG_ENCRYPTED` is set, a {@link CrbmCrypto} must be supplied:\n * the index is AES-256-GCM-decrypted (nonce/tag from the footer) and each chunk payload is decrypted after its\n * on-disk CRC passes. The footer's `chunkCount`/`totalCardinality` are zero on an encrypted object (metadata is\n * hidden), so both are derived from the decrypted index; AEAD authentication (incl. the per-location AAD)\n * replaces the cleartext-count cross-check.\n *\n * **The extension block.** An object whose footer sets `FLAG_EXTENSION` carries an extension block just before the\n * index, found from the trailer at `indexOffset - 12`. `open()` reads it with the index, from the tail or from the same range read,\n * checks its trailer, its CRC32C, its sections and the metadata in them before anything trusts them, and holds payloads\n * to the bytes before it. An object without the flag has no block, and is read exactly as before.\n */\nimport { IntegrityError, isIntegrityError, UnsupportedError, ValidationError } from '../errors';\nimport type { BlobReader } from '../blob';\nimport type { CrbmCrypto } from '../crypto';\nimport { MAX_METADATA_BYTES, metadataFromBytes } from '../metadata';\nimport type { GenerationMetadata } from '../ports';\nimport { crc32c } from './crc32c';\nimport { readVarint } from './varint';\nimport {\n AEAD_NONCE_BYTES,\n AEAD_TAG_BYTES,\n CONTAINER_CODEC_NONE,\n CRC32C_BYTES,\n DEFAULT_MAX_INDEX_BYTES,\n DEFAULT_MAX_BITMAP_BYTES,\n DEFAULT_MAX_PAYLOAD_BYTES,\n DEFAULT_TAIL_BYTES,\n ELEMENT_WIDTH_32,\n EXT_MAGIC,\n EXT_SECTION_HEADER_BYTES,\n EXT_SECTION_METADATA,\n EXT_TRAILER_BYTES,\n FLAG_ENCRYPTED,\n FLAG_EXTENSION,\n FLAG_LITTLE_ENDIAN,\n FOOTER,\n FOOTER_BYTES,\n FOOTER_CRC_COVERAGE,\n KNOWN_FLAGS,\n MAGIC,\n MAX_CHUNK_CARDINALITY,\n PAYLOAD_START,\n PREAMBLE_BYTES,\n KNOWN_PAYLOAD_CODEC_IDS,\n MAX_EXT_BYTES,\n PAYLOAD_CODEC_ROARING_PORTABLE,\n VERSION_MAJOR,\n} from './format';\n\n/**\n * A parsed index as parallel typed arrays, one slot per entry, ascending by key. Typed arrays hold each field at\n * its own width with no per-entry object, so the retained heap is the arrays' byte length and nothing else.\n * `cardinalityMinusOne` stores `cardinality - 1` because a chunk's cardinality runs `[1, 65536]` and 65,536 does\n * not fit a `u16`; an offset is a `f64` because an object past 4 GiB puts a payload beyond a `u32`.\n */\nexport interface ParsedIndex {\n readonly keys: Uint16Array;\n readonly cardinalityMinusOne: Uint16Array;\n readonly lengths: Uint32Array;\n readonly crcs: Uint32Array;\n readonly offsets: Float64Array;\n /** \u03A3 cardinality over every entry. */\n readonly cardinalitySum: number;\n}\n\n/**\n * Retained JS heap for one parsed index entry, in bytes: the `u16` key, the `u16` cardinality-minus-one, the `u32`\n * length, the `u32` CRC and the `f64` offset of {@link ParsedIndex}. It is the exact byte length of the arrays\n * (checked against the measured heap by a test), and weights the reader-cache byte bound\n * ({@link CrbmReader.retainedIndexBytes}).\n */\nexport const RETAINED_BYTES_PER_INDEX_ENTRY = 20;\n\n/**\n * The weight of a decoded metadata record, per byte of its canonical JSON and per key: what the record retains,\n * measured (`tests/core/crbm/metadata-heap.test.ts`) at about one byte a byte, plus about 130 a key when its keys are\n * unique to it, as each segment's own keys are. Counting both, with room, keeps the reader cache's byte bound honest\n * for the costly shape and over-counts the cheap ones.\n */\nconst RETAINED_BYTES_PER_METADATA_BYTE = 2;\nconst RETAINED_BYTES_PER_METADATA_KEY = 160;\n\n/** The fewest bytes one index record takes: four one-byte varints and the four-byte payload CRC. */\nconst MIN_INDEX_RECORD_BYTES = 4 + CRC32C_BYTES;\n\n/**\n * How many entries `parseIndex` makes room for, from the index's length alone: what its bytes could hold at the\n * smallest record, and never more than one entry per 16-bit key. A hostile index cannot make the reader allocate more.\n */\nexport function indexCapacity(indexLength: number): number {\n return Math.min(0x1_0000, Math.floor(indexLength / MIN_INDEX_RECORD_BYTES));\n}\n\nexport interface CrbmReaderOptions {\n /** Speculative tail size in bytes (default 256 KB; clamped up to at least the footer size). */\n readonly tailBytes?: number;\n /** Hard cap on a single chunk payload length (default: the 1 MiB decode cap, plus the 28-byte nonce and tag on an encrypted object). The engine decodes under `maxBitmapBytes`; raise this with it. */\n readonly maxPayloadBytes?: number;\n /** Hard cap on the whole index region fetched/parsed from one object (default 8 MB). */\n readonly maxIndexBytes?: number;\n /**\n * Decryption context for an encrypted object (its DEK's AEAD + AAD builder). Required iff the object's footer sets\n * `FLAG_ENCRYPTED`: an encrypted object opened without one throws {@link ValidationError}, and an object that is\n * not encrypted, opened with one, is refused with {@link IntegrityError}.\n */\n readonly crypto?: CrbmCrypto;\n /**\n * Opaque marker for the *incarnation of the name* this object belongs to \u2014 the caller's own identity for the\n * registry row it resolved. The reader never interprets it; it carries it so a caller holding an open reader\n * can tell \"the same generation of the same segment\" from \"the same generation NUMBER of a segment that was\n * deleted and re-created\", which the number alone cannot express.\n */\n readonly lineage?: unknown;\n}\n\nfunction magicMatches(bytes: Uint8Array, offset: number): boolean {\n return (\n bytes[offset] === MAGIC[0] &&\n bytes[offset + 1] === MAGIC[1] &&\n bytes[offset + 2] === MAGIC[2] &&\n bytes[offset + 3] === MAGIC[3]\n );\n}\n\n/** What names an object: its size, then its footer's CRC. One spelling, for an open reader and a footer read alone. */\nconst sizePart = (size: number): string => `${size}:`;\n/** An object's fingerprint, from its size and its footer's CRC (see {@link CrbmReader.fingerprint}). */\nexport const fingerprintFor = (size: number, footerCrc: number): string =>\n `${sizePart(size)}${footerCrc}`;\n\n/**\n * Refuses a `size` that is not a whole byte count its own tail fits in. The size is the tier's word too: one that\n * is not would turn off every bounds check an open makes against it.\n */\nfunction checkSize(tail: Uint8Array, size: number): void {\n if (!Number.isSafeInteger(size) || size < tail.length) {\n throw new IntegrityError(`.crbm size is not a byte count its tail fits in: ${size}`);\n }\n}\n\n/** The footer at the end of `tail`, once its size, magic and CRC hold, with its view and its CRC. */\nfunction checkedFooter(\n tail: Uint8Array,\n size: number,\n): { footer: Uint8Array; fview: DataView; storedFooterCrc: number } {\n checkSize(tail, size);\n if (size < PREAMBLE_BYTES + FOOTER_BYTES || tail.length < FOOTER_BYTES) {\n throw new IntegrityError(`.crbm too small: ${size}B`);\n }\n const footer = tail.subarray(tail.length - FOOTER_BYTES);\n if (!magicMatches(footer, FOOTER.endMagic)) {\n throw new IntegrityError('.crbm end magic mismatch');\n }\n const fview = new DataView(footer.buffer, footer.byteOffset, footer.byteLength);\n const storedFooterCrc = fview.getUint32(FOOTER.footerCrc32c, true);\n if (crc32c(footer.subarray(0, FOOTER_CRC_COVERAGE)) !== storedFooterCrc) {\n throw new IntegrityError('.crbm footer CRC mismatch');\n }\n return { footer, fview, storedFooterCrc };\n}\n\n/**\n * Whether the object behind `blob` says it is encrypted, from one tail read of a footer's worth, with no key and no\n * index; a footer that fails its own checks throws, as an open would. For a caller that must not open an object with\n * the key it holds: an erasure looking for an id in a cleartext object under an encrypted segment, and a rollback\n * checking that its target is what the row says.\n */\nexport async function footerSaysEncrypted(blob: BlobReader): Promise<boolean> {\n const { bytes: tail, size } = await blob.getTail(FOOTER_BYTES);\n const { fview } = checkedFooter(tail, size);\n return (fview.getUint32(FOOTER.flags, true) & FLAG_ENCRYPTED) !== 0;\n}\n\n/** Read a u64 footer field, rejecting values past JS safe-integer range (precision would be lost). */\nfunction readU64(view: DataView, offset: number, field: string): number {\n const big = view.getBigUint64(offset, true);\n if (big > BigInt(Number.MAX_SAFE_INTEGER)) {\n throw new IntegrityError(`.crbm ${field} ${big} exceeds safe-integer range`);\n }\n return Number(big);\n}\n\nexport class CrbmReader {\n private constructor(\n private readonly blob: BlobReader,\n private readonly objectSize: number,\n readonly generation: number,\n /** See {@link CrbmReaderOptions.lineage} \u2014 carried, never interpreted. */\n readonly lineage: unknown,\n readonly totalCardinality: number,\n private readonly index: ParsedIndex,\n /**\n * True when `open()` satisfied the index, and any extension block, from the tail GET alone (no second range\n * read).\n */\n readonly servedFromTail: boolean,\n /** Set iff the object is encrypted \u2014 used to decrypt each chunk payload in {@link getChunk}. */\n private readonly crypto: CrbmCrypto | undefined,\n /** The footer's own CRC, as verified at open: it covers the index's CRC, the chunk count and the generation. */\n private readonly footerCrc: number,\n /**\n * The metadata the generation was written with, checked and decrypted at open; `undefined` when it has none\n * (every format 1.0 object). Frozen: every caller shares one copy.\n */\n readonly metadata: GenerationMetadata | undefined,\n /** What that metadata adds to {@link retainedBytes}; 0 when there is none. */\n private readonly metadataWeight: number,\n ) {}\n\n /**\n * Which object this reader opened, as its size and its footer's CRC. The footer covers the index's CRC, so two\n * objects with the same fingerprint hold the same chunks, as far as a CRC can tell; a pin compares it to know that\n * the object it reopens is the one it pinned, since a purged and reloaded name reuses the generation number.\n */\n get fingerprint(): string {\n return fingerprintFor(this.objectSize, this.footerCrc);\n }\n\n /** Total object bytes (from the one-GET tail read) \u2014 for grounded storage cost. */\n get sizeBytes(): number {\n return this.objectSize;\n }\n\n /**\n * Retained JS heap of this reader's parsed index \u2014 the weight the storage reader cache bounds on (a wide\n * segment's parsed index, not its payloads, dominates the reader's footprint): the exact byte length of the\n * arrays that hold it, {@link RETAINED_BYTES_PER_INDEX_ENTRY} per entry.\n */\n get retainedIndexBytes(): number {\n const { keys, cardinalityMinusOne, lengths, crcs, offsets } = this.index;\n return (\n keys.byteLength +\n cardinalityMinusOne.byteLength +\n lengths.byteLength +\n crcs.byteLength +\n offsets.byteLength\n );\n }\n\n /**\n * What this reader holds, the weight the storage reader cache bounds on: its parsed index\n * ({@link retainedIndexBytes}), plus a weight for the metadata it decoded, from its canonical length and its number\n * of keys. Equal to {@link retainedIndexBytes} for a generation with no metadata. A cache heuristic, not a contract:\n * how the metadata is weighed may change in any release.\n */\n get retainedBytes(): number {\n return this.retainedIndexBytes + this.metadataWeight;\n }\n\n /** Per-chunk cardinality (`chunkKey \u2192 count`) from the parsed index \u2014 no payload reads. */\n cardinalities(): Map<number, number> {\n const { keys, cardinalityMinusOne } = this.index;\n const out = new Map<number, number>();\n for (let i = 0; i < keys.length; i++) out.set(keys[i]!, cardinalityMinusOne[i]! + 1);\n return out;\n }\n\n /**\n * Whether the object behind `blob` is the one `fingerprint` names ({@link fingerprint}), from one tail read of a\n * footer's worth, with no key and no index, since the footer is stored in the clear and checked by its own CRC.\n * Another size is another object, whatever its bytes hold: a short one that is no `.crbm` at all is not the one\n * pinned. At the same size the footer's CRC says, and a footer that fails its own checks says nothing about which\n * object is there, so this throws, as an open would. What a pin asks, to tell the object it opened from one that\n * has since been stored under its key, whoever wrote it.\n */\n static async sameObject(blob: BlobReader, fingerprint: string): Promise<boolean> {\n const { bytes: tail, size } = await blob.getTail(FOOTER_BYTES);\n checkSize(tail, size);\n if (!fingerprint.startsWith(sizePart(size))) return false;\n return fingerprintFor(size, checkedFooter(tail, size).storedFooterCrc) === fingerprint;\n }\n\n static async open(blob: BlobReader, options: CrbmReaderOptions = {}): Promise<CrbmReader> {\n // Always fetch at least a footer's worth, regardless of a smaller caller request.\n const tailBytes = Math.max(options.tailBytes ?? DEFAULT_TAIL_BYTES, FOOTER_BYTES);\n const maxIndexBytes = options.maxIndexBytes ?? DEFAULT_MAX_INDEX_BYTES;\n const { bytes: tail, size } = await blob.getTail(tailBytes);\n const { footer, fview, storedFooterCrc } = checkedFooter(tail, size);\n const versionMajor = footer[FOOTER.versionMajor]!;\n if (versionMajor !== VERSION_MAJOR) {\n throw new UnsupportedError(`.crbm major version ${versionMajor} not supported`);\n }\n const flags = fview.getUint32(FOOTER.flags, true);\n const encrypted = (flags & FLAG_ENCRYPTED) !== 0;\n if (encrypted && options.crypto === undefined) {\n throw new ValidationError('.crbm is encrypted but no decryption key (crypto) was provided');\n }\n if ((flags & FLAG_LITTLE_ENDIAN) === 0) {\n throw new UnsupportedError('.crbm big-endian layout not supported (v1 is little-endian)');\n }\n if ((flags & ~KNOWN_FLAGS) !== 0) {\n throw new UnsupportedError(`.crbm has unknown flag bits set: 0x${flags.toString(16)}`);\n }\n\n // Payload-decoding contract: the\n // reader must decode with the *same* element width, roaring serialization, and container codec the writer\n // stamped \u2014 a mismatch means the payloads are a format this v1 (32-bit, portable, uncompressed) reader can't\n // safely deserialize, so reject up front rather than feed them to the 32-bit deserializer and mis-count\n // fleet-wide. A 64-bit generation is the reserved >4.29 B-ids/segment escape and is a **major**-version bump\n // (auto-rejected here), never a silent minor one. All three fields are inside FOOTER_CRC_COVERAGE (verified).\n const elementWidth = footer[FOOTER.elementWidth]!;\n if (elementWidth !== ELEMENT_WIDTH_32) {\n throw new UnsupportedError(\n `.crbm element_width ${elementWidth} not supported (v1 reads 32-bit ids; 64-bit is a future major version)`,\n );\n }\n // Membership, not equality: the field says which codec wrote the payloads, and this reader accepts every\n // id it can actually decode. An unknown one fails closed rather than being handed to a decoder that would\n // misread it \u2014 see KNOWN_PAYLOAD_CODEC_IDS for why that direction is the safe one.\n const payloadCodecId = fview.getUint16(FOOTER.payloadCodecId, true);\n if (!KNOWN_PAYLOAD_CODEC_IDS.has(payloadCodecId)) {\n throw new UnsupportedError(\n `.crbm payload_codec_id ${payloadCodecId} not supported by this build ` +\n `(known: ${[...KNOWN_PAYLOAD_CODEC_IDS].join(', ')}; ` +\n `${PAYLOAD_CODEC_ROARING_PORTABLE}=roaring portable). A generation written by a different codec ` +\n `is rejected rather than decoded \u2014 a store uses one codec throughout.`,\n );\n }\n const containerCodec = footer[FOOTER.containerCodec]!;\n if (containerCodec !== CONTAINER_CODEC_NONE) {\n throw new UnsupportedError(\n `.crbm container_codec ${containerCodec} not supported (v1 defines only ${CONTAINER_CODEC_NONE}=none)`,\n );\n }\n\n const indexOffset = readU64(fview, FOOTER.indexOffset, 'index_offset');\n const indexLength = readU64(fview, FOOTER.indexLength, 'index_length');\n const indexCrc = fview.getUint32(FOOTER.indexCrc32c, true);\n const chunkCount = fview.getUint32(FOOTER.chunkCount, true);\n const totalCardinality = readU64(fview, FOOTER.totalCardinality, 'total_cardinality');\n const generation = readU64(fview, FOOTER.generation, 'generation');\n // A key is given only for an encrypted segment. A cleartext object under one was never one of its generations:\n // a publish never adds a key to a segment that has generations, and refuses a cleartext object onto a row with\n // one. So it is forged, corrupt, a cleartext write that never published, or one an earlier release published\n // while racing the segment's first keyed load, and its index and metadata are not believed.\n if (!encrypted && options.crypto !== undefined) {\n throw new IntegrityError(\n `.crbm generation ${generation} is not encrypted, but it was opened with a key: a cleartext object where an ` +\n 'encrypted one belongs is forged, corrupt, or a write that never published (a crash, before the ' +\n \"segment's key was made); a load's collection, dropSegment or deleting the object removes it\",\n );\n }\n const versionMinor = footer[FOOTER.versionMinor]!;\n const hasExtension = (flags & FLAG_EXTENSION) !== 0;\n\n // Bounds + size cap on the index region before trusting/fetching it.\n if (indexOffset < PAYLOAD_START || indexOffset + indexLength > size - FOOTER_BYTES) {\n throw new IntegrityError(\n `.crbm index region [${indexOffset}, +${indexLength}) out of bounds`,\n );\n }\n if (indexLength > maxIndexBytes) {\n throw new IntegrityError(`.crbm index ${indexLength}B exceeds cap ${maxIndexBytes}B`);\n }\n\n // Validate the front preamble too, but only when this GET already covers it (no extra request).\n const tailCoversFrom = size - tail.length;\n if (tailCoversFrom === 0) {\n if (!magicMatches(tail, 0) || tail[4] !== versionMajor || tail[5] !== versionMinor) {\n throw new IntegrityError('.crbm preamble magic/version mismatch with footer');\n }\n }\n\n // --- Index: already in the tail, or one more GET ---\n // With the block flagged, the range read starts a whole block's worth before the index, so a block of any size it may hold comes\n // with the index: the block costs bytes, never a request, unless the tail ends inside it.\n const lowest = hasExtension\n ? Math.max(PAYLOAD_START, indexOffset - EXT_TRAILER_BYTES - MAX_EXT_BYTES)\n : indexOffset;\n let region: Uint8Array;\n let regionStart: number;\n let servedFromTail: boolean;\n if (indexOffset >= tailCoversFrom) {\n region = tail;\n regionStart = tailCoversFrom;\n servedFromTail = true;\n } else {\n region = await blob.getRange(lowest, indexOffset + indexLength - lowest);\n regionStart = lowest;\n servedFromTail = false;\n }\n const indexBytes = region.subarray(\n indexOffset - regionStart,\n indexOffset - regionStart + indexLength,\n );\n if (crc32c(indexBytes) !== indexCrc) {\n throw new IntegrityError('.crbm index CRC mismatch');\n }\n\n // Decrypt the index (nonce/tag from the footer) before parsing; AAD binds it to this (segment, generation).\n // A wrong key / tampered index / wrong context fails here as an IntegrityError \u2014 never a wrong parse. Before the\n // extension block, so a wrong key is reported here, as one, and not at the sealed metadata.\n const indexForParse = encrypted\n ? options.crypto!.aead.open(\n {\n nonce: footer.subarray(FOOTER.indexNonce, FOOTER.indexNonce + AEAD_NONCE_BYTES),\n ciphertext: indexBytes,\n tag: footer.subarray(FOOTER.indexTag, FOOTER.indexTag + AEAD_TAG_BYTES),\n },\n options.crypto!.aadFor('index'),\n )\n : indexBytes;\n\n // --- Extension block (flagged): sections \u2016 u32 sectionsLength \u2016 u32 crc32c \u2016 \"CRBX\", just before the index ---\n let payloadEnd = indexOffset;\n let metadata: GenerationMetadata | undefined;\n let metadataLength = 0;\n if (hasExtension) {\n const trailerStart = indexOffset - EXT_TRAILER_BYTES;\n if (trailerStart < PAYLOAD_START) {\n throw new IntegrityError('.crbm flags an extension block, but has no room for one');\n }\n // The block's bytes come from what is already fetched, or, when the tail ends inside the block, from one read.\n let window = { bytes: region, start: regionStart };\n if (trailerStart < regionStart) {\n window = { bytes: await blob.getRange(lowest, indexOffset - lowest), start: lowest };\n servedFromTail = false;\n }\n const trailer = within(window.bytes, window.start, trailerStart, indexOffset);\n const tview = new DataView(trailer.buffer, trailer.byteOffset, trailer.byteLength);\n if (!EXT_MAGIC.every((b, i) => trailer[8 + i] === b)) {\n throw new IntegrityError('.crbm extension block trailer magic mismatch');\n }\n const sectionsLength = tview.getUint32(0, true);\n if (sectionsLength > MAX_EXT_BYTES) {\n throw new IntegrityError(\n `.crbm extension block ${sectionsLength}B exceeds cap ${MAX_EXT_BYTES}B`,\n );\n }\n const extStart = trailerStart - sectionsLength;\n if (extStart < PAYLOAD_START) {\n throw new IntegrityError(`.crbm extension block of ${sectionsLength}B out of bounds`);\n }\n // The CRC covers the sections and the length field after them.\n let covered: Uint8Array;\n if (extStart >= window.start) {\n covered = within(window.bytes, window.start, extStart, trailerStart + 4);\n } else {\n covered = await blob.getRange(extStart, sectionsLength + 4);\n servedFromTail = false;\n if (covered.length !== sectionsLength + 4) {\n throw new IntegrityError('.crbm extension block read short');\n }\n }\n if (crc32c(covered) !== tview.getUint32(4, true)) {\n throw new IntegrityError('.crbm extension block CRC mismatch');\n }\n ({ metadata, metadataLength } = extensionContents(\n covered.subarray(0, sectionsLength),\n options.crypto,\n ));\n payloadEnd = extStart;\n }\n\n // Payloads live in [PAYLOAD_START, payloadEnd), which ends at the extension block when there is one; an\n // encrypted payload is at least its nonce and tag.\n const index = parseIndex(\n indexForParse,\n payloadEnd,\n options.maxPayloadBytes ?? (encrypted ? DEFAULT_MAX_PAYLOAD_BYTES : DEFAULT_MAX_BITMAP_BYTES),\n encrypted ? AEAD_NONCE_BYTES + AEAD_TAG_BYTES : 1,\n );\n if (encrypted) {\n // Footer count/cardinality are zeroed on an encrypted object; the decrypted index is authoritative (and\n // already AEAD-authenticated, AAD-bound to this object), so derive the total from it.\n if (chunkCount !== 0 || totalCardinality !== 0) {\n throw new IntegrityError(\n '.crbm encrypted footer must zero chunk_count + total_cardinality',\n );\n }\n } else {\n // Cleartext: the footer total + count must match the index \u2014 don't trust the footer blindly.\n if (index.keys.length !== chunkCount) {\n throw new IntegrityError(\n `.crbm chunk_count ${chunkCount} != ${index.keys.length} index entries`,\n );\n }\n if (index.cardinalitySum !== totalCardinality) {\n throw new IntegrityError(\n `.crbm total_cardinality ${totalCardinality} != \u03A3 index cardinality ${index.cardinalitySum}`,\n );\n }\n }\n\n return new CrbmReader(\n blob,\n size,\n generation,\n options.lineage,\n index.cardinalitySum,\n index,\n servedFromTail,\n options.crypto,\n storedFooterCrc,\n metadata,\n metadata === undefined\n ? 0\n : RETAINED_BYTES_PER_METADATA_BYTE * metadataLength +\n RETAINED_BYTES_PER_METADATA_KEY * Object.keys(metadata).length,\n );\n }\n\n /** Chunk keys present in this generation, ascending. */\n chunkKeys(): number[] {\n return Array.from(this.index.keys);\n }\n\n /** Segment cardinality from the footer (no payload reads) \u2014 the cheap `count()` path. */\n count(): number {\n return this.totalCardinality;\n }\n\n /** The slot holding `chunkKey` (binary search over the ascending keys), or -1 when this generation has none. */\n private slotOf(chunkKey: number): number {\n const keys = this.index.keys;\n let lo = 0;\n let hi = keys.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const k = keys[mid]!;\n if (k === chunkKey) return mid;\n if (k < chunkKey) lo = mid + 1;\n else hi = mid - 1;\n }\n return -1;\n }\n\n has(chunkKey: number): boolean {\n return this.slotOf(chunkKey) >= 0;\n }\n\n /**\n * Range-GET one chunk's payload, verifying its CRC32C before returning. Returns `null` if the chunk is\n * absent. Throws `IntegrityError` on a CRC mismatch (the bytes must never reach the native\n * deserializer). The returned buffer is a **read-only view** owned by the reader/driver \u2014 callers must\n * not mutate it.\n */\n async getChunk(chunkKey: number): Promise<Uint8Array | null> {\n const slot = this.slotOf(chunkKey);\n if (slot < 0) return null;\n const offset = this.index.offsets[slot]!;\n const length = this.index.lengths[slot]!;\n // Re-validate bounds at read time (defense in depth \u2014 a buggy caller or future mutable index path).\n if (offset < PAYLOAD_START || offset + length > this.objectSize - FOOTER_BYTES) {\n throw new IntegrityError(`chunk ${chunkKey} payload out of bounds`);\n }\n const bytes = await this.blob.getRange(offset, length);\n if (crc32c(bytes) !== this.index.crcs[slot]) {\n throw new IntegrityError(`chunk ${chunkKey} payload CRC mismatch`);\n }\n if (this.crypto === undefined) return bytes;\n // Encrypted: on-disk frame is nonce \u2016 ciphertext \u2016 tag. Decrypt (AAD-bound to this chunkKey) after the CRC\n // passes \u2014 a wrong key / tamper / relocated chunk fails as an IntegrityError, never a wrong payload.\n if (bytes.length < AEAD_NONCE_BYTES + AEAD_TAG_BYTES) {\n throw new IntegrityError(`chunk ${chunkKey} encrypted payload too short`);\n }\n return this.crypto.aead.open(\n {\n nonce: bytes.subarray(0, AEAD_NONCE_BYTES),\n ciphertext: bytes.subarray(AEAD_NONCE_BYTES, bytes.length - AEAD_TAG_BYTES),\n tag: bytes.subarray(bytes.length - AEAD_TAG_BYTES),\n },\n this.crypto.aadFor(chunkKey),\n );\n }\n}\n\n/** `bytes[from, to)` of a buffer that starts at object offset `start`, or an IntegrityError when it holds less. */\nfunction within(bytes: Uint8Array, start: number, from: number, to: number): Uint8Array {\n if (from < start || to > start + bytes.length) {\n throw new IntegrityError('.crbm extension block read short');\n }\n return bytes.subarray(from - start, to - start);\n}\n\nfunction extensionCorrupt(message: string): never {\n throw new IntegrityError(`.crbm extension block: ${message}`);\n}\n\n/**\n * Read the sections of an extension block, its bytes before the trailer, once their CRC has passed. Each section is\n * `u8 type \u2016 u32 length \u2016 bytes`, in strictly ascending type order, and together they fill the region exactly. Type 1\n * is the generation's metadata, held to the metadata rules and its canonical form (and opened first with `crypto`,\n * under the metadata scope, when the object is encrypted); a type this build does not know is a later build's, and\n * is skipped. Returns the metadata, or `undefined` when the block has none. Every other shape is an\n * {@link IntegrityError}.\n *\n * Exported for the coverage-guided fuzz harness, which drives it directly, past the block's CRC; not public API.\n */\nexport function parseExtension(\n sections: Uint8Array,\n crypto: CrbmCrypto | undefined,\n): GenerationMetadata | undefined {\n return extensionContents(sections, crypto).metadata;\n}\n\n/** {@link parseExtension}'s walk, with the canonical length of the metadata it decoded, for the reader's weight. */\nfunction extensionContents(\n sections: Uint8Array,\n crypto: CrbmCrypto | undefined,\n): { metadata: GenerationMetadata | undefined; metadataLength: number } {\n const view = new DataView(sections.buffer, sections.byteOffset, sections.byteLength);\n let metadata: GenerationMetadata | undefined;\n let metadataLength = 0;\n let lastType = 0;\n let pos = 0;\n while (pos < sections.length) {\n if (sections.length - pos < EXT_SECTION_HEADER_BYTES)\n extensionCorrupt('a section header is cut off');\n const type = sections[pos]!;\n const length = view.getUint32(pos + 1, true);\n pos += EXT_SECTION_HEADER_BYTES;\n // Starting from 0 refuses type 0, a repeated type and a type out of order with one comparison.\n if (type <= lastType) {\n extensionCorrupt(\n type === 0\n ? 'section type 0 is not a section type'\n : `section type ${type} after ${lastType}`,\n );\n }\n if (length > sections.length - pos) extensionCorrupt(`section ${type} runs past the block`);\n const body = sections.subarray(pos, pos + length);\n pos += length;\n lastType = type;\n if (type === EXT_SECTION_METADATA)\n ({ metadata, metadataLength } = metadataSection(body, crypto));\n }\n return { metadata, metadataLength };\n}\n\n/** The metadata section's record: its canonical JSON, opened first when the object is encrypted. */\nfunction metadataSection(\n body: Uint8Array,\n crypto: CrbmCrypto | undefined,\n): { metadata: GenerationMetadata; metadataLength: number } {\n if (crypto === undefined) {\n return { metadata: metadataFromBytes(body, extensionCorrupt), metadataLength: body.length };\n }\n const framing = AEAD_NONCE_BYTES + AEAD_TAG_BYTES;\n if (body.length <= framing || body.length > framing + MAX_METADATA_BYTES) {\n extensionCorrupt(`sealed metadata of ${body.length}B is not a nonce, up to 1 KiB and a tag`);\n }\n let plain: Uint8Array;\n try {\n plain = crypto.aead.open(\n {\n nonce: body.subarray(0, AEAD_NONCE_BYTES),\n ciphertext: body.subarray(AEAD_NONCE_BYTES, body.length - AEAD_TAG_BYTES),\n tag: body.subarray(body.length - AEAD_TAG_BYTES),\n },\n crypto.aadFor('metadata'),\n );\n } catch (err) {\n if (!isIntegrityError(err)) throw err;\n // The index has opened under the same key and generation, so the key is right: the causes left are these, and a\n // CrbmCrypto written before the extension block existed, which maps only chunk keys and the index, is one of them.\n extensionCorrupt(\n \"the sealed metadata does not open: tampered bytes, a block moved from another object, or a CrbmCrypto whose aadFor does not map the 'metadata' scope\",\n );\n }\n return { metadata: metadataFromBytes(plain, extensionCorrupt), metadataLength: plain.length };\n}\n\n// Exported for the coverage-guided fuzz harness, which fuzzes the hand-written index parser\n// directly on raw bytes \u2014 bypassing the CRC wall that mutational fuzzing can't cross. NOT part of the public\n// API surface (`src/index.ts`); reached only via `src/testing/fuzz-core.ts` \u2192 the gitignored `fuzz/build/`.\n//\n// This is where an index is checked for internal consistency, once per open: `count()` and every other read that\n// answers from the index alone trust what it records without decoding a payload. Every entry's key is in range\n// and strictly above the last; its cardinality is in `[1, 65536]`; its payload is non-empty (at least\n// `minPayloadBytes`), within `maxPayloadBytes`, and lies inside the payload region `[PAYLOAD_START, payloadEnd)`.\n// Offsets are stored as unsigned gaps after the previous payload, so payloads ascend and never overlap by\n// construction. `open` then holds the sum and the count to the footer's, where the footer records them.\n//\n// Not checked here, because the format does not record enough: an encrypted footer hides the count and the total\n// (the index's AEAD tag stands in for them), and a cardinality is not compared with its payload, since only the\n// codec can read a payload, and no read compares them afterwards: a payload whose bits disagree with its entry's\n// cardinality decodes to what it holds, and `count()` still reports the entry's.\nexport function parseIndex(\n indexBytes: Uint8Array,\n payloadEnd: number,\n maxPayloadBytes: number,\n minPayloadBytes = 1,\n): ParsedIndex {\n // The arrays are sized from what the index could hold, never from what it claims: every record takes at least\n // MIN_INDEX_RECORD_BYTES and keys are unique within 16 bits, so a hostile index cannot make this allocate more\n // than its own bytes (and 65,536 slots) could fill.\n const capacity = indexCapacity(indexBytes.length);\n const keys = new Uint16Array(capacity);\n const cardinalityMinusOne = new Uint16Array(capacity);\n const lengths = new Uint32Array(capacity);\n const crcs = new Uint32Array(capacity);\n const offsets = new Float64Array(capacity);\n let count = 0;\n let pos = 0;\n let prevKey = 0;\n let prevEnd = PAYLOAD_START;\n let cardinalitySum = 0;\n\n // Parse entries until the index region is exactly consumed (chunk_count is hidden on encrypted objects, so\n // the byte length is the bound \u2014 readVarint throws on any overrun, so a malformed index fails cleanly).\n for (let i = 0; pos < indexBytes.length; i++) {\n const keyDeltaRead = readVarint(indexBytes, pos);\n const offDeltaRead = readVarint(indexBytes, keyDeltaRead.next);\n const lenRead = readVarint(indexBytes, offDeltaRead.next);\n const cardRead = readVarint(indexBytes, lenRead.next);\n const keyDelta = keyDeltaRead.value;\n const offDelta = offDeltaRead.value;\n const len = lenRead.value;\n const card = cardRead.value;\n pos = cardRead.next;\n if (pos + CRC32C_BYTES > indexBytes.length) {\n throw new IntegrityError('.crbm index truncated (missing payload CRC)');\n }\n const crc =\n (indexBytes[pos]! |\n (indexBytes[pos + 1]! << 8) |\n (indexBytes[pos + 2]! << 16) |\n (indexBytes[pos + 3]! << 24)) >>>\n 0;\n pos += CRC32C_BYTES;\n\n const chunkKey = prevKey + keyDelta;\n const offset = prevEnd + offDelta;\n // Unsigned deltas mean keys can only stay flat or rise; a 0 delta after the first is a duplicate.\n if (i > 0 && keyDelta === 0) {\n throw new IntegrityError(`.crbm index has a duplicate chunkKey ${chunkKey}`);\n }\n if (chunkKey > 0xffff) throw new IntegrityError(`.crbm chunkKey ${chunkKey} out of range`);\n if (len < minPayloadBytes || len > maxPayloadBytes) {\n throw new IntegrityError(\n `.crbm chunk ${chunkKey} length ${len} invalid (most is ${maxPayloadBytes})`,\n );\n }\n if (card < 1 || card > MAX_CHUNK_CARDINALITY) {\n throw new IntegrityError(`.crbm chunk ${chunkKey} cardinality ${card} invalid`);\n }\n // `offset` cannot start below the preamble: it is the previous payload's end plus an unsigned gap.\n if (offset + len > payloadEnd) {\n throw new IntegrityError(`.crbm chunk ${chunkKey} payload out of bounds`);\n }\n\n keys[count] = chunkKey;\n cardinalityMinusOne[count] = card - 1;\n lengths[count] = len;\n crcs[count] = crc;\n offsets[count] = offset;\n count++;\n cardinalitySum += card;\n prevKey = chunkKey;\n prevEnd = offset + len;\n }\n // The loop ends only when pos === indexBytes.length exactly (each entry consumes a whole record; a partial\n // trailing record makes readVarint or the CRC bound throw), so there are never unconsumed trailing bytes.\n // Trim to the entries parsed, so the arrays' byte length is exactly what the reader retains.\n if (count === capacity) {\n return { keys, cardinalityMinusOne, lengths, crcs, offsets, cardinalitySum };\n }\n return {\n keys: keys.slice(0, count),\n cardinalityMinusOne: cardinalityMinusOne.slice(0, count),\n lengths: lengths.slice(0, count),\n crcs: crcs.slice(0, count),\n offsets: offsets.slice(0, count),\n cardinalitySum,\n };\n}\n", "/**\n * `CrbmWriter` \u2014 streaming, constant-memory writer for one immutable `.crbm` generation.\n *\n * Chunks are appended in **ascending chunkKey order**; the writer holds only the small index in memory\n * (offsets aren't known until payloads are written \u2014 which is exactly why the index is a footer).\n *\n * **Encryption (opt-in via `crypto`).** When a {@link CrbmCrypto} is supplied, each chunk payload is\n * AES-256-GCM-sealed (`nonce \u2016 ciphertext \u2016 tag`, AAD = its chunkKey) and the whole index is sealed too (its\n * nonce/tag go in the footer). The footer then sets `FLAG_ENCRYPTED` and **zeroes `chunkCount` +\n * `totalCardinality`** so a leaked object reveals neither how many chunks nor how many ids it holds \u2014 the\n * reader derives both from the decrypted index. The per-chunk CRC covers the *encrypted* on-disk bytes.\n *\n * **Metadata (opt-in via `metadata`).** A generation given metadata carries an extension block between the last payload\n * and the index, flagged in the footer (`FLAG_EXTENSION`), holding the metadata's canonical JSON, sealed like the index\n * when the generation is encrypted. Without metadata the bytes are exactly those of an object written before the\n * block existed.\n */\nimport { ValidationError } from '../errors';\nimport type { AeadSealed, CrbmCrypto } from '../crypto';\nimport { metadataBytes } from '../metadata';\nimport type { GenerationMetadata } from '../ports';\nimport { crc32c } from './crc32c';\nimport { writeVarint } from './varint';\nimport type { BlobSink } from '../blob';\nimport {\n CONTAINER_CODEC_NONE,\n ELEMENT_WIDTH_32,\n EXT_MAGIC,\n EXT_SECTION_HEADER_BYTES,\n EXT_SECTION_METADATA,\n EXT_TRAILER_BYTES,\n FLAG_ENCRYPTED,\n FLAG_EXTENSION,\n FLAG_LITTLE_ENDIAN,\n FOOTER,\n FOOTER_BYTES,\n FOOTER_CRC_COVERAGE,\n MAGIC,\n MAX_CHUNK_CARDINALITY,\n PAYLOAD_START,\n PREAMBLE_BYTES,\n PAYLOAD_CODEC_ROARING_PORTABLE,\n VERSION_MAJOR,\n VERSION_MINOR,\n} from './format';\n\nexport interface CrbmWriterOptions {\n /** Self-describing generation number (also encoded in the object key). */\n readonly generation: number;\n /**\n * Which codec produced the chunk payloads. Defaults to {@link PAYLOAD_CODEC_ROARING_PORTABLE}.\n *\n * A codec id rather than a roaring-specific one, because `.crbm` is a shared container and a second codec is\n * expected before the format freezes.\n */\n readonly payloadCodecId?: number;\n readonly elementWidth?: number;\n /** When set, payloads + index are AES-256-GCM-encrypted, bound to `(segment, generation, scope)` via AAD. */\n readonly crypto?: CrbmCrypto;\n /**\n * The generation's metadata, checked against the metadata rules and copied as its canonical JSON when the writer\n * is constructed, so a later change to the caller's object never reaches the object. A value that breaks a rule\n * throws {@link ValidationError} from the constructor, before a byte is written. None, or the empty object, writes\n * format 1.0.\n */\n readonly metadata?: GenerationMetadata;\n}\n\ninterface IndexEntry {\n readonly chunkKey: number;\n readonly offset: number;\n readonly length: number;\n readonly cardinality: number;\n readonly crc32c: number;\n}\n\nexport class CrbmWriter {\n private readonly entries: IndexEntry[] = [];\n private offset = PAYLOAD_START;\n private lastKey = -1;\n private totalCardinality = 0;\n private preambleWritten = false;\n private finished = false;\n /** The metadata's canonical JSON, copied at construction; `undefined` leaves the footer's extension flag clear. */\n private readonly metadata: Uint8Array | undefined;\n /** Bytes written so far, the footer included once {@link finish} has run. */\n private written = 0;\n private footerCrc32c: number | undefined;\n\n constructor(\n private readonly sink: BlobSink,\n private readonly options: CrbmWriterOptions,\n ) {\n if (!Number.isInteger(options.generation) || options.generation < 0) {\n throw new ValidationError(\n `generation must be a non-negative integer; got ${options.generation}`,\n );\n }\n this.metadata = metadataBytes(options.metadata, (message) => {\n throw new ValidationError(message);\n });\n }\n\n /**\n * Append one chunk's payload (the roaring-serialized bytes) with its cardinality. `chunkKey` must be\n * strictly greater than the previous one (ascending, no duplicates); `cardinality` is `[1, 65536]`.\n */\n async addChunk(chunkKey: number, payload: Uint8Array, cardinality: number): Promise<void> {\n if (this.finished) throw new ValidationError('CrbmWriter already finished');\n if (!Number.isInteger(chunkKey) || chunkKey < 0 || chunkKey > 0xffff) {\n throw new ValidationError(`chunkKey must be a u16; got ${chunkKey}`);\n }\n if (chunkKey <= this.lastKey) {\n throw new ValidationError(\n `chunkKey ${chunkKey} not strictly ascending (last ${this.lastKey})`,\n );\n }\n if (!Number.isInteger(cardinality) || cardinality < 1 || cardinality > MAX_CHUNK_CARDINALITY) {\n throw new ValidationError(\n `cardinality must be in [1, ${MAX_CHUNK_CARDINALITY}]; got ${cardinality}`,\n );\n }\n if (payload.length === 0) throw new ValidationError('chunk payload must be non-empty');\n\n await this.ensurePreamble();\n // Encrypt (if configured) to the on-disk bytes; the CRC + index length cover what actually lands on disk.\n const crypto = this.options.crypto;\n const stored = crypto ? framed(crypto.aead.seal(payload, crypto.aadFor(chunkKey))) : payload;\n await this.sink.write(stored);\n this.entries.push({\n chunkKey,\n offset: this.offset,\n length: stored.length,\n cardinality,\n crc32c: crc32c(stored),\n });\n this.offset += stored.length;\n this.totalCardinality += cardinality;\n this.lastKey = chunkKey;\n }\n\n /**\n * Write the extension block (when there is metadata), the index region and the fixed footer. After this the object\n * is complete and immutable.\n */\n async finish(): Promise<void> {\n if (this.finished) throw new ValidationError('CrbmWriter already finished');\n await this.ensurePreamble();\n\n if (this.metadata !== undefined) {\n const block = this.extensionBlock(this.metadata);\n await this.sink.write(block);\n this.offset += block.length;\n }\n\n const indexPlain = this.encodeIndex();\n // Encrypt the index too (its nonce/tag go in the footer); a leaked object then reveals no chunk metadata.\n let indexRegion = indexPlain;\n let indexNonce: Uint8Array | undefined;\n let indexTag: Uint8Array | undefined;\n if (this.options.crypto !== undefined) {\n const sealed = this.options.crypto.aead.seal(indexPlain, this.options.crypto.aadFor('index'));\n indexRegion = sealed.ciphertext;\n indexNonce = sealed.nonce;\n indexTag = sealed.tag;\n }\n const indexOffset = this.offset;\n await this.sink.write(indexRegion);\n this.offset += indexRegion.length;\n\n const footer = this.buildFooter(indexOffset, indexRegion, indexNonce, indexTag);\n await this.sink.write(footer);\n this.written = this.offset + footer.length;\n this.footerCrc32c = new DataView(footer.buffer, footer.byteOffset).getUint32(\n FOOTER.footerCrc32c,\n true,\n );\n this.finished = true;\n }\n\n /**\n * The finished object's size and its footer's CRC: what names it, as a reader's fingerprint does. A writer that\n * has to tell its own object from another stored later under the same key compares these with the object's\n * footer. Throws before {@link finish}.\n */\n get identity(): { readonly size: number; readonly footerCrc: number } {\n if (this.footerCrc32c === undefined) throw new ValidationError('CrbmWriter not finished');\n return { size: this.written, footerCrc: this.footerCrc32c };\n }\n\n private async ensurePreamble(): Promise<void> {\n if (this.preambleWritten) return;\n const preamble = new Uint8Array(PREAMBLE_BYTES);\n preamble.set(MAGIC, 0);\n preamble[4] = VERSION_MAJOR;\n preamble[5] = VERSION_MINOR;\n // bytes 6-7 reserved = 0\n await this.sink.write(preamble);\n this.preambleWritten = true;\n }\n\n /**\n * The extension block: one metadata section (`u8 type \u2016 u32 length \u2016 bytes`), then the trailer\n * (`u32 sectionsLength \u2016 u32 crc32c(sections \u2016 sectionsLength) \u2016 \"CRBX\"`). Encrypted, the section holds\n * `nonce \u2016 ciphertext \u2016 tag`, sealed with the metadata scope of the associated data.\n */\n private extensionBlock(metadata: Uint8Array): Uint8Array {\n const crypto = this.options.crypto;\n const body = crypto ? framed(crypto.aead.seal(metadata, crypto.aadFor('metadata'))) : metadata;\n const sectionsLength = EXT_SECTION_HEADER_BYTES + body.length;\n const block = new Uint8Array(sectionsLength + EXT_TRAILER_BYTES);\n const view = new DataView(block.buffer);\n block[0] = EXT_SECTION_METADATA;\n view.setUint32(1, body.length, true);\n block.set(body, EXT_SECTION_HEADER_BYTES);\n view.setUint32(sectionsLength, sectionsLength, true);\n view.setUint32(sectionsLength + 4, crc32c(block.subarray(0, sectionsLength + 4)), true);\n block.set(EXT_MAGIC, sectionsLength + 8);\n return block;\n }\n\n private encodeIndex(): Uint8Array {\n const out: number[] = [];\n let prevKey = 0;\n let prevEnd = PAYLOAD_START;\n for (const e of this.entries) {\n writeVarint(out, e.chunkKey - prevKey);\n writeVarint(out, e.offset - prevEnd);\n writeVarint(out, e.length);\n writeVarint(out, e.cardinality);\n // crc32c is high-entropy \u2192 fixed 4-byte LE, not varint.\n out.push(\n e.crc32c & 0xff,\n (e.crc32c >>> 8) & 0xff,\n (e.crc32c >>> 16) & 0xff,\n (e.crc32c >>> 24) & 0xff,\n );\n prevKey = e.chunkKey;\n prevEnd = e.offset + e.length;\n }\n return Uint8Array.from(out);\n }\n\n private buildFooter(\n indexOffset: number,\n indexRegion: Uint8Array,\n indexNonce: Uint8Array | undefined,\n indexTag: Uint8Array | undefined,\n ): Uint8Array {\n const encrypted = this.options.crypto !== undefined;\n const footer = new Uint8Array(FOOTER_BYTES);\n const view = new DataView(footer.buffer);\n view.setBigUint64(FOOTER.indexOffset, BigInt(indexOffset), true);\n view.setBigUint64(FOOTER.indexLength, BigInt(indexRegion.length), true);\n view.setUint32(FOOTER.indexCrc32c, crc32c(indexRegion), true);\n view.setUint32(\n FOOTER.flags,\n FLAG_LITTLE_ENDIAN |\n (encrypted ? FLAG_ENCRYPTED : 0) |\n (this.metadata === undefined ? 0 : FLAG_EXTENSION),\n true,\n );\n view.setUint16(\n FOOTER.payloadCodecId,\n this.options.payloadCodecId ?? PAYLOAD_CODEC_ROARING_PORTABLE,\n true,\n );\n footer[FOOTER.elementWidth] = this.options.elementWidth ?? ELEMENT_WIDTH_32;\n footer[FOOTER.containerCodec] = CONTAINER_CODEC_NONE;\n footer[FOOTER.versionMajor] = VERSION_MAJOR;\n footer[FOOTER.versionMinor] = VERSION_MINOR;\n // When encrypted, the index's AEAD nonce/tag live in these reserved slots; key_id stays zero (the wrapped\n // DEKs live in the registry, not the object). Unencrypted: all crypto fields stay zero.\n if (indexNonce !== undefined) footer.set(indexNonce, FOOTER.indexNonce);\n if (indexTag !== undefined) footer.set(indexTag, FOOTER.indexTag);\n view.setBigUint64(FOOTER.generation, BigInt(this.options.generation), true);\n // Hide chunk count + cardinality on an encrypted object (a leak reveals neither); the reader derives both\n // from the decrypted index. Cleartext objects keep them for the O(1) count()/validation path.\n view.setUint32(FOOTER.chunkCount, encrypted ? 0 : this.entries.length, true);\n view.setBigUint64(FOOTER.totalCardinality, BigInt(encrypted ? 0 : this.totalCardinality), true);\n view.setUint32(FOOTER.footerCrc32c, crc32c(footer.subarray(0, FOOTER_CRC_COVERAGE)), true);\n footer.set(MAGIC, FOOTER.endMagic);\n return footer;\n }\n}\n\n/** The on-disk frame of one sealed blob, `nonce \u2016 ciphertext \u2016 tag`. */\nfunction framed(sealed: AeadSealed): Uint8Array {\n const out = new Uint8Array(sealed.nonce.length + sealed.ciphertext.length + sealed.tag.length);\n out.set(sealed.nonce, 0);\n out.set(sealed.ciphertext, sealed.nonce.length);\n out.set(sealed.tag, sealed.nonce.length + sealed.ciphertext.length);\n return out;\n}\n", "/**\n * What a load accepts: ids, or a whole bitmap as `{ serialized }` bytes or as a `{ bitmap }` that serializes to\n * them.\n *\n * Every bitmap input takes one path. The bytes are size-capped, checked structurally and decoded by the codec's\n * safe deserializer into a bitmap of the codec's own, which then writes its chunks straight from its containers:\n * no id passes through JavaScript. `{ bitmap }` is only a spelling of `{ serialized }`, serialized once, here, so\n * a caller's bitmap is never trusted as a structure and a change made to it after the call is not loaded.\n *\n * All of it runs before the load's first request, so a malformed input costs no round trip and writes nothing.\n */\nimport type { CodecBitmap, CodecInterface } from './codec';\nimport { ValidationError, isIntegrityError } from './errors';\n\n/**\n * Anything that serializes itself to portable Roaring: `roaring`'s `RoaringBitmap32` is one. Typed by shape, so\n * no codec's own class appears in a signature. A bitmap that can also say how large its serialization is\n * (`getSerializationSizeInBytes`) is asked first, so one over the cap is refused before it is serialized.\n */\nexport interface PortableBitmap {\n serialize(format: 'portable'): Uint8Array;\n getSerializationSizeInBytes?(format: 'portable'): number;\n}\n\n/**\n * What a load writes as the segment's new contents.\n *\n * - **ids**: an iterable or async iterable of integers in `[0, 2^32)`, consumed lazily. Not a `Uint8Array`,\n * `Uint8ClampedArray` or `Buffer`, which a load refuses, since each byte would load as an id.\n * - **`{ serialized }`**: one bitmap in the portable Roaring format, checked before anything is written.\n * - **`{ bitmap }`**: an object with `serialize('portable')`, loaded as `{ serialized: bitmap.serialize('portable') }`.\n */\nexport type LoadInput =\n | Iterable<number>\n | AsyncIterable<number>\n | { readonly serialized: Uint8Array }\n | { readonly bitmap: PortableBitmap };\n\n/**\n * No canonical 32-bit Roaring bitmap serializes to more than this. Under the run cookie, the larger of the two\n * headers, it is a 4-byte cookie, 8,192 bytes of run flags and 8 bytes of header for each of the 65,536\n * containers, and no container's body is over 8,192 bytes: a bitset is 8,192, and a run container is kept only\n * where it is smaller. 537,403,396 bytes, about 512.5 MiB.\n */\nexport const MAX_SERIALIZED_LOAD_BYTES = 4 + 8_192 + 8 * 65_536 + 65_536 * 8_192;\n\n/** A bitmap input, decoded into the codec's own bitmap: written from its chunks, never id by id. */\nexport class DecodedLoadInput {\n constructor(readonly bitmap: CodecBitmap) {}\n}\n\n/** `Object.prototype.toString`'s tag, which names a typed array's kind across realms and for `Buffer` too. */\nfunction tagOf(value: object): string {\n return Object.prototype.toString.call(value).slice(8, -1);\n}\n\n/** The typed arrays' own accessors, which read the view itself whatever a subclass or another realm defines. */\nconst VIEW = Object.getPrototypeOf(Uint8Array.prototype) as object;\nconst viewGetter = (name: 'buffer' | 'byteOffset' | 'byteLength'): ((this: unknown) => unknown) =>\n Object.getOwnPropertyDescriptor(VIEW, name)?.get as (this: unknown) => unknown;\nconst bufferOf = viewGetter('buffer');\nconst byteOffsetOf = viewGetter('byteOffset');\nconst byteLengthOf = viewGetter('byteLength');\n\n/**\n * The bytes a `Uint8Array` really holds, as a plain view the check and the decoder both read: never through a\n * getter a subclass overrides, so the two cannot be shown different bytes. A detached buffer holds none.\n */\nfunction plainView(bytes: Uint8Array, what: string): Uint8Array {\n let buffer: ArrayBufferLike;\n let offset: number;\n let length: number;\n try {\n buffer = bufferOf.call(bytes) as ArrayBufferLike;\n offset = byteOffsetOf.call(bytes) as number;\n length = byteLengthOf.call(bytes) as number;\n } catch {\n throw new ValidationError(`${what} must be a Uint8Array of portable Roaring bytes`);\n }\n return length === 0 ? new Uint8Array(0) : new Uint8Array(buffer, offset, length);\n}\n\n/**\n * Whether `buffer` is a `SharedArrayBuffer`, by its brand: the type's own `byteLength` getter throws on anything\n * else, where a `Symbol.toStringTag` can be given any value. False on a runtime without one.\n */\nconst sharedByteLength =\n typeof SharedArrayBuffer === 'function'\n ? (Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength')?.get as\n ((this: unknown) => unknown) | undefined)\n : undefined;\nfunction isShared(buffer: ArrayBufferLike): boolean {\n if (sharedByteLength === undefined) return false;\n try {\n sharedByteLength.call(buffer);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction overCap(what: string, length: number): ValidationError {\n return new ValidationError(\n `${what} is ${length} bytes, more than any canonical 32-bit bitmap serializes to ` +\n `(${MAX_SERIALIZED_LOAD_BYTES}). Call runOptimize() on the bitmap before serializing it.`,\n );\n}\n\n/**\n * Check a load's input and decode a bitmap input, before the load makes any request.\n *\n * @returns ids unchanged, to be consumed lazily by the write, or the decoded bitmap.\n * @throws {ValidationError} for a byte array passed as ids; bytes that are malformed, over the cap, or followed by\n * more bytes; a `{ bitmap }` with no `serialize` method, or whose `serialize('portable')` returns no `Uint8Array`;\n * and anything that is none of the three inputs. An error the caller's own `serialize` throws propagates as it is.\n */\nexport function prepareLoadInput(\n input: LoadInput,\n codec: CodecInterface,\n): Iterable<number> | AsyncIterable<number> | DecodedLoadInput {\n if (typeof input !== 'object' || input === null) {\n throw new ValidationError(\n `a load takes ids, { serialized } or { bitmap }; got ${String(input)}`,\n );\n }\n const tag = tagOf(input);\n if (tag === 'Uint8Array' || tag === 'Uint8ClampedArray') {\n throw new ValidationError(\n `a load's ids cannot be a ${tag} or Buffer: each byte would be loaded as an id. Pass portable Roaring ` +\n `bytes as { serialized }, and ids as a Uint32Array or an array of numbers.`,\n );\n }\n if (Symbol.iterator in input || Symbol.asyncIterator in input) return input;\n const keys = Object.keys(input);\n if (keys.length === 1 && keys[0] === 'serialized') {\n return decode((input as { serialized: unknown }).serialized, '{ serialized }', codec);\n }\n if (keys.length === 1 && keys[0] === 'bitmap') {\n const bitmap = (input as { bitmap: unknown }).bitmap as Partial<PortableBitmap> | null;\n if (typeof bitmap !== 'object' || bitmap === null || typeof bitmap.serialize !== 'function') {\n throw new ValidationError(`{ bitmap } needs an object with serialize('portable')`);\n }\n const what = \"{ bitmap }'s serialize('portable')\";\n if (typeof bitmap.getSerializationSizeInBytes === 'function') {\n const size = bitmap.getSerializationSizeInBytes('portable');\n if (typeof size === 'number' && size > MAX_SERIALIZED_LOAD_BYTES) throw overCap(what, size);\n }\n return decode(bitmap.serialize('portable'), what, codec);\n }\n throw new ValidationError(\n `a load takes ids (an iterable of integers), { serialized } or { bitmap }, as the only key; got an object ` +\n `with keys [${keys.join(', ')}]. Load options go in the next argument.`,\n );\n}\n\nfunction decode(bytes: unknown, what: string, codec: CodecInterface): DecodedLoadInput {\n if (typeof bytes !== 'object' || bytes === null || tagOf(bytes) !== 'Uint8Array') {\n throw new ValidationError(`${what} must be a Uint8Array of portable Roaring bytes`);\n }\n const view = plainView(bytes as Uint8Array, what);\n if (view.byteLength > MAX_SERIALIZED_LOAD_BYTES) throw overCap(what, view.byteLength);\n // Bytes in a SharedArrayBuffer are copied: another thread could change them between the check and the decode.\n const own = isShared(view.buffer) ? new Uint8Array(view) : view;\n try {\n return new DecodedLoadInput(\n codec.safeDeserialize(own, MAX_SERIALIZED_LOAD_BYTES, { whole: true }),\n );\n } catch (err) {\n // The codec reports bytes it refuses as corrupt; here they are the caller's input, not a stored object.\n if (!isIntegrityError(err)) throw err;\n throw new ValidationError(`${what} is not a valid bitmap: ${(err as Error).message}`);\n }\n}\n", "/**\n * A generation's summary on its registry row: how a writer builds it, how a reader opens it, and how it is held\n * against the object it describes.\n *\n * The row carries a cached description of the current generation, `{ generation, cardinality, metadata? }`, written by\n * the same compare-and-swap that moves the pointer. On a cleartext segment the values are in the clear. On an\n * encrypted one the row holds `{ generation, sealed }` instead: the cardinality as a fixed-width little-endian u64,\n * then the metadata's canonical JSON if there is any, sealed under the segment's data key with the summary scope of\n * the associated data, which binds it to its namespace, segment and generation. The count is fixed-width so the\n * ciphertext's length reveals only the metadata's size, never how many digits the count has. Pure: no I/O.\n *\n * The generation's `.crbm` object stays the truth, and the summary is a copy of it. {@link summaryAgrees} is the\n * comparison of one with the other.\n */\nimport type { Aead } from './crypto';\nimport { aadFor } from './crypto';\nimport { IntegrityError, isIntegrityError } from './errors';\nimport {\n MAX_METADATA_BYTES,\n canonicalMetadataJson,\n metadataBytes,\n metadataFromBytes,\n} from './metadata';\nimport type {\n ClearRegistrySummary,\n GenerationMetadata,\n RegistryRecord,\n RegistrySummary,\n SealedRegistrySummary,\n SegmentRef,\n} from './ports';\n\n/** The 12-byte nonce and 16-byte tag AES-256-GCM frames a sealed blob with, which the `.crbm` format fixes too. */\nconst NONCE_BYTES = 12;\nconst TAG_BYTES = 16;\n/** The cardinality, sealed as a little-endian u64. */\nconst COUNT_BYTES = 8;\n/** A generation holds at most every 32-bit id. */\nconst MAX_CARDINALITY = 2 ** 32;\n\n/** What a summary says about a generation: its id count, and its metadata when it has any. */\nexport interface GenerationDescription {\n readonly cardinality: number;\n readonly metadata?: GenerationMetadata | undefined;\n}\n\nconst BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/** Padded standard base64. */\nfunction toBase64(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const a = bytes[i]!;\n const b = bytes[i + 1];\n const c = bytes[i + 2];\n out += BASE64[a >> 2]! + BASE64[((a & 3) << 4) | ((b ?? 0) >> 4)]!;\n out += b === undefined ? '=' : BASE64[((b & 15) << 2) | ((c ?? 0) >> 6)]!;\n out += c === undefined ? '=' : BASE64[c & 63]!;\n }\n return out;\n}\n\n/** The bytes of padded standard base64, or `undefined` for text that is not. */\nfunction fromBase64(text: string): Uint8Array | undefined {\n if (text.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(text)) return undefined;\n const pad = text.endsWith('==') ? 2 : text.endsWith('=') ? 1 : 0;\n const out = new Uint8Array((text.length / 4) * 3 - pad);\n let o = 0;\n for (let i = 0; i < text.length; i += 4) {\n const n =\n (BASE64.indexOf(text[i]!) << 18) |\n (BASE64.indexOf(text[i + 1]!) << 12) |\n (Math.max(BASE64.indexOf(text[i + 2]!), 0) << 6) |\n Math.max(BASE64.indexOf(text[i + 3]!), 0);\n if (o < out.length) out[o++] = (n >> 16) & 255;\n if (o < out.length) out[o++] = (n >> 8) & 255;\n if (o < out.length) out[o++] = n & 255;\n }\n return out;\n}\n\n/** The clear summary of a generation on a cleartext segment. `metadata` is left off when there is none. */\nexport function clearSummary(\n generation: number,\n cardinality: number,\n metadata?: GenerationMetadata,\n): ClearRegistrySummary {\n return metadata === undefined || Object.keys(metadata).length === 0\n ? { generation, cardinality }\n : { generation, cardinality, metadata };\n}\n\n/**\n * The sealed summary of a generation on an encrypted segment: the cardinality as a u64, then the metadata's canonical\n * JSON, sealed under `aead` and bound to `ref` and `generation`.\n */\nexport function sealSummary(\n aead: Aead,\n ref: SegmentRef,\n generation: number,\n cardinality: number,\n metadata?: GenerationMetadata,\n): SealedRegistrySummary {\n const meta = metadataBytes(metadata, (message) => {\n throw new IntegrityError(`summary metadata: ${message}`);\n });\n const plain = new Uint8Array(COUNT_BYTES + (meta?.length ?? 0));\n new DataView(plain.buffer).setBigUint64(0, BigInt(cardinality), true);\n if (meta !== undefined) plain.set(meta, COUNT_BYTES);\n const { nonce, ciphertext, tag } = aead.seal(plain, aadFor(ref, generation, 'summary'));\n const framed = new Uint8Array(nonce.length + ciphertext.length + tag.length);\n framed.set(nonce, 0);\n framed.set(ciphertext, nonce.length);\n framed.set(tag, nonce.length + ciphertext.length);\n return { generation, sealed: toBase64(framed) };\n}\n\n/**\n * What a sealed summary says, opened with `aead` under the summary scope of `ref` and the summary's own generation.\n * The bytes are untrusted: a blob that is not base64, is the wrong size, fails its authentication (tampered, or moved\n * from another generation's row), or holds a count outside 0 to 2^32 or metadata that breaks the rules, is an\n * {@link IntegrityError}.\n */\nexport function openSummary(\n aead: Aead,\n ref: SegmentRef,\n summary: SealedRegistrySummary,\n): GenerationDescription {\n const fail = (message: string): never => {\n throw new IntegrityError(`registry record summary: ${message}`);\n };\n const framed = fromBase64(summary.sealed);\n const framing = NONCE_BYTES + TAG_BYTES;\n if (\n framed === undefined ||\n framed.length < framing + COUNT_BYTES ||\n framed.length > framing + COUNT_BYTES + MAX_METADATA_BYTES\n ) {\n fail('the sealed summary is not a nonce, a count, up to 1 KiB of metadata and a tag');\n }\n const bytes = framed as Uint8Array;\n let plain: Uint8Array;\n try {\n plain = aead.open(\n {\n nonce: bytes.subarray(0, NONCE_BYTES),\n ciphertext: bytes.subarray(NONCE_BYTES, bytes.length - TAG_BYTES),\n tag: bytes.subarray(bytes.length - TAG_BYTES),\n },\n aadFor(ref, summary.generation, 'summary'),\n );\n } catch (err) {\n if (!isIntegrityError(err)) throw err;\n return fail(\n 'the sealed summary does not open: tampered bytes, or a summary moved from another generation',\n );\n }\n if (plain.length < COUNT_BYTES) fail('the sealed summary is shorter than its count');\n const count = new DataView(plain.buffer, plain.byteOffset, plain.byteLength).getBigUint64(\n 0,\n true,\n );\n if (count > BigInt(MAX_CARDINALITY)) fail(`cardinality ${count} is outside 0 to 2^32`);\n const metadata =\n plain.length === COUNT_BYTES ? undefined : metadataFromBytes(plain.subarray(COUNT_BYTES), fail);\n return { cardinality: Number(count), metadata };\n}\n\n/**\n * What a row's summary says of its current generation, or `undefined` when it says nothing a reader may use. It is\n * used only for the generation it names, on an active row, and in the shape the row's keys call for: clear on a row\n * with no wrapped keys, sealed on a row with them. A sealed one is opened with `aead`, and is unusable without it,\n * or when it does not open, so a summary a tamperer moved or rewrote is never believed. Never throws for a bad\n * summary: the row's copy is a cache, and a copy that cannot be used is a copy that is not there.\n */\nexport function usableSummary(\n ref: SegmentRef,\n row: RegistryRecord,\n aead: Aead | undefined,\n): GenerationDescription | undefined {\n const summary: RegistrySummary | undefined = row.summary;\n if (summary === undefined || row.status !== 'active' || summary.generation !== row.currentGen) {\n return undefined;\n }\n const keyed = row.wrappedDeks !== undefined && row.wrappedDeks.length > 0;\n if ('sealed' in summary) {\n if (!keyed || aead === undefined) return undefined;\n try {\n return openSummary(aead, ref, summary);\n } catch (err) {\n if (isIntegrityError(err)) return undefined;\n throw err;\n }\n }\n if (keyed) return undefined;\n return { cardinality: summary.cardinality, metadata: summary.metadata };\n}\n\n/**\n * The canonical form of a metadata record, or `undefined` for none. The empty record is none: a generation without\n * metadata carries none, and the two are never told apart.\n */\nfunction canonicalOrNone(metadata: GenerationMetadata | undefined): string | undefined {\n if (metadata === undefined) return undefined;\n const json = canonicalMetadataJson(metadata, (message) => {\n throw new IntegrityError(`metadata: ${message}`);\n });\n return json === '{}' ? undefined : json;\n}\n\n/**\n * Whether a summary's description of a generation is what its object holds: the same id count and the same metadata,\n * compared in canonical form, so key order never matters. A summary with metadata over an object with none, and the\n * reverse, disagree: whether an encrypted object's metadata block is present is not authenticated (stripping it needs\n * no key), so the row's sealed copy is what a missing block is held against.\n */\nexport function summaryAgrees(\n summary: GenerationDescription,\n object: GenerationDescription,\n): boolean {\n return (\n summary.cardinality === object.cardinality &&\n canonicalOrNone(summary.metadata) === canonicalOrNone(object.metadata)\n );\n}\n\n/**\n * The metadata an erasure carries into its rewrite of a generation: the object's own, except that an object with none\n * whose row's authenticated summary of the same generation has some gets the row's. Whether an encrypted object has a\n * metadata block is not authenticated, so a block stripped from one reads as none, and the sealed summary on the row is\n * the one copy that says there was a block; carrying it keeps the rewrite from erasing the record of what it dropped.\n * Pass `authenticated` only for a summary that was opened (a clear one has no more authority than the block it sits\n * beside). Never refuses: a rewrite over a stripped block is still an erasure.\n */\nexport function metadataToCarry(\n object: GenerationMetadata | undefined,\n authenticated: GenerationDescription | undefined,\n): GenerationMetadata | undefined {\n const empty = (metadata: GenerationMetadata | undefined): boolean =>\n metadata === undefined || Object.keys(metadata).length === 0;\n return empty(object) && authenticated !== undefined && !empty(authenticated.metadata)\n ? authenticated.metadata\n : object;\n}\n\n/** The summary of a generation of `ref`, clear or sealed under `aead` as the segment's keys call for. */\nexport function summaryOf(\n ref: SegmentRef,\n generation: number,\n description: GenerationDescription,\n aead: Aead | undefined,\n): RegistrySummary {\n return aead === undefined\n ? clearSummary(generation, description.cardinality, description.metadata)\n : sealSummary(aead, ref, generation, description.cardinality, description.metadata);\n}\n", "/**\n * Bridges the `.crbm` archive format to the engine's Storage seam.\n *\n * `CrbmStorageChunkSource` implements the {@link StorageChunkSource} the engine reads through, over an\n * {@link IStorageDriver}: it resolves a segment's current generation, opens its {@link CrbmReader} once, and serves\n * per-chunk payloads. `writeCrbmGeneration` / `writeCrbmGenerationStream` are the write primitives (a generation\n * built from bitmaps, in memory or streamed), `bulkLoadCrbmGeneration` is the loader `load()` is built on, and\n * `publishGeneration` is the forward-only pointer advance every write ends with.\n */\nimport {\n DEFAULT_CURRENT_GEN_TTL_MS,\n DEFAULT_MAX_OPEN_INDEX_BYTES,\n DEFAULT_MAX_OPEN_SEGMENTS,\n REFRESH_RETRY_MS,\n} from './reader-defaults';\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport {\n IntegrityError,\n CapabilityError,\n KeyUnavailableError,\n NotFoundError,\n TransientError,\n ValidationError,\n WriteConflictError,\n isIntegrityError,\n isNotFoundError,\n isTransientError,\n isValidationError,\n isWriteConflictError,\n} from './errors';\nimport type { BlobReader } from './blob';\nimport { yieldEvery } from './cooperative';\nimport type { Yielder } from './cooperative';\nimport type { Clock, Rng } from './determinism';\nimport { sameIncarnation } from './token';\nimport { BoundedLru } from './lru';\nimport { splitId } from './bit-route';\nimport { segmentKey } from './keys';\nimport { aadFor } from './crypto';\nimport type { Aead, CrbmCrypto, IKeystore, WrappedDek } from './crypto';\nimport { validateChunkRef, validateUserRef } from './validate';\nimport type {\n ChunkRef,\n StorageChunkSource,\n GenerationMetadata,\n GenerationSummary,\n GenKey,\n IStorageDriver,\n IRegistryDriver,\n RegistryRecord,\n RegistrySummary,\n SegmentRef,\n SegmentSize,\n Token,\n} from './ports';\nimport { DEFAULT_TAIL_BYTES } from './crbm/format';\nimport { CrbmReader, fingerprintFor, footerSaysEncrypted } from './crbm/reader';\nimport type { CrbmReaderOptions } from './crbm/reader';\nimport { CrbmWriter } from './crbm/writer';\nimport type { CodecBitmap, CodecInterface, EncodedChunk } from './codec';\nimport { requireCodec } from './codec';\nimport { DecodedLoadInput } from './load-input';\nimport { summaryAgrees, summaryOf, usableSummary } from './summary';\nimport type { GenerationDescription } from './summary';\n\nexport interface CrbmStorageChunkSourceOptions extends CrbmReaderOptions {\n /**\n * Optional {@link IRegistryDriver}. When provided, the current generation is resolved via the registry's\n * authoritative `currentGen` (one cheap strong read) instead of a `list` scan of every generation \u2014 the\n * retirement of that scan. When absent, the source falls back to the `list`-scan (so the\n * in-memory / simple setups keep working with no registry). The resolved generation is cached and\n * **re-resolved on a short TTL** ({@link currentGenTtlMs}, needs a {@link clock}) so a long-lived source\n * observes another process's load within the TTL, not only when something else happens to re-resolve it.\n */\n readonly registry?: IRegistryDriver;\n /**\n * Optional {@link IKeystore} for reading **encrypted** segments. When a segment's registry record\n * carries wrapped DEK(s), the source unwraps the DEK via this keystore and decrypts each chunk/index. Reading\n * an encrypted segment without a keystore throws {@link KeyUnavailableError}; cleartext segments ignore it.\n * Requires a `registry` (that's where the wrapped DEKs live).\n */\n readonly keystore?: IKeystore;\n /**\n * Enforce that every segment read is encrypted. When true, resolving a **cleartext** segment (no\n * wrapped DEKs) throws {@link KeyUnavailableError} \u2014 a guard against silently reading a segment that should\n * have been encrypted. Off by default (encryption is opt-in).\n */\n readonly requireEncryption?: boolean;\n /**\n * Time source for the current-generation TTL refresh \u2014 the determinism seam; `core/` never reads\n * ambient time. Refresh needs **both** a clock and a `registry`. Without either there is no timed refresh, and\n * that is the only difference: a segment is still re-resolved when its reader is evicted, when a fetch finds its\n * generation swept, and on {@link CrbmStorageChunkSource.invalidate}. What goes is the bound on how long another\n * process's publish takes to arrive. The `CloudRoaring` facade passes its clock automatically, so wiring a\n * `registry` is enough to get the bounded refresh.\n */\n readonly clock?: Pick<Clock, 'now'>;\n /**\n * How long (ms) a resolved `currentGen` is trusted before the next read re-resolves it (default 2000) \u2014 the\n * bound on read staleness after a load publishes: a reader may serve the prior generation for up to this long,\n * then converges. Lazy (checked on read \u2014 no timer); \u2264 one cheap registry read per segment per window, and a\n * new {@link CrbmReader} is opened only when the generation actually changed. `0` turns the refresh off, as a\n * missing clock or registry does: {@link clock} says what that changes, and what it does not.\n */\n readonly currentGenTtlMs?: number;\n /**\n * Hard ceiling on how many segments' readers (each holding a fully-parsed `.crbm` index) are cached at once\n * (default 1024) \u2014 a memory bound for a long-running server. Past it, the least-recently-used\n * segment's reader is evicted; the next read of an evicted segment re-opens it (one cheap tail GET, since\n * generations are immutable). Raise it for a big cache working set of small segments.\n */\n readonly maxOpenSegments?: number;\n /**\n * Aggregate byte ceiling on the parsed `.crbm` indices resident in the reader cache (default 64 MiB) \u2014 the\n * **second half of that memory bound**. `maxOpenSegments` alone bounds by *count*, but a wide/dense segment's\n * parsed index can reach about 1.3 MB, so 1024 wide indices could pin over a GB and blow a small heap (e.g. a 128 MB\n * Lambda) while the count is nominally \"in bounds\". This caps the summed {@link CrbmReader.retainedBytes} (the\n * parsed index, and any metadata a generation carries) across cached readers; the least-recently-used reader is evicted once the total would exceed it \u2014 whichever\n * of the count/byte bounds binds first. Lower it for memory-tight deployments with wide segments; a single\n * segment whose index alone exceeds the budget is still cached (it can't be shrunk) but nothing else alongside.\n */\n readonly maxOpenIndexBytes?: number;\n}\n\n/** Adapt one `(driver, key)` pair to the codec's `BlobReader` seam. */\nfunction storageBlobReader(driver: IStorageDriver, key: GenKey): BlobReader {\n return {\n getRange(offset, length) {\n return driver.getRange(key, offset, length);\n },\n getTail(maxBytes) {\n return driver.getTail(key, maxBytes);\n },\n };\n}\n\n/**\n * A resolved read target, as `resolveLive` produces it: which generation is current, and its DEK wrappings if it\n * is encrypted. `lineage` is the registry row's OCC token \u2014 the identity that survives a delete, because a shipped\n * registry never gives two writes under one name the same token, but for a collision of probability 2^-128 per pair of\n * incarnations: each row's token carries a random 128-bit incarnation id, and each write a random part of its own. It is what separates two *incarnations*\n * of one name, which a generation number cannot:\n * the numbering restarts at 0 once the row is purged and the bucket emptied, so a retired-and-re-created\n * segment presents different data at the same `currentGen`. Undefined for a registry-less source, which has no\n * row: there, only the object itself tells two incarnations apart, by the fingerprint a pin records.\n */\ntype Target = { generation: number; lineage?: Token; wrappedDeks?: readonly WrappedDek[] };\n\n/**\n * What a pin holds of the object it pinned: the version its cache entries are keyed by, and the object's\n * fingerprint, which a pinned read checks each time it opens the generation. `pinGeneration` always records one.\n * A pin built by hand without one is not checked: it reads whatever object is under its generation's key.\n */\nexport interface PinnedObject {\n readonly version: string;\n readonly fingerprint?: string;\n}\n\n/** A pinned read of a generation that is no longer the object the pin opened: stated as a fact, not a cause. */\nconst notThePinned = (ref: SegmentRef, generation: number): NotFoundError =>\n new NotFoundError(\n `segment \"${ref.segment}\" generation ${generation} is no longer the object this handle pinned`,\n );\n\n/**\n * The version of one generation of one incarnation: `<generation>`, or `<generation>:<row token>` where there is\n * a row. One spelling, for the live lookup and for what a pin holds.\n */\nfunction versionOf(generation: number, lineage: unknown): string {\n return lineage === undefined ? String(generation) : `${generation}:${String(lineage)}`;\n}\n\n/**\n * What one resolution of a segment's pointer found: the target, and what its row says of the generation, each\n * computed only when asked for. The key is unwrapped once however many ask, and a failure is not remembered, so a\n * transient fault in the keystore is asked again.\n */\ninterface Live {\n readonly target: Target;\n /** The generation's decryption context (`undefined` for a cleartext segment); applies `requireEncryption`. */\n readonly crypto: () => Promise<CrbmCrypto | undefined>;\n /** The segment's unwrapped key, shared with the next resolution while the row's wrapped keys are the same. */\n readonly unwrap: (() => Promise<Aead>) | undefined;\n /** What the row's summary says of the generation, when the row's summary is usable for it. */\n readonly summary: () => Promise<GenerationDescription | undefined>;\n /** What the resolution weighs while no reader is open: the row's summary. */\n readonly bytes: number;\n}\n\n/** A summary in the shape the port returns: `metadata` left off when there is none. */\nfunction summaryOfGeneration(\n generation: number,\n description: GenerationDescription,\n): GenerationSummary {\n const { cardinality, metadata } = description;\n // A copy, frozen: the row's own object stays what the cross-check compares against whatever a caller does.\n return metadata === undefined || Object.keys(metadata).length === 0\n ? { generation, cardinality }\n : { generation, cardinality, metadata: Object.freeze({ ...metadata }) };\n}\n\n/** Whether two rows carry the same wrapped keys. */\nconst sameKeys = (a: readonly WrappedDek[] | undefined, b: readonly WrappedDek[]): boolean =>\n a !== undefined && JSON.stringify(a) === JSON.stringify(b);\n\n/** A thunk that runs `fn` once, and runs it again after a failure. */\nfunction once<T>(fn: () => Promise<T>): () => Promise<T> {\n let pending: Promise<T> | undefined;\n return () => {\n if (pending === undefined) {\n const mine = fn();\n pending = mine;\n mine.catch(() => {\n if (pending === mine) pending = undefined;\n });\n }\n return pending;\n };\n}\n\n/** Whether a reader is of the generation, and the row incarnation, a resolution found. */\nconst readerIsOf = (reader: CrbmReader, live: Live): boolean =>\n reader.generation === live.target.generation && reader.lineage === live.target.lineage;\n\n/** What a generation's object says of itself, which is what a summary is held against: its count and metadata. */\nfunction describe(reader: CrbmReader): GenerationDescription {\n let cardinality = 0;\n for (const n of reader.cardinalities().values()) cardinality += n;\n return { cardinality, metadata: reader.metadata };\n}\n\n/**\n * A memoized per-segment snapshot plus the time it was installed, for the current-generation TTL refresh. A live\n * snapshot is a resolved target, and a reader that is opened only when a read needs one: one resolution serves a\n * `count` answered from the row and a `has` that follows it, which reads the generation the count saw. A pinned\n * snapshot is its reader alone.\n */\nclass Snapshot {\n /** When the snapshot's refresh clock started. A refresh that failed transiently moves it back, so it lapses sooner. */\n installedAtMs = 0;\n /** Set by the cache, to hear of a reader as it is opened. */\n onReader: ((reader: Promise<CrbmReader | null>) => void) | undefined;\n private opened: Promise<CrbmReader | null> | undefined;\n\n private constructor(\n /** What the pointer resolved to; absent on a pinned snapshot. */\n readonly target: Promise<Live | null> | undefined,\n private readonly open: ((live: Live) => Promise<CrbmReader>) | undefined,\n /** The reader of the snapshot this one refreshes, kept if the refresh finds the same generation. */\n private prior: Promise<CrbmReader | null> | undefined,\n ) {}\n\n static live(\n target: Promise<Live | null>,\n open: (live: Live) => Promise<CrbmReader>,\n prior?: Promise<CrbmReader | null>,\n ): Snapshot {\n return new Snapshot(target, open, prior);\n }\n\n static eager(reader: Promise<CrbmReader | null>): Snapshot {\n const snap = new Snapshot(undefined, undefined, undefined);\n snap.opened = reader;\n return snap;\n }\n\n /** The reader, opened by the first call. */\n get reader(): Promise<CrbmReader | null> {\n if (this.opened === undefined) {\n const target = this.target as Promise<Live | null>;\n this.opened = target.then(async (live) => {\n if (live === null) return null;\n const kept = await this.reuse(live);\n this.prior = undefined;\n return kept ?? (this.open as (live: Live) => Promise<CrbmReader>)(live);\n });\n this.onReader?.(this.opened);\n }\n return this.opened;\n }\n\n /** The reader if one has been asked for, without asking for one. */\n get openedReader(): Promise<CrbmReader | null> | undefined {\n return this.opened;\n }\n\n /** The prior snapshot's reader, if it is of what this snapshot resolved. */\n async reuse(live: Live): Promise<CrbmReader | null> {\n const prior = this.prior;\n const reader = prior === undefined ? null : await prior.catch(() => null);\n return reader !== null && readerIsOf(reader, live) ? reader : null;\n }\n\n /** Keep the prior snapshot's reader when the refresh found its generation, so a count does not drop it. */\n async adopt(live: Live): Promise<void> {\n if (this.opened !== undefined || this.prior === undefined) return;\n const reader = await this.reuse(live);\n this.prior = undefined;\n if (reader !== null && this.opened === undefined) {\n this.opened = Promise.resolve(reader);\n this.onReader?.(this.opened);\n }\n }\n}\n\n/**\n * How many buffered remainders bulk-load holds before flushing them into their chunk bitmaps. Bounds the\n * transient JS-side buffer to **~28 MB measured** irrespective of input size, while keeping batches large\n * enough that the per-id JS\u2194native crossing is amortised away.\n */\nconst SNAPSHOT_BASE_BYTES = 256;\nconst BULK_FLUSH_IDS = 1 << 20;\n/**\n * Ids between ingest-loop yields. 16x coarser than the per-chunk cadence because the per-id work is ~40 ns\n * (a `splitId` and an array push) against ~1.3 \u00B5s per chunk \u2014 matching cadences would put the yield cost on the\n * wrong side of the work it interrupts. Sized against the *slowest* ingest, an in-memory async generator at\n * ~220 ns/id, so the resulting stretch stays a few ms there and well under 1 ms on a plain array.\n */\nconst YIELD_EVERY_IDS = 1 << 14;\n\nexport class CrbmStorageChunkSource implements StorageChunkSource {\n /**\n * One resolved reader per segment, re-resolved on a short TTL ({@link CrbmStorageChunkSourceOptions.currentGenTtlMs},\n * needs a clock). Within the TTL a segment's Storage bytes are treated as an immutable snapshot; when the TTL\n * elapses the next read cheaply re-resolves `currentGen` and, only if it advanced (a load published),\n * opens the new generation \u2014 so a long-lived source observes new generations within the TTL. The engine pairs\n * this with a **generation-keyed** cache so a bump never serves a stale decoded chunk. Without a clock or a\n * registry there is no timed refresh, and a segment's reader is replaced only when it is evicted, when a read\n * has to fetch from a generation a sweep deleted, or when {@link invalidate} drops it. A segment with no generation yet is not memoized, so\n * it's re-checked until one exists. **Bounded** by a {@link BoundedLru} ({@link CrbmStorageChunkSourceOptions.maxOpenSegments}, default\n * 1024): past the ceiling the least-recently-used segment's reader (and its parsed index) is evicted \u2014 the\n * steady-state memory bound; re-opening an evicted segment is one cheap tail GET.\n */\n private readonly snapshots: BoundedLru<string, Snapshot>;\n /**\n * Pins ({@link heldKey}) whose object has been found replaced: another object is under its generation's key. Kept,\n * and bounded as the readers are, so that a pin's later chunk reads and any reopen fail at once rather than pay for\n * the check again. A replacement can be undone, by a restore that puts the pinned object back, so\n * {@link invalidate} forgets the segment's, and a pin of the same version that opens the object forgets the one\n * against it. An object found gone is not kept: that is the open's own `NotFoundError`, and a 404 can pass. A\n * reader still memoised keeps answering from the index it holds, so the pin's `count()` does, until a later pin of\n * that version opens the object now under the key, which takes its place.\n */\n private readonly replacedPins: BoundedLru<string, true>;\n /**\n * A check of a pinned object still under way, by {@link heldKey}, so the reads that ask at once share one footer\n * read. One forgotten before it ends, by {@link invalidate} or a pin, keeps nothing it finds.\n */\n private readonly checking = new Map<string, Promise<boolean>>();\n /**\n * How many times {@link invalidate} has run, so a read that waited can tell whether the store was told something\n * meanwhile. An eviction from the reader cache is not an invalidation, and what a read found stands through one. It\n * counts every segment's invalidations: a read that waited through another segment's only costs a later read the\n * check again.\n */\n private invalidations = 0;\n /**\n * Generations whose row summary this process found to disagree with their object, by segment and generation. A\n * summary here is not used again: a count of that generation reads the object. Bounded as the readers are, so a\n * long-lived process cannot grow it without limit; one that falls out is trusted again until its next open finds\n * the disagreement again.\n */\n private readonly distrusted: BoundedLru<string, true>;\n private readonly registry: IRegistryDriver | undefined;\n private readonly keystore: IKeystore | undefined;\n private readonly requireEncryption: boolean;\n private readonly readerOptions: CrbmReaderOptions;\n private readonly clock: Pick<Clock, 'now'> | undefined;\n private readonly currentGenTtlMs: number;\n\n constructor(\n private readonly driver: IStorageDriver,\n options: CrbmStorageChunkSourceOptions = {},\n ) {\n if (!driver.capabilities().rangeRead) {\n throw new CapabilityError('Storage driver must support range reads (rangeRead)');\n }\n const {\n registry,\n keystore,\n requireEncryption,\n clock,\n currentGenTtlMs,\n maxOpenSegments,\n maxOpenIndexBytes,\n ...readerOptions\n } = options;\n if (keystore !== undefined && registry === undefined) {\n throw new CapabilityError(\n 'a keystore needs a storage backend \u2014 that is where the wrapped DEKs live. A bare IStorageDriver has no registry to hold them',\n );\n }\n if (requireEncryption === true && registry === undefined) {\n // Without a registry the source can't see wrapped DEKs or `destroyed` tombstones (it list-scans storage),\n // so encryption can't be enforced or even observed here \u2014 fail fast rather than mislead.\n throw new CapabilityError('requireEncryption needs a registry');\n }\n this.registry = registry;\n this.keystore = keystore;\n this.requireEncryption = requireEncryption ?? false;\n this.readerOptions = readerOptions;\n this.clock = clock;\n this.currentGenTtlMs = currentGenTtlMs ?? DEFAULT_CURRENT_GEN_TTL_MS;\n // Bound the reader cache by BOTH count and aggregate parsed-index bytes. No TTL on the LRU itself \u2014\n // the currentGen TTL is handled separately via each snapshot's `installedAtMs`; these ceilings only bound how\n // many segment readers/indices stay resident. Each reader's byte weight is reported once it resolves (below).\n // The cache never compares against wall-clock, so a zero clock is fine when none is injected.\n this.snapshots = new BoundedLru<string, Snapshot>({\n maxEntries: maxOpenSegments ?? DEFAULT_MAX_OPEN_SEGMENTS,\n maxBytes: maxOpenIndexBytes ?? DEFAULT_MAX_OPEN_INDEX_BYTES,\n clock: clock ?? { now: () => 0 },\n });\n this.distrusted = new BoundedLru<string, true>({\n maxEntries: maxOpenSegments ?? DEFAULT_MAX_OPEN_SEGMENTS,\n clock: { now: () => 0 },\n });\n this.replacedPins = new BoundedLru<string, true>({\n maxEntries: maxOpenSegments ?? DEFAULT_MAX_OPEN_SEGMENTS,\n clock: { now: () => 0 },\n });\n }\n\n /**\n * The segment's live snapshot, refreshing on the TTL. Cheap within the TTL window. The snapshot holds the resolved\n * target, and its reader once a read has asked for one.\n */\n private liveSnapshot(ref: SegmentRef): Snapshot {\n const key = segmentKey(ref);\n const existing = this.snapshots.get(key);\n if (existing === undefined) {\n return this.install(\n key,\n Snapshot.live(this.resolveLive(ref), (live) => this.openLive(ref, live)),\n );\n }\n if (!this.expired(existing.installedAtMs)) return existing;\n // Expired: install the in-flight refresh **synchronously** (before any await) so concurrent readers in this\n // window coalesce onto the one re-resolve \u2014 \u2264 one registry read + at most one reopen per segment per window\n // (no boundary thundering-herd). The refresh keeps the prior reader unless the generation moved.\n const snap: Snapshot = this.install(\n key,\n Snapshot.live(\n this.refreshedTarget(ref, existing, () => this.retrySoon(snap)),\n (live) => this.openLive(ref, live),\n existing.openedReader,\n ),\n );\n return snap;\n }\n\n /** The current resolved reader for a segment, refreshing on the TTL. */\n private resolvedReader(ref: SegmentRef): Promise<CrbmReader | null> {\n return this.liveSnapshot(ref).reader;\n }\n\n /**\n * A refresh failed transiently and the prior reader keeps serving: make this snapshot lapse after\n * {@link REFRESH_RETRY_MS} (never longer than the TTL) rather than after a whole TTL, so the next read after\n * that asks the registry again. It costs no read of its own; it only changes when the next refresh happens.\n */\n private retrySoon(snap: Snapshot): void {\n snap.installedAtMs =\n this.now() - (this.currentGenTtlMs - Math.min(this.currentGenTtlMs, REFRESH_RETRY_MS));\n }\n\n /**\n * TTL elapsed: cheaply re-resolve the pointer. The row read is always fresh, so what the new snapshot says of the\n * generation (its summary included) is what the row says now, never the prior snapshot's. The reader is reopened\n * only if the generation or the row changed, and only when a read asks for it.\n */\n private async refreshedTarget(\n ref: SegmentRef,\n existing: Snapshot,\n onTransientFault: () => void,\n ): Promise<Live | null> {\n const prior = await (existing.target as Promise<Live | null>).catch(() => null);\n try {\n return await this.resolveLive(ref, prior);\n } catch (err) {\n // Only a transient fault is ridden out. Anything else \u2014 an access denial, a corrupt row, a registry that\n // answers NotFound \u2014 fails this read exactly as a cold resolve of the segment would, and the snapshot is\n // forgotten, so the reader and the key it unwrapped do not outlive a refresh that could not be trusted.\n if (!isTransientError(err)) throw err;\n // Transient resolve fault: keep serving the prior snapshot if it's alive, but ask again soon rather than a\n // whole TTL later, so an outage ends the stale serving shortly after the registry answers. If it's dead (no\n // target, or a failed open), resolve again rather than re-arm a dead snapshot (else the segment reads empty for a\n // whole TTL window).\n const alive = prior !== null;\n if (!alive) return this.resolveLive(ref);\n onTransientFault();\n return prior;\n }\n }\n\n /**\n * Memoize a snapshot as the segment's at `now()`, forgetting it later if it resolves to null / throws\n * (identity-guarded, so a stale cleanup never clobbers a fresher snapshot). Installing the (pending) snapshot\n * **synchronously** is what lets concurrent callers coalesce onto one in-flight resolve.\n */\n private install(key: string, snap: Snapshot): Snapshot {\n snap.installedAtMs = this.now();\n this.snapshots.set(key, snap);\n const forgetIfStale = (): void => {\n if (this.snapshots.get(key) === snap) this.snapshots.delete(key);\n };\n // Report what the snapshot holds so the cache can bound aggregate resident bytes: the row's summary while only\n // the target is resolved, and the reader (its parsed index and any metadata) once one is open. Identity-guarded\n // via `peek` (no recency change) so a since-replaced snapshot doesn't mis-weight the fresh entry.\n snap.onReader = (reader) => {\n reader.then((r) => {\n if (r === null) {\n forgetIfStale();\n return;\n }\n if (this.snapshots.peek(key) === snap) this.snapshots.setWeight(key, r.retainedBytes);\n }, forgetIfStale);\n };\n if (snap.target !== undefined) {\n snap.target.then(async (live) => {\n if (live === null) {\n forgetIfStale();\n return;\n }\n if (snap.openedReader === undefined && this.snapshots.peek(key) === snap) {\n this.snapshots.setWeight(key, live.bytes);\n }\n await snap.adopt(live);\n }, forgetIfStale);\n }\n if (snap.openedReader !== undefined) snap.onReader(snap.openedReader);\n return snap;\n }\n\n private now(): number {\n return this.clock ? this.clock.now() : 0;\n }\n\n /**\n * How often this source re-reads a segment's pointer while the segment is being read, in ms: its TTL, or 0 when\n * it has no timed refresh (no clock, no registry, or a TTL of 0). The grounded cost report prices the refresh at\n * this.\n */\n get pointerRefreshMs(): number {\n return this.clock !== undefined && this.registry !== undefined ? this.currentGenTtlMs : 0;\n }\n\n private expired(installedAtMs: number): boolean {\n // Refresh needs a clock (the TTL) AND a registry (the *cheap* `currentGen` read the design assumes \u2014\n // without one, re-resolution is a full storage `list`-scan, and a registry-less setup is single-process\n // local, not the shared bucket that separate loaders publish into). Otherwise there is no timed refresh, and\n // only an eviction, a sweep's heal or `invalidate` re-resolves the segment.\n return (\n this.clock !== undefined &&\n this.registry !== undefined &&\n this.currentGenTtlMs > 0 &&\n this.clock.now() - installedAtMs >= this.currentGenTtlMs\n );\n }\n\n /**\n * The segment's current generation number \u2014 the engine keys its chunk cache by this so a generation bump\n * is observed instead of serving a stale decoded chunk. Served from the (TTL-refreshed)\n * snapshot, so no extra backend read within the TTL window. `null` if the segment has no committed generation.\n *\n * Heals a swept generation exactly like {@link withFreshSnapshot}: a caller that resolves the generation once\n * per op, before any chunk fetch, would otherwise fail the whole operation rather than one chunk. The engine\n * keys by {@link currentVersion} instead, which heals the same way, and falls back to this for a source that\n * cannot report a version. It is spelled out rather than delegated because a lookup like this runs once per\n * operand of every `has`/`count`/`iterate`/`intersect`, almost always served from the cached snapshot with\n * no backend call at all. Routing it through the generic helper cost ~115 ns/op on that path (a second async\n * frame, a per-call closure, and an `await` on a plain number) for a race that fires only during a\n * concurrent sweep. One retry, then propagate \u2014 same contract, same eviction rule.\n */\n async currentGeneration(ref: SegmentRef): Promise<number | null> {\n const snap = this.liveSnapshot(ref);\n try {\n return (await snap.reader)?.generation ?? null;\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n this.dropStale(segmentKey(ref), snap);\n return (await this.resolvedReader(ref))?.generation ?? null; // a second miss propagates\n }\n }\n\n /**\n * Forget everything derived from `ref`: the resolved snapshot and the open reader behind it (and with the\n * reader, the DEK it unwrapped at open time), and what its pins' objects were found to be. The next read\n * resolves the segment again.\n *\n * Needed because the TTL refresh only ever answers \"has the pointer moved?\", and every *destructive* verb \u2014\n * an erasure that deletes the generation holding the bit, a `dropSegment`, a crypto-shred, a retirement \u2014\n * leaves the pointer's answer unchanged from this source's point of view while making the snapshot wrong.\n * Until this is called the source keeps serving that snapshot with no backend read at all, so nothing on the\n * storage side can close the window; with no registry, no clock or `cache.genTtlMs: 0`, nothing bounds it at all.\n */\n invalidate(ref: SegmentRef): void {\n this.invalidations += 1;\n const key = segmentKey(ref);\n this.snapshots.delete(key);\n // \u2026and every PINNED reader of the same segment, which is memoized under `<key>@<generation>`. Dropping\n // only the live entry left a pinned handle reading a crypto-shredded segment: the pin is exactly the\n // reader that does not re-resolve on its own, so it is the one that most needs to be told.\n const pinnedPrefix = `${key}@`;\n this.distrusted.deleteWhere((k) => k.startsWith(pinnedPrefix));\n this.snapshots.deleteWhere((k) => k.startsWith(pinnedPrefix));\n // \u2026and what its pins' objects were found to be, and any check still finding out: an object restored under its\n // key is its pin's again, and a restore is what an operator invalidates for.\n this.replacedPins.deleteWhere((k) => k.startsWith(pinnedPrefix));\n for (const k of this.checking.keys()) if (k.startsWith(pinnedPrefix)) this.checking.delete(k);\n }\n\n /**\n * `<generation>` for a registry-less source, or `<generation>:<row token>` with one. The token moves on every row\n * write, so an unrelated write (a `setRetention`) costs the segment's decoded chunks once \u2014 bounded, and on an\n * admin path. Exactness in the direction that matters: with a registry, two incarnations can never share a version\n * string. Without one they can: a name purged and loaded again out of band restarts at the same bare number, and\n * a live read that reopens it after an eviction reads the new object's chunks beside the old one's cached ones. A\n * store with no registry cannot write, so that takes a change made from outside it; a pin is kept apart from it by\n * the fingerprint it records.\n *\n * This is the call the engine makes **once per operand of every read**, before any chunk fetch, to key its\n * chunk cache, so it heals a swept generation exactly as {@link currentGeneration} does: unhealed, a cold read\n * racing a publish and a `keep: 0` sweep failed the whole operation with `NotFoundError`, where the same race\n * on a chunk fetch heals. Spelled out rather than delegated for the reason `currentGeneration` gives: it is\n * almost always served from the cached snapshot with no backend call at all. One retry, then propagate.\n */\n async currentVersion(ref: SegmentRef): Promise<string | null> {\n const snap = this.liveSnapshot(ref);\n let reader: CrbmReader | null;\n try {\n reader = await snap.reader;\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n this.dropStale(segmentKey(ref), snap);\n reader = await this.resolvedReader(ref); // a second miss propagates\n }\n return reader === null ? null : versionOf(reader.generation, reader.lineage);\n }\n\n /**\n * Resolve the segment **once** and report what a pin should hold: the generation, and the version that\n * identifies those exact bytes. `null` when the segment resolves to no generation \u2014 there is nothing to pin,\n * and a caller must treat that as \"this handle reads empty\", not \"pinning is unsupported here\".\n *\n * Heals a generation swept between the registry read and the open, as {@link currentVersion} does: a publish\n * and a `keep: 0` sweep can land in that gap, and nothing has been handed out yet, so the generation current\n * once the swept one is gone is exactly what a pin taken now should hold. One retry, then propagate.\n */\n async pinGeneration(\n ref: SegmentRef,\n ): Promise<({ generation: number } & Required<PinnedObject>) | null> {\n try {\n return await this.pinOnce(ref);\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n return this.pinOnce(ref); // a second miss propagates\n }\n }\n\n private async pinOnce(\n ref: SegmentRef,\n ): Promise<({ generation: number } & Required<PinnedObject>) | null> {\n // Resolved FRESH, not through the snapshot memo. \"The generation current right now\" is the whole promise\n // of a pin, and the memo is allowed to be up to `cache.genTtlMs` behind \u2014 or, on a store with no timed refresh,\n // arbitrarily far behind. Pinning through it made a pin on such a store\n // return whatever generation the store happened to hold, however old.\n const live = await this.resolveLive(ref);\n if (live === null) return null;\n const target = live.target;\n const version = versionOf(target.generation, target.lineage);\n // Opened now, not at the first pinned read, so the pin knows which object it holds: a name purged and loaded\n // again starts again at generation 0, and only the object itself tells the two apart. With a registry the\n // version already does, since the row a name is loaded into again is a new row with a new token, so every pin of\n // this version shares one open. It is installed before it resolves, so pins taken at the same moment wait on it\n // rather than each make their own: N pins of one generation make one tail read and one key unwrap between them,\n // while the store keeps the reader. Without a registry the version is the bare generation number, which two\n // incarnations share, so every pin opens the object afresh and reads its fingerprint.\n let reader: CrbmReader | null;\n let opened = true;\n if (target.lineage === undefined) {\n reader = await this.openLive(ref, live);\n this.install(\n this.pinnedKey(ref, version, reader.fingerprint),\n Snapshot.eager(Promise.resolve(reader)),\n );\n } else {\n const epoch = this.invalidations;\n const key = this.pinnedKey(ref, version);\n let entry = this.snapshots.get(key);\n opened = entry === undefined;\n entry ??= this.install(key, Snapshot.eager(this.openLive(ref, live)));\n reader = await entry.reader;\n // A pinned read's reopen of this version, already under way, found the row gone or destroyed: the segment\n // resolves no generation now, so this pin pins nothing, as one taken a moment later would.\n if (reader === null) return null;\n const replaced = (r: CrbmReader) =>\n this.replacedPins.get(this.heldKey(ref, version, r.fingerprint)) !== undefined;\n // A memoised reader is of the object that was under the key when it was opened. One the store has since found\n // replaced is not what is there now, so this pin opens the object afresh rather than pin a replaced one, and\n // pins taken at the same moment share that one open. The replaced pin's reader goes from the memo with it, so\n // that pin's index answers, its `count()` among them, end here: what is under its key is another object now.\n const known = !opened && replaced(reader);\n // The open this pin shared can be gone by now: a replaced pin's reopen removes the reader it opened, and the\n // reader cache can evict it. With no invalidation in between, and no replacement found, that reader is still of\n // the object under the key, and this pin keeps it memoised.\n if (!known && this.invalidations === epoch && this.snapshots.peek(key) === undefined) {\n this.install(key, Snapshot.eager(Promise.resolve(reader)));\n }\n if (known) {\n const current = this.snapshots.peek(key);\n let fresh =\n current !== undefined && current !== entry\n ? current\n : this.install(key, Snapshot.eager(this.openLive(ref, live)));\n reader = await fresh.reader;\n // A reopen of this version, already under way, found the row gone or destroyed.\n if (reader === null) return null;\n // What another pin put there can be the replaced reader too, put back before its verdict was known.\n if (fresh === current && replaced(reader)) {\n fresh = this.install(key, Snapshot.eager(this.openLive(ref, live)));\n reader = await fresh.reader;\n if (reader === null) return null;\n }\n opened = fresh !== current;\n }\n }\n // Only an open this call made has read what is under the key. The object there is that one now, whatever was\n // found before: a restore puts back what a replacement took, and a verdict kept against it would fail this pin.\n if (opened) {\n const held = this.heldKey(ref, version, reader.fingerprint);\n this.replacedPins.delete(held);\n this.checking.delete(held);\n }\n return { generation: target.generation, version, fingerprint: reader.fingerprint };\n }\n\n /**\n * Where a pinned reader of one object is memoised. With a registry that is its version, which names the object:\n * a name purged and loaded again gets a new row, and with it a new token. Without one the version is the bare\n * generation number, which two incarnations share, so the object's fingerprint is added to tell them apart.\n */\n private pinnedKey(ref: SegmentRef, version: string, fingerprint?: string): string {\n return this.registry === undefined && fingerprint !== undefined\n ? this.heldKey(ref, version, fingerprint)\n : `${segmentKey(ref)}@${version}`;\n }\n\n /**\n * One pin's hold on one segment: its version, and the fingerprint of the object it pinned. A check of that object\n * is shared under it, and what the check finds is kept under it, with a registry or without one: pins of one\n * version can hold different objects, since a pin built by hand can name any, and an object replaced from outside\n * the store leaves the row, and so the version, as it was.\n */\n private heldKey(ref: SegmentRef, version: string, fingerprint: string): string {\n return `${segmentKey(ref)}@${version}#${fingerprint}`;\n }\n\n /**\n * A reader for one **specific** generation, memoized in the same bounded LRU as the live snapshots under a\n * generation-qualified key.\n *\n * Sharing the LRU is the point, not an implementation detail. A pinned reader held in a private field is\n * outside the memory ceiling the library advertises \u2014 measured at 10.34 MiB per live pin, 32 pins holding\n * 111.7 MiB against an 8 MiB configured bound. A pinned *generation number* has no such problem: an object is\n * immutable, so eviction is harmless, and re-opening at the same number reopens the same bytes unless the name\n * was purged and loaded again, which the pin's fingerprint then says. It also removes a memoized-rejection bug by\n * construction \u2014 `install` forgets a promise that rejects,\n * where a hand-rolled `this.reader ??= open()` cached the rejection for the life of the handle and made a pin\n * the one read path with no resilience.\n *\n * **`status` is re-checked here**, on every open rather than once at pin time. A pin taken before a\n * crypto-shred must not keep unwrapping a DEK the shred destroyed; a destroyed segment resolves no generation\n * for anyone, and a pin's read of one fails. That holds from the pin's next open: a reader already open keeps\n * the key it unwrapped until it is evicted or invalidated.\n *\n * **And the object is checked**, when the caller says which one it pinned. A generation number is not an\n * identity (invariant 1): once a name is purged and loaded again, its new segment starts again at generation 0,\n * so a pin of the old one would open the new one's object as its own, and read it beside the chunks it had\n * already cached from the old one. The row's token cannot tell them apart, since it moves on every write, a\n * publish included; the object's fingerprint can, and a pin whose object has been replaced fails, as one whose\n * generation has been swept does. The memo is keyed so that two pins of one generation number in two\n * incarnations never share a reader, with a registry or without one ({@link pinnedKey}).\n */\n private async readerAt(\n ref: SegmentRef,\n generation: number,\n version?: string,\n fingerprint?: string,\n ): Promise<CrbmReader | null> {\n const at = version ?? String(generation);\n const key = this.pinnedKey(ref, at, fingerprint);\n const epoch = this.invalidations;\n let entry = this.snapshots.get(key);\n const opening = entry === undefined;\n if (entry === undefined) {\n // An object already found replaced is not opened again: the open would pay a row read and a key unwrap for\n // an object that is not the pin's.\n if (\n fingerprint !== undefined &&\n this.replacedPins.get(this.heldKey(ref, at, fingerprint)) !== undefined\n )\n throw notThePinned(ref, generation);\n entry = this.install(key, Snapshot.eager(this.openAt(ref, generation)));\n }\n let reader: CrbmReader | null;\n try {\n reader = await entry.reader;\n } catch (err) {\n // An open can fail on an object that is not the pinned one: written under a key this store lacks, or stored in\n // the clear where encryption is required. Its footer says which, with no key and no row. One that is gone needs\n // no footer read to say so: that is this NotFoundError.\n if (fingerprint === undefined || isTransientError(err) || isNotFoundError(err)) throw err;\n await this.throwIfReplaced(ref, generation, at, fingerprint);\n throw err;\n }\n // A pin's read fails where a bare read of a generation reads nothing: a pin describes one instant, and one that\n // goes empty partway through a call, because its row was dropped or destroyed, has torn it.\n if (reader === null) {\n if (version === undefined) return null;\n throw new NotFoundError(\n `segment \"${ref.segment}\" generation ${generation}, which this handle pinned, can no longer be read: its ` +\n 'registry row is gone or destroyed',\n );\n }\n if (fingerprint !== undefined && reader.fingerprint !== fingerprint) {\n // The verdict is kept only if nothing was invalidated while this read waited: an invalidation forgets the\n // segment's verdicts, as after a restore, and this one is from before it. An eviction from the reader cache is\n // no reason to forget it. What this read opened is the object now under the key, which is not the pin's: left\n // memoised under the pin's key, it would hold a place in the reader cache, and the key it unwrapped, for reads\n // that can only fail, so it goes whatever was invalidated.\n if (this.invalidations === epoch)\n this.replacedPins.set(this.heldKey(ref, at, fingerprint), true);\n if (opening && this.snapshots.peek(key) === entry) this.snapshots.delete(key);\n throw notThePinned(ref, generation);\n }\n return reader;\n }\n\n private async openAt(ref: SegmentRef, generation: number): Promise<CrbmReader | null> {\n if (this.registry === undefined) return this.openForTarget(ref, { generation });\n // One read of the row, for its status, its incarnation and its key wrappings together.\n const record = await this.registry.get(ref);\n // Re-checked at open, not captured at pin: a shred between the two must be observed. A row that has gone\n // takes its key material with it, so there is nothing left to read under any generation.\n if (record === null || record.status === 'destroyed') return null;\n return this.openForTarget(ref, {\n generation,\n lineage: record.token,\n wrappedDeks: record.wrappedDeks,\n });\n }\n\n /**\n * Read one chunk of a specific generation \u2014 the pinned read path. `held`, when given, is what a pin holds of the\n * object, and the read fails if the generation is now another object.\n */\n async getChunkAt(\n ref: ChunkRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<Uint8Array | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n if (reader === null) return null;\n const fingerprint = held?.fingerprint;\n if (\n held !== undefined &&\n fingerprint !== undefined &&\n this.replacedPins.get(this.heldKey(ref, held.version, fingerprint)) !== undefined\n )\n throw notThePinned(ref, generation);\n try {\n return await reader.getChunk(ref.chunkKey);\n } catch (err) {\n // A pin's memoised reader outlives its object when the name is purged and loaded again: its index then points\n // into bytes that are not its own, so the chunk fails its checksum or, when the new object is smaller, asks\n // for a range past its end, which every driver refuses with a ValidationError. The object's footer says which\n // it was: replaced, which is a pin's NotFoundError, or not, and the error stands. Only these two errors pay\n // for the footer read. Told apart by their brands, not `instanceof`: the range error is a driver package's,\n // which may carry a copy of core of its own.\n if (held === undefined || fingerprint === undefined) throw err;\n if (!(isIntegrityError(err) || isValidationError(err))) throw err;\n await this.throwIfReplaced(ref, generation, held.version, fingerprint);\n throw err;\n }\n }\n\n /**\n * Throws the pin's NotFoundError if the object under `generation`'s key is no longer the one the pin holds. Its\n * footer says, read with no key and no row ({@link CrbmReader.sameObject}), so an object written under a key this\n * store lacks answers the same way, and so does one gone from its key. A replacement is kept\n * ({@link replacedPins}), and a check under way is shared by the reads that ask at once; neither \"gone\" nor \"the\n * same\" is kept, since either can change. A transient fault is rethrown, for the store's retries to see for what it\n * is. A footer that cannot be read otherwise says nothing about which object is there, so this returns, and the\n * caller's own error stands.\n */\n private async throwIfReplaced(\n ref: SegmentRef,\n generation: number,\n version: string,\n fingerprint: string,\n ): Promise<void> {\n const held = this.heldKey(ref, version, fingerprint);\n if (this.replacedPins.get(held) !== undefined) throw notThePinned(ref, generation);\n let check = this.checking.get(held);\n if (check === undefined) {\n const at: GenKey = { namespace: ref.namespace, segment: ref.segment, generation };\n // Whether this check is still the one asked for as it ends, which it then stops being. One forgotten before it\n // ends (see checking) keeps nothing it finds, and leaves in place the one asked for since.\n const ended = (): boolean =>\n this.checking.get(held) === started && this.checking.delete(held);\n // True when the object under the key is not the pin's: another one, or none.\n const started: Promise<boolean> = CrbmReader.sameObject(\n storageBlobReader(this.driver, at),\n fingerprint,\n ).then(\n (same) => {\n if (ended() && !same) this.replacedPins.set(held, true);\n return !same;\n },\n (err: unknown) => {\n ended();\n if (isNotFoundError(err)) return true;\n throw err;\n },\n );\n this.checking.set(held, started);\n check = started;\n }\n let replaced: boolean;\n try {\n replaced = await check;\n } catch (err) {\n if (isTransientError(err)) throw err;\n return;\n }\n if (replaced) throw notThePinned(ref, generation);\n }\n\n /** Chunk keys of a specific generation \u2014 the pinned shape read. `held` as for {@link getChunkAt}. */\n async listChunkKeysAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<number[]> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? [] : reader.chunkKeys();\n }\n\n /**\n * Per-chunk cardinalities of a specific generation \u2014 powers a pinned `count()` with no payload reads. `held` as\n * for {@link getChunkAt}.\n */\n async cardinalitiesAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<ReadonlyMap<number, number> | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? null : reader.cardinalities();\n }\n\n /** Grounded size of a specific generation. `held` as for {@link getChunkAt}. */\n async sizeOfAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<SegmentSize | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? null : { sizeBytes: reader.sizeBytes };\n }\n\n /**\n * Whether the segment is a registered name at all. With a registry that is exactly \"a row exists\" \u2014 including\n * a row with no generation yet (minted by `setRetention` before the first load) and a `destroyed` tombstone,\n * both of which are names somebody deliberately created. Without a registry there is no registration to\n * consult, so the honest answer is whether the bucket holds any generation of it.\n *\n * Deliberately NOT served from the snapshot memo: the memo resolves to `null` for both states this exists to\n * tell apart.\n */\n async exists(ref: SegmentRef): Promise<boolean> {\n if (this.registry !== undefined) return (await this.registry.get(ref)) !== null;\n for await (const key of this.driver.list(ref)) {\n void key;\n return true;\n }\n return false;\n }\n\n /**\n * Evict a snapshot we just failed to read from \u2014 matched by the snapshot itself, so a concurrent call's\n * fresher snapshot is never clobbered. The identity guard is the whole point: `install` replaces the entry\n * wholesale, so comparing anything else would drop a good snapshot on the floor.\n */\n private dropStale(key: string, snap: Snapshot): void {\n if (this.snapshots.get(key) === snap) this.snapshots.delete(key);\n }\n\n /** Open a {@link CrbmReader} for an already-resolved generation target (decrypting if the segment is encrypted). */\n private async openForTarget(ref: SegmentRef, target: Target): Promise<CrbmReader> {\n const crypto = await this.cryptoForRead(ref, target.generation, target.wrappedDeks);\n return this.openGeneration(ref, target, crypto);\n }\n\n private openGeneration(\n ref: SegmentRef,\n target: Target,\n crypto: CrbmCrypto | undefined,\n ): Promise<CrbmReader> {\n const genKey: GenKey = {\n namespace: ref.namespace,\n segment: ref.segment,\n generation: target.generation,\n };\n return openChecked(this.driver, genKey, {\n ...this.readerOptions,\n crypto,\n lineage: target.lineage,\n });\n }\n\n /**\n * Open the generation a resolution found, and hold the row's summary against the object while it is open anyway:\n * the same count and metadata, or the summary is not believed again in this process ({@link distrusted}). This\n * costs no request; a mismatch is not an error, since the object is the truth and this read has it.\n */\n private async openLive(ref: SegmentRef, live: Live): Promise<CrbmReader> {\n const reader = await this.openGeneration(ref, live.target, await live.crypto());\n const summary = await live.summary();\n if (summary !== undefined && !summaryAgrees(summary, describe(reader))) {\n this.distrusted.set(this.distrustKey(ref, live.target.generation, live.target.lineage), true);\n }\n return reader;\n }\n\n private distrustKey(ref: SegmentRef, generation: number, lineage: unknown): string {\n return `${segmentKey(ref)}@${generation}:${String(lineage)}`;\n }\n\n /** What the row says of the generation, unless it is a summary this process has found to disagree with its object. */\n private async trustedSummary(\n ref: SegmentRef,\n live: Live,\n ): Promise<GenerationDescription | undefined> {\n if (\n this.distrusted.peek(this.distrustKey(ref, live.target.generation, live.target.lineage)) !==\n undefined\n ) {\n return undefined;\n }\n return live.summary();\n }\n\n /**\n * The current generation + its DEK wrappings: the registry's authoritative record (one strong read), or a\n * `list` scan for the max generation when there's no registry (a registry-less source can only read cleartext\n * \u2014 there's nowhere a wrapped DEK could live). The row's summary rides along, to be used or not by whoever\n * asks.\n */\n private async resolveLive(ref: SegmentRef, prior?: Live | null): Promise<Live | null> {\n if (this.registry !== undefined) {\n const record = await this.registry.get(ref);\n if (record === null) return null;\n // A crypto-shredded segment reads as empty \u2014 its DEK is gone, so its Storage bytes are unrecoverable.\n if (record.status === 'destroyed') return null;\n // A row with no Storage generation yet (minted by `setSegmentRetention` ahead of the first load, so admin tools\n // can see the segment) resolves exactly like a segment with NO row: every read answers empty. Returning\n // `null` here rather than a generation is the whole reason such a row is safe to create \u2014 the alternative,\n // pointing at a generation that does not exist, is the `missing-storage-generation` state.\n if (record.currentGen === null) return null;\n const generation = record.currentGen;\n // A refresh that finds the same wrapped keys keeps the key it already unwrapped, so a count polling an encrypted\n // segment asks the keystore once, not once each `genTtlMs`. Different keys (a shred, a re-created name) unwrap afresh.\n const keys = record.wrappedDeks;\n const unwrap =\n keys === undefined || keys.length === 0 || this.keystore === undefined\n ? undefined\n : prior?.unwrap !== undefined && sameKeys(prior.target.wrappedDeks, keys)\n ? prior.unwrap\n : once(() => (this.keystore as IKeystore).openDek(keys));\n const crypto = once(() => this.cryptoForRead(ref, generation, keys, unwrap));\n return {\n target: { generation, lineage: record.token, wrappedDeks: record.wrappedDeks },\n crypto,\n unwrap,\n // The same checks as a read of the object makes (`requireEncryption`, a keystore for an encrypted row)\n // come first, then the row's summary, used only if it names this generation in the shape the keys call for.\n summary: once(async () => usableSummary(ref, record, (await crypto())?.aead)),\n bytes:\n SNAPSHOT_BASE_BYTES +\n (record.summary === undefined ? 0 : JSON.stringify(record.summary).length),\n };\n }\n let maxGen = -1;\n for await (const key of this.driver.list(ref)) {\n if (key.generation > maxGen) maxGen = key.generation;\n }\n if (maxGen < 0) return null;\n return {\n target: { generation: maxGen },\n crypto: () => Promise.resolve(undefined),\n unwrap: undefined,\n summary: () => Promise.resolve(undefined),\n bytes: SNAPSHOT_BASE_BYTES,\n };\n }\n\n /** Build the per-generation decryption context for an encrypted segment (undefined for cleartext). */\n private async cryptoForRead(\n ref: SegmentRef,\n generation: number,\n wrappedDeks: readonly WrappedDek[] | undefined,\n unwrap?: () => Promise<Aead>,\n ): Promise<CrbmCrypto | undefined> {\n if (wrappedDeks === undefined || wrappedDeks.length === 0) {\n if (this.requireEncryption) {\n throw new KeyUnavailableError(\n `requireEncryption: segment \"${ref.segment}\" is cleartext but encryption is required`,\n );\n }\n return undefined; // cleartext segment\n }\n const keystore = this.keystore;\n if (keystore === undefined) {\n throw new KeyUnavailableError(\n `segment \"${ref.segment}\" is encrypted but this CrbmStorageChunkSource has no keystore`,\n );\n }\n const aead = await (unwrap ?? (() => keystore.openDek(wrappedDeks)))();\n return { aead, aadFor: (scope) => aadFor(ref, generation, scope) };\n }\n\n async getChunk(ref: ChunkRef): Promise<Uint8Array | null> {\n validateChunkRef(ref);\n return this.withFreshSnapshot(ref, (reader) => reader.getChunk(ref.chunkKey), null);\n }\n\n async listChunkKeys(ref: SegmentRef): Promise<number[]> {\n validateUserRef(ref);\n return this.withFreshSnapshot(ref, (reader) => reader.chunkKeys(), []);\n }\n\n async sizeOf(ref: SegmentRef): Promise<SegmentSize | null> {\n validateUserRef(ref);\n return this.withFreshSnapshot<SegmentSize | null>(\n ref,\n (reader) => ({ sizeBytes: reader.sizeBytes }),\n null,\n );\n }\n\n async cardinalities(ref: SegmentRef): Promise<ReadonlyMap<number, number> | null> {\n validateUserRef(ref);\n return this.withFreshSnapshot<ReadonlyMap<number, number> | null>(\n ref,\n (reader) => reader.cardinalities(),\n null,\n );\n }\n\n /**\n * The current generation's number, id count and metadata. The answer comes from the row the snapshot resolved when\n * it can: the row's summary of the generation it names, on an active row, in the shape the row's keys call for\n * (a sealed one is opened with the segment's key), and one this process has not found to disagree with its\n * object. That is one registry read when the snapshot is cold, none when it is warm, and no read of the object.\n * With no summary it can use (an older row, one for another generation, one that does not open), it opens the\n * generation and answers from the object. A row that names an object that is gone still answers, while a read\n * of the object throws: the number is true of the generation the row names.\n *\n * What the row says is not confirmed here: a summary edited to agree with itself by whoever can write the row is\n * believed until the next time the object is opened, which holds it against the summary.\n */\n async summary(ref: SegmentRef): Promise<GenerationSummary | null> {\n validateUserRef(ref);\n for (let attempt = 0; attempt < 2; attempt++) {\n const snap = this.liveSnapshot(ref);\n try {\n const live = await (snap.target as Promise<Live | null>);\n if (live === null) return null;\n const row = await this.trustedSummary(ref, live);\n if (row !== undefined) return summaryOfGeneration(live.target.generation, row);\n const reader = await snap.reader;\n return reader === null ? null : summaryOfGeneration(reader.generation, describe(reader));\n } catch (err) {\n // The same healing a read of the object has: a generation swept from under the snapshot is resolved again.\n if (attempt === 1) throw err;\n // A fault that is neither a miss nor corruption (a keystore's, say) is not asked about again: reading the\n // snapshot's reader here would call the keystore a second time.\n const checks = isNotFoundError(err) || isIntegrityError(err) || isValidationError(err);\n if (\n !checks ||\n (!isNotFoundError(err) && !(await this.replacedUnder(ref, snap.reader, err)))\n ) {\n throw err;\n }\n this.dropStale(segmentKey(ref), snap);\n }\n }\n return null;\n }\n\n /** {@link summary} of a specific generation, from the object a pin holds. `held` as for {@link getChunkAt}. */\n async summaryAt(\n ref: SegmentRef,\n generation: number,\n held?: PinnedObject,\n ): Promise<GenerationSummary | null> {\n validateUserRef(ref);\n const reader = await this.readerAt(ref, generation, held?.version, held?.fingerprint);\n return reader === null ? null : summaryOfGeneration(reader.generation, describe(reader));\n }\n\n /**\n * Run `read` against the pinned snapshot, healing the torn-read window generation GC can open: if the\n * generation we resolved was superseded *and* swept (the grace window elapsed), the Storage driver throws\n * {@link NotFoundError}. Surfacing that as a query failure would turn a benign, recoverable race into a\n * caller-visible error, so instead we drop the stale snapshot,\n * re-resolve `currentGen`, and retry once \u2014 the read then serves the newer (committed, immutable) generation,\n * a monotonic move forward \u2014 within one incarnation of the row. A name that was retired and re-created is a\n * different segment and can resolve to a LOWER generation, which is why the snapshot carries the row's token\n * as its lineage rather than trusting the generation number (invariant 1).\n *\n * **Resolution and open are inside the retry, not before it.** Resolving `currentGen` and opening that\n * generation's object are two backend round trips with a gap between them, so the object can be swept after\n * the registry named it and before its tail is read \u2014 and that miss surfaces from awaiting the snapshot, not\n * from `read`. Awaiting outside the retry left the *first* attempt unhealed on the one path GC actually\n * races: `keep: 0`, which every id erasure passes, sweeps microseconds after the publish.\n *\n * A *second* NotFound is pathological (GC outrunning resolution) and propagates rather than fabricating an\n * absent answer \u2014 never return a wrong result. `ifGone` is returned when the segment has no committed\n * generation to serve: storage is empty, or the row was dropped or crypto-shredded, in which case reading empty\n * is the documented outcome rather than a failure.\n *\n * **Bounded to exactly two resolve-and-open round trips.** That is a cost contract, not a detail: the retry\n * now re-reads the *registry*, so an unbounded one would hammer the shared, throttle-prone resource in a\n * tight loop on a segment whose pointer names an object that is permanently absent \u2014 and an N-way\n * `intersect` would do it N times. Gated in `tests/core/storage-source-heal-open.test.ts` by counting calls.\n */\n private async withFreshSnapshot<T>(\n ref: SegmentRef,\n read: (reader: CrbmReader) => T | Promise<T>,\n ifGone: T,\n ): Promise<T> {\n for (let attempt = 0; attempt < 2; attempt++) {\n const snap = this.liveSnapshot(ref);\n const pending = snap.reader;\n try {\n const reader = await pending;\n if (reader === null) return ifGone;\n return await read(reader);\n } catch (err) {\n // Two misses are recoverable here, and only on the first try: a generation swept from under the snapshot,\n // and one whose object was replaced under the same number (see `replacedUnder`). Anything else\n // (corruption, a real second miss) propagates.\n if (attempt === 1) throw err;\n if (!isNotFoundError(err) && !(await this.replacedUnder(ref, pending, err))) throw err;\n this.dropStale(segmentKey(ref), snap); // lazily: the happy path never needs the key\n }\n }\n return ifGone;\n }\n\n /**\n * Whether `err`, raised by a read through the snapshot's reader, came from an object that is no longer the one the\n * reader opened. A generation number can be taken again once its object is deleted (an erasure removes the\n * generations above a rolled-back pointer that held the id, a collection pass can stop part-way, and a load then\n * numbers `currentGen + 1` again), so a reader that still holds the old object's index reads the new object's\n * bytes at the old offsets: a chunk checksum fails, or a range runs past the end. The object's footer says which it\n * was, as it does for a pin ({@link CrbmReader.sameObject}): replaced, or gone, re-resolves; the same object means\n * the error is real, and it stands. Only these two errors pay for the footer read, never a read that succeeded. A\n * transient fault in the check is rethrown; a footer that cannot be read says nothing, and the error stands.\n */\n private async replacedUnder(\n ref: SegmentRef,\n pending: Promise<CrbmReader | null>,\n err: unknown,\n ): Promise<boolean> {\n if (!(isIntegrityError(err) || isValidationError(err))) return false;\n let reader: CrbmReader | null;\n try {\n reader = await pending;\n } catch {\n return false;\n }\n if (reader === null) return false;\n const at: GenKey = {\n namespace: ref.namespace,\n segment: ref.segment,\n generation: reader.generation,\n };\n try {\n return !(await CrbmReader.sameObject(storageBlobReader(this.driver, at), reader.fingerprint));\n } catch (checkErr) {\n if (isNotFoundError(checkErr)) return true;\n if (isTransientError(checkErr)) throw checkErr;\n return false;\n }\n }\n}\n\n/**\n * Write one immutable generation from in-memory bitmaps. Chunks are\n * sorted ascending and empty bitmaps skipped (empty chunks are never stored). Returns the driver's\n * `{ size, sha256 }` for the written object. Pass `options.crypto` to AES-256-GCM-encrypt the generation\n * (built from the segment's DEK, with associated data bound to `(segment, generation)`).\n */\nexport async function writeCrbmGeneration(\n driver: IStorageDriver,\n key: GenKey,\n chunks: Iterable<{ chunkKey: number; bitmap: CodecBitmap }>,\n options: { crypto?: CrbmCrypto; clock?: Yielder; metadata?: GenerationMetadata } = {},\n): Promise<{ size: number; sha256: string; fingerprint: string }> {\n const sorted = [...chunks].sort((a, b) => a.chunkKey - b.chunkKey);\n const { size, sha256, fingerprint } = await writeEncodedChunks(\n driver,\n key,\n encodeEach(sorted),\n options,\n );\n return { size, sha256, fingerprint };\n}\n\n/** Each non-empty bitmap as the chunk it stores, in the order given. */\nfunction* encodeEach(\n chunks: Iterable<{ chunkKey: number; bitmap: CodecBitmap }>,\n): Generator<EncodedChunk> {\n for (const { chunkKey, bitmap } of chunks) {\n if (bitmap.isEmpty) continue;\n // Run-encode before serializing. Storage generations are immutable and read many times, so the one-off cost\n // here buys every later read a smaller fetch \u2014 see CodecBitmap.optimize for the measured factors.\n bitmap.optimize?.();\n yield { chunkKey, payload: bitmap.serialize(), cardinality: bitmap.size };\n }\n}\n\n/**\n * Write one immutable generation from chunks already encoded, which must ascend by key (the writer refuses one\n * that does not). Returns the driver's `{ size, sha256 }` and a tally of what was written.\n */\nasync function writeEncodedChunks(\n driver: IStorageDriver,\n key: GenKey,\n chunks: Iterable<EncodedChunk>,\n options: { crypto?: CrbmCrypto; clock?: Yielder; metadata?: GenerationMetadata },\n): Promise<{\n size: number;\n sha256: string;\n chunkCount: number;\n cardinality: number;\n fingerprint: string;\n}> {\n // The single longest blocking stretch in a bulk load: serialize + CRC32C + frame, once per chunk, ~62,000\n // times. `await writer.addChunk(...)` looks like it yields and does not \u2014 the sink buffers in memory, so the\n // promise is already resolved and awaiting it is a microtask. See {@link yieldEvery}.\n const tick = yieldEvery(options.clock);\n let chunkCount = 0;\n let cardinality = 0;\n let identity: { readonly size: number; readonly footerCrc: number } | undefined;\n const { size, sha256 } = await driver.putImmutable(key, async (sink) => {\n const writer = new CrbmWriter(sink, {\n generation: key.generation,\n crypto: options.crypto,\n metadata: options.metadata,\n });\n for (const chunk of chunks) {\n await writer.addChunk(chunk.chunkKey, chunk.payload, chunk.cardinality);\n chunkCount++;\n cardinality += chunk.cardinality;\n const pause = tick();\n if (pause !== null) await pause;\n }\n await writer.finish();\n identity = writer.identity;\n });\n if (identity === undefined) {\n throw new IntegrityError(\n `the driver committed ${key.segment}.${key.generation} without writing it`,\n );\n }\n // The fingerprint of what this call wrote, which tells it from an object stored later under the same key.\n return {\n size,\n sha256,\n chunkCount,\n cardinality,\n fingerprint: fingerprintFor(identity.size, identity.footerCrc),\n };\n}\n\n/**\n * Whether the object under `key` is provably the one `fingerprint` names ({@link CrbmReader.fingerprint}), from one\n * read of its footer. False when it is another object, when it is gone, and when the read fails in any way: a caller\n * that deletes on `true` deletes nothing it cannot prove is its own.\n */\nexport async function holdsObject(\n storage: IStorageDriver,\n key: GenKey,\n fingerprint: string,\n): Promise<boolean> {\n try {\n return await CrbmReader.sameObject(storageBlobReader(storage, key), fingerprint);\n } catch {\n return false;\n }\n}\n\n/** Whether the object under `key` says it is encrypted, from one read of its footer, with no key. */\nexport function objectIsEncrypted(storage: IStorageDriver, key: GenKey): Promise<boolean> {\n return footerSaysEncrypted(storageBlobReader(storage, key));\n}\n\n/**\n * `blob`, with its first tail read remembered: a later tail read of no more bytes is answered from it, so a caller that\n * asks the footer a question and then opens the object makes one request, not two.\n */\nfunction rememberingTail(blob: BlobReader): BlobReader {\n let first: { bytes: Uint8Array; size: number } | undefined;\n return {\n getRange: (offset, length) => blob.getRange(offset, length),\n async getTail(maxBytes) {\n // Answered from the first read when it holds the bytes asked for, or holds the whole object, which is all there is.\n if (\n first !== undefined &&\n (maxBytes <= first.bytes.length || first.bytes.length === first.size)\n ) {\n const take = Math.min(maxBytes, first.bytes.length);\n return { bytes: first.bytes.subarray(first.bytes.length - take), size: first.size };\n }\n const read = await blob.getTail(maxBytes);\n first ??= read;\n return read;\n },\n };\n}\n\n/**\n * What one tail read of the object under `key` tells a rollback about it: whether the footer says it is encrypted, and,\n * when the object and the key agree (an encrypted object opened with `crypto`, a cleartext one with none), the reader\n * the open gives, which holds its count and metadata. No reader when they disagree, which the caller refuses as a\n * target the row cannot use, and none for an encrypted object when the caller has no key. A footer that fails its own\n * checks, an object another generation's, and an index or metadata that do not open are an {@link IntegrityError}.\n */\nexport async function openRollbackTarget(\n storage: IStorageDriver,\n key: GenKey,\n crypto: CrbmCrypto | undefined,\n): Promise<{ encrypted: boolean; reader: CrbmReader | undefined }> {\n const blob = rememberingTail(storageBlobReader(storage, key));\n await blob.getTail(DEFAULT_TAIL_BYTES);\n const encrypted = await footerSaysEncrypted(blob);\n if (encrypted !== (crypto !== undefined)) return { encrypted, reader: undefined };\n const reader = await CrbmReader.open(blob, { crypto });\n if (reader.generation !== key.generation) {\n throw new IntegrityError(\n `segment \"${key.segment}\" generation ${key.generation}: its footer says generation ${reader.generation}`,\n );\n }\n return { encrypted, reader };\n}\n\n/** What {@link writeCrbmGenerationStream} wrote: the driver's `{ size, sha256 }` + a tally of the generation. */\nexport interface StreamWriteResult {\n readonly size: number;\n readonly sha256: string;\n /** Non-empty chunk keys written, in ascending order (for the post-write verify, since the stream is consumed). */\n readonly chunkKeys: number[];\n /** Total ids written. */\n readonly cardinality: number;\n /** The fingerprint of what this call wrote ({@link CrbmReader.fingerprint}), which tells it from another object under its key. */\n readonly fingerprint: string;\n}\n\n/**\n * Streaming variant of {@link writeCrbmGeneration} for a **constant-memory** rewrite (the erasure rewrite, a\n * sorted load): consumes an **already-ascending** async stream of `{ chunkKey, bitmap }`, feeding each to the\n * codec and freeing it, instead of materializing the whole generation. Paired with a streaming storage sink (S3\n * multipart / LocalFs temp file), the writer itself holds ~one chunk + one part. Returns a tally so the caller can verify\n * the re-opened object without re-iterating the (now-consumed) stream. Input **must** be ascending by `chunkKey`\n * \u2014 the codec rejects an out-of-order chunk; empty bitmaps are skipped.\n */\nexport async function writeCrbmGenerationStream(\n driver: IStorageDriver,\n key: GenKey,\n chunks: AsyncIterable<{ chunkKey: number; bitmap: CodecBitmap }>,\n options: { crypto?: CrbmCrypto; clock?: Yielder; metadata?: GenerationMetadata } = {},\n): Promise<StreamWriteResult> {\n const chunkKeys: number[] = [];\n let cardinality = 0;\n // A rewrite runs this over a whole segment. The `for await` is not itself a yield \u2014 a stream backed by\n // already-resident chunks resolves on a microtask \u2014 so it needs the same periodic macrotask as the\n // non-streaming writer above.\n const tick = yieldEvery(options.clock);\n let identity: { readonly size: number; readonly footerCrc: number } | undefined;\n const { size, sha256 } = await driver.putImmutable(key, async (sink) => {\n const writer = new CrbmWriter(sink, {\n generation: key.generation,\n crypto: options.crypto,\n metadata: options.metadata,\n });\n for await (const { chunkKey, bitmap } of chunks) {\n if (bitmap.isEmpty) continue;\n bitmap.optimize?.(); // same storage-write rationale as the non-streaming writer above\n await writer.addChunk(chunkKey, bitmap.serialize(), bitmap.size);\n chunkKeys.push(chunkKey);\n cardinality += bitmap.size;\n const pause = tick();\n if (pause !== null) await pause;\n }\n await writer.finish();\n identity = writer.identity;\n });\n if (identity === undefined) {\n throw new IntegrityError(\n `the driver committed ${key.segment}.${key.generation} without writing it`,\n );\n }\n return {\n size,\n sha256,\n chunkKeys,\n cardinality,\n fingerprint: fingerprintFor(identity.size, identity.footerCrc),\n };\n}\n\n/**\n * Refuses, with {@link KeyUnavailableError}, to point a row that carries key material at a generation written in\n * cleartext. The writer decided its key from the row it read before the write; another writer that created the\n * segment encrypted meanwhile leaves this one holding a cleartext object that no reader of the segment can open.\n */\nfunction refuseCleartextOntoKey(\n key: GenKey,\n record: RegistryRecord,\n cleartext: boolean | undefined,\n): void {\n if (cleartext !== true || record.wrappedDeks === undefined || record.wrappedDeks.length === 0) {\n return;\n }\n throw new KeyUnavailableError(\n `segment \"${key.segment}\" is encrypted, and generation ${key.generation} was written in cleartext: another ` +\n `writer created the segment with a key while this one was writing. Nothing was published. Re-run the ` +\n `write with the keystore that holds the segment's key.`,\n );\n}\n\n/**\n * How many times a publish sends a fresh compare-and-swap after one that ended without an answer and left the row as it\n * was: four sends of the row write in all.\n */\nconst UNANSWERED_RESENDS = 3;\n/** The wait before the first of those, in ms, as an upper bound: each next one doubles it, and a full jitter spreads it. */\nconst UNANSWERED_RESEND_BASE_MS = 500;\n\n/**\n * Point a segment's registry `currentGen` at `key.generation` \u2014 the publish step that makes a freshly-written\n * generation the authoritative latest (so registry-aware readers see it). **Forward-only and idempotent:**\n * if the registry has no row it creates one; if it's already at/ahead of `key.generation` it's a no-op (an\n * out-of-order/duplicate publish never regresses the pointer); otherwise it advances via compare-and-swap,\n * retrying a few times under contention. Separated from the Storage write so callers can publish atomically\n * after the immutable object is durable (write-then-publish).\n *\n * **Throws** rather than returning, in two cases, because each would otherwise create a state no reader can\n * recover from: a `destroyed` (crypto-shredded) row, where advancing the pointer would name an object encrypted\n * under a shredded key; and an advance carrying **new** `wrappedDeks`, which would either strand that key or make\n * the row advertise encryption over cleartext generations. Contention that never clears throws\n * {@link WriteConflictError} after the retries.\n *\n * **`expectFrom` turns forward-only into read-modify-write.** Forward-only is the right rule for a writer whose\n * content does not depend on what was current \u2014 a load computes its ids upstream, so publishing over a newer\n * generation loses nothing the loader knew about. It is the WRONG rule for a writer that *derived* its content\n * from a particular generation: the erasure rewrite streams generation `from` and clears one bit, so publishing\n * it over a newer generation silently discards whatever that generation added. Passing `expectFrom` makes the\n * publish land only while the pointer is still exactly there, and return `false` otherwise, so the caller can\n * report `superseded` and re-derive. The compare-and-swap itself carries the token read in the same iteration,\n * so the check and the write see the same row.\n *\n * **`expectToken` fences the LINEAGE, and a derived writer needs both.** A generation number identifies a\n * generation only within one incarnation of a name: the numbering restarts at `0` once the row is purged and\n * the bucket empty, so a name that is retired and re-created has a *different* segment wearing the *same*\n * `currentGen`. `expectFrom` alone matched it \u2014 and an erasure rewrite then published one incarnation's content\n * over another's, deleted the live objects with its `keep: 0` collection, and returned `erased: true`. The row's\n * OCC token is the identity that survives this: a shipped registry's later `create` gets a token never issued before\n * under that name, but for a collision of probability 2^-128 per pair of incarnations, so no token is reused across them.\n *\n * Pass the token read alongside `expectFrom` and the publish lands only on the same row it was derived from.\n * The check is deliberately **conservative**: the token also changes on writes that are not supersessions at\n * all (a `setRetention`, a due-index reindex), so such a write makes a derived publish report `superseded` and\n * the caller re-derive. That costs a re-run on a rare, unrelated write; the alternative costs a segment.\n *\n * **The driver sends a registry write once, and an unanswered one is reconciled by its effect.** A `create` or\n * `compareAndSwap` that fails with {@link TransientError} (a throttle, a lost response, a timeout) may or may not\n * have landed, and may still be in flight. The publish reads the row once more and decides from what it holds:\n *\n * - the pointer names `key.generation`, on the incarnation the write was made against (the row's `createdAt`), and\n * `holdsOwnObject` proves the object under it the caller's: the write landed, and the publish returns `true`. Only\n * the writer of a write-once object publishes its number, and a number is never taken while an object holds it;\n * - the row is still the one the write was made against (the same token, or still no row): the write may land yet. The\n * publish then waits, on the injected clock, and sends a **new** write from the row it just read: a request of its\n * own, not a replay of the driver's, carrying the same version, so under the registry's fence at most one of the two\n * lands and the other meets a conflict, which the next read settles. At most {@link UNANSWERED_RESENDS} follow, after\n * a wait under 500 ms, then 1 s, then 2 s (full jitter when an `rng` is given); each is settled the same way. Still\n * unanswered, or with no clock to wait on, this throws the registry's own `TransientError` and writes nothing more.\n * A caller must not delete its object on it;\n * - anything else: the row has moved past the state the write was conditioned on, so that write can never land, and\n * the publish goes on as after a lost race, from the row it just read. A pointer at `key.generation` that is not over\n * the caller's own object, or is on another incarnation, is a lost race too and answers `false`, never an already\n * current re-publish.\n *\n * A failed read throws `TransientError` too. The same check of the pointer runs after a {@link WriteConflictError},\n * so a write that landed and then met itself (a registry whose client re-sent it) is not taken for a lost race.\n *\n * **A `summary` goes in the same write that moves the pointer**, so a reader that sees this generation as current\n * sees the count and metadata that describe it, and every attempt sends the same one. It must be the shape the row's\n * keys call for: sealed for an object written with a key, clear for one written without.\n */\nexport async function publishGeneration(\n registry: IRegistryDriver,\n key: GenKey,\n options: {\n wrappedDeks?: readonly WrappedDek[];\n expectFrom?: number;\n expectToken?: Token;\n /**\n * Publish only while the segment still has **no registry row**.\n *\n * The fence for a caller whose decision rests on the row being ABSENT. `expectFrom` and `expectToken`\n * cannot express it: both compare against a value read from a row, so when there was no row there is\n * nothing to compare and both are simply omitted \u2014 leaving the publish a bare forward-only advance that\n * happily lands over whatever appeared in the meantime.\n *\n * That gap was a silent wipe, not a theoretical one. A guarded load into a segment that did not exist yet\n * read \"no row\", so its empty/`minRetained` bounds had nothing to judge and passed vacuously; a\n * concurrent writer then created the row and published a thousand ids; and the guarded load published an\n * EMPTY generation over them, reporting `published: true` with no reason. Reproduced through both\n * `loadSegment` and the `*Into` verbs, which hit it far more often because materialising into a\n * destination that does not exist yet is the ordinary first run of a pipeline.\n */\n expectAbsent?: boolean;\n /**\n * The generation's summary (see {@link RegistrySummary}), written by the write that moves the pointer to it. It\n * names `key.generation`. Absent, the row is left with none for this generation.\n */\n summary?: RegistrySummary;\n /**\n * The segment's row as the caller already read it (`null`: it found none). The first attempt acts on it\n * instead of reading the row again, which is sound because that attempt writes only under the row's own\n * fence: its compare-and-swap carries this row's token, and with no row it creates create-only, so a row\n * that changed or appeared since makes the write lose and the next attempt reads the row afresh. Only the\n * idempotent \"already current\" answer writes nothing, so a reused row never gives it.\n */\n row?: RegistryRecord | null;\n /**\n * The object was written without encryption. Its publish is refused, with {@link KeyUnavailableError}, onto a row\n * that carries key material: a reader of that segment opens every generation with its key, and a later\n * `destroySegment` would attest that shredding it made a readable object unreadable. A caller decides its key\n * from the row it read before the write, so this closes the window in which another writer creates the\n * segment encrypted meanwhile.\n */\n cleartext?: boolean;\n /**\n * Whether the object under `key` is the one the caller wrote, and throws when it cannot tell. Asked only when a\n * registry write failed and the row then names `key.generation`, to tell this publish's write from another\n * writer's at the same number. Every caller that writes the object passes it. Without it, a write that ended\n * without an answer over a row that names `key.generation` cannot be settled: the publish throws\n * {@link TransientError} and the caller keeps its object.\n */\n holdsOwnObject?: () => Promise<boolean>;\n /**\n * What a publish waits on before it sends a fresh compare-and-swap, after a write that ended without an answer and\n * left the row as it was. Without a `sleep`, such a write throws at once instead of being sent again.\n */\n clock?: Yielder;\n /** Spreads that wait (full jitter) so concurrent publishers do not retry together. Without it the wait is the bound. */\n rng?: Rng;\n /**\n * Called once for each registry write that ended without an answer, before the row is read back. A caller whose\n * publish then answers `false` can say that it cannot tell whether its own write landed first: the row may have\n * held its generation for a while before another writer moved on.\n */\n onUnanswered?: () => void;\n } = {},\n): Promise<boolean> {\n // A row read after a write that failed, which the next attempt acts on instead of reading it again.\n let fresh: RegistryRecord | null | undefined;\n // The row a failed write was made against. The next attempt first checks its row for that write's effect.\n let failedOn: RegistryRecord | null | undefined;\n // The error of the last attempt, while it ended without an answer over an unchanged row; and how many fresh writes\n // such answers have bought so far.\n let unanswered: TransientError | undefined;\n let resends = 0;\n // Whether any write of this call ended without an answer: what the pointer then says about this number is not\n // taken for the caller's own write without the proof.\n let sawUnanswered = false;\n // The row the last attempt acted on, which its write was made against: where the incarnation is read from when the\n // attempts run out.\n let lastRecord: RegistryRecord | null | undefined;\n for (let attempt = 0; attempt < 5; attempt++) {\n const reused = attempt === 0 && options.row !== undefined;\n const record =\n fresh !== undefined ? fresh : reused ? (options.row ?? null) : await registry.get(key);\n fresh = undefined;\n unanswered = undefined;\n lastRecord = record;\n if (failedOn !== undefined) {\n let landed: boolean;\n try {\n landed = await landedHere(record, failedOn, key, options.holdsOwnObject);\n } catch (proofErr) {\n throw outcomeUnknown(key, proofErr);\n }\n if (landed) return true;\n // The pointer names this number, but not over this publish's write: another writer's object under it, or\n // another incarnation of the name. That is not an already-current re-publish, which the branch below would\n // answer `true`: nothing this call wrote is published.\n if (record !== null && record.status === 'active' && record.currentGen === key.generation) {\n return false;\n }\n failedOn = undefined;\n }\n try {\n if (options.expectFrom !== undefined && record?.currentGen !== options.expectFrom) {\n // The pointer is no longer where the caller derived its content from \u2014 including the cases where the row\n // has vanished or has no generation at all. Not an error: the caller re-reads and re-derives.\n return false;\n }\n if (options.expectAbsent === true && record !== null) {\n // A row appeared since the caller looked. Whatever it decided from \"this segment does not exist\" no\n // longer holds \u2014 most importantly \"there is nothing here to overwrite\".\n return false;\n }\n if (options.expectToken !== undefined && record?.token !== options.expectToken) {\n // Same pointer VALUE, different row. Either the row was written since (harmless, and we re-derive\n // anyway) or the name was retired and re-created, in which case `currentGen` matching means nothing:\n // it is a different segment that restarted its generation counter at the same number.\n return false;\n }\n if (\n record === null &&\n options.summary !== undefined &&\n 'sealed' in options.summary &&\n options.wrappedDeks === undefined\n ) {\n // An object sealed under a key that a row held when the object was written (it carries no new key of its own),\n // with no row now: a row made from nothing would hold no key to open it with. A row passed in is the one the\n // caller read before it wrote, which another writer may have created since, so this reads again. A row still\n // absent then is a purged one, and the object cannot be published.\n if (reused) continue;\n return false;\n }\n if (record === null) {\n // First publish for the segment \u2014 carry the wrapped DEK(s) so encrypted reads can resolve the key.\n await registry.create(key, {\n currentGen: key.generation,\n wrappedDeks: options.wrappedDeks,\n ...(options.summary === undefined ? {} : { summary: options.summary }),\n });\n } else if (record.status === 'destroyed') {\n // Closes the window between a writer's own destroyed-check and its publish.\n //\n // `bulkLoadCrbmGeneration` reads the registry record once, refuses if it is already destroyed, and then\n // spends a KMS call plus a whole object write before getting here \u2014 seconds to minutes on a large load.\n // A check of `currentGen` alone cannot see a `destroySegment` landing inside that window: it would\n // advance the pointer on a destroyed record, leaving an object encrypted with the DEK that destroy had\n // just shredded \u2014 permanently unreadable, still paid for, and attached to a segment the registry says\n // was erased.\n //\n // Checking here rather than at the caller is what makes it a fence instead of a second guess: this\n // record was re-read moments ago inside the retry loop, so the check and the CAS that follows it see the\n // same state. This is the publish step's half of coupling the write path to destruction.\n throw new ValidationError(\n `segment \"${key.segment}\" was destroyed (crypto-shredded) while generation ${key.generation} was ` +\n `being written \u2014 refusing to publish it; the written object is unreadable. Use a new segment.`,\n );\n } else if (record.currentGen === null) {\n // The row exists with no Storage generation yet (a retention policy recorded ahead of the first load). There\n // is no pointer to regress past, so this publish advances it \u2014 and carries the wrapped\n // DEK(s) exactly like a first publish onto no row, since no generation is encrypted under the row's\n // current wrappings (there is no generation at all).\n //\n // `wrappedDeks` is spread in ONLY when there is something to store. A registry patch clears an optional\n // field by *mentioning* it (`'wrappedDeks' in patch`), so passing `wrappedDeks: undefined` unconditionally\n // would wipe key material off the row whenever a cleartext generation is published onto it \u2014 a divergence\n // from the branch below, which never touches the field.\n refuseCleartextOntoKey(key, record, options.cleartext);\n await registry.compareAndSwap(key, record.token, {\n currentGen: key.generation,\n ...(options.wrappedDeks === undefined ? {} : { wrappedDeks: options.wrappedDeks }),\n ...(options.summary === undefined ? {} : { summary: options.summary }),\n });\n } else if (record.currentGen > key.generation) {\n return false; // a newer generation is already current \u2014 forward-only, never regress\n } else if (record.currentGen === key.generation) {\n if (reused) continue; // only a fresh read can show the pointer already there\n return true; // already exactly current (an idempotent re-publish) \u2014 nothing to advance\n } else {\n // Advancing over an existing generation. `wrappedDeks` is deliberately NOT carried here, and a caller\n // that supplies it is refused rather than served.\n //\n // Reaching this branch with a freshly minted DEK means an encrypted generation is being published onto a\n // lineage whose current generation the row already describes \u2014 i.e. onto a segment whose existing\n // generations are cleartext. Both ways of resolving that silently are damaging: dropping the wrapping\n // advances the pointer to an object encrypted under a key that exists\n // nowhere, so the data is unrecoverable the moment the call returns; carrying it makes the row advertise\n // encryption over a lineage that still holds readable cleartext objects, which is what makes\n // `destroySegment` emit `segment.erase` \u2014 \"unreadable everywhere, backups included\" \u2014 over plaintext.\n // Two entry points reach it with no race: a re-load with a keystore newly wired, and an `*Into` on a\n // keystore-wired store whose destination is cleartext. So the state is refused before the pointer moves;\n // the object stays an orphan.\n if (options.wrappedDeks !== undefined && options.wrappedDeks.length > 0) {\n throw new ValidationError(\n `publishGeneration: refusing to publish generation ${key.generation} of \"${key.segment}\" with new ` +\n `key material onto a segment that already has generation ${record.currentGen}: its generations ` +\n `are not encrypted under this key, and nothing was published. If another writer published to the ` +\n `segment while this one was writing, re-run the write, which then uses the segment's own key; ` +\n `if the segment's generations are cleartext, encrypt a new segment and load into that instead.`,\n );\n }\n refuseCleartextOntoKey(key, record, options.cleartext);\n await registry.compareAndSwap(key, record.token, {\n currentGen: key.generation,\n ...(options.summary === undefined ? {} : { summary: options.summary }),\n });\n }\n return true; // created or advanced the pointer to key.generation \u2192 it is now current\n } catch (err) {\n if (isWriteConflictError(err)) {\n failedOn = record; // lost the race, or met its own landed write: the next read says which\n continue;\n }\n // Only the write itself raises a transient fault in here, and it is the one outcome that is not an answer.\n if (!isTransientError(err)) throw err;\n options.onUnanswered?.();\n sawUnanswered = true;\n let now: RegistryRecord | null;\n try {\n now = await registry.get(key);\n } catch (readErr) {\n // A row that is not one the library wrote says more than a throttle does, and the next call would raise it\n // anyway. It is not a refusal either, so the caller keeps its object. Any other fault leaves the outcome unknown.\n if (isIntegrityError(readErr)) throw readErr;\n throw outcomeUnknown(key, err);\n }\n if (sameRow(now, record)) {\n // The write did not land, or is still on its way. Send a new one from the row just read, a bounded number of\n // times; the registry's fence lets at most one of the two copies land.\n const sleep = options.clock?.sleep;\n if (sleep === undefined || resends >= UNANSWERED_RESENDS) throw outcomeUnknown(key, err);\n const bound = UNANSWERED_RESEND_BASE_MS * 2 ** resends;\n resends += 1;\n await sleep.call(options.clock, Math.floor((options.rng?.next() ?? 1) * bound));\n unanswered = outcomeUnknown(key, err);\n fresh = now;\n continue;\n }\n failedOn = record;\n fresh = now;\n }\n }\n // Exhausted: only possible if competing writers kept advancing currentGen \u2014 which likely already satisfies\n // the forward-only goal. Confirm before reporting failure, so a lost-every-race-but-already-current case\n // isn't a spurious error. Whether *this* generation is the current one decides the returned flag.\n const final = await registry.get(key);\n // `currentGen === null` after exhausting the retries is a genuine failure, not an already-current case: nothing\n // is published, so it falls through to the conflict below rather than being read as \"a newer gen won\".\n if (final !== null && final.currentGen !== null && final.currentGen >= key.generation) {\n if (final.currentGen !== key.generation) return false;\n if (!sawUnanswered) return true;\n // A write that went unanswered may have landed during the last wait, or the number may be another writer's, or\n // another incarnation's: the incarnation and the object under it decide, as after any unanswered write.\n try {\n return await landedHere(final, lastRecord ?? null, key, options.holdsOwnObject);\n } catch (proofErr) {\n throw outcomeUnknown(key, proofErr);\n }\n }\n // The attempts ran out on writes that went unanswered, not on contention: say so.\n if (unanswered !== undefined) throw unanswered;\n throw new WriteConflictError(\n `publishGeneration: contention setting currentGen for ${key.segment}`,\n );\n}\n\n/**\n * Whether `now` shows the effect of a publish's write that was made against `before`: an active row, on the same\n * incarnation (a `create` has none to compare), whose pointer names `key.generation`, over the caller's own object.\n *\n * The incarnation is the one in the row's token ({@link sameIncarnation}): exact, and no clock. A row with no id in its\n * token (one 0.11 wrote, or a registry of someone else's) is compared by its creation stamp, a clock reading that two\n * incarnations can share. So the object is always proved too, never assumed: the pointer and the incarnation alone\n * cannot say whose write this is. With no proof to ask for, the answer cannot be given, and that is an unknown outcome,\n * never a guess.\n */\nasync function landedHere(\n now: RegistryRecord | null,\n before: RegistryRecord | null,\n key: GenKey,\n holdsOwnObject: (() => Promise<boolean>) | undefined,\n): Promise<boolean> {\n if (now === null || now.status === 'destroyed' || now.currentGen !== key.generation) return false;\n if (before !== null && !sameIncarnation(before, now)) return false;\n if (holdsOwnObject === undefined) {\n throw new TransientError(\n `publish of \"${key.segment}\" generation ${key.generation}: whether its registry write landed could not be ` +\n `settled, since the caller gave no way to tell its own object from another's. Its object is kept; re-run the ` +\n `write, which numbers past it.`,\n );\n }\n return holdsOwnObject();\n}\n\n/** Whether two reads of a row saw it unwritten in between: the same token, or no row both times. */\nfunction sameRow(a: RegistryRecord | null, b: RegistryRecord | null): boolean {\n return a === null ? b === null : b !== null && a.token === b.token;\n}\n\n/**\n * The error for a publish that cannot tell whether its registry write landed: always a {@link TransientError}, so no\n * caller treats it as a refusal and deletes the object the write may yet point the row at; a re-run numbers past that\n * object. A cause that is already one (the registry's own error, whose `cause` is the SDK's) is thrown as it is, so\n * what a caller reads from it does not move; any other is wrapped.\n */\nfunction outcomeUnknown(key: GenKey, cause: unknown): TransientError {\n if (isTransientError(cause)) return cause;\n return new TransientError(\n `publish of \"${key.segment}\" generation ${key.generation}: whether its registry write landed could not be ` +\n `settled (${(cause as { name?: string } | null)?.name ?? 'error'} while checking its object). Its object is ` +\n `kept, since the write may still land; re-run the write, which numbers past it.`,\n { cause },\n );\n}\n\n/**\n * Whether the object under `key` is the one `fingerprint` names, from one read of its footer: false when it is another\n * object or gone. Unlike {@link holdsObject}, a read that cannot tell throws, for a caller that must not take \"could\n * not look\" for \"not this object\".\n */\nexport async function provesOwnObject(\n storage: IStorageDriver,\n key: GenKey,\n fingerprint: string,\n): Promise<boolean> {\n try {\n return await CrbmReader.sameObject(storageBlobReader(storage, key), fingerprint);\n } catch (err) {\n if (isNotFoundError(err)) return false;\n throw err;\n }\n}\n\n/** What a bulk-load wrote \u2014 the driver's `{ size, sha256 }` plus a summary of the built generation. */\nexport interface BulkLoadResult {\n /** Bytes written to the Storage object. */\n readonly size: number;\n /** The driver's content hash of the object. */\n readonly sha256: string;\n /** Distinct non-empty chunks written (\u2264 65536). */\n readonly chunkCount: number;\n /**\n * The DEK freshly minted for this generation, wrapped \u2014 present only when the segment is encrypted AND this\n * call minted one (an existing segment reuses its key, which is already on the row).\n *\n * Returned so a caller that deliberately does **not** pass `registry` here \u2014 writing now and publishing later,\n * which is how a load applies a guard while the old generation is still authoritative \u2014 can carry the key onto\n * its own `publishGeneration`. Without it that publish stores no DEK and the generation it makes current is\n * unreadable: the bytes are encrypted with a key nothing recorded.\n */\n readonly wrappedDeks?: readonly WrappedDek[];\n /** Whether the object was written encrypted: under the segment's key, or under one this call minted. */\n readonly encrypted: boolean;\n /** The written object's fingerprint, which tells it from an object stored later under the same key. */\n readonly fingerprint: string;\n /** Total distinct ids in the generation (post-dedup). */\n readonly cardinality: number;\n /**\n * The summary of what was written, for the write that makes it current: its id count and metadata, sealed under the\n * segment's key when the object was written with one. A caller that defers the publish (`publish: false`) passes it\n * to {@link publishGeneration}.\n */\n readonly summary: RegistrySummary;\n /**\n * Whether this generation is now the segment's **current** one \u2014 `undefined` when no `registry` was wired, so\n * there is no pointer and no publish step to report on.\n *\n * `false` means the object is durable but a **concurrent writer published a higher generation first**, so this\n * one is an orphan that no reader will ever resolve. The write succeeded and the *load* did not: whatever this\n * call was asked to make the segment contain, the segment does not contain. That is not a detail a caller can\n * be left to infer \u2014 a `*Into` materialisation that reported the generation it wrote as \"the destination's new\n * current generation\" on this path would state a plain untruth \u2014 so the flag is on the result and the verbs that\n * promise a published generation check it.\n */\n readonly becameCurrent?: boolean;\n}\n\n/**\n * **Bulk-load** a whole immutable generation from a flat id source \u2014 the batch \"seed/rebuild\" entry point.\n * Unlike {@link writeCrbmGeneration} (which takes pre-grouped bitmaps), this\n * consumes an arbitrary, **unsorted** stream of u32 ids (sync or async) and routes each into its chunk's\n * Roaring bitmap as it goes. The input is consumed lazily and ids dedup on insert, so memory is bounded by\n * the **built generation** \u2014 the in-memory Roaring representation of the *distinct* set (\u2264 one bitmap per\n * non-empty chunk) \u2014 not by the input length: you can stream a billion duplicate-heavy ids and hold only the\n * distinct result. Note this is `O(distinct set)`, **not** window-bounded like {@link SegmentEngine.intersect}:\n * bulk-load holds the whole generation in RAM, which suits batch seed/rebuild jobs but not an\n * unbounded-cardinality stream (a segment larger than RAM needs external-merge / pre-sorted input \u2014 later phase).\n *\n * Each id must be an integer in `[0, 2\u00B3\u00B2)` ({@link splitId} throws {@link ValidationError} otherwise) \u2014 the\n * source is consumed lazily, so a bad id aborts mid-stream without writing a partial object (the driver\n * commits only after the callback resolves). An empty source writes a valid empty generation.\n *\n * Writes a fresh full snapshot of a segment. The caller picks the generation number in `key` \u2014 `nextGeneration`\n * computes one from the registry and the bucket, as `nextLoadGeneration` does for a load \u2014 and re-using an existing\n * generation throws\n * {@link WriteConflictError} (write-once). Without a registry a `StorageChunkSource` serves the **highest**\n * generation present, so a too-high number silently shadows real data; with one, `publishGeneration` decides.\n */\nexport async function bulkLoadCrbmGeneration(\n driver: IStorageDriver,\n key: GenKey,\n ids: Iterable<number> | AsyncIterable<number> | DecodedLoadInput,\n options: {\n registry?: IRegistryDriver;\n keystore?: IKeystore;\n requireEncryption?: boolean;\n audit?: IAuditSink;\n /** Bitmap codec. Optional in the type; a **flavor** package binds it ({@link requireCodec}). */\n codec?: CodecInterface;\n /**\n * Injected clock. Supplying one makes bulk-load **cooperative**: it yields the event loop periodically so a\n * long load does not stall everything else on the process. Without it the load still completes, just\n * without yielding \u2014 which is the pre-existing behaviour, kept so this is purely additive.\n *\n * `@cloudbitmaps/roaring` supplies a real clock by default, so flavor users get cooperative behaviour with\n * no wiring. `core/` cannot default it: it is timer-free by lint, which is exactly why waiting goes through\n * this seam rather than `setTimeout`.\n */\n clock?: Clock;\n /**\n * Write the object but do **not** advance the pointer (default: publish when a `registry` is wired).\n *\n * For a caller that has to inspect what it wrote before deciding whether it should become current \u2014 a\n * guarded {@link loadSegment} is the one in-tree case \u2014 because the check is only meaningful while the old\n * generation is still authoritative. The `registry` is still required for an encrypted segment and still\n * consulted: it is where an existing segment's DEK lives, and reusing that key is not optional. The wrapped\n * DEK comes back on the result so the deferred publish can store it.\n *\n * A caller that defers the publish owns what it wrote: an unpublished object sits ABOVE `currentGen`, where\n * generation collection deliberately never looks, so nothing reclaims it until a later generation above it is\n * current.\n */\n publish?: boolean;\n /**\n * The generation's metadata (see {@link GenerationMetadata}), written into the object and carried in the summary.\n * The caller has checked it against the metadata rules; the writer checks it again, and refuses a record that breaks\n * one before a byte is written.\n */\n metadata?: GenerationMetadata;\n /**\n * The segment's row as the caller already read it (`null`: it found none), for a caller that defers the\n * publish (`publish: false`) and fences it on that same row. A present cleartext row is then used as read,\n * rather than read again: a row that changes in between (a publish, a drop, a purge and re-create) makes the\n * fenced publish lose, so the object is never published on the strength of the stale read. A row read as\n * absent, or one carrying key material, is read again after the ids, as with no row passed: a first load must\n * see a row another writer created meanwhile, and an encrypted segment's key is unwrapped only from a row read\n * after the ids, so a segment shredded while they streamed is refused before its key is used.\n */\n row?: RegistryRecord | null;\n } = {},\n): Promise<BulkLoadResult> {\n if (options.keystore === undefined && options.requireEncryption === true) {\n throw new ValidationError('requireEncryption: a load needs a keystore to write encrypted');\n }\n const codec = requireCodec(options.codec, 'bulkLoadCrbmGeneration');\n // A decoded bitmap writes its own chunks, which never touches an id. Ids, and the bitmap of a codec that cannot\n // encode its own chunks, are bucketed per chunk instead; both give the same bytes.\n let chunks: Iterable<EncodedChunk>;\n if (ids instanceof DecodedLoadInput && ids.bitmap.encodeChunks !== undefined) {\n // The whole-bitmap steps each get a slice of their own: the decode the load made before its first request, the\n // re-encode and serialize `encodeChunks` makes when called, and then the cut, which is lazy, so the writer's\n // periodic yield interrupts it.\n const pause = yieldEvery(options.clock, 1);\n await pause();\n chunks = ids.bitmap.encodeChunks();\n await pause();\n } else {\n chunks = encodeEach(\n await bucketIds(ids instanceof DecodedLoadInput ? ids.bitmap : ids, codec, options.clock),\n );\n }\n\n if (options.keystore !== undefined && options.registry === undefined) {\n throw new ValidationError('an encrypted load requires a registry to store the wrapped DEK');\n }\n // Read the segment's record once (when a registry is wired): to refuse writing to a crypto-shredded segment (which\n // would create unreadable/unreachable bytes), and to reuse its DEK if it's encrypted. A present cleartext row the\n // caller already read is used as read (see `row`).\n const passed = options.row;\n const existing =\n passed !== undefined &&\n passed !== null &&\n (passed.wrappedDeks === undefined || passed.wrappedDeks.length === 0)\n ? passed\n : options.registry !== undefined\n ? await options.registry.get(key)\n : null;\n if (existing?.status === 'destroyed') {\n throw new ValidationError(\n `segment \"${key.segment}\" is destroyed (crypto-shredded) \u2014 refusing to write; use a new segment`,\n );\n }\n\n // An encrypted row with no keystore is a lost key, not a cleartext segment \u2014 the same fail-fast the erasure\n // rewrite does. Without it this wrote a CLEARTEXT generation onto a row that still advertises `wrappedDeks`, and the\n // damage is not just a confusing state: `destroySegment` keys `cryptoShredded` off the *presence* of wrappings,\n // so shredding that segment emits `segment.erase` \u2014 the audit event defined as \"these bytes are unreadable\n // everywhere, backups included\" \u2014 over bytes that are plaintext and stay readable from any copy. An audit trail\n // that over-attests is the one failure it exists to prevent, so this refuses to create the state.\n if (\n existing?.wrappedDeks !== undefined &&\n existing.wrappedDeks.length > 0 &&\n options.keystore === undefined\n ) {\n throw new KeyUnavailableError(\n `segment \"${key.segment}\" is encrypted but this load has no keystore \u2014 refusing to write a cleartext ` +\n `generation onto an encrypted segment. Pass the keystore holding its DEK.`,\n );\n }\n\n // Encryption (opt-in): reuse the segment's existing DEK, or mint a fresh one on its FIRST generation.\n //\n // The segment's own posture decides, not the presence of a keystore. A keystore is wired on the store, so it is\n // in scope for every segment that store touches \u2014 including ones deliberately left cleartext \u2014 and minting on\n // that basis alone is what made \"load it again with a keystore wired\" destroy the data: the minted wrapping has\n // nowhere to live on a row whose pointer is already set (see `publishGeneration`'s advance branch), so the\n // generation ends up encrypted under a key that exists in no persistent store. `requireEncryption` is the way\n // to *demand* encryption, and on an already-cleartext lineage it fails fast rather than silently downgrading.\n let crypto: CrbmCrypto | undefined;\n let newWrapped: readonly WrappedDek[] | undefined;\n if (options.keystore !== undefined) {\n if (existing?.wrappedDeks !== undefined && existing.wrappedDeks.length > 0) {\n const aead = await options.keystore.openDek(existing.wrappedDeks); // reuse the segment's DEK\n crypto = { aead, aadFor: (scope) => aadFor(key, key.generation, scope) };\n } else if (existing !== null && existing.currentGen !== null) {\n // An existing lineage with no key material on the row: the segment is cleartext, and one segment cannot be\n // half-encrypted \u2014 a pin of a superseded generation, once its reader is reopened, would find bytes its key\n // cannot open, and a later `destroySegment` would attest that shredding one DEK made every copy unreadable\n // while the older cleartext objects stay readable from any of them.\n if (options.requireEncryption === true) {\n throw new ValidationError(\n `requireEncryption: segment \"${key.segment}\" already has generation ${existing.currentGen} in ` +\n `cleartext, so this load cannot be encrypted \u2014 encryption is chosen when a segment is first ` +\n `loaded. Load into a new segment with the keystore wired, then drop this one.`,\n );\n }\n // Otherwise the segment stays what it is: cleartext.\n } else {\n const minted = await options.keystore.createDek();\n newWrapped = minted.wrapped;\n crypto = { aead: minted.aead, aadFor: (scope) => aadFor(key, key.generation, scope) };\n }\n }\n\n const { size, sha256, chunkCount, cardinality, fingerprint } = await writeEncodedChunks(\n driver,\n key,\n chunks,\n { crypto, clock: options.clock, metadata: options.metadata },\n );\n const summary = summaryOf(\n key,\n key.generation,\n { cardinality, metadata: options.metadata },\n crypto?.aead,\n );\n // Publish only after the immutable object is durable (write-then-publish): a registry-aware reader should\n // never point at a generation that isn't fully written. A freshly minted DEK is stored on this publish.\n if (options.registry !== undefined && options.publish !== false) {\n const becameCurrent = await publishGeneration(options.registry, key, {\n wrappedDeks: newWrapped,\n summary,\n cleartext: crypto === undefined,\n holdsOwnObject: () => provesOwnObject(driver, key, fingerprint),\n clock: options.clock,\n });\n // Audit the publish only when this generation actually *became* the current one \u2014 not when a\n // forward-only publish no-oped because a newer generation was already current (the event's contract is\n // \"became the segment's current generation\"). Needs a registry to have a \"current generation\" at all.\n if (becameCurrent) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.publish',\n namespace: key.namespace,\n segment: key.segment,\n generation: key.generation,\n });\n }\n return {\n size,\n sha256,\n chunkCount,\n cardinality,\n summary,\n becameCurrent,\n wrappedDeks: newWrapped,\n encrypted: crypto !== undefined,\n fingerprint,\n };\n }\n return {\n size,\n sha256,\n chunkCount,\n cardinality,\n summary,\n wrappedDeks: newWrapped,\n encrypted: crypto !== undefined,\n fingerprint,\n };\n}\n\n/** Route each id into its chunk's bitmap, consuming the source lazily. Returns the chunks, ascending by key. */\nasync function bucketIds(\n ids: Iterable<number> | AsyncIterable<number>,\n codec: CodecInterface,\n clock: Clock | undefined,\n): Promise<Array<{ chunkKey: number; bitmap: CodecBitmap }>> {\n const byChunk = new Map<number, CodecBitmap>();\n // Batched per chunk, not one native `add()` per id.\n //\n // The obvious loop \u2014 `bitmap.add(remainder)` for every id \u2014 crosses the JS\u2194native boundary once per id, and\n // measured **1,679 ms for 1M ids** with no yield point anywhere in it. Since this is a synchronous stretch on\n // Node's only thread, a caller who wires it to a request handler stalls every other request on that instance\n // for over a second (measured separately: a 0.7 ms health check took 275 ms).\n //\n // Buffering the remainders and inserting them per chunk in one `fromValues`/`addMany` call amortises that\n // boundary crossing across the whole batch.\n //\n // WHY THE BUFFER IS CAPPED. Bucketing *everything* first and inserting once per chunk at the end is faster\n // still, but it holds every remainder as a JS number before any bitmap compression happens \u2014 and with up to\n // 65,536 chunks in play that is unbounded in exactly the way this library refuses to be. So the buffer is\n // flushed whenever the total pending count crosses `BULK_FLUSH_IDS`, bounding the extra memory regardless of\n // input size or key distribution while still getting the batching win. That bound is **~28 MB measured** at\n // 1M staged ids across ~65,000 chunks \u2014 not the ~8 MB a naive 8-bytes-per-number estimate gives, because the\n // cost is dominated by per-array and Map overhead across tens of thousands of small arrays.\n const pendingByChunk = new Map<number, number[]>();\n let pending = 0;\n // Yield periodically, NOT per chunk \u2014 see {@link yieldEvery} for why per-unit async is a 7x regression here.\n const tickChunk = yieldEvery(clock);\n const tickId = yieldEvery(clock, YIELD_EVERY_IDS);\n // Yield every N chunks, NOT per chunk. Measured: handing each chunk's insert to the threadpool\n // (`fromArrayAsync`) costs ~9 \u00B5s of dispatch against ~1.5 \u00B5s of actual work once ids are spread across\n // ~61,000 chunks \u2014 636 ms versus 92 ms, a 7x regression that would have undone the per-chunk batching this\n // function already does. Keeping the inserts synchronous and interrupting them periodically gets the\n // starvation fix without the cost \u2014 measured on the per-chunk insert microbenchmark: 88 ms wall against a\n // 92 ms unyielded baseline. The whole-load end-to-end figures are a DIFFERENT experiment and live in\n // `cooperative.ts`. Quoting this 92 ms baseline beside that experiment's 450 ms stall would describe a 92 ms\n // operation with 450 ms of starvation inside it, which is impossible on its face.\n //\n // The yield must be a REAL macrotask. `await Promise.resolve()` is a microtask and never lets I/O run, which\n // is the trap that makes naive \"just await something\" fixes measure as no change at all.\n const flushPending = async (): Promise<void> => {\n for (const [chunkKey, rems] of pendingByChunk) {\n if (rems.length === 0) continue;\n const existingBitmap = byChunk.get(chunkKey);\n if (existingBitmap === undefined) byChunk.set(chunkKey, codec.fromValues(rems));\n else existingBitmap.addMany(rems);\n rems.length = 0;\n const pause = tickChunk();\n if (pause !== null) await pause;\n }\n pending = 0;\n };\n /** Bucket one id. Returns true when the pending buffer is full and must be flushed. */\n const ingest = (id: number): boolean => {\n const { chunkKey, remainder } = splitId(id); // validates the u32 range\n let bucket = pendingByChunk.get(chunkKey);\n if (bucket === undefined) {\n bucket = [];\n pendingByChunk.set(chunkKey, bucket);\n }\n bucket.push(remainder);\n return ++pending >= BULK_FLUSH_IDS;\n };\n // The two ingest loops are deliberately NOT collapsed into one `for await` over a normalising wrapper.\n //\n // That is the tidier code and it was measured at **20x** the cost: routing a sync source through an async\n // generator forces a microtask per id, and over 1M ids that is 224 ms against 11 ms for a plain `for..of` \u2014\n // 55% of a whole bulk load spent on iteration protocol rather than on work. Since an array, a Set and a\n // generator are what callers actually hand this function most of the time, the sync path is the common one.\n //\n // Note that `for await` yields nothing to the event loop either way: a microtask per id still drains before\n // the loop turns a phase. Both paths therefore need the same explicit yield.\n if (Symbol.asyncIterator in ids) {\n for await (const id of ids as AsyncIterable<number>) {\n if (ingest(id)) await flushPending();\n const pause = tickId();\n if (pause !== null) await pause;\n }\n } else {\n for (const id of ids as Iterable<number>) {\n if (ingest(id)) await flushPending();\n const pause = tickId();\n if (pause !== null) await pause;\n }\n }\n await flushPending();\n\n // Every chunk in the map received at least one id, so no bitmap here is empty.\n return [...byChunk]\n .map(([chunkKey, bitmap]) => ({ chunkKey, bitmap }))\n .sort((a, b) => a.chunkKey - b.chunkKey);\n}\n\n/**\n * Open a {@link CrbmReader} on one generation over the storage driver's range/tail reads (decrypting if `crypto`).\n * The reader the write paths use to re-read what they wrote, and the erasure rewrite uses to stream the old\n * generation; the engine's read path goes through {@link CrbmStorageChunkSource} instead, which caches these.\n */\nexport function openGenerationReader(\n storage: IStorageDriver,\n key: GenKey,\n crypto: CrbmCrypto | undefined,\n options: Omit<CrbmReaderOptions, 'crypto'> = {},\n): Promise<CrbmReader> {\n return openChecked(storage, key, { ...options, crypto });\n}\n\n/**\n * Open a reader on `key`, refusing an object whose footer names another generation. Every writer stamps the key's\n * number, and the chunk cache, the load guard and the erasure rewrite all trust it, so an object that disagrees\n * was written under another key or altered. Every open, the live read's, a pin's and the write paths', comes here.\n */\nasync function openChecked(\n storage: IStorageDriver,\n key: GenKey,\n options: CrbmReaderOptions,\n): Promise<CrbmReader> {\n const reader = await CrbmReader.open(storageBlobReader(storage, key), options);\n if (reader.generation !== key.generation) {\n throw new IntegrityError(\n `segment \"${key.segment}\" generation ${key.generation}: its footer says generation ${reader.generation}`,\n );\n }\n return reader;\n}\n\n/**\n * Re-open a freshly written generation and assert it round-trips exactly what was streamed into it: the same\n * per-chunk key set *and* the same total cardinality (on top of the codec's own per-chunk CRC + footer checks), and the\n * metadata it was given. `expected` is the streaming writer's tally (the stream is consumed, so it can't be\n * re-iterated) \u2014 the key-set comparison catches a dropped/extra chunk that a cardinality-only check could miss when\n * two errors cancel out. The count and the metadata are what the generation's row summary is about to claim, so the\n * summary is held to the object it describes before it is published.\n * Throws {@link IntegrityError}: the object is on disk but must not be published.\n */\nexport async function verifyGeneration(\n storage: IStorageDriver,\n key: GenKey,\n expected: {\n readonly chunkKeys: readonly number[];\n readonly cardinality: number;\n readonly metadata?: GenerationMetadata | undefined;\n },\n crypto: CrbmCrypto | undefined,\n): Promise<void> {\n const expectedKeys = [...expected.chunkKeys].sort((a, b) => a - b);\n const reader = await openGenerationReader(storage, key, crypto);\n const actualKeys = [...reader.chunkKeys()].sort((a, b) => a - b);\n const keysMatch =\n actualKeys.length === expectedKeys.length && actualKeys.every((k, i) => k === expectedKeys[i]);\n if (!keysMatch) {\n throw new IntegrityError(\n `verify failed for ${key.segment}.${key.generation}: chunk-key set mismatch (${actualKeys.length} vs ${expectedKeys.length})`,\n );\n }\n if (reader.count() !== expected.cardinality) {\n throw new IntegrityError(\n `verify failed for ${key.segment}.${key.generation}: cardinality ${reader.count()} != ${expected.cardinality}`,\n );\n }\n if (!summaryAgrees(expected, { cardinality: reader.count(), metadata: reader.metadata })) {\n throw new IntegrityError(\n `verify failed for ${key.segment}.${key.generation}: the object's metadata is not the metadata it was written with`,\n );\n }\n}\n", "/**\n * A {@link StorageChunkSource} view that holds **one** segment at **one** generation, and passes everything else\n * through to the live source.\n *\n * A pin exists so a long job describes a single instant. An ordinary handle re-resolves on `cache.genTtlMs`, so a\n * publish part-way through an export, a send or a reconciliation means the second half of the job describes a\n * different instant than the first \u2014 and nothing in the result says so.\n *\n * **A pin is a generation number and the object it named, not a captured reader.** The reader behind it can live\n * in the shared bounded LRU and be evicted freely: re-opening at the same number reopens the same object, or, if\n * the name was purged and loaded again since, finds another one, which the pin's fingerprint refuses. Holding the\n * reader instead put every live pin outside the library's memory ceiling.\n *\n * **Other segments are not pinned, and that is the contract, not an omission.** `snap.intersect([other])` reads\n * `snap` at its pinned generation and `other` at whatever is current. Pinning the whole query means pinning\n * each segment. The alternative shapes are both worse: refusing the combine outright makes a pin useless to the\n * export and reconciliation jobs it exists for, and silently pinning the operands would quietly change what\n * `other` means to a caller who did not ask.\n *\n * What this must never do is the inverse \u2014 read a pinned handle's segment **live** because the call was made\n * on some other handle. That is why the routing is by segment identity here rather than by which object the\n * caller happened to start from.\n */\nimport type {\n ChunkRef,\n GenerationSummary,\n StorageChunkSource,\n SegmentRef,\n SegmentSize,\n} from './ports';\nimport type { CrbmStorageChunkSource, PinnedObject } from './crbm-storage-source';\nimport { segmentKey } from './keys';\n\n/**\n * What a pin holds for one segment: the generation, the version identifying those exact bytes, and the pinned\n * object's fingerprint, so a read can tell that the generation it opens is still that object.\n *\n * `fingerprint` is optional so that a pin built by hand, before there was one, still compiles. Nothing checks the\n * object such a pin reads, so it cannot tell a name purged and loaded again from the segment it pinned.\n * `seg.pin()` always records it.\n */\nexport interface PinnedAt {\n readonly generation: number | null;\n readonly version: string | null;\n readonly fingerprint?: string | null;\n}\n\n/** The part of a pin a pinned read checks the object against; none for a pin of no generation. */\nconst heldBy = (pin: PinnedAt): PinnedObject | undefined => {\n if (pin.version === null) return undefined;\n return pin.fingerprint == null\n ? { version: pin.version }\n : { version: pin.version, fingerprint: pin.fingerprint };\n};\n\nexport class PinnedStorageChunkSource implements StorageChunkSource {\n constructor(\n private readonly inner: CrbmStorageChunkSource,\n /**\n * The pinned segments, keyed by {@link segmentKey}. A **set** rather than one entry because a combine may\n * involve several pinned handles, and each must be read at its own pin \u2014 including when the call was made\n * on a different handle. Reading a pinned handle live because the caller started elsewhere is the silent\n * wrong answer this exists to prevent.\n */\n private readonly pins: ReadonlyMap<string, PinnedAt>,\n ) {}\n\n /** The unpinned segments refresh as the store's own source does; a pinned one never does. */\n get pointerRefreshMs(): number {\n return this.inner.pointerRefreshMs;\n }\n\n /**\n * The pin for this segment, or `undefined` if it is not pinned here.\n *\n * A pin whose `generation` is `null` is a segment that had none when it was pinned: that handle reads empty\n * for its lifetime, which is exactly what an unpinned read of it does. That is **not** the same as \"this\n * source cannot pin\" \u2014 a source that genuinely cannot is a capability gap and fails fast at `pin()`.\n */\n private pinFor(ref: SegmentRef): PinnedAt | undefined {\n return this.pins.get(segmentKey(ref));\n }\n\n getChunk(ref: ChunkRef): Promise<Uint8Array | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.getChunk(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.getChunkAt(ref, pin.generation, heldBy(pin));\n }\n\n listChunkKeys(ref: SegmentRef): Promise<number[]> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.listChunkKeys(ref);\n return pin.generation === null\n ? Promise.resolve([])\n : this.inner.listChunkKeysAt(ref, pin.generation, heldBy(pin));\n }\n\n sizeOf(ref: SegmentRef): Promise<SegmentSize | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.sizeOf(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.sizeOfAt(ref, pin.generation, heldBy(pin));\n }\n\n cardinalities(ref: SegmentRef): Promise<ReadonlyMap<number, number> | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.cardinalities(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.cardinalitiesAt(ref, pin.generation, heldBy(pin));\n }\n\n summary(ref: SegmentRef): Promise<GenerationSummary | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.summary(ref);\n return pin.generation === null\n ? Promise.resolve(null)\n : this.inner.summaryAt(ref, pin.generation, heldBy(pin));\n }\n\n currentGeneration(ref: SegmentRef): Promise<number | null> {\n const pin = this.pinFor(ref);\n return pin === undefined ? this.inner.currentGeneration(ref) : Promise.resolve(pin.generation);\n }\n\n /**\n * A pinned segment reports the version captured when it was pinned, marked as a pin's, so its decoded chunks\n * are cached under keys that no live read writes. A live read keys its fetches by the version it resolved when\n * it began, but is served whatever generation the live source holds when each fetch lands \u2014 after a publish\n * and a lapsed `cache.genTtlMs`, a reader-cache eviction, a sweep that heals the read forward, or an\n * invalidation, a different one \u2014 so an entry under a live version can hold another generation's chunk.\n * Invariant 3 lets that live call see it, and later live reads resolve the current version and never look the\n * entry up; a pin sharing the key would be handed it, and return a read that mixed two generations. A pinned\n * read fetches exactly its own generation, so the entries it fills are always that generation's.\n *\n * The pin still shares the store's chunk cache, and its memory ceiling; what it gives up is a hit on a chunk a\n * live read of the same version cached, which costs a pin one GET per such chunk. Live reads make no call for it,\n * though a pin's entries share the cache's bound with theirs, so under a small `cache.maxChunks` each can evict\n * the other. The version, not the bare generation, is still what the key carries: sharing on a generation-only\n * key was how a pinned read could resurrect an id that `eraseIdFromSegment` had reported physically gone.\n */\n currentVersion(ref: SegmentRef): Promise<string | null> {\n const pin = this.pinFor(ref);\n if (pin === undefined) return this.inner.currentVersion(ref);\n // A live version starts with its generation number, so no live version can equal a pin's. The fingerprint keeps\n // two incarnations' pins apart where the version cannot: on a store with no registry it is the bare number.\n if (pin.version === null) return Promise.resolve(null);\n return Promise.resolve(\n `pin ${pin.version}${pin.fingerprint == null ? '' : `#${pin.fingerprint}`}`,\n );\n }\n\n exists(ref: SegmentRef): Promise<boolean> {\n return this.inner.exists(ref);\n }\n\n invalidate(ref: SegmentRef): void {\n this.inner.invalidate(ref);\n }\n}\n", "/**\n * `withRetry` \u2014 the one retry/backoff primitive.\n *\n * A pure, storage-agnostic helper: it knows nothing about HTTP, AWS, or any SDK. It retries an operation\n * while the operation keeps failing with a **retryable** error (by default {@link TransientError}), waiting\n * a bounded, jittered, exponentially-growing delay between attempts. Time and randomness are **injected**\n * (the determinism seam) \u2014 production wires a `setTimeout`-backed clock + a real RNG; the\n * tests wire virtual ones \u2014 so a backoff schedule is replayable from a seed and unit tests run with no\n * real sleeping.\n *\n * It deliberately does **not** retry deterministic failures ({@link WriteConflictError},\n * {@link ValidationError}, {@link IntegrityError}, {@link NotFoundError}, \u2026): retrying those either can't\n * help or would be incorrect. A pointer conflict is retried by a *separate* loop inside\n * the publish, because each attempt must re-read the row and re-decide \u2014 a blind replay of the same\n * compare-and-swap would either fail again on a stale token or, worse, advance a pointer whose state has\n * changed underneath it. A write-once object collision is never replayed at all: the generation number is\n * taken, so the caller has to pick a new one.\n */\nimport type { Clock, Rng } from './determinism';\nimport { isTransientError, ValidationError } from './errors';\n\nexport interface RetryPolicy {\n /** Total attempts including the first (so `maxAttempts: 4` = 1 try + 3 retries). Must be \u2265 1. */\n readonly maxAttempts: number;\n /** Delay before the first retry, in ms. Grows by `backoffFactor` each subsequent retry. */\n readonly baseDelayMs: number;\n /** Upper bound on a single (pre-jitter) delay, in ms \u2014 caps the exponential growth. */\n readonly maxDelayMs: number;\n /** Exponential growth factor between retries (e.g. 2 \u21D2 base, 2\u00B7base, 4\u00B7base, \u2026). */\n readonly backoffFactor: number;\n /**\n * `'full'` \u21D2 the actual wait is uniform in `[0, computed]` (AWS-recommended full jitter: decorrelates\n * concurrent retriers so they don't reconverge into a thundering herd). `'none'` \u21D2 wait exactly `computed`.\n */\n readonly jitter: 'full' | 'none';\n}\n\n/**\n * Conservative defaults: 4 attempts, 50ms \u2192 100 \u2192 200 (\u00D72), capped at 2s, full jitter. Tuned for a cloud\n * backend's brief throttle/5xx blip \u2014 enough to ride out a transient fault without turning a hard outage\n * into a long hang. Override per store (`retry`) or per read source (`RetryingStorageChunkSource`) if your latency\n * budget differs.\n */\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = {\n maxAttempts: 4,\n baseDelayMs: 50,\n maxDelayMs: 2_000,\n backoffFactor: 2,\n jitter: 'full',\n};\n\nexport interface RetryDeps {\n readonly clock: Clock;\n readonly rng: Rng;\n /** Override which errors are retryable. Default: any {@link TransientError}. */\n readonly isRetryable?: (err: unknown) => boolean;\n /**\n * Optional hook (observability) fired before each backoff wait. `attempt` is 1-based (the one that failed).\n * Best-effort: an error it throws is swallowed, so it can neither stop the retry nor change the error thrown.\n */\n readonly onRetry?: (info: { attempt: number; delayMs: number; err: unknown }) => void;\n}\n\n/**\n * Backoff delay (ms) for the retry that follows a given 1-based attempt, before jitter is applied. Exposed\n * for tests. `attempt` 1 \u21D2 `baseDelayMs`, 2 \u21D2 `base\u00B7factor`, \u2026 capped at `maxDelayMs`.\n */\nexport function backoffDelayMs(policy: RetryPolicy, attempt: number): number {\n const raw = policy.baseDelayMs * policy.backoffFactor ** (attempt - 1);\n return Math.min(policy.maxDelayMs, raw);\n}\n\n/** Apply the policy's jitter to a computed delay using the injected RNG. */\nexport function applyJitter(policy: RetryPolicy, delayMs: number, rng: Rng): number {\n if (policy.jitter === 'none') return delayMs;\n return rng.next() * delayMs; // full jitter: uniform in [0, delayMs)\n}\n\n/**\n * The store's read retry, as a write path takes it for the reads it makes along the way: a policy (the default when\n * absent) and what {@link withRetry} needs to run it.\n */\nexport type ReadRetry = RetryDeps & { readonly policy?: RetryPolicy };\n\n/**\n * Run a read under `retry`, or once when there is none. For the reads a write makes along the way (a load guard's\n * read of the current generation, an erasure's reads of the generations it rewrites and verifies): a read is safe\n * to repeat, so a transient fault on one is retried under the store's policy rather than failing the whole write.\n * The write itself is never passed here.\n */\nexport function retryRead<T>(op: () => Promise<T>, retry: ReadRetry | undefined): Promise<T> {\n return retry === undefined ? op() : withRetry(op, retry.policy ?? DEFAULT_RETRY_POLICY, retry);\n}\n\n/**\n * Run `op`, retrying transient failures per `policy`. Resolves with `op`'s result, or rejects with the last\n * error once attempts are exhausted (or immediately for a non-retryable error). The thrown error is always\n * the *operation's* error \u2014 never a wrapper \u2014 so callers keep their typed-error branching.\n */\nexport async function withRetry<T>(\n op: () => Promise<T>,\n policy: RetryPolicy,\n deps: RetryDeps,\n): Promise<T> {\n const retryable = deps.isRetryable ?? isTransientError;\n // `Math.max(1, x)` guards 0 and negatives but NOT NaN \u2014 `Math.max(1, NaN)` is NaN, and `1 <= NaN` is false,\n // so the loop below would never execute: `op()` never called, and the function rejects with the literal\n // `undefined` from `lastErr`. Every retried read would fail without touching the backend, and callers would\n // catch a non-Error. Reachable from ordinary wiring \u2014 `maxAttempts: Number(process.env.X)` with the\n // var unset is NaN. Fail loudly instead, and floor it so a fractional value can't sleep on a final attempt\n // that never happens.\n if (!Number.isFinite(policy.maxAttempts) || policy.maxAttempts < 1) {\n throw new ValidationError(\n `retry.maxAttempts must be a finite number >= 1 (got ${String(policy.maxAttempts)})`,\n );\n }\n const attempts = Math.floor(policy.maxAttempts);\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n return await op();\n } catch (err) {\n lastErr = err;\n if (attempt >= attempts || !retryable(err)) throw err;\n const delayMs = applyJitter(policy, backoffDelayMs(policy, attempt), deps.rng);\n try {\n deps.onRetry?.({ attempt, delayMs, err });\n } catch {\n // Observability is best-effort, as for a metrics sink (`safeMetrics`): a throwing hook must neither abort\n // the retry nor replace the operation's error with its own. This module has no sink to report it to.\n }\n await deps.clock.sleep(delayMs);\n }\n }\n // Unreachable: the loop either returns or throws. Satisfies the type checker.\n throw lastErr;\n}\n", "/**\n * `loadSegment` \u2014 the loaded store's primary write path, as one call.\n *\n * A load is always the same four steps: take the next generation number, write one immutable object, move the\n * pointer, collect what the move superseded. Inside the library each is its own internal function, and the\n * step a hand-composed load leaves out is the last one, so a store that composed it would accumulate superseded\n * generations it keeps paying for.\n * Composing it here, once, is the point.\n *\n * The other reason it is one call is the **guard**. A load REPLACES a segment: whatever the stream contains is\n * what the segment contains afterwards. That makes an upstream query returning fewer rows than usual \u2014 or none \u2014\n * a silent, successful-looking wipe, and nothing downstream can tell it from a legitimate shrink. The guard is\n * the place to say what a plausible result looks like, and it has to sit **between the write and the publish**,\n * because that is the only moment where the new content is known and the old one is still authoritative.\n *\n * Refusing therefore has to clean up after itself. The object is already durable at that point, and it sits\n * ABOVE `currentGen`, where generation collection deliberately never looks (it deletes strictly below the\n * pointer), so a refused load deletes its own object before returning, while the segment's row is unchanged, gone\n * or `destroyed`. Once another write has changed the row, the object's number may name a re-created segment's live\n * object, so it stays, and collection takes it like any other generation once one above it is current.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { type CodecInterface, requireCodec } from './codec';\nimport {\n bulkLoadCrbmGeneration,\n holdsObject,\n openGenerationReader,\n provesOwnObject,\n publishGeneration,\n} from './crbm-storage-source';\nimport type { Clock, Rng } from './determinism';\nimport { aadFor } from './crypto';\nimport type { Aead, CrbmCrypto, IKeystore } from './crypto';\nimport {\n KeyUnavailableError,\n ValidationError,\n isNotFoundError,\n isValidationError,\n isWriteConflictError,\n} from './errors';\nimport { collectAfterLoad, nextLoadGeneration } from './generation-gc';\nimport { type LoadInput, prepareLoadInput } from './load-input';\nimport { copiedMetadata } from './metadata';\nimport { type ReadRetry, retryRead } from './retry';\nimport { assertRegistryCanWrite } from './ports';\nimport type {\n GenerationMetadata,\n IStorageDriver,\n IRegistryDriver,\n RegistryRecord,\n SegmentRef,\n Token,\n} from './ports';\nimport { usableSummary } from './summary';\nimport { validateUserRef } from './validate';\n\n/** What {@link loadSegment} needs: the objects, the pointer, the codec, and key material if encrypted. */\nexport interface LoadDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n readonly codec?: CodecInterface;\n readonly keystore?: IKeystore;\n readonly requireEncryption?: boolean;\n readonly clock?: Clock;\n /**\n * The random source that spreads the waits between a publish's fresh compare-and-swaps (a random time under each\n * bound). Absent, the read retry's source is used if it has one, and otherwise each wait is its bound.\n */\n readonly rng?: Rng;\n /**\n * The store's read retry, for the read the guard makes of the current generation: a transient fault there is run\n * again under it rather than failing the load. Absent, the read is made once. It does not govern the write: a\n * registry write that gets no answer is settled by reading the row, and sent again only as a bounded fresh\n * compare-and-swap.\n */\n readonly readRetry?: ReadRetry;\n /**\n * Collect by listing the segment's objects after the publish, whatever `keep` is. Absent, a load that keeps at\n * most one generation and found nothing above its pointer deletes by name the one generation its publish pushed\n * out of the window, and lists every sixteenth generation to take what that leaves; the generations older than\n * the window wait for that listing. Set it for a caller whose `keep` promises every generation below the new\n * one beyond the window is gone when the call returns: the `*Into` verbs, whose `keep` is how an operator clears\n * a destination that earlier materialisations kept in full.\n */\n readonly collectByListing?: boolean;\n}\n\n/**\n * What a plausible result looks like, so an implausible one is refused instead of published.\n *\n * Both bounds are compared against the **current** generation's cardinality, read from the `.crbm` index rather\n * than its payload \u2014 one object-header read, and only when a bound is actually set.\n */\nexport interface LoadGuard {\n /** Refuse a generation with fewer than this many ids. Use for \"this segment is never legitimately tiny\". */\n readonly minCardinality?: number;\n /**\n * Refuse a generation that retains less than this fraction of the current one \u2014 `0.5` means \"at least half the\n * ids must survive\". A *growing* load is never refused by this bound, and neither is the first load of a\n * segment, which has nothing to shrink from.\n *\n * Phrased as a floor rather than a ceiling on the loss so that `0` means the same thing here as it does on\n * `minCardinality` above: **no bound**. The inverse spelling (`maxShrink`) would make `0` mean \"refuse any\n * shrink at all\" \u2014 maximally strict \u2014 sitting in the same object literal as a field where `0` is maximally\n * permissive, which is where an inversion would eventually be written and not noticed.\n */\n readonly minRetained?: number;\n}\n\nexport interface LoadOptions {\n /**\n * Publish an empty generation over a non-empty one. Off by default: an empty result is far more often an\n * upstream failure than an intent, and the failure is indistinguishable from success once it lands.\n */\n readonly allowEmpty?: boolean;\n readonly guard?: LoadGuard;\n /**\n * Generations to keep below the new pointer as a grace window (default 1). A non-negative integer: anything\n * else (negative, fractional, `NaN`, infinite) throws `ValidationError` before anything is written. Keep at\n * least one: a read that is\n * still fetching chunks from the just-superseded generation would otherwise have it deleted out from under it\n * and pay a re-resolve. A wider window costs storage, and it is what a pinned handle needs: a pin is never\n * re-resolved, so a chunk a pinned read has not fetched fails with `NotFoundError` once its generation is\n * collected. Keep at least one generation for every one that can be written above the pinned one while your\n * longest pinned job runs, on every writer that loads the segment \u2014 see \"Generations and `keep`\" in the loading\n * guide.\n *\n * With `keep` of 0 or 1, a load that found nothing above the pointer collects without listing: it deletes the one\n * generation its publish pushed out of the window, and lists the segment's objects on every sixteenth generation to\n * take whatever that pass leaves, such as the generations an earlier, wider `keep` held. With `keep` of 2 or more\n * it lists on every load, and so does one whose check found the current generation's object gone: the guard's read of\n * it, or, when the guard took the size from the row's summary, one zero-byte read made before a `keep` of 1 takes a\n * name. A `keep` at least the generation published collects nothing and asks for nothing.\n */\n readonly keep?: number;\n /**\n * Small, immutable metadata of your own for this generation: what it was computed from, such as a definition's\n * version, the time its data landed, or a run id. A flat object of string keys and string or finite-number values,\n * at most 1,024 bytes as canonical JSON (keys sorted by UTF-16 code unit, no whitespace), and no key longer than 128\n * bytes. It is stored in the generation's object, and written to the segment's row by the write that makes the\n * generation current, so whoever sees this generation as current sees its metadata. It never changes once written:\n * a new generation is how it changes. `undefined` and the empty object store nothing. Anything else that breaks a\n * rule (a boolean, `null`, an array, nesting, `NaN`, a key that is not well-formed text) throws `ValidationError`\n * before a request is made. **Never put a subject's id in it**: an erasure rewrites the ids and carries the metadata\n * over as it is. Sealed under the segment's key when the segment is encrypted.\n */\n readonly metadata?: GenerationMetadata;\n readonly audit?: IAuditSink;\n}\n\n/** Why a load did not become current. */\nexport type LoadRefusal =\n | 'empty'\n | 'min-cardinality'\n | 'min-retained'\n /**\n * Another writer got there first. Either another load wrote the same generation number first, so the write-once\n * put refused this one and it wrote nothing (`size: 0`); or the segment's registry row changed while the load was\n * writing: another load published, a retention change, a rollback or an erasure wrote the row, or the row was\n * deleted. Once another write has changed the row, the object stays in the bucket, because its generation number\n * may by then name another incarnation's live object; once the row is `destroyed` (dropped or crypto-shredded),\n * the object is deleted, and once the row is gone, so is an object its footer proves this load's own.\n */\n | 'superseded';\n\nexport interface LoadResult {\n /**\n * The generation written. Present even when refused: a refusal deletes that object while the segment's row is\n * unchanged or `destroyed`, or gone once its footer proves it this load's own, and leaves it in the bucket once another write has changed the row. A load that lost its\n * generation number to another wrote nothing, and reports `size: 0`.\n */\n readonly generation: number;\n /** Whether this generation is now the segment's current one. */\n readonly published: boolean;\n /** Set only when `published` is false. */\n readonly reason?: LoadRefusal;\n readonly size: number;\n readonly sha256: string;\n readonly chunkCount: number;\n /** Distinct ids in the generation that was written, post-dedup. */\n readonly cardinality: number;\n /**\n * What the segment held when the guard judged it \u2014 `null` when there was no current generation, or when no\n * bound needed it. Returned because an alert on `reason: 'min-retained'` is useless without it: \"refused\" is a\n * page, \"refused: 4 \u2192 1\" is a diagnosis, and re-reading the segment to find out costs a round trip and races\n * whatever happens next.\n */\n readonly cardinalityBefore: number | null;\n /**\n * The superseded generations collected after publishing. Empty when nothing was published.\n *\n * When collection deleted by name (see {@link LoadOptions.keep}) this is the name it deleted, and that generation\n * may have been gone already: a delete of an absent object succeeds on every backend and does not say so, so the\n * list names what the pass asked the bucket to delete, not what it found there. A listing pass names the\n * generations it found and deleted. Neither is a receipt, since a concurrent collector may have taken a generation\n * first.\n */\n readonly collected: readonly number[];\n}\n\n/** What the guard learned of the segment's current generation, and how it learned it. */\ninterface CurrentSize {\n /** The number of ids it holds, or `null` when there is no current generation to compare against, or its object is gone. */\n readonly cardinality: number | null;\n /** Whether the number came from the row's summary, so the object was not opened and nothing says it is there. */\n readonly fromSummary: boolean;\n}\n\n/**\n * Cardinality of the segment's current generation, from the row's summary of it when the row has one it can use, and\n * otherwise from the `.crbm` index rather than its payload. `null` when there is no current generation to compare\n * against.\n *\n * A summary is used only for the generation it names, in the shape the row's keys call for, and a sealed one only if\n * it opens: anything else is no summary, and the tail is read exactly as it was before rows carried one. Using it\n * opens nothing, so a row that names an object that is gone is judged by what it remembers of it, and a caller that\n * must know the object is there (the collection after the publish) looks for itself.\n *\n * The crypto is derived **here** rather than taken from the caller, and that is not a convenience. AAD is bound\n * to a specific generation, so a `CrbmCrypto` passed in from outside would have to be pre-bound to whichever\n * generation turns out to be current \u2014 which only this function learns, by reading the row. A caller cannot\n * satisfy that seam even in principle, so offering it would be offering a parameter nobody can fill correctly.\n */\nasync function currentCardinality(\n ref: SegmentRef,\n deps: LoadDeps,\n record: RegistryRecord | null,\n): Promise<CurrentSize> {\n if (record === null || record.currentGen === null || record.status === 'destroyed') {\n return { cardinality: null, fromSummary: false };\n }\n const generation = record.currentGen;\n let aead: Aead | undefined;\n let crypto: CrbmCrypto | undefined;\n const wrapped = record.wrappedDeks;\n if (wrapped !== undefined && wrapped.length > 0) {\n if (deps.keystore === undefined) {\n throw new KeyUnavailableError(\n `segment \"${ref.segment}\" is encrypted but load was given no keystore`,\n );\n }\n aead = await deps.keystore.openDek(wrapped);\n const opened = aead;\n crypto = { aead: opened, aadFor: (scope) => aadFor(ref, generation, scope) };\n }\n const described = usableSummary(ref, record, aead);\n if (described !== undefined) return { cardinality: described.cardinality, fromSummary: true };\n let reader;\n try {\n reader = await retryRead(\n () => openGenerationReader(deps.storage, { ...ref, generation }, crypto),\n deps.readRetry,\n );\n } catch (err) {\n // The row names a generation whose OBJECT is gone \u2014 the `missing-storage-generation` state a consistency\n // check reports, produced by a partial `dropSegment`, a bucket lifecycle rule, or a restore that brought\n // the registry back without the bucket.\n //\n // `null`, not a throw. The guard exists to protect ids that are still there, and these are already gone:\n // refusing to write would leave the segment unreadable AND unrepairable, which is the opposite of what a\n // guard is for. Writing over it is precisely the repair, and it is what this path did before the guard\n // reached it. A caller who wants to be told instead can run `checkConsistency()`, whose job that is.\n if (!isNotFoundError(err)) throw err;\n return { cardinality: null, fromSummary: false };\n }\n let total = 0;\n for (const n of reader.cardinalities().values()) total += n;\n return { cardinality: total, fromSummary: false };\n}\n\n/**\n * Replace a segment's contents with `input`, as one immutable generation, and make it current.\n *\n * `input` is ids, or a whole bitmap as `{ serialized }` portable Roaring bytes or a `{ bitmap }` with\n * `serialize('portable')` ({@link LoadInput}). A bitmap input is checked, decoded and serialized before the first\n * request, and is written from its own chunks; the generation is byte for byte the one its ids would write.\n *\n * Returns what was written and whether it became current. A refusal is **not** an exception: `published: false`\n * with a `reason` is a normal outcome a caller branches on, in the same shape as a successful load, because the\n * interesting cases (a guard tripped, a racing writer won) are operational facts rather than faults.\n */\nexport async function loadSegment(\n ref: SegmentRef,\n input: LoadInput,\n deps: LoadDeps,\n options: LoadOptions = {},\n): Promise<LoadResult> {\n validateUserRef(ref);\n // Before any round trip: a core caller that forgot the codec learns it from this call's name, not the loader's.\n const codec = requireCodec(deps.codec, 'loadSegment');\n const keep = options.keep ?? 1;\n if (!Number.isInteger(keep) || keep < 0) {\n throw new ValidationError(`keep must be a non-negative integer; got ${String(keep)}`);\n }\n const guard = options.guard;\n if (guard?.minCardinality !== undefined) {\n const m = guard.minCardinality;\n if (!Number.isInteger(m) || m < 0) {\n throw new ValidationError(\n `guard.minCardinality must be a non-negative integer; got ${String(m)}`,\n );\n }\n }\n if (guard?.minRetained !== undefined) {\n const s = guard.minRetained;\n if (!Number.isFinite(s) || s < 0 || s > 1) {\n throw new ValidationError(`guard.minRetained must be a fraction in 0..1; got ${String(s)}`);\n }\n }\n // The metadata as of this call, checked and copied before any round trip: what is stored is what the caller passed\n // now, however long the load runs and whatever its object does meanwhile.\n const metadata = copiedMetadata(options.metadata, (message) => {\n throw new ValidationError(message);\n });\n const audit = safeAudit(options.audit ?? NOOP_AUDIT);\n // Still before any round trip: a malformed input costs none, and a `{ bitmap }` is the bitmap as of this call.\n const ids = prepareLoadInput(input, codec);\n // Before the first request: the generation is written before the row, so a registry that cannot write a row\n // would leave it behind.\n assertRegistryCanWrite(deps.registry, 'load');\n\n // One row read, and on a cleartext segment the only one before the publish: the guard's \"before\", the incarnation\n // this call is acting on, the pointer it derived its decision from, the number it takes next, the write's\n // destroyed check, and the publish's first attempt all come from it. Reusing it is sound because the publish is\n // fenced on this row (its token, the pointer the guard judged, or its absence), so a row that changes before then\n // makes the publish lose rather than land on the strength of a stale read. The write reads the row again after\n // the ids when this read found none, or found key material (see `bulkLoadCrbmGeneration`'s `row`).\n const row = await deps.registry.get(ref);\n const fromToken: Token | undefined = row?.token;\n const fromGeneration = row?.currentGen ?? undefined;\n\n // Read the \"before\" cardinality ONLY when a bound needs it. The empty guard needs to know whether the current\n // generation is non-empty; `minRetained` needs its size. `minCardinality` compares against the new generation\n // alone, so it costs nothing extra.\n const needsBefore = guard?.minRetained !== undefined || options.allowEmpty !== true;\n const current: CurrentSize = needsBefore\n ? await currentCardinality(ref, deps, row)\n : { cardinality: null, fromSummary: false };\n const before = current.cardinality;\n\n const { generation, checked } = await nextLoadGeneration(ref, deps, row);\n const key = { namespace: ref.namespace, segment: ref.segment, generation };\n\n // `publish: false`, deliberately: the guard has to run while the old generation is still authoritative, so the\n // pointer moves below rather than here. The registry is still passed \u2014 it is where an existing encrypted\n // segment's DEK lives, and reusing that key is not optional \u2014 and the freshly minted one comes back on the\n // result so the deferred publish can store it.\n let written;\n try {\n written = await bulkLoadCrbmGeneration(deps.storage, key, ids, {\n registry: deps.registry,\n publish: false,\n keystore: deps.keystore,\n requireEncryption: deps.requireEncryption,\n codec: deps.codec,\n clock: deps.clock,\n metadata,\n row,\n });\n } catch (err) {\n // Another loader took this generation number first \u2014 write-once refused the second put. That is a lost race,\n // and `LoadRefusal` documents a lost race as `'superseded'`; letting a `WriteConflictError` escape here would\n // make a caller who branches on `published` (as the doc-comment tells them to) miss the one outcome they were\n // told to expect. Nothing was written, so there is nothing to clean up; the refusal is still audited, as every\n // other one is, since a downstream reconciliation needs to know the replacement it asked for did not happen.\n if (!isWriteConflictError(err)) throw err;\n audit.onEvent({\n kind: 'segment.load-refused',\n segment: ref.segment,\n namespace: ref.namespace,\n generation,\n reason: 'superseded',\n cardinality: 0,\n });\n return {\n generation,\n published: false,\n reason: 'superseded',\n size: 0,\n sha256: '',\n chunkCount: 0,\n cardinality: 0,\n cardinalityBefore: before,\n collected: [],\n };\n }\n\n /**\n * Reclaim this load's object after a DEFINITE refusal: a guard that said no before any publish, or a publish that\n * answered `false` or threw a refusal of its own. Never after an ambiguous outcome (a lost response, a timeout):\n * a publish that may still land must not find its object gone.\n *\n * The object is durable and sits above `currentGen`, where collection never looks until a generation above it is\n * current, so the refusal reclaims it here, and deletes only what it can prove is its own. A generation number\n * identifies a generation within one incarnation of a row, and nothing more (invariant 1): once the row was\n * purged, the object under this load's key can be another writer's, because a re-created name's numbering restarts\n * and its loads take numbers a drop or a collection freed, this one included. Deleting that would put a row over a\n * missing generation, the forbidden `missing-storage-generation` state; leaving an orphan behind is strictly the\n * better failure.\n *\n * - The same row (an unchanged token): nothing was written since this load read it, so the number is its own.\n * - A `destroyed` row: no reader resolves any of its generations, so whatever is under the key is garbage.\n * - No row, or a row with key material when this load wrote cleartext (a cleartext object has no place in an\n * encrypted segment's bucket, where a rollback could point at it and a shred would attest its bytes unreadable):\n * the object under the key must be proved this load's by its fingerprint, from one read of its footer. One that\n * is gone, is another object, or cannot be read is kept.\n */\n const reclaim = async (): Promise<void> => {\n const now = await deps.registry.get(ref);\n if (now !== null && (now.token === fromToken || now.status === 'destroyed')) {\n await deps.storage.delete(key);\n return;\n }\n const keyed = now !== null && now.wrappedDeks !== undefined && now.wrappedDeks.length > 0;\n if (\n (now === null || (keyed && !written.encrypted)) &&\n (await holdsObject(deps.storage, key, written.fingerprint))\n ) {\n await deps.storage.delete(key);\n }\n };\n\n // Set when a registry write of this load's publish ended without an answer: a refusal after that cannot say whether\n // the write landed first.\n let unanswered = false;\n\n const refuse = async (reason: LoadRefusal): Promise<LoadResult> => {\n await reclaim();\n audit.onEvent({\n kind: 'segment.load-refused',\n segment: ref.segment,\n namespace: ref.namespace,\n generation,\n reason,\n cardinality: written.cardinality,\n ...(unanswered ? { unanswered: true as const } : {}),\n });\n // Constructed field by field, never spread from the write result. That result now carries `wrappedDeks` \u2014\n // wrapped key material \u2014 and a spread would put it on a public, JSON-serialisable object that a load job\n // will reasonably `logger.info({ result })`. Declared shapes are not a filter at runtime.\n return {\n generation,\n published: false,\n reason,\n size: written.size,\n sha256: written.sha256,\n chunkCount: written.chunkCount,\n cardinality: written.cardinality,\n cardinalityBefore: before,\n collected: [],\n };\n };\n\n if (written.cardinality === 0 && options.allowEmpty !== true && (before ?? 0) > 0) {\n return refuse('empty');\n }\n if (guard?.minCardinality !== undefined && written.cardinality < guard.minCardinality) {\n return refuse('min-cardinality');\n }\n if (guard?.minRetained !== undefined && before !== null) {\n if (written.cardinality < before * guard.minRetained) return refuse('min-retained');\n }\n\n // Fence the publish on the row the guard judged.\n //\n // Forward-only is right for an UNGUARDED load: its ids come from upstream, so losing a race costs nothing that\n // the winner did not also bring. A guarded load is a different animal \u2014 it derived its decision from a\n // particular generation's cardinality, which makes it exactly the writer invariant 1 says must publish with\n // `expectFrom`. Without that, anything published between the \"before\" read and this publish voids the guard's\n // premise while the guard still reports success: reproduced, two loaders on a fresh segment let an EMPTY\n // generation land over a thousand ids, under default options, because `before` was read as \"no row\".\n //\n // `expectToken` goes on regardless. It is incarnation identity rather than a derivation fence, it costs\n // nothing legitimate \u2014 a token only changes when the row does \u2014 and it is what stops this call publishing\n // into a segment that merely reuses the name it started with.\n let published: boolean;\n try {\n published = await publishGeneration(deps.registry, key, {\n row,\n wrappedDeks: written.wrappedDeks,\n summary: written.summary,\n cleartext: !written.encrypted,\n ...(fromToken === undefined ? {} : { expectToken: fromToken }),\n ...(needsBefore && fromGeneration !== undefined ? { expectFrom: fromGeneration } : {}),\n // The third case, and the one the two fences above structurally cannot cover: the guard judged a segment\n // that had NO ROW. Both `expectFrom` and `expectToken` compare against a value read from a row, so with no\n // row both are omitted and the publish becomes a bare forward-only advance \u2014 which lands over anything\n // that appeared in between. `before` was `null`, so the empty and `minRetained` bounds had nothing to\n // judge and passed vacuously. Verified: an empty generation published over a thousand ids that a\n // concurrent writer had created meanwhile, reporting success. A guarded write therefore has to fence on\n // the ABSENCE it relied on, exactly as it fences on the pointer it relied on.\n ...(needsBefore && row === null ? { expectAbsent: true } : {}),\n // A registry write that fails without an answer is reconciled by reading the row: the pointer at this number\n // is this load's publish only over the object this load wrote, which one footer read proves.\n holdsOwnObject: () => provesOwnObject(deps.storage, key, written.fingerprint),\n // And the wait before a fresh write, when the first left the row as it was.\n clock: deps.clock,\n rng: deps.rng ?? deps.readRetry?.rng,\n onUnanswered: () => {\n unanswered = true;\n },\n });\n } catch (err) {\n // A refusal the publish states by throwing is as definite as a `false`: each of these is raised before that\n // attempt's compare-and-swap or create is sent, so nothing landed from it. `publishGeneration` raises three: a\n // `destroyed` row (no fence answered first, as for an unguarded load that found no row), new key material for a\n // segment that already has a generation, and a cleartext object for a row with key material. The registries raise\n // a `ValidationError` only from checks made before a write is sent (the ref, the record or patch, the row's size\n // cap), and never a `KeyUnavailableError`. Anything else may still land, and keeps the object: above all the\n // `TransientError` of a registry write the publish could not settle by reading the row back.\n if (isValidationError(err) || err instanceof KeyUnavailableError) await reclaim();\n throw err;\n }\n if (!published) return refuse('superseded');\n\n audit.onEvent({\n kind: 'segment.publish',\n segment: ref.segment,\n namespace: ref.namespace,\n generation,\n });\n\n // The guard's read of the current generation found its object gone: the row named a generation that is not in the\n // bucket, so the generation below the new one that a listing keeps as the window is the one a name would take.\n // This load repairs the gap, and lists. `before` is null for a row that names a generation only when its object was\n // not found (a row with no pointer numbers 0, which has nothing below it to take, and a destroyed row is refused\n // at its publish). A guard that took the size from the row's summary opened nothing, so it learned nothing about\n // the object: the collection looks for itself, with one zero-byte read of it, only if it is about to delete by name\n // a generation the window keeps. A load that made no read at all (`allowEmpty` without `minRetained`) cannot tell.\n const currentObjectGone = needsBefore && before === null;\n const collected = await collectAfterLoad(ref, deps, {\n generation,\n keep,\n byName: checked && !currentObjectGone && deps.collectByListing !== true,\n ...(current.fromSummary && fromGeneration !== undefined\n ? { proveCurrent: { ...ref, generation: fromGeneration } }\n : {}),\n });\n return {\n generation,\n published: true,\n size: written.size,\n sha256: written.sha256,\n chunkCount: written.chunkCount,\n cardinality: written.cardinality,\n cardinalityBefore: before,\n collected,\n };\n}\n", "/**\n * `listGenerations` and `rollbackSegment` \u2014 see what a segment has been, and put it back.\n *\n * Immutable generations mean the previous version of a segment is usually still sitting in the bucket: a load\n * that replaced it did not overwrite anything, it wrote a new object and moved a pointer. So recovering from a\n * bad load is, in principle, moving the pointer back, which no load, publish or sweep can do, because every other\n * write path in the library only ever advances the pointer, and refuses a move that would lower it.\n *\n * That refusal is right for a *writer*: a load whose ids came from upstream loses nothing by being out-raced, and\n * letting it regress the pointer would let a slow loader silently undo a fast one. It is wrong for an *operator*,\n * who has looked at the segment, decided the current generation is wrong, and knows which one they want. So\n * rollback is the one call that moves the pointer backwards, and it is deliberately not reachable from any\n * automatic path \u2014 no sweep, no retry, no reconciliation calls it.\n *\n * It is audited for the same reason. Every other pointer move can be reconstructed from \"a load happened\"; this\n * one is a human overriding the ordering rule the rest of the system relies on, which is exactly the event an\n * Art. 30 record or an incident review wants to find.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { openRollbackTarget, provesOwnObject } from './crbm-storage-source';\nimport { aadFor } from './crypto';\nimport type { Aead, CrbmCrypto, IKeystore } from './crypto';\nimport { IntegrityError, NotFoundError, ValidationError, isIntegrityError } from './errors';\nimport type {\n IStorageDriver,\n GenerationMetadata,\n IRegistryDriver,\n RegistryRecord,\n RegistrySummary,\n SegmentRef,\n} from './ports';\nimport { summaryOf, usableSummary } from './summary';\nimport { validateUserRef } from './validate';\n\n/** What the generation helpers need: the objects, and the pointer that says which one is current. */\nexport interface GenerationListDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n /**\n * The key of an encrypted segment, so a rollback can read its target's count and metadata and seal them into the\n * row. Without it, a rollback of an encrypted segment still works and leaves the row with no summary of the\n * generation it moves to, which a reader then takes from the object.\n */\n readonly keystore?: IKeystore;\n}\n\n/** One generation present in the bucket. */\nexport interface GenerationEntry {\n readonly generation: number;\n /** Whether the registry pointer currently names this generation. */\n readonly current: boolean;\n /**\n * The current generation's id count, from its registry row, with no read of the object. Present only on the current\n * entry, and only when the row's summary can be used: for an encrypted segment that takes a keystore that opens the\n * segment's key. It is the row's word, not confirmed against the object.\n */\n readonly cardinality?: number;\n /** The metadata the current generation was loaded with, from the same row; absent when it has none. */\n readonly metadata?: GenerationMetadata;\n}\n\n/**\n * Every generation still present for a segment, ascending, with the current one marked.\n *\n * This is what the bucket holds, not what the segment has ever been: generation collection deletes superseded\n * objects, so the list is the grace window plus whatever has not been collected yet. It is the set a\n * {@link rollbackSegment} can choose from, which is the reason to look at it.\n *\n * One registry read and one `list` call, whether or not the segment has a registry row, so it also finds the\n * objects a purged row left behind. It does not open the objects, so it costs nothing per generation and tells you\n * nothing about their contents \u2014 read the sizes through the store's own report if you need them. The current entry\n * carries the id count and metadata its row records, from the same read.\n */\nexport async function listGenerations(\n ref: SegmentRef,\n deps: GenerationListDeps,\n): Promise<GenerationEntry[]> {\n validateUserRef(ref);\n const record = await deps.registry.get(ref);\n const current = record?.currentGen ?? null;\n const seen = new Set<number>();\n for await (const key of deps.storage.list(ref)) seen.add(key.generation);\n const described = record === null ? undefined : await currentDescription(ref, record, deps);\n return [...seen]\n .sort((a, b) => a - b)\n .map((generation) =>\n generation === current\n ? { generation, current: true, ...described }\n : { generation, current: false },\n );\n}\n\n/**\n * What the row's summary says of its current generation, or `undefined` when it says nothing usable. A key that\n * cannot be opened leaves it out, as a rollback does: the entry is then listed without a description.\n */\nasync function currentDescription(\n ref: SegmentRef,\n record: RegistryRecord,\n deps: GenerationListDeps,\n): Promise<{ cardinality: number; metadata?: GenerationMetadata } | undefined> {\n if (record.summary === undefined) return undefined;\n let aead: Aead | undefined;\n if (\n 'sealed' in record.summary &&\n record.wrappedDeks !== undefined &&\n deps.keystore !== undefined\n ) {\n try {\n aead = await deps.keystore.openDek(record.wrappedDeks);\n } catch {\n return undefined;\n }\n }\n const usable = usableSummary(ref, record, aead);\n if (usable === undefined) return undefined;\n const metadata = usable.metadata;\n return metadata === undefined || Object.keys(metadata).length === 0\n ? { cardinality: usable.cardinality }\n : { cardinality: usable.cardinality, metadata: Object.freeze({ ...metadata }) };\n}\n\nexport interface RollbackResult {\n /** The generation the pointer named before the rollback. `null` when the segment had no current generation. */\n readonly fromGeneration: number | null;\n /** The generation the pointer names now. */\n readonly generation: number;\n}\n\n/**\n * Move a segment's pointer **back** to a generation that is still in the bucket.\n *\n * The only non-forward-only publish in the library, and the only one an operator has to ask for by name. It\n * refuses rather than guesses:\n *\n * - a generation whose object is **not in the bucket** \u2014 collected, or never written \u2014 is refused with\n * `NotFoundError` rather than pointed at, because a pointer naming a missing object is the one state the whole\n * design exists to avoid;\n * - a **crypto-shredded** segment is refused: every generation of it is unreadable, so a rollback would produce a\n * segment that resolves and then fails;\n * - a target that is **not what the row says the segment is** is refused with `IntegrityError`: a cleartext object\n * under a row with keys, or an encrypted one under a row with none, either of which every read would then refuse.\n * One tail read of the target decides it, as does a footer that fails its own checks;\n * - rolling to the generation **already current** is a no-op that reports itself, not an error.\n *\n * The same tail read opens the target when the segment's key is at hand, and the rollback writes the target's own id\n * count and metadata into the row, in the compare-and-swap that moves the pointer: a reader that sees the target as\n * current sees what describes it. (One tail read, and a range read as well when the target's index is longer than the\n * tail read: a target of very many chunks.) A store with no keystore, or one that cannot open the segment's key for any\n * reason, a keystore that is unreachable or timed out included, still rolls an encrypted segment back and leaves the\n * row with no summary of the target. The undo of a rollback whose target was collected meanwhile puts back the summary\n * the row had, and so does the undo of an `allowForward` rollback whose target was replaced meanwhile: an object above\n * the pointer can be deleted by an erasure and its number taken by a load, between the read and the swap, so after the\n * swap such a target is read once more, and the pointer put back unless it is the object whose summary was written.\n *\n * What it does **not** do is delete anything. The generations above the new pointer stay in the bucket, which is\n * what makes the rollback reversible \u2014 roll forward again by naming one of them. They are also then *above*\n * `currentGen`, where generation collection never looks. They stay until loads pass them (the first load whose\n * number one of them holds numbers above them all, and collection then keeps the newest `keep` of what is below its\n * pointer), `dropSegment` deletes them, or a subject erasure\n * deletes them: all of those present when it rewrites, and only those holding the id when the current generation does not.\n * Not collecting them is a deliberate trade: an operator who has just undone a bad load should not have the\n * evidence collected out from under them, while a rollback target that still holds erased data would make the\n * erasure undoable by an ordinary operator action.\n */\nexport async function rollbackSegment(\n ref: SegmentRef,\n toGeneration: number,\n deps: GenerationListDeps,\n options: { audit?: IAuditSink; allowForward?: boolean } = {},\n): Promise<RollbackResult> {\n validateUserRef(ref);\n if (!Number.isInteger(toGeneration) || toGeneration < 0) {\n throw new ValidationError(\n `rollback: generation must be a non-negative integer; got ${String(toGeneration)}`,\n );\n }\n\n const record = await deps.registry.get(ref);\n if (record === null) {\n throw new NotFoundError(`rollback: no registry row for segment \"${ref.segment}\"`);\n }\n if (record.status === 'destroyed') {\n throw new ValidationError(\n `rollback: segment \"${ref.segment}\" is destroyed (crypto-shredded) \u2014 every generation of it is unreadable`,\n );\n }\n if (record.currentGen === toGeneration) {\n return { fromGeneration: record.currentGen, generation: toGeneration };\n }\n\n // A first look, for the affordance rather than the safety: an operator who named a collected generation needs\n // to be told what IS available, and that is much nicer to produce before anything has moved.\n const present = new Set<number>();\n for await (const key of deps.storage.list(ref)) present.add(key.generation);\n if (!present.has(toGeneration)) {\n const available = [...present].sort((a, b) => a - b);\n throw new NotFoundError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" is not in the bucket` +\n (available.length === 0\n ? ' (no generations remain)'\n : ` \u2014 present: ${available.join(', ')}`),\n );\n }\n if (\n record.currentGen !== null &&\n toGeneration > record.currentGen &&\n options.allowForward !== true\n ) {\n // Above the pointer is not \"a later version of this segment\". It is where objects live that were never\n // authoritative: a load that wrote its object and died before publishing, and a guard-refused load whose\n // cleanup was skipped because the row had changed. Rolling onto one of those makes current the very\n // generation a guard refused \u2014 an empty one, typically. The legitimate above-pointer case is undoing a\n // rollback, which is what the opt-in is for.\n throw new ValidationError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" is above the current pointer ` +\n `(${record.currentGen}) \u2014 it may never have been published. Pass { allowForward: true } if you are ` +\n `undoing an earlier rollback.`,\n );\n }\n\n // The target must be what the row says the segment is, encrypted under its key or cleartext, or every read of the\n // segment would refuse it once the pointer names it. One tail read of it says which it is, with no key needed for that:\n // a cleartext target under a row with keys is a write that never published, from before the segment's key was made,\n // and an encrypted one under a row with none was sealed under a key no row holds. The same read opens the target when\n // the key is at hand, which gives the count and metadata the row's summary of it says, so nothing is read twice. A\n // target whose footer, index or metadata does not open is refused: the pointer is not moved onto what no read opens.\n const keyed = record.wrappedDeks !== undefined && record.wrappedDeks.length > 0;\n let aead: Aead | undefined;\n if (keyed && deps.keystore !== undefined) {\n try {\n aead = await deps.keystore.openDek(record.wrappedDeks!);\n } catch {\n // A key the store cannot open, for any reason (it holds none of the segment's KEKs, one that does not unwrap it, a\n // keystore that is unreachable or timed out) is no key at hand, and the rollback still happens, as it could\n // before it touched the keystore: this is the call an operator reaches for when something is wrong, and the key\n // service being down is often it. The row then carries no summary of the target.\n }\n }\n const crypto: CrbmCrypto | undefined =\n aead === undefined ? undefined : { aead, aadFor: (scope) => aadFor(ref, toGeneration, scope) };\n const { encrypted, reader } = await openRollbackTarget(\n deps.storage,\n { ...ref, generation: toGeneration },\n crypto,\n );\n if (encrypted !== keyed) {\n throw new IntegrityError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" is ${encrypted ? 'encrypted' : 'cleartext'}, ` +\n `but the segment is ${keyed ? 'encrypted' : 'cleartext'}, so no read of the segment could use it; it was ` +\n 'never one of its generations. Roll back to another generation.',\n );\n }\n // The target's own count and metadata, read from its object, in the same write that moves the pointer, so a reader that\n // sees it as current sees them. None when the key was not at hand to open an encrypted target.\n const summary: RegistrySummary | undefined =\n reader === undefined\n ? undefined\n : summaryOf(\n ref,\n toGeneration,\n { cardinality: reader.count(), metadata: reader.metadata },\n aead,\n );\n\n // Fenced on the row this decision was made against. A rollback is the most derived write there is \u2014 an\n // operator looked at a particular state and chose \u2014 so publishing it into a row that has moved since would\n // undo whatever moved it, which is the opposite of what they asked for.\n const { token } = await deps.registry.compareAndSwap(ref, record.token, {\n currentGen: toGeneration,\n summary,\n });\n\n // THE check, and it has to be here rather than above. Every target but an `allowForward` one is below the old\n // pointer, which is precisely generation collection's range \u2014 and a collector never writes the row, so the\n // token fence above cannot see it coming. A listing taken before the swap therefore proves nothing: the object\n // can be collected between that listing and this swap, leaving the pointer naming a missing object.\n //\n // Once the swap lands, the target is safe \u2014 collection only ever takes what is strictly below `currentGen`,\n // and the target now IS `currentGen`. So the ordering is: move first, then verify, and put the pointer back if\n // the object went. Re-pointing can itself be raced, which is why it is fenced on the token the swap returned\n // and why failing to undo is reported rather than swallowed.\n let stillThere = false;\n for await (const key of deps.storage.list(ref)) {\n if (key.generation === toGeneration) {\n stillThere = true;\n break;\n }\n }\n // An object above the old pointer is where collection never looks, but an erasure can delete one and a load can then\n // take its number, between the read of the target and the swap: the number is there, and it is another object than the\n // one the summary just written describes. So a target above the pointer is read once more, by its footer, and is the\n // one that was read or is put back like a vanished one. Below the pointer a number cannot be taken again.\n let replaced = false;\n if (stillThere && reader !== undefined && toGeneration > (record.currentGen ?? -1)) {\n try {\n replaced = !(await provesOwnObject(\n deps.storage,\n { ...ref, generation: toGeneration },\n reader.fingerprint,\n ));\n } catch (err) {\n if (!isIntegrityError(err)) throw err;\n replaced = true; // a footer that no longer checks is not the object that was read\n }\n }\n if (!stillThere || replaced) {\n let undone = false;\n try {\n await deps.registry.compareAndSwap(ref, token, {\n currentGen: record.currentGen,\n summary: describingSummary(record),\n });\n undone = true;\n } catch {\n // Not proof the undo did not land: a swap can apply and still throw, as when its response is lost. Only a\n // read of the row says where the pointer is, so the message below says \"may\", not \"does\".\n }\n throw new NotFoundError(\n `rollback: generation ${toGeneration} of \"${ref.segment}\" was ${replaced ? 'replaced' : 'collected'} while the pointer was moving` +\n (undone\n ? ' \u2014 the pointer was put back'\n : `, and the move back failed, so the pointer may still name ${toGeneration} (a write that landed and lost ` +\n 'its response reads as a failure here). Check which generation is current, then re-run a rollback to a ' +\n 'generation that exists, or restore the object.'),\n );\n }\n\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.rollback',\n segment: ref.segment,\n namespace: ref.namespace,\n fromGeneration: record.currentGen,\n generation: toGeneration,\n });\n\n return { fromGeneration: record.currentGen, generation: toGeneration };\n}\n\n/**\n * The summary a row had, for a write that puts the row back as it was: the one the row carried if it described the\n * generation the row named and had the shape its keys called for, and none otherwise, since a registry refuses to write\n * a summary that does not. A row a rollback is undone onto carries what it carried before the rollback moved it.\n */\nfunction describingSummary(record: RegistryRecord): RegistrySummary | undefined {\n const { summary } = record;\n if (summary === undefined || summary.generation !== record.currentGen) return undefined;\n const keyed = record.wrappedDeks !== undefined && record.wrappedDeks.length > 0;\n return 'sealed' in summary === keyed ? summary : undefined;\n}\n", "/**\n * Subject erasure on a loaded segment (GDPR Art. 17): remove **one id** from a segment by rewriting its current\n * generation without that id.\n *\n * There is no per-id delete on an immutable object, and there is no mutable tier to hold a tombstone, so erasure\n * is what every other write in this library is: a new generation. The current generation is streamed chunk by\n * chunk through the ascending writer \u2014 every chunk decoded, range-checked and re-encoded, the one holding the id\n * with that bit cleared \u2014 then published fenced on the generation it streamed, and that generation is collected immediately\n * (`keep: 0`), so the bit is **physically gone from the bucket when this returns**. Bounded memory: a window of 32\n * chunk reads ahead of the writer, never the whole segment.\n *\n * The rewrite is the same generation without one id, so it keeps everything else: the new object carries the source's\n * metadata as it is, and the row's summary of it, built from what was written, counts one id fewer and holds the same\n * metadata. Metadata is not scanned for the id. On an encrypted segment, a source object with no metadata block whose\n * row's sealed summary of it has metadata is rewritten with the row's: the block's presence is not authenticated, so\n * the authenticated copy decides, and the erasure still goes through.\n *\n * **`erased: true` is a claim about every generation of the segment, not only the one it replaced.** A rollback\n * leaves generations above the pointer that were once current and can be made current again, so a holder can sit\n * above the pointer as well as below it, and there can be several. Before it reports `erased: true` the call\n * therefore lists the bucket once more and reads every generation it has not already seen to be clean; any that\n * still holds the id makes it report or throw instead. In the ordinary case that list returns one generation, the\n * current one, and costs one `list`.\n *\n * What it does NOT reach: backups, replicas and noncurrent object versions hold the old object until their own\n * lifecycle removes it. For an at-rest guarantee that survives those, use encryption plus crypto-shred\n * (`destroySegment`), which is per segment \u2014 a per-id shred is infeasible because one DEK covers the whole segment.\n *\n * Concurrency. This is a **read-modify-write**, not a plain forward-only publish, and that distinction is where\n * its correctness lives: the new generation is `from` minus one bit, so it is a valid successor to `from` and to\n * nothing else. The publish is therefore fenced on `from` **and on the row it was read from**\n * (`publishGeneration`'s `expectFrom` + `expectToken`) and lands only while the pointer is still exactly there,\n * on that same row. Both halves are needed: a generation number identifies a generation only within one\n * incarnation of a name, and a name that is retired and re-created restarts at `0`, so the number alone matched\n * a different segment entirely. Anything else \u2014 a load that published in the meantime, or another\n * erasure that got there first \u2014 is reported as `reason: 'superseded'`, `erased: false`, and the caller re-runs\n * against the new generation. A writer that claims the same generation *number* first surfaces as a\n * `WriteConflictError` (write-once).\n *\n * The same answer covers the read half, and for the same reason. An erasure collects with `keep: 0`, which takes\n * every generation below its new pointer \u2014 so a racing erasure can delete the generation this call is streaming,\n * and (once it is below the winner's pointer) the object this call just wrote. All three exposed round trips (the\n * reader open, any chunk of the whole-segment rewrite, the verify) therefore report a *reason* rather than a bare\n * `NotFoundError`, which was the same event wearing an unactionable face: through `eraseSubject` it became a note\n * the facade documents as ambiguous about which side of the publish it landed on, telling an operator to triage\n * where the truth was to re-run.\n *\n * Which reason comes from **re-reading the row**, not from assuming a supersession: a moved pointer is\n * `'superseded'`, a tombstoned row `'destroyed'`, a purged row `'absent'`, a row with no pointer\n * `'no-generation'`. A pointer still on `from` means the object it names is genuinely gone \u2014 the forbidden\n * `missing-storage-generation` state \u2014 and that throws, because no re-run fixes it. That `NotFoundError` is the only\n * signal of that state, so a faulting re-read rethrows it rather than replacing it with a transient-looking\n * registry error.\n *\n * **A refused rewrite deletes its own object when it would outlive the winner above the pointer.** `putImmutable`\n * commits atomically, so a rewrite whose stream throws leaves no object at all. One that completed is either\n * below the winner's pointer, where the winner's collection takes it, or above it: the case where this call took\n * its number after the winner's object was already in the bucket. Up there nothing collects it, and it was derived\n * from a generation the winner has replaced, so when the winner was another erasure it still holds the id that\n * erasure has just reported gone. That object is deleted before this call returns \u2014 see `discardRefused`.\n *\n * Without that fence the failure was silent and severe, and both halves were reproduced: `nextGeneration` picks a\n * number above everything in the bucket, so a forward-only publish here always won \u2014 discarding a concurrent\n * load's whole set, and letting two concurrent erasures each return `erased: true` while the second one's\n * generation put the first one's id back and collected the generation that had evidenced its removal. A false\n * Art. 17 receipt is the worst output this module can produce.\n *\n * A live read still fetching from the collected generation heals forward to the rewrite, and a pin of it fails with\n * `NotFoundError` for any chunk it has yet to read: that is the documented cost of physical deletion on return. **Do not re-load the id while erasing it**: a load that lands after this rewrite\n * carries whatever its source held, and the library cannot know that source was meant to exclude the id.\n * A load already in flight is the same case: it writes its object above the pointer before it publishes, and an\n * erasure that finds the id in that object deletes it as it does any holder there, so a load that then publishes\n * leaves the pointer naming a missing object.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { MAX_REMAINDER, splitId } from './bit-route';\nimport { ChunkWindow } from './chunk-window';\nimport type { CodecBitmap, CodecInterface } from './codec';\nimport { requireCodec } from './codec';\nimport type { Yielder } from './cooperative';\nimport type { Rng } from './determinism';\nimport {\n objectIsEncrypted,\n openGenerationReader,\n provesOwnObject,\n publishGeneration,\n verifyGeneration,\n writeCrbmGenerationStream,\n} from './crbm-storage-source';\nimport { DEFAULT_MAX_BITMAP_BYTES } from './crbm/format';\nimport type { CrbmReader } from './crbm/reader';\nimport { aadFor } from './crypto';\nimport type { Aead, CrbmCrypto, IKeystore } from './crypto';\nimport {\n IntegrityError,\n KeyUnavailableError,\n ValidationError,\n WriteConflictError,\n isIntegrityError,\n isNotFoundError,\n} from './errors';\nimport { gcOrphanGenerations, nextGeneration } from './generation-gc';\nimport { assertRegistryCanWrite } from './ports';\nimport type {\n GenKey,\n IStorageDriver,\n IRegistryDriver,\n RegistryRecord,\n RegistrySummary,\n SegmentRef,\n} from './ports';\nimport { type ReadRetry, retryRead } from './retry';\nimport { metadataToCarry, summaryOf, usableSummary } from './summary';\nimport { validateUserRef } from './validate';\n\n/** What {@link eraseIdFromSegment} needs: the objects, the pointer, the codec, and the key material if encrypted. */\nexport interface EraseIdDeps {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n /**\n * Bitmap codec \u2014 the rewrite decodes and re-encodes every chunk through it. Optional in the type so this stays\n * call-compatible public API; a **flavor** package binds it (see {@link requireCodec}).\n */\n readonly codec?: CodecInterface;\n /**\n * Keystore for an **encrypted** segment: its registry record carries wrapped DEK(s), and the rewrite reuses\n * that DEK \u2014 unwrapping it to decrypt the old generation and encrypt the new one under a `(segment, generation)`\n * -bound AAD. Rewriting an encrypted segment without a keystore throws {@link KeyUnavailableError}.\n */\n readonly keystore?: IKeystore;\n /** When true, refuse to rewrite a **cleartext** segment (the same guard the read path and a load offer). */\n readonly requireEncryption?: boolean;\n /** Supplying a clock that can yield makes the rewrite cooperative, as it makes a load. */\n readonly clock?: Yielder;\n /**\n * The random source that spreads the waits between the rewrite's fresh compare-and-swaps. Absent, the read retry's\n * source is used if it has one, and otherwise each wait is its bound.\n */\n readonly rng?: Rng;\n /** Per-chunk decode ceiling (invariant 5); defaults to 1 MiB. The `.crbm` reader refuses an entry above its own `maxPayloadBytes` (1 MiB, plus 28 bytes when encrypted) at open, so raise that on the chunk source too. */\n readonly maxBitmapBytes?: number;\n /**\n * The store's read retry, for the reads the rewrite makes along the way: the generation it rewrites and each of its\n * chunks, the read-back that verifies the generation it wrote, and any other generation it checks for the id. A\n * transient fault on one is run again under it rather than failing the erasure. Absent, each read is made once.\n * The deletes are not retried, and the rewrite's registry write is settled as a load's is.\n */\n readonly readRetry?: ReadRetry;\n}\n\nexport interface EraseIdResult {\n readonly segment: string;\n readonly namespace?: string;\n /**\n * True iff the id **was** in the segment \u2014 in its current generation, or in any other generation still in the\n * bucket, above the pointer or below it \u2014 and **no generation of the segment holds it now**. The current\n * generation is one without it, every generation that held it has been deleted (see `collected`), and the call\n * listed the bucket and read what was left before saying so. This is the field an erasure ledger records.\n */\n readonly erased: boolean;\n /**\n * Why the id was not erased, when `erased` is false. `'absent'` (no registry row), `'destroyed'` (a crypto-shred\n * tombstone \u2014 already unreadable), `'no-generation'` (a row with no Storage data yet), `'not-member'` (no\n * generation in the bucket holds the id \u2014 the common case across a fleet scan), or `'superseded'`.\n *\n * **`'superseded'` means this call did not erase the id, not that the id is still there.** Another writer \u2014 a\n * load, another erasure, or a rollback \u2014 moved the pointer off the generation this call read, so what it was\n * doing no longer follows from what is current: a rewrite derived from that generation is not a valid successor\n * to the new one, and a generation it meant to delete above the pointer may be the one the pointer now names.\n * Re-run against the new generation: if the id is still present it is erased then; if the racing writer was\n * another erasure of the *same* id, the re-run reports `'not-member'` because it is already gone. Either way the\n * re-run settles it, which is why it is the documented action for this reason and for no other.\n *\n * The first three reasons are also returned when the row changes *underneath* a call already in flight, not\n * only when it is read up front \u2014 a concurrent `dropSegment` gives `'destroyed'`, a retention sweep that purges\n * the row gives `'absent'`. A caller branching on `reason` never has to care which it was.\n */\n readonly reason?: 'absent' | 'destroyed' | 'no-generation' | 'not-member' | 'superseded';\n /**\n * The generation the id was found in (present whenever one was read) \u2014 the newest of them, when the current\n * generation did not hold it and several others did.\n */\n readonly fromGeneration?: number;\n /**\n * The generation written without the id (present whenever one was written, even if `superseded`). A refused\n * rewrite whose object sits above the winner's pointer deletes that object again before it returns.\n */\n readonly generation?: number;\n /**\n * Generations this call deleted. On a rewrite that is normally `[fromGeneration]`, plus any older orphans. When\n * the current generation did not hold the id, it is every generation below the pointer (`keep: 0` takes them\n * together) and each one above the pointer that held the id; the ones above it that did not hold the id stay,\n * because they are an operator's rollback targets. Empty when nothing was deleted.\n *\n * **It is what THIS call deleted, not the proof that the id is gone** \u2014 those differ: a concurrent collector can\n * take a generation holding the id first, and then `erased: true` is returned without it in `collected`, because\n * the claim is about the bucket rather than about who emptied it. Treat a non-empty `collected` as evidence and\n * a missing holder as \"someone else got there\", never as a failure.\n *\n * A call that cannot establish the claim **throws** `WriteConflictError` or the underlying fault rather than\n * report `erased: true` over bytes that are still there. Four ways that happens: a Storage `delete` fault; a\n * collection pass that could not prove the segment was still the same one (which an ordinary retirement landing\n * mid-call is enough to cause); the same refusal on the path where the current generation did not hold the id\n * and nothing was published at all; and a generation still holding the id when the bucket is listed at the end\n * \u2014 one an operator rolled the pointer onto while a rewrite was collecting, say.\n *\n * **Re-run it**, and read what the re-run says rather than assuming it finished the job. The re-run looks for\n * the id in every generation in the bucket, not only the current one, and there are three outcomes:\n *\n * - `erased: true` naming the generation it found the id in \u2014 the ordinary case, and the one that gives you\n * the receipt the failed call could not;\n * - `'not-member'` \u2014 a racing collector took that generation first. The bit is gone, but **no run reports a\n * receipt for it**, so keep the failed call's error alongside your ledger if you need the audit trail;\n * - `'absent'` \u2014 the segment's registry row is gone, so it is no longer a segment at all and\n * `store.eraseSubject` will not even scan it. Anything left in the bucket is an **orphan**: list it with\n * `listGenerations` (`store.generations`) and delete it with `dropSegment` (`store.dropSegment`), which both\n * read the bucket as well as the row. `checkConsistency()` and the generation collection start from the row, so\n * neither reaches a segment that has none.\n */\n readonly collected: readonly number[];\n}\n\n/**\n * Remove `id` from `ref`: rewrite its current generation without it, and delete every other generation that holds\n * it. See the module note for the contract.\n *\n * Emits one `segment.rewrite` audit event at the publish (before the superseded generation is collected), so the\n * compliance record exists the moment the generation without the id is authoritative.\n */\nexport async function eraseIdFromSegment(\n ref: SegmentRef,\n id: number,\n deps: EraseIdDeps,\n options: { audit?: IAuditSink } = {},\n): Promise<EraseIdResult> {\n validateUserRef(ref);\n const { chunkKey, remainder } = splitId(id); // validates the u32 range\n const codec = requireCodec(deps.codec, 'eraseIdFromSegment');\n const maxBytes = deps.maxBitmapBytes ?? DEFAULT_MAX_BITMAP_BYTES;\n const base = { segment: ref.segment, namespace: ref.namespace };\n /** A read this call makes, under the store's read retry when it was given one. */\n const read = <T>(op: () => Promise<T>): Promise<T> => retryRead(op, deps.readRetry);\n\n const record = await deps.registry.get(ref);\n if (record === null) return { ...base, erased: false, reason: 'absent', collected: [] };\n if (record.status === 'destroyed')\n return { ...base, erased: false, reason: 'destroyed', collected: [] };\n if (record.currentGen === null)\n return { ...base, erased: false, reason: 'no-generation', collected: [] };\n const from = record.currentGen;\n /**\n * The row this rewrite derives its content from, not just the number it points at. `nextGeneration` restarts\n * at `0` once a row is purged and the bucket emptied, so a retired-and-re-created name presents a *different*\n * segment at the *same* `currentGen` \u2014 which `expectFrom` alone matched, republishing one incarnation's\n * content over another's and reporting `erased: true`. A token is not reused across incarnations, but for a collision of probability 2^-128 per pair.\n */\n const fromToken = record.token;\n\n // The segment's DEK, reused across generations. Resolved before any object I/O so a slow keystore/KMS call\n // sits outside the read-write window; an encrypted row with no keystore is a lost key, not a cleartext segment.\n let aead: Aead | undefined;\n if (record.wrappedDeks !== undefined && record.wrappedDeks.length > 0) {\n if (deps.keystore === undefined) {\n throw new KeyUnavailableError(\n `segment \"${ref.segment}\" is encrypted but eraseIdFromSegment has no keystore`,\n );\n }\n aead = await deps.keystore.openDek(record.wrappedDeks);\n } else if (deps.requireEncryption === true) {\n throw new ValidationError(`requireEncryption: segment \"${ref.segment}\" is cleartext`);\n }\n // The AAD binds each chunk/index to (segment, generation), so reading gen g and writing gen g' use distinct\n // contexts over the same DEK.\n const cryptoAt = (generation: number): CrbmCrypto | undefined =>\n aead === undefined ? undefined : { aead, aadFor: (scope) => aadFor(ref, generation, scope) };\n\n const fromKey: GenKey = { ...base, generation: from };\n const refused = (\n reason: 'superseded' | 'absent' | 'no-generation' | 'destroyed',\n generation?: number,\n ): EraseIdResult => ({\n ...base,\n erased: false,\n reason,\n fromGeneration: from,\n generation,\n collected: [],\n });\n\n /**\n * What the row says about the premise this call was working from \u2014 `null` when the premise still holds\n * (the pointer is exactly `from`, so nothing raced us), otherwise the reason to report.\n *\n * Each state gets the answer this function already gives when it reads that state *up front*, so a caller\n * branching on `reason` never has to care at which point in the call it was discovered: a tombstoned row is\n * `'destroyed'` (a concurrent `dropSegment` leaves `currentGen` where it was, so testing the pointer alone\n * would miss it and report an error for a segment the operator deliberately dropped), a vanished row is\n * `'absent'` (the retention sweep purged a tombstone while we worked), a row with no pointer is\n * `'no-generation'`, and a pointer that moved is `'superseded'`.\n */\n const rowVerdict = (\n row: RegistryRecord | null,\n ): 'superseded' | 'absent' | 'no-generation' | 'destroyed' | null => {\n if (row === null) return 'absent';\n if (row.status === 'destroyed') return 'destroyed';\n if (row.currentGen === null) return 'no-generation';\n // A different row is a different lineage even at the same pointer value \u2014 see `fromToken`.\n return row.currentGen === from && row.token === fromToken ? null : 'superseded';\n };\n\n /**\n * Whether `generation` still holds the id: its index is opened, and the id's chunk is fetched only if the index\n * lists it. `null` when the object is gone \u2014 a concurrent collector took it, which is not a failure of this call\n * but the outcome it wants. Any other fault propagates: it must never be swallowed into a clean receipt.\n */\n const holds = async (generation: number): Promise<boolean | null> => {\n const key: GenKey = { ...base, generation };\n const chunkIn = (crypto: CrbmCrypto | undefined): Promise<Uint8Array | null> =>\n read(async () => (await openGenerationReader(deps.storage, key, crypto)).getChunk(chunkKey));\n try {\n let bytes: Uint8Array | null;\n try {\n bytes = await chunkIn(cryptoAt(generation));\n } catch (err) {\n // A cleartext object under an encrypted segment was never one of its generations, so no read believes it.\n // It may still hold the subject in the clear, so the erasure looks in it without the key, and deletes it\n // when it holds the id, as it does any holder. Only its footer is asked first, on this path alone.\n if (\n !isIntegrityError(err) ||\n cryptoAt(generation) === undefined ||\n (await read(() => objectIsEncrypted(deps.storage, key)))\n ) {\n throw err;\n }\n bytes = await chunkIn(undefined);\n }\n return bytes !== null && codec.safeDeserialize(bytes, maxBytes).has(remainder);\n } catch (err) {\n if (isNotFoundError(err)) return null;\n throw err;\n }\n };\n\n /**\n * The receipt check, and the one place `erased: true` is decided: the newest generation still in the bucket that\n * holds the id, or `undefined` when none does. `clean` names the generations this call has already read or\n * written without the id; every other generation present is read now.\n *\n * It verifies the **claim** \u2014 no generation of the segment holds the id \u2014 rather than what this call deleted,\n * in both directions. A holder missing from `collected` does not mean it survived: a concurrent collector\n * (the generation collection with `keep: 0`, which this call and a retirement run) may simply have\n * taken it first, and then the claim is true no matter who made it true. And a collection pass that declined\n * does not mean nothing was left: it returns an empty list when the row was gone as it started, the same value\n * it returns when there was genuinely nothing to collect, and a retirement only has to land between this call's\n * publish and its collect for that to happen.\n *\n * Nor is the generation this call replaced the only one to look at. A rollback can move the pointer onto a\n * holder while the collection runs, and the collection then takes the lower pointer as its bound and leaves\n * that holder current; and a concurrent writer that derived its object from the generation this call replaced\n * can leave one above the pointer. So the list is of everything, and in the ordinary case it returns only the\n * current generation, which is in `clean`: one `list`, and no read.\n */\n const holderLeft = async (clean: ReadonlySet<number>): Promise<number | undefined> => {\n const present = new Set<number>();\n for await (const key of deps.storage.list(ref)) present.add(key.generation);\n for (const generation of [...present].sort((a, b) => b - a)) {\n if (!clean.has(generation) && (await holds(generation)) === true) return generation;\n }\n return undefined;\n };\n\n const cannotRemove = (generation: number): WriteConflictError =>\n new WriteConflictError(\n `erasure of segment ${ref.segment} could not remove generation ${generation}, which still holds the id and is still in the bucket; re-run`,\n );\n\n /**\n * The id is not in the current generation \u2014 which is **not** the same as not being in the bucket.\n *\n * A re-seed that simply stops including someone leaves their bit in the generation it dropped them from, and\n * the collection a load runs after it publishes (default `keep: 1`) *retains* exactly that\n * generation as the reader grace window. So the ordinary lifecycle of a rotating audience leaves an ex-member's\n * bit sitting in a retained object. Answering `'not-member'` there would filter the segment out of the\n * `eraseSubject` ledger entirely: a clean Art. 17 receipt over bytes still in the bucket, for the one\n * population most likely to be asking, people who already left.\n *\n * And the generations **above** the pointer count too. After a rollback they include the ones it rolled back\n * from, which `rollbackSegment` with `allowForward` makes current again, so a holder there is one ordinary\n * operator action from being served. There can be several: every generation loaded after the one the pointer\n * was rolled back to.\n *\n * So look, at all of them. A holder cannot be rewritten \u2014 a rewrite of a non-current generation would regress\n * the pointer \u2014 so the only remedy is to delete it:\n *\n * - **Below the pointer**, `gcOrphanGenerations` with `keep: 0` takes every generation at once, re-proving the\n * row before each delete. It costs the segment its grace window and its older rollback targets, which is\n * proportionate: only a segment that genuinely held the subject pays it.\n * - **Above the pointer**, collection never looks, so each holder is deleted here, and only the holders: a\n * generation up there without the id is still an operator's rollback target and stays. This is the one place\n * the library deletes an above-pointer object that it did not write, and it costs `rollbackSegment` a\n * target, deliberately: a rollback point that still contains data we were required to erase is not a\n * rollback point, and keeping it would make the erasure undoable by an ordinary operator action. Each delete\n * is re-proved against the row first, as collection's are, because the danger is the same one: a rollback\n * that lands on a generation this call has queued, which would leave the pointer naming a missing object.\n * If the pointer has moved at all, the deletes stop. One round trip remains between that read and the delete,\n * exactly as in collection's own loop: a rollback that lands inside it onto the generation being deleted\n * leaves the pointer naming a missing object. The rollback's own move-then-verify catches every such landing\n * except one whose check runs before the delete, and no storage port offers a conditional delete to close it.\n *\n * Then {@link holderLeft} decides. If nothing in the bucket holds the id, the claim is true however the pointer\n * moved meanwhile: a load that published mid-call puts every holder below its pointer, where the collection\n * takes them. If something still holds it and the pointer moved, the row says why, as it does everywhere else in\n * this function. If something holds it and the pointer did not move, the delete that should have taken it did\n * not, and that throws.\n *\n * **The cost is paid only where it is owed.** The cheap filter comes first: one `list`, and if the segment has no\n * other generations at all \u2014 true of any store that collects with `keep: 0`, and of a segment loaded once \u2014\n * nothing else is read. Then per generation, the index is opened and the chunk is fetched only if the index\n * says that chunk exists: every generation above the pointer, since each holder there must be found; and below\n * it only until the first holder, since `keep: 0` takes the rest regardless \u2014 and not at all once a holder was\n * found above. `eraseSubject` fans this out across every registered segment, so the filter is what keeps a\n * fleet-wide subject scan from doubling its reads on segments that never held the id.\n */\n const notInCurrent = async (): Promise<EraseIdResult> => {\n const notMember: EraseIdResult = {\n ...base,\n erased: false,\n reason: 'not-member',\n fromGeneration: from,\n collected: [],\n };\n const others = new Set<number>();\n for await (const key of deps.storage.list(ref)) {\n if (key.generation !== from) others.add(key.generation);\n }\n if (others.size === 0) return notMember;\n const newestFirst = [...others].sort((a, b) => b - a);\n\n const clean = new Set<number>([from]);\n const holdersAbove: number[] = []; // newest first\n for (const generation of newestFirst.filter((g) => g > from)) {\n const held = await holds(generation);\n if (held === true) holdersAbove.push(generation);\n else if (held === false) clean.add(generation);\n }\n let holderBelow: number | undefined;\n if (holdersAbove.length === 0) {\n for (const generation of newestFirst.filter((g) => g < from)) {\n const held = await holds(generation);\n if (held === true) {\n holderBelow = generation;\n break;\n }\n if (held === false) clean.add(generation);\n }\n }\n const newest = holdersAbove[0] ?? holderBelow;\n if (newest === undefined) return notMember;\n\n const collected = [...(await gcOrphanGenerations(ref, deps, { keep: 0 }))];\n let moved: ReturnType<typeof rowVerdict> = null;\n for (const generation of holdersAbove) {\n moved = rowVerdict(await deps.registry.get(ref));\n if (moved !== null) break;\n await deps.storage.delete({ ...base, generation });\n collected.push(generation);\n }\n\n const left = await holderLeft(clean);\n if (left === undefined) return { ...base, erased: true, fromGeneration: newest, collected };\n if (moved !== null) {\n return { ...base, erased: false, reason: moved, fromGeneration: newest, collected };\n }\n throw cannotRemove(left);\n };\n\n /**\n * Read `from`, write its successor, and verify it \u2014 the read-modify-write whose premise is that the pointer is\n * still at `from`. Returns a staged generation, or the result to report if the premise stopped holding.\n *\n * **Every object this phase touches can be deleted underneath it, by a concurrent call of this very\n * function.** An erasure collects with `keep: 0`, which takes every generation below its new pointer \u2014 so a\n * racing erasure deletes `from` while we are still streaming it, and deletes *the object we just wrote* too,\n * since ours is below its pointer and can never become current. There are three separate round trips exposed:\n * the reader open, every chunk of the rewrite (a whole-segment read \u2014 by far the longest window), and the\n * verify.\n *\n * The documented answer to that race is `reason: 'superseded'` \u2014 *this call did not erase it; re-run against\n * the new generation* \u2014 and it is what the publish already reports when its `expectFrom` fence fails. A bare\n * `NotFoundError` instead is the same event wearing a different, unactionable face; through `eraseSubject` it\n * becomes a note the facade documents as ambiguous about which side of the publish it landed on, so an\n * Art. 17 operator is told *triage this* where the truth is *re-run*.\n *\n * **A `NotFoundError` is translated only after re-reading the row, and the row decides which answer.** The\n * pointer still at exactly `from` means the object it names is genuinely absent \u2014 the forbidden\n * `missing-storage-generation` state a failed publish leaves behind \u2014 and that **throws**, because it is an\n * integrity problem rather than a race and no re-run fixes it. A pointer that moved is `'superseded'`. The\n * other two states are not supersessions and are not reported as one: the row **gone** (the retention sweep\n * purged a tombstone while we worked) is `'absent'`, and a row whose `currentGen` is `null` is\n * `'no-generation'` \u2014 the same answers this function gives when it reads either state up front, so a caller\n * branching on `reason` never has to special-case where in the call it was discovered.\n *\n * `generation` is set only once the object is **durable**. Reporting it from `nextGeneration` was wrong in a\n * way worth naming: if the row is gone by then, `nextGeneration` finds no row and no objects and restarts at\n * `0`, so the ledger read `fromGeneration: 0 \u2192 generation: 0` \u2014 the generation that *held* the bit named as\n * the one written *without* it, for an object that was never written.\n */\n const stage = async (): Promise<\n | EraseIdResult\n | { generation: number; key: GenKey; fingerprint: string; summary: RegistrySummary }\n > => {\n /** Set only once the object exists in the bucket \u2014 see the note above. */\n let written: number | undefined;\n try {\n const reader = await read(() => openGenerationReader(deps.storage, fromKey, cryptoAt(from)));\n const bytes = await read(() => reader.getChunk(chunkKey));\n // `return await`, not `return`: the sweep's rejection must land in the `catch` below, which is where a\n // `NotFoundError` is translated by re-reading the row. A bare `return` of the promise hands it past the `try`.\n if (bytes === null) return await notInCurrent();\n const target = codec.safeDeserialize(bytes, maxBytes);\n assertRemaindersInRange(target, chunkKey); // invariant 5, on the chunk we are about to re-encode\n if (!target.has(remainder)) return await notInCurrent();\n target.remove(remainder);\n\n // The rewrite is written before its row is: refuse here, not after, on a registry that cannot write one.\n assertRegistryCanWrite(deps.registry, 'eraseIdFromSegment');\n const generation = await nextGeneration(ref, deps);\n const key: GenKey = { ...base, generation };\n // The generation's metadata is part of it: the rewrite is the same generation without one id, so the new object\n // carries the source's metadata as it is, and the row's summary of it says so. An erasure does not scan the\n // metadata; it is the caller's to keep free of ids. On an encrypted segment a source with no metadata block, whose\n // row's sealed summary of this generation has metadata, is carried with the row's: the block's presence is not\n // authenticated and the summary is, so a stripped block is not made permanent by the rewrite.\n const metadata = metadataToCarry(\n reader.metadata,\n aead === undefined ? undefined : usableSummary(ref, record, aead),\n );\n const tally = await writeCrbmGenerationStream(\n deps.storage,\n key,\n rewrite(reader, chunkKey, target, codec, maxBytes, read),\n { crypto: cryptoAt(generation), clock: deps.clock, metadata },\n );\n written = generation; // `putImmutable` commits atomically, so the object exists exactly now\n // Re-check the pointer before spending the verification read. The fence below is what makes the publish\n // correct; this only saves a round trip on the common race, which the catch reports either way.\n const beforeVerify = await deps.registry.get(ref);\n const early = rowVerdict(beforeVerify);\n if (early !== null) return refused(early, written);\n await read(() =>\n verifyGeneration(deps.storage, key, { ...tally, metadata }, cryptoAt(generation)),\n );\n // The row's summary is built from what was written, never copied from the row it replaces: whatever that one\n // held, a cached count or metadata that disagreed with the object it described goes with it.\n const summary = summaryOf(\n ref,\n generation,\n { cardinality: tally.cardinality, metadata },\n aead,\n );\n return { generation, key, fingerprint: tally.fingerprint, summary };\n } catch (err) {\n if (!isNotFoundError(err)) throw err;\n // The row decides which answer this is \u2014 but only if it can be read. A re-read that faults must not\n // replace the `NotFoundError`: that error is the ONLY signal of the integrity state below, and swapping\n // it for a transient-looking registry fault loses the one thing an operator needs to see.\n let now: RegistryRecord | null;\n try {\n now = await deps.registry.get(ref);\n } catch {\n throw err;\n }\n const verdict = rowVerdict(now);\n if (verdict !== null) return refused(verdict, written);\n throw err; // the pointer still names the missing object: genuinely absent, not a race\n }\n };\n\n /**\n * Delete the object a refused rewrite wrote, when it would otherwise outlive the winner above its pointer.\n *\n * Only one position needs it: the pointer moved forward past `from` and stopped **below** `written`. Two winners\n * leave it there: one whose object was already in the bucket when this call took its number (`nextGeneration`\n * numbers above everything present), and a load that numbered after it from its own row, which takes\n * `currentGen + 1` whenever no object holds that number, below this call's object. Either way the object is\n * above the winner's pointer, where no collection ever looks. The\n * object was derived from `from`, so when the winner was another erasure it still holds the id that erasure\n * has just reported gone, one `rollback({ allowForward: true })` from being served, and the newest superseded\n * generation once a later load raises the pointer past it, which is exactly what that load's `keep` retains.\n *\n * Everywhere else the object is left alone, as a refused load leaves its own once the row has changed. At or\n * above `written` the pointer either names it or belongs to a writer that numbered after it and whose\n * collection takes it; at or below `from` nothing replaced `from`; and a row that is gone, tombstoned or\n * without a pointer is not one this call can reason about. The position is read from a fresh row, just before\n * the delete, and the number still names this call's own object: a name re-created since would have had to\n * lose this object and then write that many generations of its own, and only its pointer landing on `written`\n * could make the delete unsafe, which the bound excludes.\n */\n const discardRefused = async (written: number): Promise<void> => {\n const row = await deps.registry.get(ref);\n if (row === null || row.status !== 'active' || row.currentGen === null) return;\n if (row.currentGen <= from || row.currentGen >= written) return;\n await deps.storage.delete({ ...base, generation: written });\n };\n\n const staged = await stage();\n if ('erased' in staged) {\n if (!staged.erased && staged.generation !== undefined) await discardRefused(staged.generation);\n return staged;\n }\n const { generation, key, fingerprint, summary } = staged;\n\n // Read-modify-write, not merely forward-only, and the distinction is the whole correctness of this function.\n //\n // The new generation's content is `from` minus one bit, so it is only a valid successor to `from`. Publishing\n // it forward-only would land it over ANY newer generation, because `nextGeneration` deliberately picks a number\n // above everything in the bucket \u2014 including an object a concurrent writer just published. Two ways that goes\n // wrong: a load that published between our read and our publish would have its whole set discarded and its\n // object collected; and of two concurrent erasures, the second one's generation would put the FIRST one's id\n // back, deleting the generation that evidenced its removal, while both returned `erased: true` with a\n // `segment.rewrite` event. A false Art. 17 receipt is the worst output this module can produce.\n //\n // `expectFrom` makes the publish land only while the pointer is still exactly `from`.\n //\n // Reported, not thrown \u2014 the caller re-runs against the new generation, which may or may not still hold the id.\n const published = await publishGeneration(deps.registry, key, {\n expectFrom: from,\n expectToken: fromToken,\n summary,\n // A write that ends without an answer is settled by reading the row, and a pointer at this number is this\n // rewrite's only over the object it wrote: the footer proves it, so another incarnation's cannot pass for it.\n holdsOwnObject: () => provesOwnObject(deps.storage, key, fingerprint),\n clock: deps.clock,\n rng: deps.rng ?? deps.readRetry?.rng,\n });\n if (!published) {\n await discardRefused(generation);\n return {\n ...base,\n erased: false,\n reason: 'superseded',\n fromGeneration: from,\n generation,\n collected: [],\n };\n }\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.rewrite',\n namespace: ref.namespace,\n segment: ref.segment,\n fromGeneration: from,\n generation,\n });\n // `keep: 0`: the whole point is that the generation holding the bit does not survive this call. Every\n // generation below the new pointer is safe to take: the CAS succeeded while the pointer was still at `from`, so\n // nothing in `(from, generation)` had been published before us, and no read that resolves the pointer can reach\n // any of them. Deleting them is the strongest form of the \"physically gone on return\" claim rather than a risk to\n // a load still in flight. The pointer can still move DOWN onto one of them \u2014 an operator's `rollbackSegment`,\n // or a purge-and-recreate (invariant 1) \u2014 which is why the collector re-proves the row before every delete\n // rather than trusting the one this call read, and why the receipt below reads what is left rather than\n // assuming.\n const collected = await gcOrphanGenerations(ref, deps, { keep: 0 });\n const left = await holderLeft(new Set([generation]));\n if (left !== undefined) throw cannotRemove(left);\n return { ...base, erased: true, fromGeneration: from, generation, collected };\n}\n\n/** How many chunk reads the erasure rewrite keeps open ahead of the writer. */\nconst REWRITE_READ_AHEAD = 32;\n\n/**\n * The new generation's chunks, ascending: every chunk of the old generation decoded and passed through, except\n * `chunkKey`, which is replaced by `replacement` (already missing the id). Decoding rather than copying bytes is\n * deliberate \u2014 it puts every chunk through the **safe** deserializer and the size cap on the one path that\n * rewrites a whole segment, so a chunk that is not decodable, or is larger than the cap, stops the rewrite\n * instead of being copied forward \u2014 and the writer skips a chunk the removal emptied. Reads run ahead of the writer\n * through a window of {@link REWRITE_READ_AHEAD} chunks, in key order, so the erasure costs a few round trips\n * rather than one per chunk; each chunk is decoded only as the writer reaches it, so a corrupt one still stops the\n * rewrite naming that chunk and not a later one. At most that many raw chunk payloads are held ahead of the writer,\n * plus the one being decoded: about 8 KiB each serialized for a well-formed segment, and never more than the reader's\n * per-chunk payload cap, which refuses a longer index entry when the object is opened.\n *\n * Both halves of **invariant 5** apply, including the remainder range: a chunk of a 16-bit-keyed segment cannot\n * hold a value above `MAX_REMAINDER`, and one that does was not written by this codec. Carrying it forward would\n * re-encode pre-existing corruption into a brand-new object and then report `erased: true` over a segment that\n * still cannot be read \u2014 so the rewrite stops instead, naming the chunk. An `IntegrityError` here says \"this\n * segment is corrupt\", which is what the operator needs to know; a successful-looking erasure says nothing. The\n * check is one `maximum()` call per chunk against a bitmap this function has already decoded, so honouring the\n * invariant costs nothing measurable on a path that is re-encoding every chunk anyway. Each chunk is read through\n * `read`, the caller's read retry.\n */\nasync function* rewrite(\n reader: CrbmReader,\n chunkKey: number,\n replacement: CodecBitmap,\n codec: CodecInterface,\n maxBytes: number,\n read: <T>(op: () => Promise<T>) => Promise<T>,\n): AsyncGenerator<{ chunkKey: number; bitmap: CodecBitmap }> {\n const keys = [...reader.chunkKeys()].sort((a, b) => a - b);\n // The replaced chunk is never read: it is already in hand. Every other chunk is read, so the window opens at its\n // full width at once; a ramp would only add round trips here, since nothing stops this read early.\n const window = new ChunkWindow<Uint8Array | null>(\n keys.filter((k) => k !== chunkKey),\n (k) => read(() => reader.getChunk(k)),\n REWRITE_READ_AHEAD,\n false,\n );\n for (const k of keys) {\n if (k === chunkKey) {\n yield { chunkKey: k, bitmap: replacement };\n continue;\n }\n const bytes = await window.take();\n if (bytes === null) continue; // listed but absent: nothing to carry forward\n const bitmap = codec.safeDeserialize(bytes, maxBytes);\n assertRemaindersInRange(bitmap, k);\n yield { chunkKey: k, bitmap };\n }\n}\n\n/**\n * Invariant 5's range half, for one decoded chunk: every value in a chunk is a 16-bit remainder, so a value above\n * `MAX_REMAINDER` means the object was not written by this codec. `maximum()` is optional on the codec seam (a\n * codec with no size decision to make may omit it); when it is absent the check is skipped rather than faked.\n */\nfunction assertRemaindersInRange(bitmap: CodecBitmap, chunkKey: number): void {\n const max = bitmap.maximum?.();\n if (max !== undefined && max > MAX_REMAINDER) {\n throw new IntegrityError(\n `chunk ${chunkKey} payload holds value ${max}, outside the 16-bit remainder range [0, ${MAX_REMAINDER}] \u2014 ` +\n `the stored object is corrupt or was not written by this codec; refusing to carry it into a new generation`,\n );\n }\n}\n", "/**\n * Concrete AES-256-GCM crypto (lives outside `core/`, which lint-bans `node:crypto`). Implements the injected\n * {@link Aead}/{@link IKeystore} seams from `core/crypto`: a `NodeAead` over `aes-256-gcm`, and an in-process\n * **BYOK** keystore that envelope-wraps a per-segment DEK under one or more operator-held KEKs.\n *\n * Default, dependency-free key management: you bring 32-byte KEK(s); we never call a cloud service. KMS/Vault\n * are future optional adapters against the same {@link IKeystore} interface. **The KEK is the\n * one secret to back up** \u2014 lose it and that segment's at-rest bytes are unrecoverable by design (re-seed the\n * segment from your source of truth); see the encryption guide.\n */\nimport { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport type { Aead, AeadSealed, IKeystore, WrappedDek } from '../core/crypto';\nimport { IntegrityError, KeyUnavailableError, ValidationError } from '../core/errors';\n\nconst KEY_BYTES = 32; // AES-256\nconst NONCE_BYTES = 12; // GCM standard nonce\nconst TAG_BYTES = 16; // GCM tag\n\n/** AES-256-GCM AEAD bound to one 32-byte key. A fresh CSPRNG 96-bit nonce per `seal`; `aad` is authenticated,\n * never stored. Nonce-reuse safety: the random-96-bit birthday bound is ~q\u00B2/2\u2079\u2077, negligible below ~2\u00B3\u00B2 seals\n * under one key. A per-segment DEK is **reused across all generations** (every load and rewrite encrypts under it), so\n * seal count is cumulative over the segment's life \u2014 a segment approaching ~2\u00B3\u00B2 lifetime chunk-seals should be\n * re-seeded (a fresh segment \u21D2 fresh DEK). Far beyond any realistic workload; noted for completeness. */\nexport class NodeAead implements Aead {\n private readonly key: Buffer;\n\n constructor(key: Uint8Array) {\n if (key.length !== KEY_BYTES)\n throw new ValidationError(`AEAD key must be ${KEY_BYTES} bytes, got ${key.length}`);\n this.key = Buffer.from(key);\n }\n\n seal(plaintext: Uint8Array, aad: Uint8Array): AeadSealed {\n const nonce = randomBytes(NONCE_BYTES);\n const cipher = createCipheriv('aes-256-gcm', this.key, nonce);\n cipher.setAAD(aad);\n const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n return { nonce, ciphertext, tag: cipher.getAuthTag() };\n }\n\n open(sealed: AeadSealed, aad: Uint8Array): Uint8Array {\n if (sealed.nonce.length !== NONCE_BYTES || sealed.tag.length !== TAG_BYTES)\n throw new IntegrityError('AEAD nonce/tag have the wrong length');\n const decipher = createDecipheriv('aes-256-gcm', this.key, sealed.nonce);\n decipher.setAAD(aad);\n decipher.setAuthTag(Buffer.from(sealed.tag));\n try {\n return Buffer.concat([decipher.update(sealed.ciphertext), decipher.final()]);\n } catch {\n // Wrong key, tampered ciphertext/tag, or mismatched AAD \u2014 never leak which, never return plaintext.\n throw new IntegrityError(\n 'AEAD authentication failed (wrong key, tampered data, or wrong context)',\n );\n }\n }\n}\n\n/** Wrap/unwrap a DEK under a KEK with AES-256-GCM, binding the wrapping to its `keyId` via AAD so a wrapping\n * can't be relabelled to another KEK. Stored blob = nonce \u2016 ciphertext \u2016 tag, base64. */\nfunction wrapKey(dek: Uint8Array, kek: Uint8Array, keyId: string): string {\n const sealed = new NodeAead(kek).seal(dek, new TextEncoder().encode(keyId));\n return Buffer.concat([sealed.nonce, sealed.ciphertext, sealed.tag]).toString('base64');\n}\n\nfunction unwrapKey(wrapped: string, kek: Uint8Array, keyId: string): Uint8Array {\n const blob = Buffer.from(wrapped, 'base64');\n if (blob.length < NONCE_BYTES + TAG_BYTES)\n throw new IntegrityError('wrapped DEK is too short to be valid');\n const nonce = blob.subarray(0, NONCE_BYTES);\n const tag = blob.subarray(blob.length - TAG_BYTES);\n const ciphertext = blob.subarray(NONCE_BYTES, blob.length - TAG_BYTES);\n return new NodeAead(kek).open({ nonce, ciphertext, tag }, new TextEncoder().encode(keyId));\n}\n\n/**\n * Options for {@link InProcessKeystore}. `keys` maps a `keyId` \u2192 its 32-byte KEK; `activeKeyId` names the KEK\n * new DEKs are wrapped under; an optional `recoveryKeyId` additionally wraps each new DEK under a second\n * (e.g. offline) KEK, so losing the active KEK isn't fatal. Rotating the active KEK needs no data\n * re-encryption: add the new KEK, point `activeKeyId` at it, keep the old one to unwrap existing segments.\n */\nexport interface InProcessKeystoreOptions {\n readonly keys: Readonly<Record<string, Uint8Array>>;\n readonly activeKeyId: string;\n readonly recoveryKeyId?: string;\n}\n\n/**\n * Dependency-free BYOK keystore: envelope-wraps a fresh random per-segment DEK under the active (and optional\n * recovery) KEK using `node:crypto`. No external service. The raw DEK never leaves a returned {@link NodeAead}.\n */\nexport class InProcessKeystore implements IKeystore {\n private readonly keys: Map<string, Uint8Array>;\n private readonly activeKeyId: string;\n private readonly recoveryKeyId: string | undefined;\n\n constructor(options: InProcessKeystoreOptions) {\n const entries = Object.entries(options.keys);\n if (entries.length === 0) throw new ValidationError('keystore needs at least one KEK');\n for (const [id, k] of entries) {\n if (k.length !== KEY_BYTES)\n throw new ValidationError(`KEK \"${id}\" must be ${KEY_BYTES} bytes, got ${k.length}`);\n }\n if (!(options.activeKeyId in options.keys))\n throw new ValidationError(`activeKeyId \"${options.activeKeyId}\" is not in keys`);\n if (options.recoveryKeyId !== undefined && !(options.recoveryKeyId in options.keys))\n throw new ValidationError(`recoveryKeyId \"${options.recoveryKeyId}\" is not in keys`);\n this.keys = new Map(entries.map(([id, k]) => [id, Buffer.from(k)]));\n this.activeKeyId = options.activeKeyId;\n this.recoveryKeyId =\n options.recoveryKeyId === options.activeKeyId ? undefined : options.recoveryKeyId;\n }\n\n // async to satisfy the IKeystore seam (a KMS adapter awaits a network call here); the in-process path is\n // synchronous, but staying async keeps unwrap failures as rejections, matching a remote keystore.\n async createDek(): Promise<{ wrapped: WrappedDek[]; aead: Aead }> {\n const dek = randomBytes(KEY_BYTES);\n const ids = this.recoveryKeyId ? [this.activeKeyId, this.recoveryKeyId] : [this.activeKeyId];\n const wrapped = ids.map((keyId) => ({\n keyId,\n wrapped: wrapKey(dek, this.keys.get(keyId)!, keyId),\n }));\n return { wrapped, aead: new NodeAead(dek) };\n }\n\n // async for the same reason as createDek \u2014 a synchronous unwrap failure surfaces as a promise rejection.\n async openDek(wrapped: readonly WrappedDek[]): Promise<Aead> {\n // Try EVERY held wrapping, not just the first: a DEK is wrapped under both the active KEK and (if\n // configured) the offline recovery KEK precisely so a damaged/tampered active-KEK wrapping can be\n // recovered from the other. Returning on the first held keyId and letting a corrupt unwrap throw would\n // defeat that insurance \u2014 the recovery wrapping would never be attempted. So catch a failed unwrap and\n // fall through to the next held wrapping; only give up once none succeeds.\n let anyKekHeld = false;\n let lastUnwrapError: unknown;\n for (const w of wrapped) {\n const kek = this.keys.get(w.keyId);\n if (kek === undefined) continue;\n anyKekHeld = true;\n try {\n return new NodeAead(unwrapKey(w.wrapped, kek, w.keyId));\n } catch (err) {\n lastUnwrapError = err; // held KEK but this wrapping is corrupt/tampered \u2014 try the next one\n }\n }\n // A KEK WAS held but every held wrapping failed to unwrap \u2192 the wrapping(s) are corrupt/tampered, not\n // missing. Surface that integrity failure in its original type rather than masking it as \"no held KEK\".\n if (anyKekHeld) throw lastUnwrapError;\n const have = [...this.keys.keys()].join(', ') || '(none)';\n const need = wrapped.map((w) => w.keyId).join(', ') || '(none)';\n throw new KeyUnavailableError(\n `no held KEK can unwrap this DEK \u2014 have [${have}], wrappings reference [${need}]. ` +\n `Restore the missing KEK (or its recovery KEK), or rebuild the segment from source.`,\n );\n }\n}\n", "/**\n * Whole-segment erasure: crypto-shred (`destroySegment`, `eraseNamespace`) and disposal (`dropSegment`).\n *\n * GDPR \"right to erasure\" on immutable, backed-up storage: you can't delete a `.crbm` object from every\n * backup, but you can **delete its key**. `destroySegment` drops the segment's wrapped DEK(s) from the registry\n * (a CAS to a `destroyed` tombstone) \u2014 the encrypted Storage bytes are then permanently unreadable, everywhere,\n * forever. **Immediate + irreversible**, so it's gated behind an explicit confirmation (name the exact\n * segment/namespace). A `destroyed` segment reads as empty (its DEK is gone). Only works on an **encrypted**\n * segment; a cleartext segment has no key to shred.\n *\n * The tombstone is also the fence every writer respects: a load refuses\n * a `destroyed` row, so a load racing an erasure cannot resurrect the segment. A single id's erasure is a different\n * operation \u2014 `eraseIdFromSegment` rewrites the generation without it.\n */\nimport { type IAuditSink, NOOP_AUDIT, safeAudit } from './audit';\nimport { mapWithConcurrency } from './concurrency';\nimport { ValidationError, WriteConflictError, isWriteConflictError } from './errors';\nimport type { IStorageDriver, IRegistryDriver, SegmentRef } from './ports';\nimport { validateUserNamespace, validateUserRef } from './validate';\nimport { DEFAULT_MAX_SCAN_SEGMENTS, drainRegistry } from './registry-scan';\n\nexport interface EraseDeps {\n readonly registry: IRegistryDriver;\n}\n\nexport interface DestroyResult {\n readonly segment: string;\n readonly namespace?: string;\n /** True iff the segment is now a `destroyed` tombstone (incl. the idempotent already-destroyed case). */\n readonly destroyed: boolean;\n /**\n * True iff **this call** performed a genuine crypto-shred \u2014 an encrypted segment whose wrapped DEK(s) were\n * dropped, so its Storage bytes are now permanently unreadable. False for a cleartext tombstone (bytes remain\n * readable), for the idempotent already-destroyed case, and for an absent segment. This is what the\n * `segment.erase` audit event keys off \u2014 a cleartext \"erase\" is not an irreversible destruction.\n */\n readonly cryptoShredded: boolean;\n /**\n * Why this segment was not a fresh crypto-shred. `'absent'` (no registry row), `'already'` (already\n * destroyed), `'cleartext'` (no DEK to shred) \u2014 and from `eraseNamespace` only, where per-segment faults are\n * isolated so one failure cannot discard the ledger: `'contended'` (the row was rewritten during every CAS\n * attempt) or `` `failed: <message>` `` (any other fault).\n *\n * The last two come with `destroyed: false` and mean **the segment still holds data**. A namespace erase\n * returns them rather than throwing, so its entries have to be inspected \u2014 see {@link eraseNamespace}.\n */\n readonly reason?: string;\n}\n\nconst MAX_CAS_ATTEMPTS = 8;\n/**\n * Storage deletes in flight at once. Erasure is a fan-out over *independent* keys, so serial would pay one\n * round-trip per generation for work that has no ordering between items; unbounded would be a self-inflicted\n * thundering herd against one bucket. Not configurable: an admin path called by a human or a nightly job.\n */\nconst ERASE_CONCURRENCY = 8;\n/**\n * Storage list-then-delete passes a drop will make before giving up and reporting the residual.\n *\n * Two is the honest floor and three is the working value: pass 1 clears what was there, pass 2 catches an object\n * a load that had read the row before the tombstone landed went on to write (its publish is then refused, but the\n * object write completes), pass 3 covers a second such writer. It terminates regardless \u2014 the tombstone\n * hard-fences *publishing* a new generation, and any residual is reported rather than silently dropped. A load still\n * consuming its ids can write after the last pass; it deletes its own object once its publish is refused.\n */\nconst MAX_STORAGE_SWEEPS = 3;\n\n/**\n * Crypto-shred one segment. **Irreversible.** `confirmSegment` must equal `ref.segment` (a guard against an\n * accidental destroy \u2014 you must name the exact segment). A non-encrypted segment is rejected unless you opt in\n * via `allowCleartext` (there's no key to shred; its Storage bytes stay readable \u2014 the tombstone only stops the\n * segment resolving).\n */\nexport async function destroySegment(\n ref: SegmentRef,\n deps: EraseDeps,\n options: { confirmSegment: string; allowCleartext?: boolean; audit?: IAuditSink },\n): Promise<DestroyResult> {\n validateUserRef(ref);\n if (options.confirmSegment !== ref.segment) {\n throw new ValidationError(\n `destroySegment: confirmSegment must equal the segment name \"${ref.segment}\" (guard against accidental crypto-shred)`,\n );\n }\n const result = await shredSegment(ref, deps, options.allowCleartext ?? false);\n // Audit only a genuine crypto-shred \u2014 not the idempotent already-destroyed/absent no-ops, and not a\n // cleartext tombstone (whose Storage bytes stay readable, so it is not an irreversible erasure).\n if (result.cryptoShredded) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.erase',\n namespace: ref.namespace,\n segment: ref.segment,\n });\n }\n return result;\n}\n\n/**\n * Crypto-shred every segment in a namespace. **Irreversible.** `confirmNamespace` must equal `namespace`.\n * Returns a per-segment result (skips cleartext segments unless `allowCleartext`).\n *\n * **Bounded.** It lists the whole namespace before it destroys anything, and holds the listing resident, so the\n * listing is capped at `maxScanSegments` (default {@link DEFAULT_MAX_SCAN_SEGMENTS}, 250,000), the ceiling every\n * other fleet scan keeps. A namespace over it throws `BudgetExceededError` **before any segment is destroyed**,\n * never part-way through; raise `maxScanSegments` when the namespace really is that large and the memory is there.\n * A `maxScanSegments` that is not a finite number >= 1 throws `ValidationError`, again before anything is destroyed.\n */\nexport async function eraseNamespace(\n namespace: string,\n deps: EraseDeps,\n options: {\n confirmNamespace: string;\n allowCleartext?: boolean;\n audit?: IAuditSink;\n maxScanSegments?: number;\n },\n): Promise<{ destroyed: DestroyResult[] }> {\n if (typeof namespace !== 'string' || namespace.length === 0) {\n throw new ValidationError('eraseNamespace: namespace must be a non-empty string');\n }\n validateUserNamespace(namespace);\n if (options.confirmNamespace !== namespace) {\n throw new ValidationError(\n `eraseNamespace: confirmNamespace must equal the namespace \"${namespace}\" (guard against accidental erasure)`,\n );\n }\n // The whole listing is drained, and bounded, before the first segment is destroyed: a namespace over the\n // ceiling is refused with nothing erased, not abandoned part-way through an irreversible loop.\n const rows = await drainRegistry(deps.registry, {\n namespace,\n maxScanSegments: options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS,\n op: 'eraseNamespace',\n narrowable: false, // already one namespace: the only remedy is a higher ceiling\n });\n const refs: SegmentRef[] = rows.map((rec) => ({\n namespace: rec.namespace,\n segment: rec.segment,\n }));\n const audit = safeAudit(options.audit ?? NOOP_AUDIT);\n const destroyed: DestroyResult[] = [];\n let segmentsShredded = 0;\n for (const ref of refs) {\n // Per-segment faults stay isolated so one failure cannot discard the ledger, mirroring `eraseSubject`'s\n // entries (\"one failure never aborts the ledger\") and for the same reason: on an erasure command the\n // caller's load-bearing question is *which segments are now destroyed*, and an exception thrown from the\n // middle of the loop answers it for none of them while having already destroyed some.\n //\n // Every error is caught, not just contention \u2014 again matching `eraseSubject`, which records any thrown\n // fault as a ledger entry rather than deciding which faults deserve a record. A `KeyUnavailableError` on\n // segment 3 is exactly as important to report as a `WriteConflictError`, and just as segment-local.\n //\n // NOTE the caller's obligation, because this is quieter than a throw: entries must be INSPECTED.\n // `destroyed: false` with a `reason` is a segment that still holds data. The `namespace.erase` audit event\n // carries the honest `segmentsShredded` count, which will be lower than the segment count, so an audit\n // trail still shows the shortfall even if the return value is ignored.\n let result: DestroyResult;\n try {\n result = await shredSegment(ref, deps, options.allowCleartext ?? false);\n } catch (err) {\n result = {\n segment: ref.segment,\n namespace: ref.namespace,\n destroyed: false,\n cryptoShredded: false,\n reason: isWriteConflictError(err)\n ? 'contended'\n : `failed: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n destroyed.push(result);\n // A per-segment record for each genuine crypto-shred, so the trail can prove *which* segments were\n // destroyed (each carries the namespace) \u2014 not just that the command ran.\n if (result.cryptoShredded) {\n segmentsShredded += 1;\n audit.onEvent({ kind: 'segment.erase', namespace: ref.namespace, segment: ref.segment });\n }\n }\n // Plus one namespace-level record of the erasure command, carrying the honest count actually shredded\n // (may be 0 \u2014 e.g. an empty namespace, or all-cleartext without `allowCleartext`): the command ran, but\n // the count keeps the record from over-attesting a destruction that did not happen.\n audit.onEvent({ kind: 'namespace.erase', namespace, segmentsShredded });\n return { destroyed };\n}\n\n/**\n * Deps for {@link dropSegment}. Adds `storage`, because unlike a crypto-shred this one deletes the objects.\n */\nexport interface DropDeps extends EraseDeps {\n readonly storage: IStorageDriver;\n}\n\nexport interface DropResult {\n readonly segment: string;\n readonly namespace?: string;\n /**\n * **True iff this segment is now empty as a result of this call \u2014 or already was.** This is the field a\n * retention job should branch on: `false` means the call found nothing to do and your ref is probably wrong.\n */\n readonly dropped: boolean;\n /** Storage generations physically deleted, ascending. Empty on a dry run \u2014 see {@link DropResult.wouldDelete}. */\n readonly generationsDeleted: readonly number[];\n /**\n * Generations still present in Storage when the sweep gave up, ascending. **Empty is the normal outcome** \u2014 a\n * non-empty value means the storage was NOT fully reclaimed and the drop should be re-run.\n *\n * Without it, a drop would return `dropped: true` with a populated `generationsDeleted` and no `reason` even\n * when an object holding the **complete set** had just been left in the bucket by a writer that was already\n * mid-write when the tombstone landed (its publish is refused, but the object survives). For a cleartext\n * segment those bytes are readable \u2014 and the collection only takes a destroyed segment's generations\n * when something runs it. That result would look like a clean drop, and an operator would get no signal to\n * re-run.\n */\n readonly generationsRemaining: readonly number[];\n /**\n * On a dry run, the generations that **would** be deleted. `undefined` on a real run.\n *\n * This exists because the confirmation guard the erasure calls use \u2014 naming the segment twice \u2014 protects a\n * hand-typed literal and nothing else. In the loop this operation is actually *for*\n * (`for (const day of expired) drop({segment: day}, {confirmSegment: day})`) the same variable appears twice\n * and the guard is pure ceremony. A dry run is the guard that still works when a machine is calling.\n *\n * It is a **snapshot**: a later real run can see a different set.\n */\n readonly wouldDelete?: readonly number[];\n /**\n * On a dry run, whether the real drop would **also** crypto-shred (i.e. the segment is encrypted).\n * `undefined` on a real run. This is the one fact in the preview that is irreversible *everywhere*, backups\n * included \u2014 a dry run that did not surface it was previewing the recoverable half only.\n */\n readonly wouldCryptoShred?: boolean;\n /**\n * True iff the segment was encrypted and its wrapped DEK(s) were dropped as part of this call \u2014 so its bytes\n * are unreadable *everywhere*, backups included, not merely deleted from the bucket. Deleting an object does\n * not reach a noncurrent version, a replica, or a PITR snapshot; discarding the key does.\n */\n readonly cryptoShredded: boolean;\n /**\n * How the segment got to its current state. Absent on a fresh, ordinary drop (tombstone written, Storage swept).\n *\n * - `'already'` \u2014 it was already a tombstone. Idempotent; pairs with `dropped: true`. A re-drop is **not** a\n * no-op \u2014 it re-sweeps Storage, so it is how a residual in `generationsRemaining` is collected.\n * - `'absent'` \u2014 **nothing existed.** No registry row and no Storage objects. Pairs with `dropped: false`, and it\n * is the one value worth alerting on: the usual cause is a mistyped name or an omitted `namespace`, both of\n * which address a *different* segment than you meant.\n */\n readonly reason?: string;\n}\n\n/**\n * **Dispose of a segment: tombstone it, then delete its Storage objects.** Irreversible.\n *\n * WHY THIS EXISTS, given {@link destroySegment} already erases. Because `destroySegment` answers a *compliance*\n * question and this one answers an *operational* question, and they are not the same:\n *\n * - `destroySegment` **crypto-shreds** \u2014 it discards the key so the bytes are unreadable everywhere including\n * immutable backups, which is the only erasure that survives WORM. But it **leaves the objects in the\n * bucket**, still billed, and it *requires* encryption because a cleartext segment has no key to discard.\n * - `dropSegment` **removes the storage**. It works on a cleartext segment, and on an encrypted one it *also*\n * drops the DEKs, so it is a strict superset there.\n *\n * It is the supported way to delete a segment and stop paying for it. The obvious workaround \u2014 an object-store\n * lifecycle rule on the key prefix \u2014 deletes the bytes while the registry still points at them. That is exactly the `missing-storage-generation` state the DR runbook says not to serve traffic\n * on, and it surfaces *intermittently*, because a read consults the cache before Storage: cached chunks answer\n * correctly and evicted ones throw. The whole value of this function is that the ordering below cannot be got\n * wrong by a caller.\n *\n * **THE ORDER IS THE CONTRACT \u2014 registry first, then Storage.**\n *\n * 1. **Registry first.** After the tombstone nothing resolves a generation for this segment, so no reader can\n * reach for bytes that are about to disappear, and no writer can publish onto it (a\n * load refuses a `destroyed` row).\n * 2. **Storage second, best-effort, and re-swept.** Once the pointer is a tombstone the segment resolves as empty,\n * so a failure part-way through leaves **orphaned bytes, not a wrong answer.** Orphans cost money and are\n * cleaned up by re-running; a torn pointer costs correctness and is not self-healing. Given the choice, leak\n * bytes \u2014 but say so: whatever survives the sweep is reported in {@link DropResult.generationsRemaining}.\n *\n * **Why step 2 sweeps more than once.** A load that had read the row before the tombstone landed, whether it was\n * already writing its object or still consuming its ids, still finishes the write \u2014 its publish is then refused,\n * but the object exists, and it holds the complete set. A single list-then-delete misses it entirely. The re-sweep\n * converges because the tombstone *is* a hard fence on **publishing**, so only loads already under way can write\n * and they are finite. One that writes after the last pass deletes its own object when its publish is refused, as a\n * refused load does under a `destroyed` row; only one whose process stops in between, or whose publish fails without\n * a definite answer (a lost response, a timeout), leaves it behind, for a re-run of the drop, and\n * `generationsRemaining` cannot report an object written after this call returned.\n *\n * **When \"reads as empty\" starts being true.** Not instantly, for a store that has already read this segment: a\n * resolved generation is cached and decoded chunks sit in the cache, so an in-flight reader can answer from\n * cache for a window. A fresh store, or any reader that had not touched the segment, sees empty at once.\n *\n * That window is bounded by `cache.genTtlMs` (default 2 s) **only for a reader whose storage source has both a clock\n * and a registry and a positive TTL** \u2014 expiry needs all three. A source built without a clock or a registry, or\n * with `cache.genTtlMs: 0`, has no timed refresh: it notices the drop only when a read has to fetch from a deleted\n * generation or its reader cache evicts the segment. Because a cache-LRU hit never reaches Storage, such a reader\n * can answer `true` for a dropped segment **indefinitely**, and must be invalidated or restarted. This is the *same* caching that makes the delete-bytes-first ordering fail intermittently rather\n * than loudly \u2014 it cuts both ways.\n *\n * `confirmSegment` must equal `ref.segment`, matching `destroySegment`/`eraseNamespace`. For an automated\n * caller that guard is ceremony \u2014 use `dryRun` first, which reports what would go without touching anything.\n */\nexport async function dropSegment(\n ref: SegmentRef,\n deps: DropDeps,\n options: { confirmSegment: string; dryRun?: boolean; audit?: IAuditSink },\n): Promise<DropResult> {\n validateUserRef(ref);\n if (options.confirmSegment !== ref.segment) {\n throw new ValidationError(\n `dropSegment: confirmSegment must equal the segment name \"${ref.segment}\" (guard against accidental deletion)`,\n );\n }\n const base = { segment: ref.segment, namespace: ref.namespace };\n\n if (options.dryRun === true) {\n const record = await deps.registry.get(ref);\n const wouldDelete = await listGenerations(deps.storage, ref);\n return {\n ...base,\n dropped: false,\n generationsDeleted: [],\n generationsRemaining: [],\n wouldDelete,\n wouldCryptoShred: record?.wrappedDeks !== undefined && record.wrappedDeks.length > 0,\n cryptoShredded: false,\n reason:\n record === null\n ? wouldDelete.length === 0\n ? 'absent'\n : undefined\n : record.status === 'destroyed'\n ? 'already'\n : undefined,\n };\n }\n\n // Step 1, reused wholesale. `allowCleartext` is true because deleting objects does not need a key \u2014 the\n // encryption requirement belongs to crypto-shred, not to disposal.\n let shred = await shredSegment(ref, deps, true, 'dropSegment');\n\n // \u2500\u2500 THE ABSENT CASE. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // `shredSegment` returns `absent` having written NOTHING when there is no registry row \u2014 and going on to delete\n // every Storage generation anyway would skip the one step that makes the ordering safe while still running the\n // destructive one: a drop landing between a load's object write and its publish (minutes apart on a large\n // load) would leave a published pointer with no object behind it \u2014 precisely the\n // `missing-storage-generation` state this function exists to PREVENT.\n //\n // So the identity is claimed before anything is deleted. A `destroyed` row is exactly the fence the\n // writers already respect \u2014 a load refuses one \u2014 so creating it\n // converts the race into \"the writer is refused and the bytes are collected\".\n //\n // Only when Storage actually holds something, though. A drop against a *genuinely* nonexistent segment (the\n // typo the facade docs warn about) must not leave a `destroyed` row behind: that is registry litter, and worse,\n // it would refuse a later legitimate load of that name forever. No row and no objects \u21D2 nothing existed \u21D2 say\n // `absent` and touch nothing, which is what that reason is documented to mean.\n if (shred.reason === 'absent') {\n const orphans = await listGenerations(deps.storage, ref);\n if (orphans.length === 0) {\n return {\n ...base,\n dropped: false,\n generationsDeleted: [],\n generationsRemaining: [],\n cryptoShredded: false,\n reason: 'absent',\n };\n }\n try {\n await deps.registry.create(ref, {\n // The max listed generation, so the row is consistent with what is on disk if anything reads it before\n // the sweep finishes. It is a tombstone, so no reader resolves through it either way.\n currentGen: orphans[orphans.length - 1]!,\n status: 'destroyed',\n });\n shred = { ...shred, destroyed: true, reason: undefined };\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n // A row appeared between our `get` and our `create` \u2014 that is the racing writer we were trying to fence,\n // and it won. Fall back into the normal path, which now has a row to CAS into a tombstone.\n shred = await shredSegment(ref, deps, true, 'dropSegment');\n }\n }\n\n // `segment.erase` ONLY on a genuine crypto-shred \u2014 exactly the condition `destroySegment` uses, and\n // deliberately NOT `|| generationsDeleted.length > 0`.\n //\n // Adding that disjunct would be wrong in a way that matters. Four documents \u2014 including\n // `docs/guide/dashboards.md`, which calls this event the compliance *receipt* \u2014 define `segment.erase` as\n // proof of an irreversible crypto-shred: bytes unreadable everywhere, backups included. Deleting an object is\n // a weaker guarantee, because a noncurrent version, a cross-region replica or a PITR snapshot still holds the\n // cleartext. Emitting one event for both would make a compliance dashboard **over-attest**, which is the one\n // failure an audit trail exists to prevent.\n //\n // The cleartext case is NOT silent \u2014 it emits `segment.dispose` after the sweep (below). That kind exists\n // precisely so this one does not have to lie: disposal is attested as disposal, shredding as shredding, and an\n // encrypted drop emits both because both genuinely happened.\n //\n // And it goes out BEFORE the Storage sweep, not after. A genuine crypto-shred is complete the moment\n // `shredSegment` returns \u2014 the DEK wrappings are gone and the bytes are unreadable everywhere. If the sweep\n // then throws (a Storage driver that cannot list), emitting afterwards would mean **no receipt for a destruction\n // that really happened** \u2014 the exact mirror of the over-attestation above, and just as wrong. Both directions\n // of a false audit trail are defects; only one of them was obvious.\n if (shred.cryptoShredded) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.erase',\n namespace: ref.namespace,\n segment: ref.segment,\n });\n }\n\n // Step 2, swept until Storage comes back empty. See \"Why step 2 sweeps more than once\" above. `mapWithConcurrency`\n // returns results in *input* order, so each pass contributes ascending generations and the concatenation stays\n // ascending. Failures are swallowed per item so the pool never aborts \u2014 one unreachable generation must not\n // leave the rest orphaned too.\n const generationsDeleted: number[] = [];\n for (let pass = 0; pass < MAX_STORAGE_SWEEPS; pass++) {\n const present = await listGenerations(deps.storage, ref);\n if (present.length === 0) break;\n const outcomes = await mapWithConcurrency(present, ERASE_CONCURRENCY, async (generation) => {\n try {\n await deps.storage.delete({ ...ref, generation });\n return generation;\n } catch {\n // Leave it orphaned. The segment already reads as empty, so this is a billing problem, not a\n // correctness one, and re-running the drop collects whatever was missed.\n return null;\n }\n });\n let deletedThisPass = 0;\n for (const g of outcomes) {\n if (g !== null) {\n generationsDeleted.push(g);\n deletedThisPass += 1;\n }\n }\n if (deletedThisPass === 0) break; // nothing went \u2014 another pass will not help\n }\n // Enumerate once more rather than inferring the residual from the last pass, so the reported value is exactly\n // what is still in Storage whichever way the loop ended: empty, a delete that kept failing, or the pass budget\n // exhausted while a writer kept finishing. Inferring it would report `[]` in precisely the case that matters\n // \u2014 a final pass that deleted everything it saw, after which one more object appeared.\n const generationsRemaining = await listGenerations(deps.storage, ref);\n\n // `segment.dispose` attests the *storage reclamation*, which is the weaker but still compliance-relevant fact,\n // and it is emitted AFTER the sweep because unlike a crypto-shred it is not established until the sweep runs.\n // Only when a tombstone was actually written \u2014 an absent no-op disposed of nothing.\n if (shred.destroyed) {\n safeAudit(options.audit ?? NOOP_AUDIT).onEvent({\n kind: 'segment.dispose',\n namespace: ref.namespace,\n segment: ref.segment,\n generationsDeleted: generationsDeleted.length,\n });\n }\n\n return {\n ...base,\n dropped: shred.destroyed,\n generationsDeleted,\n generationsRemaining,\n cryptoShredded: shred.cryptoShredded,\n reason: shred.reason,\n };\n}\n\n/**\n * Every generation currently present in Storage for a segment, ascending.\n *\n * No error handling on purpose: `IStorageDriver.list` is mandatory (not optional in `ports.ts`), so a driver that\n * cannot list cannot exist, and one that *throws* from `list` should propagate.\n *\n * **Be precise about what a throw means at each call site.** From the `dryRun` branch it is accurate that the\n * call \"has established nothing\". From the real run it is NOT: by then the tombstone is written, so the segment\n * IS dropped and only the bytes leaked. The throw is still the right behaviour \u2014 it is the signal to re-run, and\n * a re-run takes the `'already'` path and re-attempts the sweep \u2014 but a caller who logs \"drop failed\" is wrong\n * about the segment's state. This is also why the audit event is emitted *before* the sweep: an irreversible\n * crypto-shred that really happened must not go unrecorded because a later, weaker step threw.\n */\nasync function listGenerations(storage: IStorageDriver, ref: SegmentRef): Promise<number[]> {\n const generations: number[] = [];\n for await (const key of storage.list(ref)) generations.push(key.generation);\n return generations.sort((a, b) => a - b);\n}\n\n/**\n * The shred itself: CAS the registry row to a `destroyed` tombstone with no wrappings.\n *\n * `op` is only for error text \u2014 `destroySegment` and `dropSegment` both come through here, and a message naming\n * the wrong one is operator-facing text on the single path where the operator must act.\n */\nasync function shredSegment(\n ref: SegmentRef,\n deps: EraseDeps,\n allowCleartext: boolean,\n op: 'destroySegment' | 'dropSegment' = 'destroySegment',\n): Promise<DestroyResult> {\n const base = { segment: ref.segment, namespace: ref.namespace };\n for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {\n const record = await deps.registry.get(ref);\n if (record === null) {\n // No authoritative row \u2192 nothing to crypto-shred.\n return { ...base, destroyed: false, cryptoShredded: false, reason: 'absent' };\n }\n if (record.status === 'destroyed') {\n return { ...base, destroyed: true, cryptoShredded: false, reason: 'already' };\n }\n const encrypted = record.wrappedDeks !== undefined && record.wrappedDeks.length > 0;\n if (!encrypted && !allowCleartext) {\n return { ...base, destroyed: false, cryptoShredded: false, reason: 'cleartext' };\n }\n try {\n await deps.registry.compareAndSwap(ref, record.token, {\n status: 'destroyed',\n wrappedDeks: undefined, // \u2190 the crypto-shred: the only copy of the DEK wrappings is gone\n keyId: undefined,\n // The current generation's cached count and metadata go with it: sealed, they cannot be opened without\n // the wrappings; clear, they would outlive the segment on its tombstone.\n summary: undefined,\n });\n // A genuine crypto-shred only when there were wrappings to drop; a cleartext opt-in tombstone leaves the\n // Storage bytes readable, so it is not an irreversible destruction (and does not emit `segment.erase`).\n return { ...base, destroyed: true, cryptoShredded: encrypted };\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n // A concurrent publish or policy write advanced the row \u2014 re-read and shred again (it always converges).\n }\n }\n throw new WriteConflictError(`${op}: contention shredding \"${ref.segment}\" \u2014 retry`);\n}\n", "/**\n * Segment retention policy \u2014 **when** a segment becomes eligible for retirement.\n *\n * This module only *records and reads the intent*. Nothing here deletes anything, and nothing here runs on a\n * timer: setting a policy is one registry write, and the sweep that acts on it (`retireExpired`) is a separate\n * call the operator schedules. That split is deliberate \u2014 see the \"who owns what\" note below.\n *\n * **The expiry is an absolute epoch-ms the writer sets, not a duration the library derives.** A relative TTL\n * anchored to anything the library knows would be wrong: every load republishes `currentGen` and touches\n * `updatedAt`, so a \"30 days since last write\" rule would keep a daily bucket alive forever precisely because\n * it keeps being reloaded. The caller who knows what the segment *means* computes the instant:\n *\n * ```ts\n * const DAY = 86_400_000;\n * await store.setRetention({ namespace: 'active-daily', segment: '2026-08-05' }, {\n * expiresAt: Date.now() + 30 * DAY,\n * });\n * ```\n *\n * **Who owns what.** The library owns the policy field, its validation, and the sweep that acts on it. **You own\n * the heartbeat** \u2014 an EventBridge rule, a Kubernetes CronJob, `cron`, a queue consumer, whatever already runs in\n * your deployment. There is no background thread here, and there will not be one: this library runs in a Lambda,\n * an edge isolate and a long-lived server alike, and a timer that only works in one of those is worse than none.\n *\n * **Storage.** The policy lives in the registry row's free-form `retention` metadata, which already round-trips\n * through every driver and is carried by `list()` \u2014 so a fleet-wide sweep reads it from the enumeration with no\n * per-segment `get()`. A segment that has no row yet (a policy recorded before the first load) gets one, with\n * `currentGen: null`: the row exists so the segment is *enumerable*, and it claims no Storage generation, so every\n * read answers empty until the first load publishes onto it.\n */\nimport { ValidationError, WriteConflictError, isWriteConflictError } from './errors';\nimport { canIndex, dueBucket, dueIndexRef } from './due-index';\nimport type { GovernanceMeta, IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\nimport { validateUserRef } from './validate';\n\n/** The key the policy is stored under inside the row's `retention` metadata. */\nconst EXPIRES_AT = 'expiresAt';\n\n/** How many times a policy write re-reads and retries under contention before reporting a conflict. */\nconst RETENTION_CAS_ATTEMPTS = 5;\n\n/**\n * The smallest accepted `expiresAt`: 2001-09-09, the point below which an epoch-**seconds** value would land.\n *\n * This is a deliberate guard against the single most damaging typo available here. `Date.now() / 1000 + 30 * 86400`\n * is a natural thing to write, and it produces a number ~1.8 billion \u2014 an instant in **1970**, which is to say\n * *already expired*. Without this check, that typo is not a validation error, it is a sweep that retires the\n * segment on its very next pass. Anything below this floor is far likelier to be seconds than a genuine intent to\n * expire a segment two decades ago, and the caller who really means \"retire it now\" can say so with `dropSegment`.\n */\nexport const MIN_EXPIRES_AT_MS = 1_000_000_000_000;\n\n/** A segment's retention policy: the instant it becomes eligible for retirement. */\nexport interface RetentionPolicy {\n /**\n * Absolute epoch-**milliseconds**. Once `now >= expiresAt`, a retention sweep may retire the segment \u2014 tombstone\n * it and delete its Storage generations. A value in the past is legal (backfilling a policy onto existing\n * buckets is a normal thing to do) and means \"eligible on the next sweep\".\n */\n readonly expiresAt: number;\n}\n\nexport interface RetentionDeps {\n readonly registry: IRegistryDriver;\n}\n\nexport interface SetRetentionResult {\n readonly segment: string;\n readonly namespace?: string;\n /** The policy now stored on the row. */\n readonly expiresAt: number;\n /**\n * True iff this call **minted the registry row** \u2014 a policy recorded before the segment's first load. The\n * segment is now enumerable by `registry.list()` and therefore by every fleet-wide operation; the row claims no\n * Storage generation (`currentGen: null`), so reads answer empty until the first load publishes onto it.\n */\n readonly createdRow: boolean;\n /**\n * True iff the **due-index pointer** for this policy was written, so a fast sweep will find this segment by\n * reading only its expiry day instead of scanning the fleet.\n *\n * `false` is a **degradation, not an error**, and the policy is committed either way: either the ref is too\n * long to encode into one index-row name, or the pointer write failed. The full-scan repair pass still sees\n * the segment's own row, so it is retired on the repair cadence rather than the fast one \u2014 slower, never\n * never. Alarm on a *sustained* run of `false`, not on one.\n */\n readonly indexed: boolean;\n}\n\n/** Fail-fast validation of a caller-supplied policy. Boundary check \u2014 untrusted-input posture. */\nfunction validateRetentionPolicy(policy: RetentionPolicy): void {\n const { expiresAt } = policy;\n if (typeof expiresAt !== 'number' || !Number.isInteger(expiresAt)) {\n throw new ValidationError(\n `retention.expiresAt must be an integer epoch-ms; got ${String(expiresAt)}`,\n );\n }\n if (expiresAt < MIN_EXPIRES_AT_MS) {\n throw new ValidationError(\n `retention.expiresAt (${expiresAt}) is before ${MIN_EXPIRES_AT_MS} \u2014 that is almost certainly epoch ` +\n `SECONDS rather than milliseconds, which would read as \"already expired\" and retire the segment on the ` +\n `next sweep. Use \\`Date.now() + days * 86_400_000\\`. To retire a segment now, call \\`dropSegment\\`.`,\n );\n }\n}\n\n/**\n * Read the policy off a registry row's `retention` metadata. Returns `null` when there is no policy, and\n * `'invalid'` when the key is present but unusable (wrong type, or below the epoch-seconds floor).\n *\n * The three-way answer exists so a fleet sweep can *report* a malformed policy instead of choosing between two\n * bad options: treating it as \"no policy\" hides an operator error on a segment someone believes is expiring, and\n * throwing aborts a ledger over one bad row. Nothing writes an invalid policy through\n * {@link setSegmentRetention} \u2014 hand-edited rows, an older/newer writer, or a restore are how it happens.\n */\nexport function readRetentionPolicy(\n meta: GovernanceMeta | undefined,\n): RetentionPolicy | null | 'invalid' {\n if (meta === undefined) return null;\n // Guard the CONTAINER before the `in` test, not just the value inside it. `in` dereferences its operand, so a\n // stored `retention` of `null` (or a string, a number, an array) threw an untyped `TypeError` from here \u2014 and a\n // fleet sweep calls this *outside* its per-segment `try`, so one malformed row aborted the whole sweep and the\n // healthy expired segment next to it was never retired. That is precisely the outcome the three-way answer\n // exists to prevent. Both registry boundaries now reject a non-object blob, so this is the third line of defence\n // rather than the only one \u2014 but it is the one that keeps a ledger alive.\n if (meta === null || typeof meta !== 'object' || Array.isArray(meta)) return 'invalid';\n if (!(EXPIRES_AT in meta)) return null;\n const raw = meta[EXPIRES_AT];\n if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < MIN_EXPIRES_AT_MS)\n return 'invalid';\n return { expiresAt: raw };\n}\n\n/** The stored policy for one segment, or `null` if it has none (and `'invalid'` for a malformed one). */\nexport async function getSegmentRetention(\n ref: SegmentRef,\n deps: RetentionDeps,\n): Promise<RetentionPolicy | null | 'invalid'> {\n validateUserRef(ref);\n const record = await deps.registry.get(ref);\n if (record === null || record.status === 'destroyed') return null;\n return readRetentionPolicy(record.retention);\n}\n\n/**\n * Record when a segment becomes eligible for retirement, creating its registry row if it has none.\n *\n * Idempotent and safe to re-run: writing the same instant twice is a no-op in effect. Other keys already in the\n * row's `retention` metadata are preserved \u2014 this owns one key, not the whole object.\n *\n * Refuses a `destroyed` segment: a tombstone has nothing left to retire, and putting a policy on one would make\n * a sweep repeatedly \"retire\" bytes that are already gone.\n */\n/** The expiry currently recorded on a row, if any \u2014 the input to deciding which old pointer to remove. */\nfunction readExpiresAt(record: { retention?: GovernanceMeta } | null): number | undefined {\n const parsed = record === null ? null : readRetentionPolicy(record.retention);\n return parsed === null || parsed === 'invalid' ? undefined : parsed.expiresAt;\n}\n\n/**\n * Move the due-index pointer to match a policy change: add the new bucket's pointer, drop the old one.\n *\n * **Best-effort by construction, and that is the design rather than a shortcut.** The policy is already\n * committed when this runs, and the index is a fast path that nothing depends on for correctness \u2014 a pointer\n * that fails to appear costs a repair-cadence delay, and one that fails to disappear costs a single wasted read\n * when its bucket comes due (the sweep re-reads the live row and skips). So a failure here must never turn a\n * successful policy write into a thrown error; it is reported through `indexed` instead.\n *\n * Order matters the other way round from intuition: the **new pointer is written first**. Interrupted between\n * the two writes, the segment is then reachable from both buckets \u2014 a duplicate the sweep handles by re-reading\n * \u2014 whereas the reverse order would leave a window in which it is reachable from neither.\n */\nasync function reindex(\n ref: SegmentRef,\n deps: RetentionDeps,\n expiresAt: number | undefined,\n previousExpiresAt: number | undefined,\n): Promise<boolean> {\n // Not independently observable: `dueIndexRef` would throw for the same refs and the catch below would swallow\n // it to the same `indexed: false`. Kept because relying on a thrown ValidationError for ordinary control flow\n // reads as an accident, and because it keeps a programming condition out of a try block whose catch exists for\n // I/O failures. No mutation kills it, and that is a property of the equivalence, not of the tests.\n if (!canIndex(ref)) return false;\n const nextBucket = expiresAt === undefined ? undefined : dueBucket(expiresAt);\n const oldBucket = previousExpiresAt === undefined ? undefined : dueBucket(previousExpiresAt);\n let indexed = expiresAt === undefined;\n if (nextBucket !== undefined) {\n try {\n await deps.registry.create(dueIndexRef(nextBucket, ref), { currentGen: null });\n indexed = true;\n } catch (err) {\n // Already there \u2014 the pointer is idempotent, and a second policy write in the same day is the common case.\n if (isWriteConflictError(err)) indexed = true;\n }\n }\n if (oldBucket !== undefined && oldBucket !== nextBucket) {\n try {\n await deps.registry.delete(dueIndexRef(oldBucket, ref));\n } catch {\n // A pointer left behind is read once, when its bucket comes due, and skipped. Nothing to recover.\n }\n }\n return indexed;\n}\n\nexport async function setSegmentRetention(\n ref: SegmentRef,\n deps: RetentionDeps,\n policy: RetentionPolicy,\n): Promise<SetRetentionResult> {\n validateUserRef(ref);\n validateRetentionPolicy(policy);\n const base = { segment: ref.segment, namespace: ref.namespace };\n /** The bucket this segment was already in, if any \u2014 its pointer has to be removed when the expiry moves. */\n let previousExpiresAt: number | undefined;\n for (let attempt = 0; attempt < RETENTION_CAS_ATTEMPTS; attempt += 1) {\n const record = await deps.registry.get(ref);\n try {\n previousExpiresAt = readExpiresAt(record);\n if (record === null) {\n // A policy ahead of the first load. `currentGen: null` is what makes this safe: the row exists purely so\n // the segment is enumerable, and it claims no Storage generation, so generation resolution takes the same\n // path it takes for a segment with no row at all \u2014 and the first publish advances it.\n await deps.registry.create(ref, {\n currentGen: null,\n retention: { [EXPIRES_AT]: policy.expiresAt },\n });\n return {\n ...base,\n expiresAt: policy.expiresAt,\n createdRow: true,\n indexed: await reindex(ref, deps, policy.expiresAt, undefined),\n };\n }\n assertNotDestroyed(record, ref);\n await deps.registry.compareAndSwap(ref, record.token, {\n retention: { ...record.retention, [EXPIRES_AT]: policy.expiresAt },\n });\n return {\n ...base,\n expiresAt: policy.expiresAt,\n createdRow: false,\n indexed: await reindex(ref, deps, policy.expiresAt, previousExpiresAt),\n };\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n // Lost the race (a load's publish, another policy write) \u2014 re-read and retry.\n }\n }\n throw new WriteConflictError(\n `setRetention: contention writing the retention policy for segment \"${ref.segment}\"`,\n );\n}\n\n/**\n * Remove a segment's expiry so no sweep will retire it. Returns whether a policy was actually removed (`false`\n * for a segment with no row, or one that had no policy) \u2014 a separate verb from setting one, because \"cancel the\n * expiry\" and \"expire at time T\" are different intents and a magic sentinel value for the first is how a typo\n * becomes a deletion.\n *\n * Other `retention` keys are left in place; if removing the expiry empties the metadata, the field is cleared.\n */\nexport async function clearSegmentRetention(\n ref: SegmentRef,\n deps: RetentionDeps,\n): Promise<boolean> {\n validateUserRef(ref);\n for (let attempt = 0; attempt < RETENTION_CAS_ATTEMPTS; attempt += 1) {\n const record = await deps.registry.get(ref);\n if (record === null) return false; // nothing to clear \u2014 and creating a row to say so would be litter\n if (record.status === 'destroyed') return false; // terminal; a tombstone has no expiry to cancel\n if (record.retention === undefined || !(EXPIRES_AT in record.retention)) return false;\n const rest = { ...record.retention };\n delete rest[EXPIRES_AT];\n try {\n // `retention: undefined` CLEARS the field (a patch clears by mentioning), which is what an emptied policy\n // object should become \u2014 a row carrying `{}` reads as \"has retention metadata\" to anything inspecting it.\n await deps.registry.compareAndSwap(ref, record.token, {\n retention: Object.keys(rest).length === 0 ? undefined : rest,\n });\n await reindex(ref, deps, undefined, readExpiresAt(record));\n return true;\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n }\n }\n throw new WriteConflictError(\n `clearRetention: contention clearing the retention policy for segment \"${ref.segment}\"`,\n );\n}\n\nfunction assertNotDestroyed(record: RegistryRecord, ref: SegmentRef): void {\n if (record.status === 'destroyed') {\n throw new ValidationError(\n `segment \"${ref.segment}\" is destroyed (crypto-shredded) \u2014 refusing to set a retention policy on a ` +\n `tombstone; there is nothing left to retire. Use a new segment.`,\n );\n }\n}\n", "/**\n * The retention **sweep** \u2014 the thing that acts on the policies `setSegmentRetention` records.\n *\n * `retireExpired` enumerates the registry, selects the segments whose `expiresAt` has passed, and retires each\n * one through {@link dropSegment}. It deliberately **delegates rather than reimplements**: the registry \u2192 Storage\n * ordering, the re-sweep for an object a load was still writing, and the `generationsRemaining` report are all\n * load-bearing and already live there. A sweep that open-coded the deletions would be a second\n * implementation of the most dangerous ordering in the library.\n *\n * **This is a call, not a daemon.** Nothing here schedules itself. You run it from whatever heartbeat your\n * deployment already has \u2014 an EventBridge rule, a Kubernetes CronJob, a queue consumer, the job that runs your\n * loads \u2014 and the library stays a library: it has to behave identically in a Lambda, an edge isolate and a\n * long-lived server, and a timer that only works in one of those is worse than none.\n *\n * Three properties make it safe to point at a fleet:\n *\n * - **`dryRun` at the sweep level.** `dropSegment`'s `confirmSegment` guard is vacuous in a loop (it is the same\n * variable twice), so the meaningful preview is here: it reports what it *would* retire, per segment, and\n * touches nothing.\n * - **A per-cycle `limit`.** Clock skew, a bad backfill, or a namespace-wide policy mistake should cost one\n * bounded batch, not the fleet. When the limit bites, the result says so (`limited: true`) rather than looking\n * like a completed sweep.\n * - **A ledger, never an exception.** A per-segment fault is recorded and the sweep continues \u2014 the caller's\n * load-bearing question is *which* segments were retired, and a throw from the middle of the loop answers it\n * for none of them while having already retired some. Same posture as `eraseNamespace`.\n *\n * It also **purges the tombstones its own retirements leave behind**. A retired segment that had a registry row\n * gets a `destroyed` row, and one dead row per retired daily bucket \u2014 or per retired dedup wave \u2014 is exactly the\n * registry litter `dropSegment` already refuses to create for a row-less accumulator. Purging is narrow on\n * purpose: only a tombstone carrying the **stamp this sweep writes on its own retirements** (so it is attributably\n * ours: a crypto-shred's tombstone does not carry it, unless the shred lands between this sweep's re-read of the\n * row and its drop, which then reports the tombstone dropped and stamps it), only after a grace period, and only\n * once Storage is provably empty for it \u2014\n * because deleting the row is what makes the name reusable and takes the segment out of reach of\n * the generation collection.\n *\n * **On a registry that reports `conditionalDelete`, the purge removes the row for good**, so a full scan stops paying a\n * read for every name a namespace ever held: the scan costs what is live and what is inside its grace. The delete is\n * fenced on the token the purge judged and applied by the store only to that version of the row. Each retirement\n * files a pointer in the due index under the day its tombstone's grace ends, so `scan: 'index'` purges as well as\n * retires; nothing on the row records that day, which the purge derives from the stamp. The purge removes every pointer\n * it read to the row, and a pointer that outlives its row anyway (a purge that ran with another grace, a delete that\n * landed and lost its response, a pointer older than an index scan's lookback) is removed by the next scan that reads\n * it: an index scan in its buckets, and an unscoped fleet scan, which reads every pointer, in all of them. Where a\n * delete only rewrites the row as a tombstone, none of this is done: nothing is removed for good, so a pointer would\n * only add a row for every scan to read.\n */\nimport { type IAuditSink } from './audit';\nimport { BudgetExceededError, ValidationError, isWriteConflictError } from './errors';\nimport { gcOrphanGenerations } from './generation-gc';\nimport { drainRegistry } from './registry-scan';\nimport { dropSegment } from './erasure';\nimport type { DropDeps, DropResult } from './erasure';\nimport { MIN_EXPIRES_AT_MS, readRetentionPolicy } from './retention';\nimport { DEFAULT_MAX_SCAN_SEGMENTS } from './registry-scan';\nimport {\n canIndex,\n decodeDueName,\n dueBucket,\n dueBucketsAt,\n dueIndexRef,\n dueNamespace,\n} from './due-index';\nimport { segmentKey, shardOf } from './keys';\nimport type { IRegistryDriver, RegistryRecord, Token } from './ports';\nimport type { GovernanceMeta, IStorageDriver, SegmentRef } from './ports';\nimport { isReservedNamespace, validateUserNamespace } from './validate';\n\n/** Default cap on retirements per sweep \u2014 a bounded batch, so a policy mistake costs one batch, not the fleet. */\nexport const DEFAULT_RETIRE_LIMIT = 100;\n\n/**\n * How many purges in a row the registry may refuse in one call before the sweep stops purging for the rest of it. A\n * blanket refusal (a policy that denies delete) costs this many attempts and no more; a refusal particular to one row\n * (a blob with a snapshot, an object under a legal hold) does not hold the purges behind it, since a purge that succeeds\n * starts the count again.\n */\nexport const MAX_CONSECUTIVE_PURGE_FAULTS = 3;\n\n/**\n * Default delay before a retirement's own tombstone row is purged: 24 h.\n *\n * The row is a fence \u2014 while it exists, a load refuses the segment, so\n * a load that was mid-write when the drop landed cannot resurrect it. That window is\n * seconds to minutes in practice; a day of margin costs one tiny row and removes any need to reason about it.\n */\nexport const DEFAULT_TOMBSTONE_GRACE_MS = 86_400_000;\n\nexport interface RetireExpiredOptions {\n /** Scope the sweep to one namespace. Omit to sweep every namespace the registry knows. */\n readonly namespace?: string;\n /**\n * The instant to compare policies against (epoch-ms). Required here because `core/` takes its time from the\n * caller, never from the platform \u2014 `store.retireExpired()` fills it in from the store's clock.\n */\n readonly now: number;\n /**\n * Maximum segments to retire in this cycle (default 100). It also bounds the purges attempted (a purge the registry\n * refuses is not charged; {@link RetireExpiredResult.purgeFaults} says how purging stops after refusals) and the\n * due-index pointers to nothing one call removes.\n */\n readonly limit?: number;\n /** Report what would be retired and change nothing. */\n readonly dryRun?: boolean;\n /** Forwarded to each `dropSegment`, so every retirement lands in the audit trail as `segment.dispose`. */\n readonly audit?: IAuditSink;\n /** Ceiling on rows enumerated (default 250,000); exceeding it throws. */\n readonly maxScanSegments?: number;\n /**\n * **Where the candidates come from.**\n *\n * - `'fleet'` (default) \u2014 drain `registry.list()` and filter. Cost tracks the **fleet**, every cycle, even\n * when nothing expires. Complete by construction: it cannot miss a policy.\n * - `'index'` \u2014 read only the due buckets of the due index. Cost tracks **what is\n * expiring**. Each candidate's live row is still re-read before anything is decided, so a stale pointer\n * costs one read and retires nothing.\n *\n * **`'index'` is not a drop-in replacement for `'fleet'`; it is the fast half of a pair.** A policy whose\n * pointer write failed (`indexed: false`), or whose ref is too long to index, has no pointer \u2014 so a\n * deployment that *only* ever runs `'index'` will never retire those. Run `'fleet'` periodically as the\n * repair pass. The default is `'fleet'` because it is complete by construction.\n *\n * An index scan purges too, on a registry that reports `conditionalDelete`: each retirement files a pointer to its\n * tombstone under the day the tombstone's grace ends, and the scan reads it back with the expiry pointers. A pointer\n * whose segment is gone is removed once read, by an index scan in the buckets it reads, and by an unscoped `'fleet'`\n * scan (no `namespace`) in every bucket.\n */\n readonly scan?: 'fleet' | 'index';\n /**\n * **The shards this worker owns**, with {@link totalShards}. Without them every replica sweeps the whole\n * fleet and contends over the same segments. A stable hash of the segment key, so a worker owns the same slice\n * across restarts.\n */\n readonly shards?: readonly number[];\n /** Total shards the fleet is split into. Required with {@link shards}; ignored without it. */\n readonly totalShards?: number;\n /**\n * How many **past** buckets an `'index'` scan reads besides the current one (default 7, one per day).\n * A sweep that did not run \u2014 scaled to zero, a failed deploy, a paused\n * schedule \u2014 leaves its buckets behind, and this is how far back a later cycle reaches for them. Bounded so a\n * long outage costs a bounded number of list calls per cycle rather than one per day since the epoch;\n * anything older is the `'fleet'` repair pass's job.\n */\n readonly lookbackBuckets?: number;\n /**\n * Whether to delete the tombstone rows this sweep's own past retirements left (default `true`): removed for good on\n * a registry that reports `conditionalDelete`, tombstoned on one that does not. Set `false` to\n * keep every one of them \u2014 the right choice if something outside this library treats the presence of a\n * `destroyed` row as an attestation. That includes the row of a retirement whose drop found no Storage\n * generation to delete and left none behind: with the default that row is deleted in the same pass, since it\n * would do nothing but fence the name against every writer, and with `false` it stays, stamped like any other\n * of this sweep's tombstones, so a later sweep with purging on deletes it once `tombstoneGraceMs` has passed.\n * While it stays, it fences the name like every kept tombstone. What records a retirement either way is its\n * ledger entry, and its `segment.dispose` event (`generationsDeleted: 0` for an empty one) when an `audit` sink\n * is passed.\n *\n * Two knobs rather than one `number | 'never'`, deliberately: `0` would have had to mean \"purge immediately\"\n * here while `cache.genTtlMs: 0` in this same library means \"never refresh on a timer\", and one option whose\n * zero is the opposite of another's is a reading hazard for whoever tunes both.\n */\n readonly purgeTombstones?: boolean;\n /** How long a retirement's tombstone must age before this sweep deletes it (default 24 h). */\n readonly tombstoneGraceMs?: number;\n}\n\n/** What happened to one segment in a sweep. */\nexport type RetireEntry =\n | {\n readonly segment: string;\n readonly namespace?: string;\n /** Retired: `result` is `dropSegment`'s full report \u2014 **check `generationsRemaining`**. */\n readonly action: 'retired';\n readonly expiresAt: number;\n readonly result: DropResult;\n /**\n * Present when the retirement **completed the destructive part and then faulted** \u2014 the tombstone is written\n * and the segment reads empty, but something after that (the Storage sweep) threw. The storage may not be fully\n * reclaimed; re-run. Reported as `retired` rather than `skipped` because the segment really is retired, and\n * saying otherwise is the one thing a caller cannot recover from.\n */\n readonly fault?: `failed: ${string}`;\n }\n | {\n readonly segment: string;\n readonly namespace?: string;\n /** `dryRun` only \u2014 what a real sweep would have retired, with `dropSegment`'s own preview attached. */\n readonly action: 'would-retire';\n readonly expiresAt: number;\n readonly result: DropResult;\n }\n | {\n readonly segment: string;\n readonly namespace?: string;\n /** A tombstone row from an earlier retirement was deleted (or would be, under `dryRun`). */\n readonly action: 'purged-tombstone' | 'would-purge-tombstone';\n }\n | {\n readonly segment: string;\n readonly namespace?: string;\n readonly action: 'skipped';\n /**\n * `'invalid-policy'` \u2014 the row has an `expiresAt` that is not usable (hand-edited, or restored from another\n * schema). Reported rather than ignored: reading as \"never expires\" on a segment someone believes is\n * expiring is the silence that costs a retention commitment.\n * `'limit'` \u2014 eligible, but this cycle's `limit` was already spent. Re-run to continue.\n * `'tombstone-not-empty'` \u2014 a tombstone whose Storage generations are not gone even after a GC\n * attempt, so its row is kept: the row is what keeps the segment reachable by the generation collection and\n * refused by every writer. Several causes, all self-healing: the storage really could not be reclaimed,\n * the collection *declined* because the row changed underneath it (`WriteConflictError`, which this\n * sweep swallows deliberately), or this was a `dryRun`, which reports the reason without attempting the\n * collection at all. Except under `dryRun` the next cycle retries.\n * `'policy-changed'` \u2014 the live row no longer says \"expired\" (a `clearRetention`, a new `expiresAt`, or\n * someone else's drop landed between the enumeration and this segment's turn). Not an error: the sweep\n * re-reads before every deletion precisely so cancelling an expiry works on a sweep already in flight.\n * `` `failed: \u2026` `` \u2014 any fault, isolated to this segment. A fault that happened *after* the tombstone\n * landed is reported as `retired` with a `fault` instead, because that segment IS retired.\n */\n readonly reason:\n 'invalid-policy' | 'limit' | 'tombstone-not-empty' | 'policy-changed' | `failed: ${string}`;\n };\n\nexport interface RetireExpiredResult {\n /** Registry rows enumerated. */\n readonly scanned: number;\n /** Rows whose policy said \"expired\" \u2014 including any the `limit` deferred. */\n readonly eligible: number;\n /**\n * Segments **actually retired**. Zero under `dryRun` \u2014 see `wouldRetire`. Kept honest because this is the field\n * most likely to end up on a dashboard: a counter that means \"deleted\" in one mode and\n * \"would delete\" in another produces phantom deletions on any graph that does not also join on `dryRun`.\n */\n readonly retired: number;\n /** Segments a real sweep **would** have retired. Only ever non-zero under `dryRun`. */\n readonly wouldRetire: number;\n /** Tombstone rows actually deleted. Zero under `dryRun`; the `would-purge-tombstone` entries carry the preview. */\n readonly tombstonesPurged: number;\n /** True when `limit` cut the cycle short \u2014 **more segments are still eligible**. Re-run. */\n readonly limited: boolean;\n /**\n * Deletes this sweep attempted that the registry **refused for a reason other than a lost race**: a tombstone's\n * purge, or the removal of a due-index pointer. A policy that denies delete, an Azure blob with a snapshot, or any\n * raw provider error is one; a write that landed between the sweep's read and its fenced delete is not (that is\n * `failed: contended` in the ledger). Each leaves its row or pointer in place, so a purge that keeps failing never\n * frees the name.\n *\n * A refused purge is not charged to `limit`, so retirements go on: a tombstone that cannot be purged does not hold\n * the segments behind it past their expiry. **Purging stops for the rest of the call after\n * {@link MAX_CONSECUTIVE_PURGE_FAULTS} (3) refused purges in a row**, and a purge that succeeds starts the count\n * again. A blanket refusal therefore costs three attempts a call, and a refusal particular to one row (a blob with a\n * snapshot, an object under a legal hold) holds nothing behind it. The next call tries again. Check this field; a\n * ledger entry for each is `skipped`, but a caller that reads only `retired` sees none of it.\n */\n readonly purgeFaults: number;\n /** The first of those faults, as a ledger reason (`failed: \u2026` with the provider's message). Absent when there were none. */\n readonly firstPurgeFault?: `failed: ${string}`;\n readonly dryRun: boolean;\n /** Per-segment ledger. Inspect it: a `skipped` or failed entry is a segment that still holds data. */\n readonly entries: readonly RetireEntry[];\n}\n\n/**\n * How many past buckets an index scan reads besides the current one. A week: long enough that a weekend outage\n * or a paused schedule recovers on its own, short enough that a cycle is eight list calls rather than hundreds.\n */\nexport const DEFAULT_LOOKBACK_BUCKETS = 7;\n\n/** A due-index pointer as a scan found it: where it is, the segment it points at, and its token. */\ninterface FoundPointer {\n /** The pointer row's own ref, in its `cbm.due.<day>` namespace. */\n readonly ref: SegmentRef;\n /** The segment it points at. */\n readonly target: SegmentRef;\n /** The pointer row's token, which fences its removal; absent for a pointer computed rather than read. */\n readonly token?: Token;\n}\n\n/** What an index scan found: the candidates' live rows, the pointers to each, and the pointers to nothing. */\ninterface IndexScan {\n readonly rows: RegistryRecord[];\n /** Every pointer the scan read to each candidate, by the candidate's segment key. */\n readonly pointers: ReadonlyMap<string, readonly FoundPointer[]>;\n /** Pointers whose segment has no row: litter a retirement or a purge left when removing them failed. */\n readonly litter: readonly FoundPointer[];\n}\n\n/**\n * Candidates from the due index: read the buckets that are due, resolve each pointer, and **re-read the live\n * row**.\n *\n * That re-read is the load-bearing line. The index is a fast path and the segment's own row is the truth, so a\n * pointer whose policy has since been cleared, moved, or destroyed must cost one read and change nothing \u2014 the\n * ordinary eligibility check downstream then skips it, using exactly the same logic the fleet scan uses. There\n * is no second decision path to keep in step, which is the property that makes a second index safe here. A\n * `destroyed` row is handed to the same purge branch the fleet scan uses, so the pointer a retirement files for its\n * tombstone lets an index scan purge it too.\n *\n * A pointer we cannot decode is skipped. One whose segment no longer exists is returned as litter, for the sweep to\n * remove: this function decides nothing irreversible.\n */\nasync function rowsFromDueIndex(\n registry: IRegistryDriver,\n options: { now: number; lookbackBuckets: number; namespace?: string; maxScanSegments: number },\n): Promise<IndexScan> {\n const rows: RegistryRecord[] = [];\n const pointers = new Map<string, FoundPointer[]>();\n const litter: FoundPointer[] = [];\n const gone = new Set<string>();\n for (const bucket of dueBucketsAt(options.now, options.lookbackBuckets)) {\n for await (const pointer of registry.list(dueNamespace(bucket))) {\n const ref = decodeDueName(pointer.segment);\n if (ref === null) continue; // a foreign row in the reserved namespace \u2014 ignored, never acted on\n // A pointer to a segment in the reserved namespace itself: one written before that namespace was refused.\n // The fleet scan skips such a row as bookkeeping, so this scan does too, rather than fail on it each cycle.\n if (isReservedNamespace(ref.namespace)) continue;\n if (options.namespace !== undefined && ref.namespace !== options.namespace) continue;\n const found: FoundPointer = {\n ref: { namespace: pointer.namespace, segment: pointer.segment },\n target: ref,\n token: pointer.token,\n };\n // A segment can appear in two buckets at once: `reindex` writes the new pointer before deleting the old,\n // so an interruption leaves both, and a retirement's tombstone has a pointer of its own. De-duplicate here\n // rather than retiring twice and reporting a phantom, and keep every pointer for the purge to remove.\n const key = segmentKey(ref);\n const known = pointers.get(key);\n if (known !== undefined) {\n known.push(found);\n continue;\n }\n if (gone.has(key)) {\n litter.push(found);\n continue;\n }\n if (rows.length >= options.maxScanSegments) {\n throw new BudgetExceededError(\n `retireExpired: the due index yielded more than ${options.maxScanSegments} segments \u2014 the scan was ` +\n `abandoned there rather than completed. Raise \\`maxScanSegments\\`, narrow with \\`namespace\\`, or ` +\n `reduce \\`lookbackBuckets\\`.`,\n );\n }\n const live = await registry.get(ref);\n if (live === null) {\n gone.add(key);\n litter.push(found); // the segment is gone; nothing will read this pointer usefully again\n continue;\n }\n rows.push(live);\n pointers.set(key, [found]);\n }\n }\n return { rows, pointers, litter };\n}\n\n/**\n * Candidates from the whole registry, as `registry.list()` yields them, and the due-index pointers it yielded on the\n * way. An unscoped listing reads every pointer (the listing pays a read for each before the scan skips it), and a\n * pointer whose segment is not among the rows is litter, as it is to an index scan: whatever left it (a purge that\n * ran with another grace than the one that filed it, a delete that landed and lost its response, a pointer older than\n * an index scan's lookback) the fleet scan, which reads every row, is the one that can see it. A scan limited to a\n * `namespace` lists no pointers, and finds none.\n *\n * At most `maxScanSegments` pointers are held; the rest are left for a later scan. This decides nothing irreversible:\n * a pointer is removed only after its segment is read again, and the row it names is still absent.\n */\nasync function rowsFromFleet(\n registry: IRegistryDriver,\n options: { namespace?: string; maxScanSegments: number },\n): Promise<IndexScan> {\n const held: FoundPointer[] = [];\n const rows = await drainRegistry(registry, {\n namespace: options.namespace,\n maxScanSegments: options.maxScanSegments,\n op: 'retireExpired',\n onReserved: (pointer) => {\n if (held.length >= options.maxScanSegments) return;\n const target = decodeDueName(pointer.segment);\n // A foreign row in the reserved namespace, or a pointer to a segment in that namespace itself (written before it\n // was refused): not the sweep's to read, and never acted on.\n if (target === null || isReservedNamespace(target.namespace)) return;\n held.push({\n ref: { namespace: pointer.namespace, segment: pointer.segment },\n target,\n token: pointer.token,\n });\n },\n });\n const pointers = new Map<string, FoundPointer[]>();\n const litter: FoundPointer[] = [];\n if (held.length === 0) return { rows, pointers, litter };\n const present = new Set(rows.map((rec) => segmentKey(rec)));\n for (const found of held) {\n const key = segmentKey(found.target);\n if (!present.has(key)) litter.push(found);\n else if (pointers.has(key)) pointers.get(key)!.push(found);\n else pointers.set(key, [found]);\n }\n return { rows, pointers, litter };\n}\n\n/**\n * Best-effort removal of a retired segment's due-index pointer. A failure here leaves litter that costs one\n * read when its bucket next comes due and is then skipped (the segment is gone, so the live re-read yields\n * `null`) \u2014 never a wrong retirement, so it must not turn a successful retirement into a fault.\n */\nasync function forgetDuePointer(\n registry: IRegistryDriver,\n ref: SegmentRef,\n expiresAt: number,\n onFault: (err: unknown) => void,\n): Promise<void> {\n if (!canIndex(ref)) return;\n try {\n await registry.delete(dueIndexRef(dueBucket(expiresAt), ref));\n } catch (err) {\n // See above: litter, not a wrong retirement. The caller is told, though: a delete the registry refuses is counted.\n onFault(err);\n }\n}\n\n/**\n * File the pointer that lets an index scan purge a tombstone: under the day its grace ends, the stamp plus the grace.\n * No field of the row records that day; the bucket is the index, and the purge derives the day from the stamp.\n *\n * Best-effort, like every pointer write: a pointer already there is the same pointer, and one that fails to appear\n * leaves the tombstone to the fleet scan, which purges it on the repair cadence.\n */\nasync function filePurgePointer(\n registry: IRegistryDriver,\n ref: SegmentRef,\n purgeAt: number,\n): Promise<void> {\n if (!canIndex(ref)) return;\n try {\n await registry.create(dueIndexRef(dueBucket(purgeAt), ref), { currentGen: null });\n } catch {\n // Already there, or not written: see above.\n }\n}\n\n/**\n * Remove the pointers to a purged tombstone: every one the index scan read to it, each fenced on the token it was\n * read with, and the one this sweep's grace would have filed, which a fleet scan has not read. Best-effort: a pointer\n * left behind costs one read when its bucket is next scanned, which then finds its row gone and removes it.\n *\n * Runs only once the row is gone, never after a delete whose outcome is unknown, so a pointer is only ever removed\n * from a tombstone that no longer needs finding.\n */\nasync function forgetPurgePointers(\n registry: IRegistryDriver,\n ref: SegmentRef,\n purgeAt: number,\n found: readonly FoundPointer[],\n onFault: (err: unknown) => void,\n): Promise<void> {\n if (!canIndex(ref)) return;\n const filed = dueIndexRef(dueBucket(purgeAt), ref);\n const all = found.some((p) => p.ref.namespace === filed.namespace)\n ? found\n : [...found, { ref: filed, target: ref }];\n for (const pointer of all) {\n try {\n await registry.delete(pointer.ref, pointer.token);\n } catch (err) {\n // See above: litter, not a wrong purge. A lost race is the pointer changing under the delete, and is no fault.\n onFault(err);\n }\n }\n}\n\n/**\n * Retire every segment whose retention policy has expired, and clean up the tombstones earlier sweeps left.\n *\n * Returns a ledger; **never throws for a per-segment fault** (those become entries). It does throw for a bad\n * argument, and for a fleet larger than `maxScanSegments` \u2014 a scan that cannot be held in memory is a fail-loud\n * condition, not a partial result to be mistaken for a complete sweep.\n */\nexport async function retireExpired(\n deps: DropDeps,\n options: RetireExpiredOptions,\n): Promise<RetireExpiredResult> {\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n const now = options.now;\n if (!Number.isFinite(now)) {\n throw new ValidationError(\n `retireExpired: \\`now\\` must be a finite epoch-ms; got ${String(now)}`,\n );\n }\n // A floor on `now` for the same reason `expiresAt` has one, and in the direction that actually destroys data: a\n // clock returning seconds makes every policy in the fleet look expired. (Too *small* a `now` is harmless \u2014\n // nothing expires \u2014 but a sweep against a pre-2001 instant cannot legitimately expire anything anyway, so\n // refusing it costs nothing and catches the units mistake.)\n if (now < MIN_EXPIRES_AT_MS) {\n throw new ValidationError(\n `retireExpired: \\`now\\` (${now}) is before ${MIN_EXPIRES_AT_MS} \u2014 that is almost certainly epoch SECONDS ` +\n `rather than milliseconds. A sweep with a wrong-units clock treats every policy as expired.`,\n );\n }\n const limit = options.limit ?? DEFAULT_RETIRE_LIMIT;\n if (!Number.isInteger(limit) || limit < 1) {\n throw new ValidationError(`retireExpired: \\`limit\\` must be a positive integer; got ${limit}`);\n }\n const purgeTombstones = options.purgeTombstones ?? true;\n const grace = options.tombstoneGraceMs ?? DEFAULT_TOMBSTONE_GRACE_MS;\n if (!Number.isFinite(grace) || grace < 0) {\n throw new ValidationError(\n `retireExpired: \\`tombstoneGraceMs\\` must be a non-negative finite number; got ${String(grace)}`,\n );\n }\n const maxScanSegments = options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS;\n if (!Number.isFinite(maxScanSegments) || maxScanSegments < 1) {\n throw new ValidationError(\n `retireExpired: \\`maxScanSegments\\` must be a finite number >= 1; got ${String(maxScanSegments)}`,\n );\n }\n const dryRun = options.dryRun === true;\n\n // Drain the enumeration first, bounded. The sweep mutates rows as it goes (a tombstone CAS, a row delete), and\n // iterating a live listing while doing so is driver-dependent \u2014 a paged listing may or may not observe its own\n // writes. Draining makes the candidate set a snapshot; the retire path then RE-READS each row before acting on\n // it, because deciding an irreversible deletion from a minutes-old copy is not the same as enumerating from one.\n const scan = options.scan ?? 'fleet';\n const scanned =\n scan === 'index'\n ? await rowsFromDueIndex(deps.registry, {\n now,\n lookbackBuckets: options.lookbackBuckets ?? DEFAULT_LOOKBACK_BUCKETS,\n namespace: options.namespace,\n maxScanSegments,\n })\n : await rowsFromFleet(deps.registry, { namespace: options.namespace, maxScanSegments });\n const rows = scanned.rows;\n\n // Whether a delete removes a row for good. Read after the enumeration: a registry that settles this lazily (S3, from\n // the host its client resolves) has done so by its first listing. Where it does not, a purge only rewrites the row as\n // a tombstone, which a full scan reads for ever, so a pointer to find it by, or to remove, buys nothing and costs a\n // read and a write at retirement and again at purge.\n const removesRows = deps.registry.capabilities().conditionalDelete === true;\n\n const shards = options.shards;\n const totalShards = options.totalShards ?? 0;\n const owned = (ref: SegmentRef): boolean =>\n shards === undefined ||\n totalShards <= 1 ||\n shards.includes(shardOf(segmentKey(ref), totalShards));\n const mine = rows.filter(owned);\n\n const entries: RetireEntry[] = [];\n let eligible = 0;\n let retired = 0;\n let wouldRetire = 0;\n let tombstonesPurged = 0;\n let limited = false;\n // Deletes the registry refused for a reason other than a lost race, and the first one's reason. A lost race is the\n // row or pointer changing under a fenced delete, which is the fence working; anything else (a denied delete, a blob\n // with a snapshot, a raw provider error) will happen again for the next one.\n let purgeFaults = 0;\n let firstPurgeFault: `failed: ${string}` | undefined;\n const noteFault = (err: unknown): void => {\n if (isWriteConflictError(err)) return;\n purgeFaults += 1;\n firstPurgeFault ??= failureReason(err);\n };\n // Purging stops after this many refused purges in a row, and a purge that succeeds starts the count again. Each refused\n // attempt costs reads before it fails, and a blanket refusal would fail them all; one row's refusal must not hold the rest.\n let purgeRun = 0;\n let purging = true;\n // The budget is charged on ATTEMPT, not on success, and that distinction is the whole guard. `dropSegment`\n // writes the tombstone BEFORE sweeping Storage, so a fault in the Storage phase is a segment that is\n // already retired. Counting only successes would let a partial storage outage march through the entire fleet\n // with the cap never engaging, reporting `retired: 0, limited: false` (a \"completed sweep that retired nothing\")\n // while every segment in the namespace is tombstoned.\n let attempted = 0;\n\n for (const rec of mine) {\n const ref: SegmentRef = { namespace: rec.namespace, segment: rec.segment };\n const base = { segment: rec.segment, namespace: rec.namespace };\n const policy = readRetentionPolicy(rec.retention);\n\n if (rec.status === 'destroyed') {\n if (!purgeTombstones || !purging) continue;\n // Attribution is a POSITIVE MARKER the sweep writes on its own retirements, never an inference from\n // \"destroyed + an expired policy\". That inference would be wrong, and the consequence serious: `shredSegment`\n // never touches `retention`, so the ordinary ordering \u2014 set a 30-day policy, then a GDPR request arrives\n // mid-window and you `destroySegment` \u2014 leaves a **crypto-shred** tombstone carrying an expired policy.\n // Deleting that row destroys the local attestation for a right-to-erasure execution and un-fences the name\n // for every writer. A marker cannot be forged by that ordering.\n const retiredAt = retirementStamp(rec.retention);\n if (retiredAt === null) continue; // not ours \u2014 a GDPR tombstone, or one from a manual drop\n if (now - retiredAt < grace) continue; // inside the fence window; not ledger noise\n if (attempted >= limit) {\n limited = true;\n break;\n }\n attempted += 1;\n try {\n if (!(await isFullyReclaimed(deps, ref))) {\n // Self-heal rather than report-and-wait: `gcOrphanGenerations` takes EVERY generation of a destroyed\n // row, and nothing else will ever call it for this segment (no load publishes onto a tombstone, and the\n // erasure rewrite refuses one). Without this the row is stuck forever, the\n // objects are billed forever, and the sweep pays two list calls per cycle to say so again. Measured.\n if (!dryRun) await gcOrphanGenerations(ref, deps).catch(() => undefined);\n if (!(await isFullyReclaimed(deps, ref))) {\n entries.push({ ...base, action: 'skipped', reason: 'tombstone-not-empty' });\n continue;\n }\n }\n // Fenced on the token of the row this decision was made from (the marker and its grace window), so a\n // tombstone that was purged and re-created, or rewritten, since the scan is refused rather than deleted.\n // Storage went first, above: the row is what keeps a straggler generation reachable by the collection.\n // On a registry with a conditional delete the row is removed for good, under the version this delete\n // reads, which carries that token; elsewhere it is tombstoned. Its pointers go only once it is gone: a\n // delete that threw, its outcome unknown, removes nothing more, and the next scan reads the row again.\n if (!dryRun) {\n await deps.registry.delete(ref, rec.token);\n tombstonesPurged += 1;\n purgeRun = 0;\n if (removesRows) {\n await forgetPurgePointers(\n deps.registry,\n ref,\n retiredAt + grace,\n scanned.pointers.get(segmentKey(ref)) ?? [],\n noteFault,\n );\n }\n }\n entries.push({\n ...base,\n action: dryRun ? 'would-purge-tombstone' : 'purged-tombstone',\n });\n } catch (err) {\n if (!isWriteConflictError(err)) {\n // Refused for a reason other than a lost race. Not charged to the limit, so the retirements behind it still\n // get their turn; and after enough in a row, no more purges are tried this call.\n attempted -= 1;\n purgeRun += 1;\n if (purgeRun >= MAX_CONSECUTIVE_PURGE_FAULTS) purging = false;\n noteFault(err);\n }\n entries.push({ ...base, action: 'skipped', reason: failureReason(err) });\n }\n continue;\n }\n\n if (policy === null) continue; // no policy \u2014 this segment is not the sweep's business\n if (policy === 'invalid') {\n entries.push({ ...base, action: 'skipped', reason: 'invalid-policy' });\n continue;\n }\n if (policy.expiresAt > now) continue; // not yet\n eligible += 1;\n if (attempted >= limit) {\n // Stop SCANNING, not just stop acting. A `limit` entry per deferred row would make the ledger scale with the\n // fleet rather than with the batch \u2014 250,000 rows behind a bad backfill is ~15 MB of entries the caller did\n // not ask for, all in one result object. They are not information: the next run picks them up, which is\n // what `limited` says.\n limited = true;\n break;\n }\n\n try {\n // Re-read the AUTHORITATIVE row before deleting anything. The enumeration is a snapshot, and on a large\n // fleet the gap between drawing it and reaching this segment is the whole sweep \u2014 minutes. Cancelling an\n // expiry is exactly the operator's recovery action for the bad-backfill case `limit` exists to survive, and\n // it did not work if a sweep was already running: the segment was retired from the stale copy. One strong\n // read per *eligible* segment, so the fleet enumeration stays a single `list()`. (`consistency.ts` takes the\n // same care for a read-only check; an irreversible deletion deserves at least as much.)\n const live = await deps.registry.get(ref);\n const livePolicy = live === null ? null : readRetentionPolicy(live.retention);\n if (\n live === null ||\n live.status === 'destroyed' ||\n livePolicy === null ||\n livePolicy === 'invalid' ||\n livePolicy.expiresAt > now\n ) {\n entries.push({ ...base, action: 'skipped', reason: 'policy-changed' });\n continue;\n }\n\n attempted += 1;\n // `confirmSegment` is satisfied structurally here \u2014 in a loop the guard is the same value twice, which is\n // why `dryRun` is the real preview. The dry run goes through `dropSegment` too, so the preview reports the\n // generations a real sweep would delete rather than a guess.\n const result = await dropSegment(ref, deps, {\n confirmSegment: rec.segment,\n dryRun,\n audit: options.audit,\n });\n if (dryRun) {\n wouldRetire += 1;\n entries.push({ ...base, action: 'would-retire', expiresAt: livePolicy.expiresAt, result });\n continue;\n }\n if (!result.dropped) {\n // `dropped: false` means the call found nothing to do, and the documented cause is a ref that does not\n // address what the caller meant. Counting that as a retirement is how a sweep addressing the wrong segment\n // reports success \u2014 so it is a ledger entry instead, carrying the reason `dropSegment` gave.\n //\n // Unreachable in practice now that the live re-read above proves the row exists (the only `dropped: false`\n // path needs no row at all), except through the microsecond in which someone else deletes it. Kept, and\n // labelled rather than claimed as covered: the invariant is \"never count a no-op as a retirement\", and it\n // must hold if either that re-read or `dropSegment`'s reporting ever changes.\n entries.push({\n ...base,\n action: 'skipped',\n reason: `failed: drop reported ${result.reason}`,\n });\n continue;\n }\n retired += 1;\n entries.push({ ...base, action: 'retired', expiresAt: livePolicy.expiresAt, result });\n // The pointer has done its job. Dropping it keeps a bucket from accumulating rows that every subsequent\n // lookback re-reads forever \u2014 the index would otherwise grow monotonically and slowly undo its own\n // purpose. Best-effort and unconditional on `scan`: a fleet sweep retires index-pointed segments too, and\n // leaving their pointers behind would make a later index scan re-read segments that no longer exist.\n await forgetDuePointer(deps.registry, ref, livePolicy.expiresAt, noteFault);\n if (\n purgeTombstones &&\n result.generationsDeleted.length === 0 &&\n result.generationsRemaining.length === 0\n ) {\n // The segment really held nothing, so `dropSegment` has just written a tombstone for a name that was\n // empty. Left in place that row FENCES the name against every writer \u2014 and `setRetention` will mint a\n // row for any name, including a typo'd one, so this is reachable from a single mistake. Nothing\n // existed, so there is nothing a delete could resurrect: remove the row instead of bricking the name.\n //\n // **Only while `purgeTombstones` is on.** `false` is the caller asking for every row of this sweep's\n // retirements to stay, this one included; it falls through to the stamp below, so a later sweep with\n // purging on can still delete it after the grace window (an empty segment is trivially fully reclaimed).\n // Nothing re-processes it meanwhile: a `destroyed` row is skipped before any policy is read.\n //\n // **Both halves of the predicate are load-bearing.** `generationsDeleted: []` alone does NOT mean the\n // segment was empty \u2014 it is equally what a segment whose every `storage.delete` threw produces, because\n // `dropSegment`'s sweep loop stops once a pass deletes nothing. Purging the row on that reading left\n // the expired objects readable and billed, and then unreachable by everything that could have\n // collected them: `gcOrphanGenerations` returns `[]` with no row to compare against, the next sweep\n // never sees the name again (no row to enumerate), and `dropSegment` takes its `'absent'` path. The\n // data that was contractually supposed to expire stayed, silently, forever. `generationsRemaining` is\n // the honest question and `dropSegment` computes it one field over for exactly this reason.\n //\n // When something DOES remain, falling through is the right move rather than a special case: the row\n // keeps its tombstone, so reads stay refused, and the tombstone-purge pass above re-sweeps it with\n // `gcOrphanGenerations` (which takes every generation of a destroyed row) and purges the row once it is\n // genuinely empty. The residual is visible in this entry's `result.generationsRemaining` meanwhile.\n //\n // Fenced on the tombstone just written, so a name purged and re-created by someone else since is not\n // tombstoned by this delete: only a `destroyed` row is removed, and only at the token it was read with.\n //\n // **A delete that fails falls through to the stamp as well.** Left unstamped, the row is indistinguishable\n // from a crypto-shred's tombstone, which no sweep may ever delete, so one transient registry fault would\n // keep a fenced name for good. Stamped, it is this sweep's own tombstone, and a later sweep purges it. The\n // stamp itself only touches a `destroyed` row, so a name that was re-created meanwhile is left alone.\n const tombstone = await deps.registry.get(ref).catch(() => null);\n if (tombstone?.status === 'destroyed') {\n const deleted = await deps.registry.delete(ref, tombstone.token).then(\n () => true,\n (err: unknown) => {\n noteFault(err);\n return false;\n },\n );\n if (deleted) continue;\n }\n }\n // Stamp the tombstone as OURS, so a later sweep may purge the row (see the attribution note above), and file\n // the pointer an index scan finds it by on the day its grace ends. A failure here only means the row is never\n // auto-purged, or only by the fleet scan \u2014 never data loss \u2014 so it is best-effort.\n const stamped = await stampRetirement(deps.registry, ref, now).catch(() => false);\n if (stamped && removesRows) await filePurgePointer(deps.registry, ref, now + grace);\n } catch (err) {\n // A fault AFTER the tombstone landed is a segment that IS retired, and reporting it as skipped told the\n // caller the opposite of the truth (\"a skipped entry is a segment that still holds data\"). One cheap read\n // settles which side of the tombstone we failed on.\n const after = await deps.registry.get(ref).catch(() => null);\n if (after?.status === 'destroyed') {\n // Stamp it here too. Without this a retirement that faulted after the tombstone landed is a row no later\n // sweep can attribute to itself, so it is never auto-purged \u2014 exactly the litter the purge exists to\n // prevent, and reachable from any transient Storage fault.\n const stamped = await stampRetirement(deps.registry, ref, now).catch(() => false);\n if (stamped && removesRows) await filePurgePointer(deps.registry, ref, now + grace);\n retired += 1;\n entries.push({\n ...base,\n action: 'retired',\n expiresAt: policy.expiresAt,\n result: {\n ...base,\n dropped: true,\n generationsDeleted: [],\n generationsRemaining: [],\n cryptoShredded: false,\n reason: undefined,\n },\n fault: failureReason(err),\n });\n continue;\n }\n entries.push({ ...base, action: 'skipped', reason: failureReason(err) });\n }\n }\n\n // Pointers to nothing, from either scan: remove each, so it is not read for again. Only those for this sweep's own slice\n // of the fleet, and never under `dryRun`. The segment is read again just before, since the scan may be minutes old: a\n // `setRetention` that created the name since files its pointer after its row, and finding one already at that key,\n // takes it as its own. Fenced on the token the scan read, so a pointer filed anew is left. What remains is a round trip\n // in which such a pointer can still go, and then the fleet scan retires that segment. At most `limit` of them per call:\n // each costs reads before its delete, and a long backlog must not make one call arbitrarily long. A pointer attempted\n // counts whether or not it goes, as a retirement does.\n if (!dryRun && removesRows) {\n let attemptedLitter = 0;\n for (const pointer of scanned.litter) {\n if (!owned(pointer.target)) continue;\n if (attemptedLitter >= limit) break;\n attemptedLitter += 1;\n try {\n if ((await deps.registry.get(pointer.target)) !== null) continue;\n await deps.registry.delete(pointer.ref, pointer.token);\n } catch (err) {\n // Left for a later scan: litter, not a wrong sweep. A delete the registry refuses is counted.\n noteFault(err);\n }\n }\n }\n\n return {\n scanned: mine.length,\n eligible,\n retired,\n wouldRetire,\n tombstonesPurged,\n limited,\n purgeFaults,\n ...(firstPurgeFault === undefined ? {} : { firstPurgeFault }),\n dryRun,\n entries,\n };\n}\n\n/** The key the sweep stamps on its own tombstones, so a purge is attributable rather than inferred. */\nconst RETIRED_AT = 'retiredBySweepAt';\n\n/** Read the sweep's own retirement stamp off a row, or `null` if this tombstone is not one of ours. */\nfunction retirementStamp(meta: GovernanceMeta | undefined): number | null {\n if (meta === undefined || meta === null || typeof meta !== 'object' || Array.isArray(meta)) {\n return null;\n }\n const raw = (meta as Record<string, unknown>)[RETIRED_AT];\n return typeof raw === 'number' && Number.isInteger(raw) && raw >= MIN_EXPIRES_AT_MS ? raw : null;\n}\n\n/**\n * Mark a freshly written tombstone as this sweep's own work, preserving whatever else the row's `retention`\n * metadata carried, and say whether it did. Retried a couple of times on contention, then given up on: an unstamped\n * tombstone is simply never auto-purged, which is the safe direction.\n */\nasync function stampRetirement(\n registry: DropDeps['registry'],\n ref: SegmentRef,\n now: number,\n): Promise<boolean> {\n for (let attempt = 0; attempt < 3; attempt += 1) {\n const rec = await registry.get(ref);\n if (rec === null || rec.status !== 'destroyed') return false; // nothing to stamp\n try {\n await registry.compareAndSwap(ref, rec.token, {\n retention: { ...rec.retention, [RETIRED_AT]: now },\n });\n return true;\n } catch (err) {\n if (!isWriteConflictError(err)) throw err;\n }\n }\n return false;\n}\n\n/**\n * Whether a tombstoned segment's storage is provably gone \u2014 no Storage generations.\n *\n * The check is about what deleting the row would break rather than about tidiness: `gcOrphanGenerations` reads\n * the registry row to decide what to collect and returns empty when there is none, so deleting the row while\n * objects remain strands them permanently \u2014 billed forever, reachable by nothing. (An object a load was still\n * writing when the tombstone landed is exactly how they get there, which is why `dropSegment` reports\n * `generationsRemaining` at all.)\n *\n * Leaving the tombstone in place is self-healing: this sweep collects the orphan generations itself\n * (`gcOrphanGenerations` takes *every* generation of a destroyed row), and the next cycle purges the row.\n */\nasync function isFullyReclaimed(\n deps: { readonly storage: IStorageDriver },\n ref: SegmentRef,\n): Promise<boolean> {\n for await (const key of deps.storage.list(ref)) {\n void key;\n return false;\n }\n return true;\n}\n\nfunction failureReason(err: unknown): `failed: ${string}` {\n if (isWriteConflictError(err)) return 'failed: contended';\n return `failed: ${err instanceof Error ? err.message : String(err)}`;\n}\n", "/**\n * Fail-safe cross-store disaster-recovery check. The registry (`currentGen`) and the immutable `.crbm`\n * generations can be restored **independently**, so a failover can recover the registry *ahead of* the storage\n * objects \u2014 leaving `currentGen` pointing at a generation whose `.crbm` isn't present yet. That is likelier\n * than it sounds even with both in one bucket: a restore scoped to a prefix, or replayed\n * per-object from a version history, recovers the two prefixes at different points. That's a torn restore: reads of the affected segment then throw. This scan\n * detects it up front (run it at startup after a restore) instead of discovering it on the first read.\n *\n * Read-only; bounded fan-out. `destroyed` (crypto-shredded) segments are skipped \u2014 their Storage is intentionally\n * gone/unreadable, not a torn restore. A segment whose Storage/registry can't be read this pass is recorded in\n * `errored` (never aborts the scan). Each segment is checked against its **authoritative live pointer** \u2014 one\n * strong `registry.get` per segment \u2014 never the enumeration snapshot from `registry.list`, which a driver may\n * serve eventually-consistently and so lag a recent in-place pointer advance: trusting it would both\n * miss a torn *live* generation and cry torn on a generation the pointer has already advanced past (GC'd during\n * the scan). Residual: a load's publish plus a GC landing in the tiny per-segment get\u2192list gap can still yield a\n * transient false positive \u2014 run the scan against a quiesced fleet (the documented restore procedure), or re-run\n * to confirm a reported tear.\n */\n\nimport { mapWithConcurrency } from './concurrency';\nimport { ValidationError, isCloudRoaringError, isIntegrityError } from './errors';\nimport { DEFAULT_MAX_SCAN_SEGMENTS, drainRegistry } from './registry-scan';\nimport type { IStorageDriver, IRegistryDriver, RegistryRecord, SegmentRef } from './ports';\nimport { openGenerationReader } from './crbm-storage-source';\nimport type { Aead, IKeystore, WrappedDek } from './crypto';\nimport { aadFor } from './crypto';\nimport { summaryAgrees, usableSummary } from './summary';\nimport { validateUserNamespace } from './validate';\n\n/** Default in-flight fan-out for the consistency scan \u2014 bounded, no thundering herd. */\n/**\n * Default ceiling on how many registry records one consistency check may hold resident: 250,000.\n *\n * **Named `maxScanSegments`, not `maxSegments`, on purpose.** A `maxSegments`-style option caps how much work\n * one cycle does and then *continues normally* (the retention sweep's `limit` is that shape). This one caps how\n * much a scan may hold and *refuses* past it. Same-sounding names with opposite behaviour at the limit is a\n * trap; the name says what it is \u2014 a ceiling on a scan that fails closed.\n *\n * Generous \u2014 fleets of 100K+ segments are the design target \u2014 while still bounding a DR drill's memory to\n * something a modest operator box survives. Raisable, because a ceiling you cannot lift is a landmine.\n */\n// Re-exported from its original home because this module is where the ceiling is documented; the value and the\n// loop that enforces it live in `registry-scan.ts`, shared with the retention sweep. This is an INTERNAL\n// re-export: it is not on `@cloudbitmaps/core`'s main entry, and a caller passes their own `maxScanSegments` rather\n// than read ours.\nexport { DEFAULT_MAX_SCAN_SEGMENTS } from './registry-scan';\nconst DEFAULT_CHECK_CONCURRENCY = 8;\n\nexport interface ConsistencyIssue {\n readonly segment: string;\n readonly namespace?: string;\n /** The registry's `currentGen` for the segment \u2014 the generation whose `.crbm` is missing from Storage. */\n readonly currentGen: number;\n /**\n * `missing-storage-generation`: `currentGen` references a Storage generation that is not present (torn restore).\n * `summary-mismatch` (only when asked for): the generation is there, but its row's summary says a different id\n * count or metadata than the object holds (a row restored from another point than its bucket, or a number re-taken\n * since). A count answers from that summary until a read opens the object, so it is what a restore leaves wrong.\n */\n readonly issue: 'missing-storage-generation' | 'summary-mismatch';\n}\n\n/** A segment that could not be checked this pass (Storage/registry read fault) \u2014 not proof of a torn restore. */\nexport interface ConsistencyErrorEntry {\n readonly segment: string;\n readonly namespace?: string;\n /** The read error's message (never the raw error, so the report stays serializable/loggable). */\n readonly error: string;\n}\n\nexport interface ConsistencyReport {\n /** Registered segments scanned (a `destroyed` segment counts as scanned but is never an issue). */\n readonly checked: number;\n /** Segments whose `currentGen` `.crbm` is absent \u2014 recover the object store (or restore to a coherent point). */\n readonly inconsistent: ConsistencyIssue[];\n /**\n * Segments that couldn't be read this pass (a transient/partial object store during a restore is exactly when\n * this runs) \u2014 **triage these too**: an unread segment is neither proven-coherent nor proven-torn. Empty on a\n * clean pass.\n */\n readonly errored: ConsistencyErrorEntry[];\n /**\n * With `summaries: true`, the segments whose row's summary could not be held against its object because it is\n * sealed and no keystore was given, or does not open under the one given: neither checked nor found wrong. Present\n * only when summaries were asked for.\n */\n readonly summariesUnchecked?: number;\n}\n\n/** Collect the set of generations the object store currently lists for a segment. */\nasync function generationsPresent(storage: IStorageDriver, ref: SegmentRef): Promise<Set<number>> {\n const present = new Set<number>();\n for await (const key of storage.list(ref)) present.add(key.generation);\n return present;\n}\n\ntype Outcome =\n | { readonly kind: 'ok'; readonly unchecked?: boolean }\n | { readonly kind: 'issue'; readonly issue: ConsistencyIssue }\n | { readonly kind: 'error'; readonly error: ConsistencyErrorEntry };\n\n/** Hold a row's summary against the object it describes, which is present. */\nasync function checkSummary(\n ref: SegmentRef,\n live: RegistryRecord,\n deps: { readonly storage: IStorageDriver; readonly keystore?: IKeystore },\n): Promise<Outcome> {\n const generation = live.currentGen as number;\n const keyed = live.wrappedDeks !== undefined && live.wrappedDeks.length > 0;\n let aead: Aead | undefined;\n if (keyed) {\n if (deps.keystore === undefined) return { kind: 'ok', unchecked: true };\n try {\n aead = await deps.keystore.openDek(live.wrappedDeks as readonly WrappedDek[]);\n } catch (err) {\n if (isIntegrityError(err) || (isCloudRoaringError(err) && err.name === 'KeyUnavailableError'))\n return { kind: 'ok', unchecked: true };\n throw err;\n }\n }\n const described = usableSummary(ref, live, aead);\n if (described === undefined) return { kind: 'ok', unchecked: keyed };\n const reader = await openGenerationReader(\n deps.storage,\n { namespace: ref.namespace, segment: ref.segment, generation },\n aead === undefined ? undefined : { aead, aadFor: (scope) => aadFor(ref, generation, scope) },\n );\n let cardinality = 0;\n for (const n of reader.cardinalities().values()) cardinality += n;\n if (summaryAgrees(described, { cardinality, metadata: reader.metadata })) return { kind: 'ok' };\n return {\n kind: 'issue',\n issue: {\n segment: ref.segment,\n namespace: ref.namespace,\n currentGen: generation,\n issue: 'summary-mismatch',\n },\n };\n}\n\n/**\n * Verify every registered segment's `currentGen` `.crbm` actually exists in Storage. Enumerates the registry\n * (optionally one namespace) and, for each non-`destroyed` segment, checks the object store lists that\n * generation. Returns the torn segments in `inconsistent` (empty \u21D2 coherent) and any unreadable segments in\n * `errored`.\n *\n * With `summaries: true` it also opens each segment's current object (one tail read, and a second for an index longer\n * than it) and holds the row's summary against it, reporting `summary-mismatch` where they disagree. A sealed summary\n * needs `deps.keystore`; without it the segment is counted in `summariesUnchecked`. Off by default, since the\n * default check only lists.\n */\nexport async function runConsistencyCheck(\n deps: {\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n readonly keystore?: IKeystore;\n },\n options: {\n namespace?: string;\n concurrency?: number;\n maxScanSegments?: number;\n summaries?: boolean;\n } = {},\n): Promise<ConsistencyReport> {\n if (options.namespace !== undefined) validateUserNamespace(options.namespace);\n if (options.summaries !== undefined && typeof options.summaries !== 'boolean') {\n throw new ValidationError(`summaries must be a boolean; got ${String(options.summaries)}`);\n }\n const concurrency = options.concurrency ?? DEFAULT_CHECK_CONCURRENCY;\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n // Fail fast before the (possibly huge) registry scan, not after.\n throw new ValidationError(`concurrency must be a positive integer; got ${concurrency}`);\n }\n const maxScanSegments = options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS;\n // Bounded enumeration, shared with the retention sweep \u2014 see `registry-scan.ts` for why a fleet-wide scan is\n // drained rather than streamed, and why the ceiling is not optional.\n const recs = await drainRegistry(deps.registry, {\n namespace: options.namespace,\n maxScanSegments,\n op: 'checkConsistency',\n // Two callers reach this refusal, and only one of them can raise the ceiling: `store.checkConsistency` takes\n // no `maxScanSegments`, so the advice names the call that does.\n raise:\n 'raise `maxScanSegments` on `runConsistencyCheck` (`store.checkConsistency` takes no ceiling, so run ' +\n '`runConsistencyCheck({ storage: backend.storage, registry: backend.registry }, { maxScanSegments })` ' +\n 'over the same backend instead)',\n });\n const results = await mapWithConcurrency(recs, concurrency, async (rec): Promise<Outcome> => {\n if (rec.status === 'destroyed') return { kind: 'ok' }; // Storage intentionally gone \u2014 not a torn restore\n const ref: SegmentRef = { segment: rec.segment, namespace: rec.namespace };\n try {\n // Resolve the AUTHORITATIVE live pointer (strong read) \u2014 not the drained `rec.currentGen`, which the\n // enumeration may have read eventually-consistently and can lag the live pointer. `rec` is used only to\n // enumerate + skip destroyed segments.\n const live = await deps.registry.get(ref);\n if (!live || live.status === 'destroyed') return { kind: 'ok' }; // vanished/shredded \u2014 no live pointer\n // A row with no Storage generation is *deliberately* Storage-less \u2014 a retention policy recorded before the first\n // load, given a row so the segment is enumerable at all. There is no generation that ought to exist, so nothing can be missing. Reporting it\n // would make `missing-storage-generation` fire on the healthy steady state of every such segment, which is the\n // opposite of what a DR triage needs: the one real signal drowned in expected noise.\n if (live.currentGen === null) return { kind: 'ok' };\n const present = await generationsPresent(deps.storage, ref);\n if (present.has(live.currentGen)) {\n if (options.summaries !== true || live.summary === undefined) return { kind: 'ok' };\n return await checkSummary(ref, live, deps);\n }\n return {\n kind: 'issue',\n issue: {\n segment: rec.segment,\n namespace: rec.namespace,\n currentGen: live.currentGen,\n issue: 'missing-storage-generation',\n },\n };\n } catch (error) {\n // Fault isolation: one unreadable segment (a partial/transient object store mid-restore) must not abort\n // the whole triage. Record it and keep scanning the rest.\n return {\n kind: 'error',\n error: {\n segment: rec.segment,\n namespace: rec.namespace,\n error: error instanceof Error ? error.message : String(error),\n },\n };\n }\n });\n const inconsistent: ConsistencyIssue[] = [];\n const errored: ConsistencyErrorEntry[] = [];\n let summariesUnchecked = 0;\n for (const r of results) {\n if (r.kind === 'issue') inconsistent.push(r.issue);\n else if (r.kind === 'error') errored.push(r.error);\n else if (r.unchecked === true) summariesUnchecked += 1;\n }\n return options.summaries === true\n ? { checked: recs.length, inconsistent, errored, summariesUnchecked }\n : { checked: recs.length, inconsistent, errored };\n}\n", "/**\n * The store's read retry.\n *\n * {@link RetryingStorageChunkSource} wraps a {@link StorageChunkSource} so every read it serves retries\n * **transient** faults with bounded, jittered backoff, through the one shared `core/retry` primitive, so every\n * backend (S3, GCS, Azure Blob, LocalFS, \u2026) inherits the same policy instead of each rolling its own. `CloudRoaring`\n * wraps the source it reads segment data through in one \u2014 a `StorageChunkSource` passed as `storage` included, so\n * wrapping that one first multiplies each read's attempts \u2014 and a pinned handle's source too, unless it is built\n * with `retry: false`.\n * Pure composition over the port (no SDK, no I/O of its own); the driver under the source is responsible for\n * *classifying* its transient faults (raising {@link TransientError}), and this wrapper decides *whether and when*\n * to retry.\n *\n * **Reads only, on purpose.** A read changes nothing, so running it again is safe. A write is not: a conditional put\n * or compare-and-swap that lands and then loses its response would, replayed, find its own write already there and\n * report it as a conflict. So nothing here wraps a write, and the store's writes and admin calls report a transient\n * fault to their caller, who re-runs the call or checks what landed.\n *\n * What is **not** retried: every deterministic error (`ValidationError`/`IntegrityError`/`NotFoundError`/\u2026).\n * Default classifier: {@link isTransientError}.\n */\nimport type { Clock, Rng } from '../../core/determinism';\nimport { withRetry, DEFAULT_RETRY_POLICY } from '../../core/retry';\nimport { isTransientError } from '../../core/errors';\nimport type { RetryPolicy } from '../../core/retry';\nimport type {\n ChunkRef,\n GenerationSummary,\n StorageChunkSource,\n SegmentRef,\n SegmentSize,\n} from '../../core/ports';\n\nexport interface RetryingOptions {\n readonly clock: Clock;\n readonly rng: Rng;\n /** Defaults to {@link DEFAULT_RETRY_POLICY}. */\n readonly policy?: RetryPolicy;\n /** Override which errors are retryable. Default: {@link isTransientError} (any `TransientError`). */\n readonly isRetryable?: (err: unknown) => boolean;\n /** Observability hook fired before each backoff wait. */\n readonly onRetry?: (info: { attempt: number; delayMs: number; err: unknown }) => void;\n}\n\n/** Internal: resolve the options into the `core/retry` shape once, at construction. */\nfunction toRetry(opts: RetryingOptions): {\n policy: RetryPolicy;\n deps: {\n clock: Clock;\n rng: Rng;\n isRetryable: (e: unknown) => boolean;\n onRetry?: RetryingOptions['onRetry'];\n };\n} {\n return {\n policy: opts.policy ?? DEFAULT_RETRY_POLICY,\n deps: {\n clock: opts.clock,\n rng: opts.rng,\n isRetryable: opts.isRetryable ?? isTransientError,\n onRetry: opts.onRetry,\n },\n };\n}\n\n/** Wrap a storage chunk source so its reads retry transient faults. */\nexport class RetryingStorageChunkSource implements StorageChunkSource {\n private readonly inner: StorageChunkSource;\n private readonly policy: RetryPolicy;\n private readonly deps: ReturnType<typeof toRetry>['deps'];\n /** Present only when the inner source supports it \u2014 so capability detection stays honest. */\n readonly sizeOf?: (ref: SegmentRef) => Promise<SegmentSize | null>;\n readonly cardinalities?: (ref: SegmentRef) => Promise<ReadonlyMap<number, number> | null>;\n readonly summary?: (ref: SegmentRef) => Promise<GenerationSummary | null>;\n readonly currentGeneration?: (ref: SegmentRef) => Promise<number | null>;\n readonly invalidate?: (ref: SegmentRef) => void;\n readonly exists?: (ref: SegmentRef) => Promise<boolean>;\n readonly currentVersion?: (ref: SegmentRef) => Promise<string | null>;\n\n /**\n * Forwarded, not retried: a property, with no request behind it. Without it the grounded cost report priced a\n * retrying store's refresh at the default TTL whatever the store was configured with.\n */\n get pointerRefreshMs(): number | undefined {\n return this.inner.pointerRefreshMs;\n }\n\n constructor(inner: StorageChunkSource, opts: RetryingOptions) {\n this.inner = inner;\n const r = toRetry(opts);\n this.policy = r.policy;\n this.deps = r.deps;\n const innerSizeOf = inner.sizeOf;\n if (innerSizeOf) {\n this.sizeOf = (ref) => withRetry(() => innerSizeOf.call(inner, ref), this.policy, this.deps);\n }\n const innerCardinalities = inner.cardinalities;\n if (innerCardinalities) {\n this.cardinalities = (ref) =>\n withRetry(() => innerCardinalities.call(inner, ref), this.policy, this.deps);\n }\n const innerSummary = inner.summary;\n if (innerSummary) {\n this.summary = (ref) =>\n withRetry(() => innerSummary.call(inner, ref), this.policy, this.deps);\n }\n const innerCurrentGeneration = inner.currentGeneration;\n if (innerCurrentGeneration) {\n this.currentGeneration = (ref) =>\n withRetry(() => innerCurrentGeneration.call(inner, ref), this.policy, this.deps);\n }\n // Forwarded, NOT retried: it is synchronous and cannot fail, and dropping memoized state has no transient\n // mode to back off from. Forgetting to forward it would be silent \u2014 the wrapper would satisfy the port\n // while the invalidation stopped one layer short of the source that holds the snapshot and the DEK.\n const innerInvalidate = inner.invalidate;\n if (innerInvalidate) {\n this.invalidate = (ref) => {\n innerInvalidate.call(inner, ref);\n };\n }\n // Retried, unlike `invalidate`: this one reads the registry, so it has a transient mode to back off from.\n const innerExists = inner.exists;\n if (innerExists) {\n this.exists = (ref) => withRetry(() => innerExists.call(inner, ref), this.policy, this.deps);\n }\n const innerCurrentVersion = inner.currentVersion;\n if (innerCurrentVersion) {\n this.currentVersion = (ref) =>\n withRetry(() => innerCurrentVersion.call(inner, ref), this.policy, this.deps);\n }\n }\n\n getChunk(ref: ChunkRef): Promise<Uint8Array | null> {\n return withRetry(() => this.inner.getChunk(ref), this.policy, this.deps);\n }\n\n listChunkKeys(ref: SegmentRef): Promise<number[]> {\n return withRetry(() => this.inner.listChunkKeys(ref), this.policy, this.deps);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["total", "open", "shown", "join", "FS", "createHash", "open", "open", "FS", "createHash", "FS", "randomUUID", "mkdir", "open", "readdir", "unlink", "dirname", "join", "dirname", "join", "readdir", "unlink", "open", "FS", "mkdir", "randomUUID", "join", "framed", "open", "generation", "key", "collected", "left", "summary", "NONCE_BYTES", "TAG_BYTES", "listGenerations"]
7
7
  }