@astilba/cache 0.0.0-placeholder.0 → 0.1.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.
@@ -0,0 +1,930 @@
1
+ //#region src/knowledge.d.ts
2
+ /** Public consistency levels. `local` was deleted from the public union (M6). */
3
+ type Consistency = "strong" | "eventual";
4
+ /**
5
+ * Dual watermark epochs for a tag — independent, monotone, max-on-write. A
6
+ * later soft purge can never regress a hard purge (invariant 14).
7
+ */
8
+ interface Watermark {
9
+ readonly softEpoch: number;
10
+ readonly hardEpoch: number;
11
+ }
12
+ /**
13
+ * Tri-state tag knowledge. Either `known` (with epoch provenance) or
14
+ * absent = `unknown`. An absent record is `unknown`, never `{0,0}`. `unknown`
15
+ * fails closed: never fresh, never grace (invariant 11) — a gap of unknown
16
+ * hardness may hide a hard purge.
17
+ */
18
+ type TagKnowledge = {
19
+ readonly known: true;
20
+ readonly softEpoch: number;
21
+ readonly hardEpoch: number;
22
+ readonly throughEpoch: number;
23
+ } | {
24
+ readonly known: false;
25
+ };
26
+ /** The outcome of validating an entry against tag knowledge (table). */
27
+ type Validity = "fresh" | "stale" | "dead" | "unknown";
28
+ //#endregion
29
+ //#region src/telemetry.d.ts
30
+ /**
31
+ * The telemetry event-name CATALOG — the single, kernel-hosted registry of every
32
+ * event `type` this library emits, the same shape as the exported error classes. Each member is an
33
+ * event NAME; the value equals the key so a producer can reference a name as `TELEMETRY_EVENTS.<name>`
34
+ * (a literal type, refactor-safe) while `TelemetryEventName` (below) is the union of them all.
35
+ *
36
+ * The names are grouped by their emitting LAYER (kernel / L1 driver / react-router adapter /
37
+ * cloudflare adapter) — the annotation the charter requires. The machine-checked layer map (which
38
+ * fails to compile if a member is unannotated) lives in the catalog's exhaustiveness tooth in the
39
+ * test lane.
40
+ *
41
+ * GROWTH: adding a name is ADDITIVE and consumer-SAFE (a sink RECEIVES events, so a producer that
42
+ * emits a wider vocabulary hands a consumer a value from a set it already types against — narrowing,
43
+ * not widening, at the receiver) — so a new name is a MINOR-version change. Removing or renaming one
44
+ * is breaking.
45
+ */
46
+ declare const TELEMETRY_EVENTS: {
47
+ readonly writeback_throttled: "writeback_throttled";
48
+ readonly l2_write_error: "l2_write_error";
49
+ readonly singleflight_option_mismatch: "singleflight_option_mismatch";
50
+ readonly store_read_suppressed: "store_read_suppressed";
51
+ readonly neg_suppressed: "neg_suppressed";
52
+ readonly private_evicted: "private_evicted";
53
+ readonly poll_tick_failed: "poll_tick_failed";
54
+ readonly l3_ineligible: "l3_ineligible";
55
+ readonly regid_divergence: "regid_divergence";
56
+ readonly bus_dial_failed: "bus_dial_failed";
57
+ readonly strong_degraded: "strong_degraded";
58
+ readonly registry_degraded: "registry_degraded";
59
+ readonly state_stale: "state_stale";
60
+ };
61
+ /** The closed vocabulary of telemetry event names. `TelemetryEvent.type` is this
62
+ * union, and `safeEmit` accepts only members of it — an off-catalog emit is a COMPILE error. */
63
+ type TelemetryEventName = (typeof TELEMETRY_EVENTS)[keyof typeof TELEMETRY_EVENTS];
64
+ /**
65
+ * The reason CLASS a `strong_degraded` payload reports — why a strong read could not
66
+ * live-check and served under eventual's posture instead. A CLASS, never the error's own words: the
67
+ * registry's failure text is arbitrary app/platform detail (it can carry a URL, an id, a stack), and
68
+ * this event's whole job is to be safe to point a plain sink at. `registry_unreachable` is the only
69
+ * member today; it is a union rather than a bare literal so a second cause arrives as a widened
70
+ * payload the sink already types against, not as a new event name.
71
+ */
72
+ type StrongDegradedReason = "registry_unreachable";
73
+ /**
74
+ * A telemetry event. `type` is a CATALOG member; additional fields are
75
+ * event-specific. Hosted sinks pseudonymize all string fields (invariant 15). The `type`
76
+ * narrowing from a bare `string` is consumer-safe by variance: a sink is a RECEIVER, so an app
77
+ * typed against the older `string` still accepts every catalog member.
78
+ */
79
+ interface TelemetryEvent {
80
+ readonly type: TelemetryEventName;
81
+ readonly [field: string]: unknown;
82
+ }
83
+ /** A telemetry sink (`telemetry`). Receives events as they are emitted. */
84
+ type TelemetrySink = (event: TelemetryEvent) => void;
85
+ /**
86
+ * The hook `safeEmit` calls when it SWALLOWS an app-supplied sink's failure. It is
87
+ * INJECTABLE — passed in at the emit site, never a module global — so "observed exactly once" is
88
+ * testable with no module-latch order-coupling: two instances each carry their own hook and never
89
+ * cross-talk. It receives the swallowed error, and is itself the LAST link in the swallow chain, so
90
+ * if it throws that too is swallowed (safeEmit's never-throws contract is total).
91
+ */
92
+ type SinkErrorHook = (error: unknown) => void;
93
+ /**
94
+ * Hosted telemetry configuration. When `hosted` is set, the kernel pseudonymizes every
95
+ * emitted string field before delivery — key/tag references become per-project HMAC-salted hashes
96
+ * (never raw), and `detail` becomes error codes + hashed refs. `salt` is the per-project HMAC salt
97
+ * that keeps short or guessable identifiers from being reversed out of the hash. `onSinkError` (see
98
+ * `SinkErrorHook`) is the injectable broken-sink hook `safeEmit` calls on a swallowed sink failure.
99
+ * Invariant 15.
100
+ */
101
+ interface TelemetryConfig {
102
+ readonly sink: TelemetrySink;
103
+ readonly hosted?: boolean;
104
+ readonly salt?: string;
105
+ readonly onSinkError?: SinkErrorHook;
106
+ }
107
+ //#endregion
108
+ //#region src/drivers.d.ts
109
+ /** Injected logical clock — the ONLY source of `now` in kernel code. */
110
+ interface Clock {
111
+ /** Milliseconds on the logical timeline. Never a wall clock in the correctness path. */
112
+ now(): number;
113
+ }
114
+ /** Injected deterministic RNG — the ONLY source of randomness in kernel code. */
115
+ interface Rng {
116
+ /** A float in [0, 1). Seeded and reproducible. */
117
+ next(): number;
118
+ }
119
+ type StoreMetadata = Readonly<Record<string, unknown>>;
120
+ /** A value read from / written to a KV-like store. */
121
+ interface StoreValue {
122
+ readonly value: string;
123
+ readonly metadata?: StoreMetadata;
124
+ }
125
+ interface StoreWriteOptions {
126
+ /** Physical residency in seconds (KV floors this at 60s). */
127
+ readonly expirationTtl?: number;
128
+ readonly metadata?: StoreMetadata;
129
+ }
130
+ /**
131
+ * Which immutable mirror object a WRITE carries. The executor passes it to
132
+ * `putImmutable` so a host maps kind → physical residency, NEVER by parsing the (coped) key —
133
+ * the fourth-class "key edition" 2b-2-0 cured. The single home for the mirror-object taxonomy;
134
+ * `@astilba/cache/registry` re-exports it.
135
+ */
136
+ type ImmutableKind = "delta" | "snap";
137
+ /**
138
+ * The mirror-object intent a READ carries — the write taxonomy PLUS the mutable `pointer`
139
+ * (`__state`). `Store.get`'s optional hint, symmetric with `putImmutable`: intent travels as a
140
+ * PARAMETER so a driver maps kind → read-cache policy, never by parsing the key. One vocabulary for
141
+ * mirror-object intent in both directions.
142
+ */
143
+ type ReadKind = "pointer" | ImmutableKind;
144
+ /**
145
+ * A KV-like tier driver. `memory` (L1, isolate-local) and `cloudflareKV` /
146
+ * `redis` (L2, durable) implement it. The harness `FakeStore` models N replica
147
+ * views with per-key visibility delay, per-key write throttle (1 write/s → 429),
148
+ * TTL, and injectable get/set failures; a single-view `FakeStore` is the
149
+ * strongly-consistent Redis stand-in.
150
+ */
151
+ interface Store {
152
+ /** `readKind` is an optional read-cache hint: a driver may map it to a
153
+ * per-object read-cache policy (the DO KV `cacheTtl`). Unhinted reads inherit the platform
154
+ * default — for L2 KV that is a 60s edge cache, NOT minimal caching (Appendix C trap), so mirror
155
+ * reads pass the hint and entry reads stay allowed eventual. Fakes ignore or record it. */
156
+ get(key: string, readKind?: ReadKind): Promise<StoreValue | undefined>;
157
+ set(key: string, value: string, options?: StoreWriteOptions): Promise<void>;
158
+ delete(key: string): Promise<void>;
159
+ }
160
+ /**
161
+ * The structural failure contract a `Store` driver raises to the kernel on a rejected
162
+ * write. It is a plain SHAPE, never a class: the kernel discriminates on
163
+ * `code` alone (`isStoreWriteError`) so a fake's throttle error and a real driver's
164
+ * classified rejection speak ONE contract WITHOUT sharing a class — cross-realm and
165
+ * cross-package `instanceof` identity is exactly the trap that makes a shared error
166
+ * class unshippable. The kernel absorbs a classified write failure into an outcome +
167
+ * telemetry; a caller never sees the raw rejection.
168
+ *
169
+ * - `throttled` — the write hit the tier's per-key write-rate ceiling (KV's
170
+ * 1 write/s/key). Transient: `retryable` is true.
171
+ * - `too_large` — the value exceeds the tier's max value size (KV's 25 MiB
172
+ * ceiling). Typed and rejected, NEVER truncated; not retryable.
173
+ * - `unavailable` — a transient tier failure (the durable store was momentarily
174
+ * unreachable).
175
+ */
176
+ interface StoreWriteError {
177
+ readonly code: "throttled" | "too_large" | "unavailable";
178
+ readonly retryable: boolean;
179
+ readonly cause?: unknown;
180
+ }
181
+ /**
182
+ * Structural type-guard for `StoreWriteError`. Purely shape-based — it NEVER
183
+ * uses `instanceof`, so a harness fake and a real driver are recognized by the SAME
184
+ * check without a shared class. Total and safe on arbitrary `unknown`: rejects
185
+ * non-objects/null, requires `code` to be exactly one of the three literals and
186
+ * `retryable` to be a boolean.
187
+ */
188
+ declare function isStoreWriteError(e: unknown): e is StoreWriteError;
189
+ /**
190
+ * Ordering key an atomic compare-and-set must never regress: epoch first,
191
+ * then fence. A born-monotone write-back is arbitrated on exactly this pair (INV-17).
192
+ */
193
+ interface CasOrder {
194
+ readonly epoch: number;
195
+ readonly fence: number;
196
+ }
197
+ /** A versioned CAS slot: `token` is the optimistic-concurrency version. */
198
+ interface CasRecord {
199
+ readonly token: number;
200
+ readonly order: CasOrder;
201
+ }
202
+ /**
203
+ * The atomic compare-and-set a CAS-capable durable driver (Redis Lua, DO
204
+ * transactional storage) layers ON TOP OF the base `Store`. An OPTIONAL
205
+ * capability: the base `Store` — and the v0.1 fakes — expose none, so the kernel's
206
+ * floor stays read-compare-write (INV-17) and prefer-CAS consumption is deferred to
207
+ * the first real CAS driver. Declared here so the conformance layer can pin the
208
+ * capability and a real driver can later satisfy the same shape.
209
+ */
210
+ interface CasStore {
211
+ seed(key: string, order: CasOrder): Promise<void>;
212
+ load(key: string): Promise<CasRecord | undefined>;
213
+ /** Apply `next` iff the stored token still equals `expectedToken`; else a lost race. */
214
+ swap(key: string, expectedToken: number, next: CasOrder): Promise<number | undefined>;
215
+ }
216
+ /** Acknowledgement of a durably-accepted registry mutation. */
217
+ interface RegistryAck {
218
+ /** The Coordinator epoch after applying this mutation. */
219
+ readonly epoch: number;
220
+ }
221
+ /**
222
+ * The authoritative registry / Coordinator. Holds dual watermark
223
+ * epochs per tag (monotone, max-on-write) and a persisted monotone epoch counter.
224
+ * `check` is the live, un-memoized strong read used by strong reads and the
225
+ * write-back fence. `doRegistry` / `redisRegistry` implement it in phase 2;
226
+ * the harness `FakeRegistry` is the fake.
227
+ */
228
+ interface Registry {
229
+ /** The registry id scoping this Coordinator's mirror keyspace. The per-isolate
230
+ * reader scopes the keys it resyncs off to this value AND asserts the pointer payload echoes
231
+ * it — a mismatch reads an empty keyspace and fails closed. `doRegistry`/`FakeRegistry`
232
+ * default it to `"default"`; the authoring channel is `configure` (env `REGISTRY_ID`). */
233
+ readonly regId: string;
234
+ /** Live watermark knowledge for these tags. */
235
+ check(tags: readonly string[]): Promise<readonly TagKnowledge[]>;
236
+ /** Soft purge: max-on-write bump of `softEpoch`. */
237
+ expire(tags: readonly string[]): Promise<RegistryAck>;
238
+ /** Hard purge: max-on-write bump of `hardEpoch`. */
239
+ delete(tags: readonly string[]): Promise<RegistryAck>;
240
+ /** Register this instance's retention lease at bind. */
241
+ registerRetention(retentionMs: number): Promise<void>;
242
+ }
243
+ /** One tag's watermark change within a replication frame. */
244
+ interface TagChange {
245
+ readonly tag: string;
246
+ readonly softEpoch?: number;
247
+ readonly hardEpoch?: number;
248
+ }
249
+ /** A bus frame carrying a contiguous epoch range of changes. */
250
+ interface BusFrame {
251
+ readonly fromEpoch: number;
252
+ readonly toEpoch: number;
253
+ readonly changes: readonly TagChange[];
254
+ }
255
+ /**
256
+ * One event delivered to a bus subscriber. The transport hands the
257
+ * subscriber one of these discriminated shapes; epoch contiguity is validated by the
258
+ * KERNEL, never asserted by the bus (bus transports, it does not vouch):
259
+ *
260
+ * - `frame`: a replication frame carrying its own `{fromEpoch,toEpoch}` range.
261
+ * - `gap`: a frame was dropped or lost in transit — the subscriber must treat its
262
+ * knowledge as suspect and resync before warm trust resumes. Carries `head`:
263
+ *a lower bound ≥ the `toEpoch` of every frame emitted before the
264
+ * signal, so the loss-revealing event ITSELF declares the head the reader must
265
+ * reach to account for what it missed (detection carries its own remedy).
266
+ * - `reset`: the transport was (re)established — emitted BEFORE any frame on every
267
+ * establishment; it suspends all warm trust until a resync
268
+ * handshake reconciles the subscriber's applied epoch.
269
+ * - `hello`: the live channel declares its current head epoch — emitted right AFTER
270
+ * `reset` on every establishment, before any frame. Convergence
271
+ * anchors to a head DECLARED (`hello`/`gap`/a non-contiguous frame's `toEpoch`),
272
+ * never to pointer freshness; an idle channel's `hello` head equals the applied
273
+ * epoch, so a caught-up subscriber reconverges at once.
274
+ * - `lost`: the transport is DOWN — a dial that did not come up, or an established socket
275
+ * that closed. It is the counterpart of `reset`, and it exists because its
276
+ * absence is unreadable: with only `reset`/`hello` on the wire, a channel that never
277
+ * connected and a healthy channel with nothing to say produce the SAME stream —
278
+ * none. A subscriber cannot distinguish silence-because-quiet from
279
+ * silence-because-dead by waiting longer, so the transport declares it instead.
280
+ * It carries no epoch: a channel that is down declares nothing ABOUT epochs, and a
281
+ * `head` here would be a number invented by the thing that just failed. It does NOT
282
+ * raise suspicion — the suspicion gate owns that posture and a down channel is not a gapped map
283
+ * (a never-established reader is DECLARED degraded, which is a different, honest
284
+ * state); it is tracked and surfaced, and what holds the reader correct in the
285
+ * meantime is the polling driver plus its refusal to guess at a tag it cannot
286
+ * establish.
287
+ */
288
+ type BusEvent = {
289
+ readonly kind: "frame";
290
+ readonly frame: BusFrame;
291
+ } | {
292
+ readonly kind: "gap";
293
+ readonly head: number;
294
+ } | {
295
+ readonly kind: "reset";
296
+ } | {
297
+ readonly kind: "hello";
298
+ readonly head: number;
299
+ } | {
300
+ readonly kind: "lost";
301
+ };
302
+ interface Subscription {
303
+ close(): void;
304
+ }
305
+ /**
306
+ * Cross-instance invalidation bus. A subscriber consumes a `BusEvent`
307
+ * stream — frames plus the out-of-band `gap`/`reset` signals — and the KERNEL, not
308
+ * the bus, asserts frame contiguity and forces a resync on any gap or reset.
309
+ * `doBus` / `redisBus` implement it in phase 2; the harness `FakeBus` models drop,
310
+ * delay, reorder, and disconnect/reconnect over this same union.
311
+ */
312
+ interface Bus {
313
+ subscribe(onEvent: (event: BusEvent) => void): Subscription;
314
+ }
315
+ /** CDN purge acceptance modes for `delete`. */
316
+ type CdnMode = "enqueue" | "await" | "block";
317
+ /**
318
+ * CDN / L3 purge driver. Its own persisted drain queue with its own
319
+ * alarm; acceptance surfaces via `edgePurged()`. `cloudflarePurge`
320
+ * implements it in phase 2.
321
+ */
322
+ interface Cdn {
323
+ purge(tags: readonly string[], mode: CdnMode): Promise<void>;
324
+ }
325
+ /**
326
+ * A pluggable value codec. `id` is the codec name + wire version — the
327
+ * `Envelope.codecId` source of truth. A decode failure or an `id` mismatch
328
+ * (not in `acceptCodecs`) fails closed to a miss (invariants 8, 27).
329
+ */
330
+ interface Codec {
331
+ readonly id: string;
332
+ encode(value: unknown): string;
333
+ decode(value: string): unknown;
334
+ }
335
+ /** A held cross-isolate lock carrying a monotonic fencing token. */
336
+ interface LockHandle {
337
+ /**
338
+ * Monotonic fencing token — the tie-breaker used above an equal `bornEpoch`
339
+ * and always above `bornMs`; a locked fill carries it into `Envelope.fence`.
340
+ */
341
+ readonly fence: number;
342
+ release(): Promise<void>;
343
+ }
344
+ /**
345
+ * Opt-in cross-isolate lock — DO or Redis primary only. Grants mutual
346
+ * exclusion plus a monotonic fencing token; the born-monotone write-back rule
347
+ * is what bounds a stale lease-holder's late write. `doRegistry` /
348
+ * `redis` back it in phase 2; the harness `FakeLock` is the fake.
349
+ */
350
+ interface Lock {
351
+ acquire(key: string): Promise<LockHandle>;
352
+ }
353
+ /**
354
+ * L1 driver options. The `clock` is OPTIONAL: a clock-less `memory({ maxEntries,
355
+ * maxBytes })` stays valid (the quickstart form) and compliant with the no-ambient-time rule — the kernel
356
+ * passes no `expirationTtl` to L1 today, so residency is production-inert there. A
357
+ * clock-PRESENT `memory({ clock, … })` honors `expirationTtl` (TTL residency); a set that
358
+ * carries `expirationTtl` with no injected clock fails loud rather than silently ignore it.
359
+ *
360
+ * `telemetry` is the OPTIONAL private-eviction disclosure channel: when a PRIVATE (`usr:`-scoped) entry is
361
+ * evicted from the LRU under memory pressure, a `private_evicted` event is emitted through it
362
+ * (payload: reason class + counts + bytes ONLY, never a key/hash/tag — payload discipline). It
363
+ * is guarded by `safeEmit`, so a throwing/rejecting sink is swallowed and `onSinkError` notified.
364
+ */
365
+ interface MemoryOptions {
366
+ /** Injected logical clock — the ONLY `now` source; required to honor `expirationTtl`. */
367
+ readonly clock?: Clock;
368
+ readonly maxEntries?: number;
369
+ readonly maxBytes?: number;
370
+ /** Private-eviction disclosure sink. Optional; off by default. */
371
+ readonly telemetry?: TelemetrySink;
372
+ /** Injectable broken-sink hook `safeEmit` calls when it swallows a `telemetry` failure. */
373
+ readonly onSinkError?: SinkErrorHook;
374
+ }
375
+ /**
376
+ * In-process L1 store — isolate-local, bounded by `maxEntries` / `maxBytes` with LRU
377
+ * eviction on either bound (an insertion-ordered `Map`, re-inserted on touch, so recency —
378
+ * not mere insertion order — picks the victim). Each `memory()` owns a PER-INSTANCE byte
379
+ * accounter (Σ value UTF-8 byte lengths).
380
+ *
381
+ * an optional `telemetry` sink receives a `private_evicted` event when a
382
+ * PRIVATE (`usr:`-scoped) entry is evicted from the LRU under memory pressure — reason class +
383
+ * counts + bytes ONLY, so an operator sees L1 pressure on private data without the event ever
384
+ * naming WHAT was evicted (payload discipline). The `usr:`-priority-protected LRU (evict
385
+ * private entries LAST) is a spec MAY and stays UNBUILT: this discloses whatever the current LRU
386
+ * policy evicts, it does not change the victim. The cross-instance shared/joint reserve (warn
387
+ * on Σ`maxBytes` overflow across isolate L1s) is a separate advisory extra, still DEFERRED.
388
+ */
389
+ declare function memory(options?: MemoryOptions): Store;
390
+ //#endregion
391
+ //#region src/duration.d.ts
392
+ /** Duration units. `m` = minutes (no month unit). */
393
+ type DurationUnit = "ms" | "s" | "m" | "h" | "d";
394
+ /** A duration literal, e.g. `'5m'`, `'1.5h'`, `'250ms'`. */
395
+ type Duration = `${number}${DurationUnit}`;
396
+ /**
397
+ * Build a `Duration` from computed values — the trust point for
398
+ * programmatically-derived durations (as opposed to a `'5m'` string literal the type
399
+ * system already constrains). Rejects a non-positive or non-finite `value`, and — and,
400
+ * the case a naive impl passes — a `value × unitFactor` product that is not a finite,
401
+ * positive, SAFE integer once multiplied out. So `duration(Number.MAX_SAFE_INTEGER, 'd')`
402
+ * is REJECTED even though the input is a safe integer: the overflow lives in the product.
403
+ * Throws `InvalidDurationError` on any violation; never coerces.
404
+ */
405
+ declare function duration(value: number, unit: DurationUnit): Duration;
406
+ //#endregion
407
+ //#region src/envelope.d.ts
408
+ /** `val` = a cached value; `neg` = a remembered 404 (hard-expire only). */
409
+ type EnvelopeKind = "val" | "neg";
410
+ /** Wire container of the stored bytes. `codecId` is the codec identity. */
411
+ type EnvelopeEnc = "json" | "json+gz" | "bin";
412
+ /**
413
+ * The stored entry (schema version 3). `bornEpoch` is the invalidation clock;
414
+ * `bornMs` is TTL/grace/GC/age arithmetic ONLY and is never an arbiter.
415
+ *`tags` are sorted, deduped, and include `__k:<h>` and `__ns:<name>`.
416
+ */
417
+ interface Envelope<T = unknown> {
418
+ readonly v: 3;
419
+ /** `<ns-version>:<ns>:<scope>:<user-key>`. */
420
+ readonly key: string;
421
+ readonly val: T;
422
+ /** Writer's applied registry epoch at FILL START — the invalidation clock. */
423
+ readonly bornEpoch: number;
424
+ /** Wall time at fill start — TTL/grace/GC/age arithmetic only; never an arbiter. */
425
+ readonly bornMs: number;
426
+ /** Observability only. */
427
+ readonly storedAt: number;
428
+ readonly ttl: number;
429
+ readonly grace: number;
430
+ /** Sorted, deduped; includes `__k:<h>` and `__ns:<name>`. */
431
+ readonly tags: readonly string[];
432
+ /** `'pub' | 'usr:<h>' | 'ten:<h>'`. */
433
+ readonly scope: string;
434
+ readonly src: string;
435
+ readonly kind: EnvelopeKind;
436
+ readonly enc: EnvelopeEnc;
437
+ readonly size: number;
438
+ /** Codec name + wire version; decode fails closed on mismatch. */
439
+ readonly codecId: string;
440
+ /** Lock fencing token, carried by locked fills; an arbiter above `bornMs`. */
441
+ readonly fence?: number;
442
+ }
443
+ //#endregion
444
+ //#region src/invalidation.d.ts
445
+ /**
446
+ * The live channel's state, as the reader has observed it. A LOCAL FACT — three states,
447
+ * because two would hide the one that matters:
448
+ *
449
+ * - `never-established` — no `hello` has ever arrived on any socket. The reader has NEVER had a
450
+ * live channel, so it runs degraded and SAYS so: what keeps it correct meanwhile is the poll
451
+ * carrier plus failing closed on any unknown tag, and each of those checks teaches the map
452
+ * something it keeps. This is NOT suspicion — the suspect flag is for a map that
453
+ * knows it MISSED something; a reader that never connected has missed nothing, because it never
454
+ * had a channel to miss anything on. Conflating them
455
+ * would either make every cold boot look gapped or make a dead bus look healthy; it is its own
456
+ * state precisely so it can be neither.
457
+ * - `established` — a `hello` arrived and the socket is up.
458
+ * - `lost` — a socket that HAD established is now down. Distinguished from `never-established`
459
+ * because the two have different remedies and different histories: one has knowledge to resync
460
+ * against a head it once heard, the other has never heard one.
461
+ */
462
+ type ChannelState = "never-established" | "established" | "lost";
463
+ //#endregion
464
+ //#region src/tags.d.ts
465
+ /**
466
+ * A validated cache tag. Produced ONLY by `t`, `compound`, or `globalTag`; a raw
467
+ * string is never assignable to `Tag`. This nominal brand is how the library kills
468
+ * stringly-typed tags (the failure class it exists to prevent).
469
+ */
470
+ type Tag = string & {
471
+ readonly "astilba.tag": true;
472
+ };
473
+ /** A positional argument to `compound()` / an interpolation in `t`. */
474
+ type TagPart = string | number;
475
+ /**
476
+ * Template helper for a single tag, e.g. ``t`book:${id}` ``. The tag TRUST
477
+ * BOUNDARY: the interpolations (`values`) are UNTRUSTED user data, so `t` assembles the
478
+ * template, then VALIDATES-and-REJECTS the final string against the full grammar
479
+ * (non-empty, charset, ≤256 UTF-8 bytes, not `__`-prefixed) — throwing `InvalidTagError`
480
+ * on any violation. It never escapes-by-guess: escaping is `compound()`'s separate
481
+ * contract, and silently rewriting an interpolation is exactly how tag injection slips
482
+ * through. On success returns the branded `Tag`.
483
+ */
484
+ declare function t(strings: TemplateStringsArray, ...values: readonly TagPart[]): Tag;
485
+ /**
486
+ * Injective compound tag over an argument vector, e.g. `compound('book', id,
487
+ * 'locale', locale)`. Escaping; empty vector distinct from
488
+ * `['']`.
489
+ *
490
+ * Every part is escaped so an escaped part can never itself
491
+ * contain an unescaped `|`; joining escaped parts on `|` is therefore an
492
+ * injective join. An explicit leading arity count (itself `|`-terminated)
493
+ * makes the count unambiguous — it is always a decimal-digit run followed by
494
+ * the first `|` — which in turn makes vectors of differing length (including
495
+ * the empty vector vs. `['']`) distinct regardless of what the escaped parts
496
+ * contain.
497
+ */
498
+ declare function compound(...parts: readonly TagPart[]): Tag;
499
+ /**
500
+ * A globally-scoped tag — its own scope-prefix escape hatch. Deferred to v0.4:
501
+ * its escape-FORM is a wire-compatibility decision reserved for the scope-qualification
502
+ * design, and ships with that consumer rather than standalone here.
503
+ */
504
+ declare function globalTag(name: string): Tag;
505
+ //#endregion
506
+ //#region src/cache.d.ts
507
+ /**
508
+ * A scope claim. `'public'` is an enforced shared-tier claim; `{tenant}` is
509
+ * tenant scoping. Omitting `scope` is private-by-default (principal-derived,
510
+ * `usr:` ⇒ L1-only).
511
+ */
512
+ type Scope = "public" | {
513
+ readonly tenant: string;
514
+ };
515
+ /** The tier an entry was served from (`tier`). */
516
+ type Tier = "l1" | "l1.5" | "l2" | "origin" | "miss";
517
+ /** How fail-closed `unknown` resolves (adds `'error'`). */
518
+ type UnknownPolicy = "registry-check" | "miss" | "error";
519
+ /** Envelope provenance exposed to a factory for the graced value. */
520
+ interface GracedInfo {
521
+ readonly value: unknown;
522
+ readonly src: string;
523
+ readonly v: number;
524
+ readonly bornMs: number;
525
+ }
526
+ /**
527
+ * An adapter-provided request object. In a public-scoped factory dev mode
528
+ * wraps it in a Proxy for the frame, so any property read demotes the entry off the
529
+ * shared tier (closures over raw request state are the boundary invariant 4 names).
530
+ */
531
+ type RequestContext = Readonly<Record<string, unknown>>;
532
+ /**
533
+ * The factory context for `getOrSet`. Deliberately has NO `skip()` — the
534
+ * plain-`T` API cannot skip (M10; invariant 24). `reuseGraced()` is the typed
535
+ * primitive for reusing stale bytes — never launder `graced.value` (an
536
+ * `unknown`) to `T`.
537
+ */
538
+ interface FactoryCtx<T> {
539
+ readonly signal: AbortSignal;
540
+ readonly graced?: GracedInfo;
541
+ /** The adapter request object — a guarded accessor under public scope. */
542
+ readonly request?: RequestContext;
543
+ /** Throw a classified error; bare `fail()` is non-retriable. */
544
+ fail(err?: unknown): never;
545
+ /** Contribute a dependency tag; `l3:false` marks it droppable for shared caches. */
546
+ dependsOn(tag: Tag, options?: {
547
+ readonly l3?: boolean;
548
+ }): void;
549
+ setTags(tags: readonly Tag[]): void;
550
+ setTtl(ttl: Duration): void;
551
+ /** Reuse the graced value with an identity + provenance/codec check. */
552
+ reuseGraced(): T;
553
+ }
554
+ /**
555
+ * The factory context for `getOrSetEntry`. Adds `skip()` — reachable ONLY
556
+ * through the entry form, with sound `never` typing so it cannot silently resolve
557
+ * `undefined` typed as `T` (M10; invariant 24).
558
+ */
559
+ interface EntryFactoryCtx<T> extends FactoryCtx<T> {
560
+ skip(): never;
561
+ }
562
+ /** Options common to `getOrSet` and `getOrSetEntry`. */
563
+ interface GetOptions {
564
+ readonly key: string;
565
+ readonly tags?: readonly Tag[];
566
+ /**
567
+ * Declared time-to-live. In v0.2 this is part of the singleflight join identity — two
568
+ * concurrent same-key fills declaring different ttls are an options mismatch — but it is
569
+ * NOT stored with the entry and does not drive expiry: validity is decided by epoch comparison,
570
+ * never a wall clock, and the write path passes no residency hint to the stores. So an
571
+ * entry outlives its declared ttl until a purge or store eviction removes it. Wiring ttl to
572
+ * residency and to stored identity is the v0.4 ttl design slot, beside `FactoryCtx.setTtl`.
573
+ */
574
+ readonly ttl?: Duration;
575
+ readonly grace?: Duration;
576
+ readonly notFoundTtl?: Duration;
577
+ readonly scope?: Scope;
578
+ /**
579
+ * Read consistency. Part of the singleflight join identity: a `strong`
580
+ * request never joins a non-strong fill.
581
+ */
582
+ readonly consistency?: Consistency;
583
+ /** Opt in to a cross-isolate lock; the fill carries `Envelope.fence`. */
584
+ readonly lock?: boolean;
585
+ /** The adapter request object for this call, surfaced as a guarded accessor. */
586
+ readonly request?: RequestContext;
587
+ }
588
+ /** `getOrSet` options — plain-`T` factory, no `skip`. */
589
+ interface GetOrSetOptions<T> extends GetOptions {
590
+ readonly factory: (ctx: FactoryCtx<T>) => Promise<T>;
591
+ }
592
+ /** `getOrSetEntry` options — entry-form factory, `skip`-capable. */
593
+ interface GetOrSetEntryOptions<T> extends GetOptions {
594
+ readonly factory: (ctx: EntryFactoryCtx<T>) => Promise<T>;
595
+ }
596
+ /** The `getOrSetEntry` result (adds `durable`, makes it optional). */
597
+ interface CacheEntry<T> {
598
+ readonly value: T | undefined;
599
+ readonly skipped: boolean;
600
+ readonly stale: boolean;
601
+ readonly age: number;
602
+ readonly tier: Tier;
603
+ readonly servedOnError?: boolean;
604
+ /**
605
+ * What THIS serve can prove about the value's durability:
606
+ * - `true` — the value is in the shared L2 tier (a fill that wrote it there, or a read that
607
+ * found it there: either way, proof).
608
+ * - `false` — the value is known NOT durable: the fill was confined to L1 (a `usr:` scope, a
609
+ * dev-mode guarded-accessor demotion), its L2 write-back was suppressed,
610
+ * or the guard REFUSED the write outright — a `kind:'neg'` candidate over a
611
+ * stored val the letter protects. Only the last was never issued at all; `false`
612
+ * reports this turn's own write either way, because in every one of them the
613
+ * kernel positively knows no durable copy of what it tried to write exists.
614
+ * - ABSENT — unknowable on this serve: a `pub`/`ten:` entry found in L1 carries no
615
+ * write-back record, and a serve does not consult L2 on an L1 hit.
616
+ *
617
+ * An absent field is a real answer, not a `false` in disguise. A truthiness test collapses it into
618
+ * the conservative direction (not provably durable), which is a safe default; code that must
619
+ * distinguish known-not-durable from unknowable compares against `false` explicitly.
620
+ */
621
+ readonly durable?: boolean;
622
+ }
623
+ /** Best-effort match hint on a purge result (renamed from `matched`). */
624
+ type MatchedHint = "yes" | "no-such-scope" | "unknown";
625
+ /** The result of `expire` / `delete` / `clear`. */
626
+ interface PurgeResult {
627
+ readonly epoch: number;
628
+ /** Best-effort; legal workflows must not branch on it. */
629
+ readonly matchedHint: MatchedHint;
630
+ /** Durable + bus handoff + KV batch accepted. NOT the edge. */
631
+ flushed(options?: {
632
+ readonly timeout?: Duration;
633
+ }): Promise<void>;
634
+ /** CDN purge accepted (queue drained) — a distinct promise. */
635
+ edgePurged(options?: {
636
+ readonly timeout?: Duration;
637
+ }): Promise<void>;
638
+ }
639
+ /** `expire` selector — a tag (optionally scope-qualified) or a key. */
640
+ type ExpireSelector = {
641
+ readonly tag: Tag;
642
+ readonly scope?: Scope;
643
+ } | {
644
+ readonly key: string;
645
+ };
646
+ /** `delete` selector — like `expire`, plus a CDN mode. */
647
+ type DeleteSelector = {
648
+ readonly tag: Tag;
649
+ readonly scope?: Scope;
650
+ readonly cdn?: CdnMode;
651
+ } | {
652
+ readonly key: string;
653
+ readonly cdn?: CdnMode;
654
+ };
655
+ /** The explicit acknowledgement `expireAll` / `deleteAll` require. */
656
+ interface OriginLoadGuard {
657
+ readonly iUnderstandTheOriginLoad: true;
658
+ }
659
+ /** Header-byte budget for shared-cache emission (16 KB / ~1,000-tag). */
660
+ interface L3Budget {
661
+ readonly maxBytes?: number;
662
+ readonly maxTags?: number;
663
+ }
664
+ /**
665
+ * Why a response is ineligible for shared HTTP caches.
666
+ *
667
+ * - `late-tag` / `budget`: the timing + header-budget triggers.
668
+ * - `scope`: the render consumed a dependency whose stored scope is readable but
669
+ * NOT `pub` (a `usr:`/`ten:` entry) — per-principal content.
670
+ * - `scope-unreadable`: a consumed dependency's stored scope could not be read at
671
+ * all (absent / malformed). It fails CLOSED to non-public: an unverifiable scope
672
+ * is treated exactly like a private one.
673
+ */
674
+ type L3Ineligibility = "late-tag" | "budget" | "scope" | "scope-unreadable";
675
+ /** The shared-cache emission decision at header commit. */
676
+ interface L3Emission {
677
+ /** `false` ⇒ `Cache-Control: private` / no `s-maxage`. */
678
+ readonly eligible: boolean;
679
+ /** Cache-Tag values to emit; `__k`/`__ns` are never emitted. Always empty
680
+ * on a scope-ineligible emission — a poisoned response emits no tag at all. */
681
+ readonly cacheTags: readonly string[];
682
+ readonly ineligibleReason?: L3Ineligibility;
683
+ }
684
+ /**
685
+ * Request-scoped render-time dependency-tag collector. Cache hits and fills during
686
+ * the request contribute tags AND their scope evidence automatically (that automatic
687
+ * contribution is what makes the gate safe-by-default: a render cannot forget to
688
+ * declare the private entry it just read). `commitHeaders` decides shared-HTTP-cache
689
+ * eligibility at header-commit time: a non-public consumed dependency poisons the response
690
+ * outright, and beneath that gate the timing/budget rules still apply — required
691
+ * tags discovered late, or exceeding the L3 budget, make the response ineligible
692
+ * (invariant 13).
693
+ */
694
+ interface RenderCollector {
695
+ dependsOn(tag: Tag, options?: {
696
+ readonly l3?: boolean;
697
+ }): void;
698
+ commitHeaders(budget?: L3Budget): L3Emission;
699
+ }
700
+ /**
701
+ * A stored entry's scope as read from the surface, preserving the readable-vs-
702
+ * unreadable distinction: a `pub` / `usr:<h>` / `ten:<h>` literal read verbatim, or the fail-closed
703
+ * `unreadable` marker when the surface returned no readable scope (absent / non-string).
704
+ */
705
+ type ExplainScope = {
706
+ readonly kind: "readable";
707
+ readonly literal: string;
708
+ } | {
709
+ readonly kind: "unreadable";
710
+ };
711
+ /**
712
+ * A stored entry's ttl as read from the surface, preserving the stored-vs-not-stored
713
+ * distinction: the number the stored format carries, or `not-stored` where it carries none. The two
714
+ * arms are structurally distinct and there is no sentinel — a stored ttl of `0` and a ttl the format
715
+ * never wrote are different states, and only the store's own bytes decide which. The v0.2 format
716
+ * persists no ttl (see the v0.4 ttl slot named beside `FactoryCtx.setTtl`), so the witness reports
717
+ * `not-stored` today; a format that later carries one is reported through this same arm.
718
+ */
719
+ type ExplainTtl = {
720
+ readonly kind: "stored";
721
+ readonly seconds: number;
722
+ } | {
723
+ readonly kind: "not-stored";
724
+ };
725
+ /**
726
+ * The scope evidence one recorded render dependency carries — the public projection
727
+ * of the collector's per-dependency scope. Adds `none` (a bare `dependsOn` with no managed entry
728
+ * behind it, which carries no scope claim) to the stored two-arm form.
729
+ */
730
+ type ExplainDependencyScope = {
731
+ readonly kind: "none";
732
+ } | ExplainScope;
733
+ /**
734
+ * One render dependency `explain` attributes to the in-flight request — its tags, its
735
+ * scope evidence, and whether it is droppable for shared caches (`l3:false`). Request-scoped: only
736
+ * surfaced while a render collector is bound.
737
+ */
738
+ interface ExplainedDependency {
739
+ readonly tags: readonly string[];
740
+ readonly scope: ExplainDependencyScope;
741
+ readonly l3?: boolean;
742
+ }
743
+ /**
744
+ * The entry's stored identity as `explain` reads it from the store: the
745
+ * stored tag set, the scope as stored (readable-vs-unreadable), the born epoch, the ttl as stored
746
+ * (stored-vs-not-stored), and the entry kind. Every field is read off the stored bytes: a
747
+ * reconstruction default the reader supplies is not stored identity, and reporting one as identity
748
+ * would be the witness testifying to a value the store never carried.
749
+ */
750
+ interface ExplainIdentity {
751
+ readonly tags: readonly string[];
752
+ readonly scope: ExplainScope;
753
+ readonly bornEpoch: number;
754
+ readonly ttl: ExplainTtl;
755
+ readonly kind: EnvelopeKind;
756
+ }
757
+ /**
758
+ * The classification `explain` reaches and the epochs behind it: the reader's
759
+ * current-knowledge verdict over the entry, plus the tag-knowledge watermarks the born epoch was
760
+ * weighed against (the purge epochs behind a `stale`/`dead` verdict, `0` when nothing is known).
761
+ * `unknown` stays `unknown` — the fail-closed narration is the point, not a gap.
762
+ */
763
+ interface ExplainVerdict {
764
+ readonly validity: Validity;
765
+ readonly softEpoch: number;
766
+ readonly hardEpoch: number;
767
+ }
768
+ /**
769
+ * The reader's own already-tracked state at the moment of the call,
770
+ * surfaced via read-only accessors — `explain` adds no tracking of its own.
771
+ * `appliedEpoch`/`suspect`/`channel` come from the invalidation reader; `terminal` from its
772
+ * replication shell. A registry-absent instance has no reader, so all four rest at their neutral
773
+ * values.
774
+ *
775
+ * `channel` is conformant on the letter that matters: the reader tracks it for its own use (the
776
+ * lazy redial needs to know whether it has a channel), so surfacing it ADDS NO BOOKKEEPING — it
777
+ * exposes a fact already on the object. The witness may speak; it may not touch cache state, and
778
+ * reading this getter does not dial, subscribe, or record.
779
+ *
780
+ * It is here because `suspect` alone cannot answer the question an operator actually asks. A reader
781
+ * whose bus never connected is NOT suspect — it has no gap, it has no channel — so a healthy-looking
782
+ * `suspect: false` is exactly what a dead bus produces. `channel` is the field that tells those two
783
+ * apart.
784
+ */
785
+ interface ExplainReaderState {
786
+ readonly appliedEpoch: number;
787
+ readonly suspect: boolean;
788
+ readonly terminal: boolean;
789
+ /** The live channel's state — `never-established` until the first `hello` ever arrives. */
790
+ readonly channel: ChannelState;
791
+ }
792
+ /** Fields common to every arm of an `Explanation` — what the witness knows regardless of
793
+ * what the store said. `tier` is NOT here: it is an observation over the tiers, and a probe that
794
+ * failed observed none, so the arm that could not read carries no tier to report. */
795
+ interface ExplanationBase {
796
+ readonly key: string;
797
+ readonly reader: ExplainReaderState;
798
+ /** Request-scoped per-dependency attribution — present (possibly empty) only
799
+ * while a render collector is bound to the in-flight request; ABSENT otherwise. It lives and dies
800
+ * with the request. */
801
+ readonly requestDependencies?: readonly ExplainedDependency[];
802
+ }
803
+ /**
804
+ * What `explain(key)` reports — three structurally distinct arms on one `kind`
805
+ * discriminant, the same two-arm discipline `ExplainScope`/`ExplainTtl` already carry, extended to
806
+ * the state the store itself can withhold. There is no sentinel, and no arm is reachable by reading
807
+ * a field off another.
808
+ *
809
+ * - `present` — the entry was read: its stored identity and the reader's verdict, found in
810
+ * `tier`.
811
+ * - `absent` — every tier was read and none holds the key. A REPORTABLE state, not an error:
812
+ * `explain` never throws for a missing key.
813
+ * - `read-failed` — a tier's read failed and was suppressed, and no other tier held the key,
814
+ * so whether it exists is UNKNOWN. Distinct from `absent` because the difference
815
+ * is real: one is the store answering "nothing", the other is the store not
816
+ * answering. Reporting the second as the first would be the witness laundering
817
+ * an error into an answer — what the closing sentence forbids. It carries NO
818
+ * `tier` and NO `identity`: a probe that could not read observed neither.
819
+ *
820
+ * The precedence is what each state can support: a found entry is FOUND however the other tiers
821
+ * behaved; and a failed read outranks absence, because a tier that never answered cannot be counted
822
+ * as one that answered "no". `explain` cannot refill, so the serving-path remedy does not transfer
823
+ * — the honest report is the whole remedy available here.
824
+ */
825
+ type Explanation = (ExplanationBase & {
826
+ readonly kind: "present";
827
+ /** The tier the entry was found in — an OBSERVATION over the tiers, never a hydrating read.*/
828
+ readonly tier: Tier;
829
+ readonly identity: ExplainIdentity;
830
+ readonly verdict: ExplainVerdict;
831
+ }) | (ExplanationBase & {
832
+ readonly kind: "absent";
833
+ readonly tier: "miss";
834
+ }) | (ExplanationBase & {
835
+ readonly kind: "read-failed";
836
+ });
837
+ /** Instance defaults. */
838
+ interface CacheDefaults {
839
+ readonly ttl?: Duration;
840
+ readonly grace?: Duration;
841
+ /** Registered with the Coordinator at bind. */
842
+ readonly maxEntryRetention?: Duration;
843
+ readonly consistency?: Consistency;
844
+ readonly unknownPolicy?: UnknownPolicy;
845
+ readonly staleIfError?: (err: unknown) => boolean;
846
+ readonly graceBackoff?: Duration;
847
+ readonly maxSyncLag?: Duration;
848
+ /** Decode-only codec compatibility for intentional migrations. */
849
+ readonly acceptCodecs?: readonly string[];
850
+ /** Paid idle-heartbeat interval for `__state` liveness. */
851
+ readonly heartbeatInterval?: Duration;
852
+ /** Strong opt-out: degrade to eventual instead of throwing. */
853
+ readonly onUnavailable?: "eventual";
854
+ /**
855
+ * `unknown` resolves to `'error'` — the read THROWS rather than refilling.
856
+ *
857
+ * SETTABLE HERE, not only at the top level, because `defaults` is the surface a composition
858
+ * actually threads: `createWorkersCache` forwards `defaults` wholesale, so a flag that existed
859
+ * only on `CacheConfig` could not be reached through any factory that wraps `createCache`. A
860
+ * legal-embargo control that only the raw path can set is a control most callers do not have.
861
+ *
862
+ * It is an OVERRIDE, not a default: it outranks `unknownPolicy` — see `unknownResolution`.
863
+ */
864
+ readonly takedownSensitive?: boolean;
865
+ }
866
+ /** `createCache` configuration. `clock`/`rng` are injected. */
867
+ interface CacheConfig {
868
+ readonly namespace: string;
869
+ readonly clock: Clock;
870
+ readonly rng: Rng;
871
+ readonly l1?: Store;
872
+ readonly l2?: Store;
873
+ readonly registry?: Registry;
874
+ readonly bus?: Bus;
875
+ readonly cdn?: Cdn;
876
+ /** Opt-in cross-isolate lock backend. */
877
+ readonly lock?: Lock;
878
+ /** Pluggable value codec. Defaults to the library's safe-mode codec. */
879
+ readonly codec?: Codec;
880
+ readonly defaults?: CacheDefaults;
881
+ /** A plain sink, or a hosted config that pseudonymizes every string field. */
882
+ readonly telemetry?: TelemetrySink | TelemetryConfig;
883
+ /** `unknown` + registry-unreachable resolves to `'error'`, not `'miss'`. */
884
+ readonly takedownSensitive?: boolean;
885
+ /**
886
+ * Dev mode: a singleflight option mismatch throws + a guarded-accessor read in a
887
+ * public factory throws; prod (`false`) runs a separate fill and emits a
888
+ * `singleflight_option_mismatch` event instead.
889
+ */
890
+ readonly dev?: boolean;
891
+ }
892
+ /** The public cache instance. */
893
+ interface Cache {
894
+ getOrSet<T>(options: GetOrSetOptions<T>): Promise<T>;
895
+ getOrSetEntry<T>(options: GetOrSetEntryOptions<T>): Promise<CacheEntry<T>>;
896
+ expire(selector: ExpireSelector): Promise<PurgeResult>;
897
+ delete(selector: DeleteSelector): Promise<PurgeResult>;
898
+ /** Namespace version bump + hard watermark on the reserved `__ns` tag. */
899
+ clear(): Promise<PurgeResult>;
900
+ expireAll(guard: OriginLoadGuard): Promise<PurgeResult>;
901
+ deleteAll(guard: OriginLoadGuard): Promise<PurgeResult>;
902
+ /** Open a request-scoped render-time tag collector. */
903
+ collect(): RenderCollector;
904
+ /** Witness a key's stored identity, the reader's verdict + evidence, its serving tier, the
905
+ * reader's own state, and any request-scoped dependency attribution. Absence is a
906
+ * reportable state — never throws for a missing key. Introspection adds no tracked state.
907
+ *
908
+ * Two consequences of witnessing rather than simulating, both deliberate:
909
+ * - `usr:`-scoped entries are addressable only through request-scoped attribution; `explain(key)`
910
+ * cannot construct a private key, so it fails closed rather than guess a scope.
911
+ * - The L1 probe reads through the store's own `get`, so it takes that tier's LRU touch: the
912
+ * witness perturbs recency, which is why it is an introspection tool and not a hot-path read.
913
+ * A non-touching `peek` on the store surface is a v0.4 slot — it waits for a second consumer. */
914
+ explain(key: string): Promise<Explanation>;
915
+ }
916
+ /**
917
+ * Build a cache instance. Wires the dual-epoch invalidation core when a
918
+ * registry + bus are present; otherwise runs the registry-absent codec path.
919
+ * Not-yet-implemented verbs still throw `NotImplementedError`.
920
+ *
921
+ * CONSTRUCTION PERFORMS NO I/O. What happens here is bookkeeping over the arguments —
922
+ * addresses are recorded, components are pointed at each other, and no door is opened. Every
923
+ * deferred piece (the bus subscription via `Invalidation`'s lazy dial; the retention lease via
924
+ * `registerRetention` below) waits for the first read that needs it and runs inside that read's
925
+ * request context. This is what makes one cache per isolate, built at module scope, a legal and
926
+ * expected shape rather than an isolate that refuses to start.
927
+ */
928
+ declare function createCache(config: CacheConfig): Cache;
929
+ //#endregion
930
+ export { Clock as $, Tier as A, EnvelopeEnc as B, L3Ineligibility as C, TelemetrySink as Ct, RenderCollector as D, Watermark as Dt, PurgeResult as E, Validity as Et, compound as F, Bus as G, Duration as H, globalTag as I, CasOrder as J, BusEvent as K, t as L, createCache as M, Tag as N, RequestContext as O, TagPart as P, CdnMode as Q, ChannelState as R, L3Emission as S, TelemetryEventName as St, OriginLoadGuard as T, TagKnowledge as Tt, DurationUnit as U, EnvelopeKind as V, duration as W, CasStore as X, CasRecord as Y, Cdn as Z, GetOptions as _, SinkErrorHook as _t, DeleteSelector as a, Registry as at, GracedInfo as b, TelemetryConfig as bt, ExplainDependencyScope as c, Store as ct, ExplainScope as d, StoreWriteError as dt, Codec as et, ExplainTtl as f, StoreWriteOptions as ft, FactoryCtx as g, memory as gt, Explanation as h, isStoreWriteError as ht, CacheEntry as i, ReadKind as it, UnknownPolicy as j, Scope as k, ExplainIdentity as l, StoreMetadata as lt, ExplainedDependency as m, TagChange as mt, CacheConfig as n, LockHandle as nt, EntryFactoryCtx as o, RegistryAck as ot, ExplainVerdict as p, Subscription as pt, BusFrame as q, CacheDefaults as r, MemoryOptions as rt, ExpireSelector as s, Rng as st, Cache as t, Lock as tt, ExplainReaderState as u, StoreValue as ut, GetOrSetEntryOptions as v, StrongDegradedReason as vt, MatchedHint as w, Consistency as wt, L3Budget as x, TelemetryEvent as xt, GetOrSetOptions as y, TELEMETRY_EVENTS as yt, Envelope as z };