@helipod/client 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.
- package/dist/chunk-DW55SNHW.js +286 -0
- package/dist/chunk-DW55SNHW.js.map +1 -0
- package/dist/chunk-I7ZJFW4E.js +2232 -0
- package/dist/chunk-I7ZJFW4E.js.map +1 -0
- package/dist/client-mJZjEkhK.d.ts +1144 -0
- package/dist/index.d.ts +268 -0
- package/dist/index.js +642 -0
- package/dist/index.js.map +1 -0
- package/dist/outbox-fs.d.ts +102 -0
- package/dist/outbox-fs.js +361 -0
- package/dist/outbox-fs.js.map +1 -0
- package/dist/outbox-storage-C6VHkXs9.d.ts +191 -0
- package/dist/react.d.ts +114 -0
- package/dist/react.js +139 -0
- package/dist/react.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api.ts","../src/delivery-policy.ts","../src/optimistic-store.ts","../src/outbox-drain.ts","../src/connect-handshake.ts","../src/identity-fingerprint.ts","../src/client.ts","../src/layered-store.ts","../src/reconcile.ts","../src/mutation-log.ts"],"sourcesContent":["import type { AnyFunctionReference } from \"./function-types\";\n\n/**\n * The runtime `api` — a proxy that turns `api.messages.list` into a function reference whose\n * `__path` is `\"messages:list\"` (module path + function name). Typed as the generated `Api`\n * type, this is what `useQuery(api.messages.list, …)` passes. Nested modules join with `/`\n * (e.g. `api.admin.users.list` → `\"admin/users:list\"`).\n */\nexport interface FunctionReference {\n __path: string;\n}\n\n/**\n * T5's type reconciliation (verdict §(b), flagged latent by T3's report): this package's own\n * `FunctionReference` (`{ __path }`, above) and codegen's generated `Api` type (`FunctionReference<\n * Type, Vis, Args, Returns>` — `__type`/`__visibility`/`__args`/`__returns`, NO `__path` in its\n * TYPE) are structurally incompatible AT THE TYPE LEVEL ONLY. At runtime they are the exact same\n * object — every app's `_generated/server.ts` does `export const api = anyApi as Api`, and\n * `anyApi`'s `Proxy` answers any string property access, `__path` included. This union is the\n * bridge: every public entry point that accepts a function reference (`client.query`/`mutation`/\n * `subscribe`/`action`, `useQuery`/`useMutation`/`useAction`, `OptimisticLocalStore`) is typed\n * against `AnyFunctionRef` (or overloaded across its two members) so a generated typed `api` value\n * compiles wherever the client's own untyped `anyApi` value already did.\n */\nexport type AnyFunctionRef = FunctionReference | AnyFunctionReference<any, any> | string;\n\nexport function getFunctionPath(ref: AnyFunctionRef): string {\n return typeof ref === \"string\" ? ref : (ref as unknown as FunctionReference).__path;\n}\n\nfunction makeProxy(segments: string[]): unknown {\n return new Proxy(\n {},\n {\n get(_target, prop): unknown {\n if (typeof prop !== \"string\") return undefined;\n if (prop === \"__path\") {\n if (segments.length < 2) return segments.join(\":\");\n return `${segments.slice(0, -1).join(\"/\")}:${segments[segments.length - 1]}`;\n }\n return makeProxy([...segments, prop]);\n },\n },\n );\n}\n\n/** The untyped api proxy; cast to your generated `Api` type at the import site. */\nexport const anyApi: unknown = makeProxy([]);\n","/**\n * S4 — `DeliveryPolicy`. Routes transport lifecycle events (v1: only *close*) into log\n * transitions. The v1 policy is verdict §(c) event 6 verbatim: at close, NO optimistic layer of\n * any kind survives into a new session (the ts-gate is only sound over a feed whose ts is\n * monotone for THIS client, and a reconnect's resubscribe baseline arrives with the fresh\n * session's ts — carrying a completed layer across would replay it on top of its own echo).\n *\n * - `unsent` → **retained** (never hit the wire; safe to (re)send on reconnect — T6).\n * - `inflight` → the promise **rejects** with `MutationUndeliveredError` and its layer **drops**\n * (outcome genuinely unknowable — no server dedup exists; a blind resend would\n * double-apply) — UNLESS the S4 swap is `armed` AND this entry's durable append\n * has already committed, in which case it **parks** instead (Task 2): the promise\n * stays PENDING (a future drain resolves it under its recorded `(clientId, seq)`),\n * while its layer still drops (the no-layer-crosses-a-session rule is unchanged).\n * - `completed`→ already resolved at `MutationResponse` (D3); its layer **drops** too.\n * - `parked` → already parked by an earlier close, with no drain yet built to resend it\n * (T2's honest boundary — T4 owns the drain): left exactly as is.\n */\nimport type { PendingMutation } from \"./mutation-log\";\n\n/**\n * Rejection for a mutation whose outcome is unknowable because the transport dropped before its\n * `MutationResponse` arrived. Typed so apps can distinguish \"the server rejected this\" (a plain\n * `Error` from the handler) from \"we never learned what happened\" (retry is unsafe — there is no\n * server-side dedup yet). The message deliberately contains \"connection closed\".\n */\nexport class MutationUndeliveredError extends Error {\n constructor(message = \"mutation outcome unknown: the connection closed before a response arrived\") {\n super(message);\n this.name = \"MutationUndeliveredError\";\n }\n}\n\n/** How each pending entry is disposed when the session closes. */\nexport interface CloseDisposition {\n /** `inflight`-and-NOT-parked request ids — their promises reject with `MutationUndeliveredError`. */\n reject: string[];\n /** request ids whose optimistic layers drop (`inflight` [rejected or parked] + `completed`). */\n drop: string[];\n /** `unsent` (or already-`parked`) request ids — retained in the log; `unsent` for a reconnect\n * flush (T6), `parked` because there is no drain yet to hand them to (T2's honest boundary). */\n retain: string[];\n /** `inflight` request ids whose durable append had already committed, closed while the S4 swap\n * is armed — Task 2's park swap. Included in `drop` too (the layer still drops); listed here\n * separately so the caller knows NOT to reject their promise and NOT to remove them from the\n * log (unlike every other id in `drop`). */\n park: string[];\n}\n\n/** The S4 swap's capability flag — true only once a `ConnectAck` has proven server-side receipt\n * dedup exists for THIS session (verdict §(d) \"S4 swap, feature-detected\"; T3 sets it via\n * `client.ts#setOutboxArmed`). Defaults `false`: a client with no outbox, a fresh/pre-handshake\n * session, or an old server all get today's fail-fast, byte-for-byte. */\nexport interface CloseDispositionOptions {\n armed?: boolean;\n}\n\n/** Compute the close disposition for the current log (verdict §(c) event 6, extended by Task 2's\n * park swap). `closeDisposition(entries)` with no second argument is BYTE-IDENTICAL to the\n * pre-Task-2 behavior — `armed` defaults `false`, and an entry's `durable` flag is irrelevant\n * when unarmed, so every existing call site (and every entry that never touched an outbox, whose\n * `durable` is always falsy) is unaffected. */\nexport function closeDisposition(entries: Iterable<PendingMutation>, opts: CloseDispositionOptions = {}): CloseDisposition {\n const armed = opts.armed ?? false;\n const reject: string[] = [];\n const drop: string[] = [];\n const retain: string[] = [];\n const park: string[] = [];\n for (const e of entries) {\n switch (e.status.type) {\n case \"unsent\":\n retain.push(e.requestId);\n break;\n case \"parked\":\n // Already parked by an earlier close; no drain exists yet to hand it to — stays put.\n retain.push(e.requestId);\n break;\n case \"inflight\":\n if (armed && e.durable) {\n park.push(e.requestId);\n drop.push(e.requestId);\n } else {\n reject.push(e.requestId);\n drop.push(e.requestId);\n }\n break;\n case \"completed\":\n drop.push(e.requestId);\n break;\n }\n }\n return { reject, drop, retain, park };\n}\n","/**\n * T5 — the public, typed `OptimisticLocalStore` (verdict §(b)). Wraps T4's internal, untyped\n * `OptimisticStoreView` (the raw read/write composed-state view `LayeredQueryStore.recompose`\n * threads through an updater) with the v1 API surface a `withOptimisticUpdate` closure actually\n * sees:\n *\n * - **Typed `getQuery`/`setQuery`/`getAllQueries`** — `Q`'s declared `__args`/`__returns` (a\n * codegen-generated `FunctionReference`) drive `args`/`value`'s types; the client's own untyped\n * `{ __path }` ref or a raw string path fall back to `Record<string, Value>`/`Value` (see\n * `RefArgs`/`RefReturn` below — the same fallback shape `api.ts`'s `AnyFunctionRef` bridges).\n * - **`placeholderId(table)`** — deterministic per (entry, table, call-ordinal): replaying the\n * SAME pending mutation (same `entry.seed`, verdict §(c) event 2d's rebuild) mints the SAME id\n * sequence, while two calls to `placeholderId(\"messages\")` within ONE updater invocation are\n * ordinal-distinct, and two DIFFERENT entries (distinct `seed.entropy`) never collide. Built from\n * `entry.seed.entropy` — NOT `crypto.randomUUID()` (verdict D11's replay-purity rule; Convex's\n * own docs example is the footgun this API shape is designed to make structurally impossible to\n * reach for).\n * - **`now()`** — `entry.seed.now`, fixed at mutation creation, stable across every replay. NOT\n * `Date.now()` (same D11 rule).\n * - **dev-mode `Object.freeze`** on every value `getQuery`/`getAllQueries` hands back — Convex's\n * documented \"mutating the returned value in place will corrupt the client's internal state\"\n * footgun becomes an immediate `TypeError` in development (strict-mode ESM) instead of silent\n * corruption. Gated on `process.env.NODE_ENV !== \"production\"` (no existing dev/prod convention\n * was found in this package at T1 — this is the convention going forward) so a production build\n * pays no freeze cost.\n */\nimport type { Value } from \"@helipod/values\";\nimport type { AnyFunctionRef } from \"./api\";\nimport type { AnyFunctionReference } from \"./function-types\";\nimport type { OptimisticStoreView } from \"./layered-store\";\n\n/** `Q`'s declared args if `Q` carries codegen's `__args`/`__returns`; else the untyped default —\n * covers the client's own `{ __path }` ref and a raw string path (mirrors `api.ts`'s `AnyFunctionRef`). */\nexport type RefArgs<Q> = Q extends AnyFunctionReference<infer A, any> ? A : Record<string, Value>;\n\n/** `Q`'s declared return type — same fallback rule as `RefArgs`. */\nexport type RefReturn<Q> = Q extends AnyFunctionReference<any, infer R> ? R : Value;\n\n/** The typed store an optimistic updater receives (verdict §(b)'s v1 API surface, verbatim). */\nexport interface OptimisticLocalStore {\n getQuery<Q extends AnyFunctionRef>(ref: Q, args?: RefArgs<Q>): RefReturn<Q> | undefined;\n setQuery<Q extends AnyFunctionRef>(ref: Q, args: RefArgs<Q>, value: RefReturn<Q> | undefined): void;\n getAllQueries<Q extends AnyFunctionRef>(ref: Q): Array<{ args: RefArgs<Q>; value: RefReturn<Q> | undefined }>;\n /** Deterministic per (entry, table, call-ordinal) — see file doc. NOT `crypto.randomUUID()`. */\n placeholderId(table: string): string;\n /** Entry-creation time, stable across every replay. NOT `Date.now()`. */\n now(): number;\n}\n\n/**\n * T5 — the `optimisticUpdates` registry's updater shape (verdict §(d) \"Reload and rendering\",\n * spec §(k)6). The SAME `(store, args) => void` contract `useMutation(...).withOptimisticUpdate`\n * accepts, but with `args` untyped (`Value`): the registry maps ONE function type across every\n * udfPath key (`Partial<Record<UdfPathOf<Api>, OptimisticUpdateFn>>` — codegen emits the `UdfPathOf`\n * union of KEYS; there is no per-key Args inference threaded through a `Record` at this level, so\n * the VALUE type stays single and generic). Consulted ONLY when a durable entry is hydrated after a\n * reload (never for a live call — \"call-site closure wins for the live call, the registry is\n * consulted only at hydrate\"). An updater here should tolerate `store.getQuery(...)` returning\n * `undefined` (no persisted query baseline exists pre-reconnect — the documented\n * `if (list === undefined) return` recipe, `docs/enduser/offline.md`).\n */\nexport type OptimisticUpdateFn = (store: OptimisticLocalStore, args: Value) => void;\n\n/** Reads `process.env.NODE_ENV` via `globalThis` (no ambient `process` type — this package's\n * tsconfig disables Node's global types, and browser bundles may not define `process` at all).\n * Exported (T5) so `client.ts`'s R9 \"dev-mode loud `console.error` default\" (a durable terminal\n * failure with no `onMutationFailed` registered) shares this ONE dev/prod convention rather than\n * re-deriving it. */\nexport function isDevMode(): boolean {\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env;\n return env?.NODE_ENV !== \"production\";\n}\n\nfunction freezeDev<T>(value: T): T {\n if (isDevMode() && value !== null && typeof value === \"object\") Object.freeze(value);\n return value;\n}\n\n/**\n * Builds the typed store for ONE updater invocation. The per-table ordinal counter is created\n * fresh here (a local `Map`, not carried on `seed`) — every call to this factory starts every\n * table's ordinal back at 0, which is exactly what makes replaying the same entry (same `seed`)\n * deterministic: the Nth `placeholderId(\"messages\")` call within a run always gets the same id.\n */\nexport function createOptimisticLocalStore(\n view: OptimisticStoreView,\n seed: { entropy: string; now: number },\n): OptimisticLocalStore {\n const ordinals = new Map<string, number>();\n return {\n getQuery: (ref, args) => freezeDev(view.getQuery(ref as unknown as string, args as Record<string, Value> | undefined) as any),\n setQuery: (ref, args, value) =>\n view.setQuery(ref as unknown as string, args as Record<string, Value>, value as Value | undefined),\n getAllQueries: (ref) =>\n view.getAllQueries(ref as unknown as string).map(({ args, value }) => ({\n args: args as any,\n value: freezeDev(value as any),\n })),\n placeholderId: (table) => {\n const n = ordinals.get(table) ?? 0;\n ordinals.set(table, n + 1);\n return `${seed.entropy}:${table}:${n}`;\n },\n now: () => seed.now,\n };\n}\n","/**\n * Task 4 — the drain (verdict §(d) \"Drain\", `docs/dev/research/offline-outbox/verdict.md`). The\n * state machine that turns the durable queue (`OutboxStorage`) into exactly-once server effects.\n *\n * Shape (verdict §(d)):\n * - **Web Locks leader** (`helipod:outbox:<origin>:<deployment>`) — probe `navigator.locks`,\n * single-tab fallback when it's absent. Locks are *efficiency*, not correctness: two drainers\n * double-sending is harmless because Plan A's exact-match receipts replay-ack the loser (verdict\n * §(d): \"locks are efficiency; correctness is the records\"). Mid-drain lock loss (the tab killed,\n * or `stop()` on close) → stop cleanly; the durable records make the successor safe (hazard 7).\n * - **Hydrate** the durable queue into the in-memory log under each entry's RECORDED `(clientId,\n * seq)` (cross-session entries a prior page-load left behind), then prune dead-clientId meta rows.\n * - **FIFO by persisted `order`**, sent as `MutationBatch` chunks (default 50 — repair 4, the\n * 500-drain arithmetic in verdict §(a)/(h)); ONE unacked chunk in flight at a time.\n * - **Per-unit resolution** of each chunk's per-entry `MutationResponse`:\n * - `applied` → settle (resolve + dequeue) and advance; the layer drop is routed by `replayed` —\n * a replay (historical, predates this session's baseline) uses T3's unconditional\n * baseline-gated drop, a fresh first-ever apply (this session) uses the same-session\n * `onMutationSuccess` gate a direct-send gets, to stay flicker-free (review-caught, T4).\n * - coded (terminal) failure → default **skip-and-record**: the SERVER recorded the terminal\n * verdict, so the client settles the promise terminally and CONTINUES past it; the\n * `poisonPolicy: \"pause\"` option instead HALTS the drain and surfaces (verdict §(c) R5).\n * - codeless (transient/infra) failure → back off (a local mirror of the scheduler's\n * `computeBackoff`) and re-send FROM the failed unit.\n * - the **transient-stop chunk contract** (Plan A's server semantics, its E2E is the\n * reference): a transient failure STOPS the server mid-batch, so every unit AFTER it gets NO\n * response — those remain queued and re-send on the next chunk.\n * - **Identity gate per entry at flush**: an entry whose stored `identityFingerprint` no longer\n * matches the session's current one terminal-fails LOUDLY with `OFFLINE_IDENTITY_CHANGED`\n * (hazard 9) — a mutation queued as user A must never flush as user B.\n * - **Wakes**: on enqueue, on reconnect-after-baseline, and on an interval nudge — NEVER\n * `navigator.onLine` (hazard 13: it lies).\n *\n * The drain owns no promise callbacks and no wire types beyond `MutationBatch`/`MutationResponse`;\n * every settlement routes back through `client.ts` via {@link DrainHost} so the T3 seam\n * (`settleVerdict`'s primitives — resolve/reject, dequeue, the drop rule) is reused, not forked.\n */\nimport { jsonToConvex, type JSONValue, type Value } from \"@helipod/values\";\nimport type { MutationBatchEntry, ServerMessage } from \"@helipod/sync\";\nimport type { PendingMutation } from \"./mutation-log\";\nimport type { OutboxEntry, OutboxStorage } from \"./outbox-storage\";\n\ntype MutationResponse = Extract<ServerMessage, { type: \"MutationResponse\" }>;\n\n/** How the drain treats a coded (terminal, server-recorded) mutation failure (verdict §(c) R5). */\nexport type PoisonPolicy =\n /** DEFAULT — skip-and-record: settle the promise terminally and CONTINUE draining. The server\n * already recorded the verdict, so a restart can never un-skip it. */\n | \"skip\"\n /** Halt the whole drain on the first coded failure and surface it (A's argument, honored as an\n * option, not the default). */\n | \"pause\";\n\n/* -------------------------------------------------------------------------------------------------\n * Backoff — a LOCAL MIRROR of `computeBackoff` from `components/scheduler/src/backoff.ts`.\n *\n * The client is a browser SDK; it must NOT depend on the server-side `@helipod/scheduler`\n * component (which pulls in `@helipod/executor` and the whole engine). So the formula is\n * duplicated here with attribution — keep the two in sync. `attempts` is the transient-failure\n * count AFTER this failure is recorded (call with the already-incremented count), matching the\n * scheduler's contract exactly. Jitter is 50–100% of the raw backoff; capped so a long offline\n * streak never schedules an absurd delay.\n * ------------------------------------------------------------------------------------------------- */\nconst DRAIN_INITIAL_BACKOFF_MS = 250;\nconst DRAIN_BACKOFF_BASE = 2;\nconst DRAIN_MAX_BACKOFF_MS = 30_000;\n\nexport function computeDrainBackoff(attempts: number, rng: () => number = Math.random): number {\n const raw = DRAIN_INITIAL_BACKOFF_MS * DRAIN_BACKOFF_BASE ** (attempts + 1);\n const jittered = Math.round(raw * (0.5 + 0.5 * rng()));\n return Math.min(jittered, DRAIN_MAX_BACKOFF_MS);\n}\n\n/** The default `MutationBatch` chunk size (verdict §(a)/(h) repair 4). */\nexport const DEFAULT_DRAIN_CHUNK_SIZE = 50;\n/** The default interval-nudge period — the backstop wake (verdict §(d): never `navigator.onLine`). */\nexport const DEFAULT_DRAIN_INTERVAL_MS = 5_000;\n\n/* -------------------------------------------------------------------------------------------------\n * The Web Locks seam (feature-probed, fake-able in tests).\n * ------------------------------------------------------------------------------------------------- */\n\n/** The one method the drain needs from `navigator.locks` — a minimal, structurally-fakeable seam.\n * `request(name, options, cb)` holds the named lock for the lifetime of `cb`'s returned promise\n * (exactly the real `LockManager.request` 3-arg contract). The callback's `lock` parameter mirrors\n * the real `LockGrantedCallback` shape (`null` under `{ifAvailable: true}` when the lock could not\n * be granted immediately) — declared optional so every existing zero-arg callback (`async () =>\n * {...}`) stays assignable unchanged; callers that DO need the availability signal (e.g.\n * `headless-drain.ts#isLockAvailable`) can read it with no cast. */\nexport interface OutboxLockManager {\n request(\n name: string,\n options: { signal?: AbortSignal; mode?: \"exclusive\" | \"shared\"; ifAvailable?: boolean },\n callback: (lock?: unknown) => Promise<unknown> | unknown,\n ): Promise<unknown>;\n}\n\n/** Probe the ambient `navigator.locks`, wrapped into the seam (avoids overload-variance friction of\n * passing the DOM `LockManager` type straight through). Returns `undefined` when unavailable — the\n * drain then runs single-tab (correctness is the records, not the lock). */\nfunction probeLockManager(): OutboxLockManager | undefined {\n const nav = (globalThis as { navigator?: { locks?: { request?: unknown } } }).navigator;\n const locks = nav?.locks;\n if (locks && typeof locks.request === \"function\") {\n return {\n request: (name, options, callback) =>\n (locks as { request: OutboxLockManager[\"request\"] }).request(name, options, callback),\n };\n }\n return undefined;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * The host seam — what the drain needs from `client.ts` (so the T3 settlement primitives are reused).\n * ------------------------------------------------------------------------------------------------- */\n\nexport interface DrainHost {\n readonly outbox: OutboxStorage;\n /** This tab-session's current clientId (re-minted on `onClientReset`). */\n currentClientId(): string | undefined;\n /** The session's current identity fingerprint — the flush-time identity gate compares against it. */\n currentFingerprint(): string;\n /** The transport is up (not closed) — the drain never sends while down. */\n transportOpen(): boolean;\n /** A `ConnectAck` has proven server-side receipt dedup for this session (S4 armed) — the drain\n * never flushes before this, so it can never re-execute against an old/undedup'd server. */\n isArmed(): boolean;\n /** Drain-eligible entries (`unsent`/`parked`, durable, `clientId`+`seq` set), FIFO by `order`. */\n drainable(): PendingMutation[];\n /** Add a hydrated durable entry to the log under a FRESH requestId (idempotent by `(clientId,\n * seq)`). The persisted requestId was session-correlation only, so a fresh one avoids colliding\n * with this session's own requestId counter. */\n addHydrated(entry: OutboxEntry): void;\n /** Send the first-connect `Connect` handshake if one hasn't gone out on this connection yet\n * (idempotent) — the reload analog of T3's reopen handshake (verdict §(d): \"the drain starts\n * only after the reconnect baseline Transition has been adopted\"). */\n ensureInitialHandshake(): void;\n /** Flip an in-log entry's status (drain-owned transitions: `inflight` on flush, back to `unsent`\n * on a transient revert). */\n setStatus(entry: PendingMutation, status: \"inflight\" | \"unsent\"): void;\n /** Build the wire `MutationBatchEntry` for an in-log entry. */\n batchEntry(entry: PendingMutation): MutationBatchEntry;\n /** Send one `MutationBatch` chunk. */\n sendBatch(entries: MutationBatchEntry[]): void;\n /** applied/replayed settlement (verdict §(d) drop rule, T3's `settleVerdict` primitives): resolve\n * the awaiting promise (if any), dequeue the durable record, then drop the layer — routed by\n * `replayed`. A REPLAY (`replayed: true`, a resend whose commit predates this session's `Connect`)\n * drops unconditionally once the baseline is adopted (T3's rule — sound because the baseline\n * snapshot already renders it). A FRESH first-ever apply (`replayed` absent/false — the server\n * executed it for the first time, THIS session, `ts` genuinely after the baseline) must NOT drop\n * early: it needs the same same-session gated hold `onMutationSuccess` gives a direct-send, or the\n * layer disappears a frame before the row it represents actually renders (a flicker). */\n settleApplied(requestId: string, value: Value | null, replayed: boolean, ts: number | undefined): void;\n /** Terminal settlement: reject the awaiting promise (coded), dequeue, drop the layer. */\n settleTerminal(requestId: string, code: string | undefined, message: string): void;\n /** Resolves once the first post-`Connect` baseline Transition has been adopted (verdict §(d) —\n * the drain's send gate). Resolves immediately when no handshake is in flight. */\n whenBaselineAdopted(): Promise<void>;\n}\n\n/** Terminal code for the flush-time identity gate (hazard 9). */\nexport const OFFLINE_IDENTITY_CHANGED = \"OFFLINE_IDENTITY_CHANGED\";\n\n/** The auth-refresh mutation's canonical udf path — mirrors `auth-client.ts`'s default `refreshPath`\n * convention (`\"auth:refresh\"`). A durable entry hydrated under this path is DROPPED at hydrate\n * time (see `hydrateOnce` below) rather than resent — defense-in-depth for a row a PRE-FIX client\n * version already durably enqueued, before `client.ts#mutation`'s `{transient: true}` escape hatch\n * existed. Replaying a refresh mutation is never safe: there is no live promise awaiter for the\n * mint result across a reload, and blindly resending a stale refresh token risks tripping the\n * session's reuse-detection and force-signing-out an honest user (`auth-client.ts`'s file doc,\n * `REFRESH_REUSED`). A fixed client never durably enqueues this path in the first place, so this\n * should only ever match a row a stale/pre-fix build left behind. */\nexport const AUTH_REFRESH_UDF_PATH = \"auth:refresh\";\n\n/** Terminal code stamped on a hydrate-time-dropped refresh entry — see {@link AUTH_REFRESH_UDF_PATH}. */\nexport const NON_REPLAYABLE_MUTATION_DROPPED = \"NON_REPLAYABLE_MUTATION_DROPPED\";\n\n/** Defense-in-depth (see {@link AUTH_REFRESH_UDF_PATH}'s doc): if `entry.udfPath` names the\n * non-replayable auth-refresh mutation, settle its durable row terminally `\"failed\"` (never\n * silently dequeued — visible via `pendingMutations()`/`dismiss()` like any other terminal\n * outcome) and return `true`; the caller must skip adding it to a live log. EVERY path a durable\n * `OutboxEntry` can enter a live reconciler log through must call this FIRST: `hydrateOnce` below,\n * `client.ts`'s `addHydratedEntry` (itself the chokepoint for BOTH `OutboxDrain#hydrateOnce`'s\n * `DrainHost.addHydrated` call and the separate `mirrorFromStore`/cross-tab-backstop path), and the\n * headless drain's own store-only `addHydrated` — a single shared check so none of those three\n * independent ingestion paths can forget it or drift out of sync. */\nexport function dropIfNonReplayable(outbox: OutboxStorage, entry: OutboxEntry): boolean {\n if (entry.udfPath !== AUTH_REFRESH_UDF_PATH) return false;\n console.error(\n `[helipod] outbox: dropping a queued \"${entry.udfPath}\" entry at hydrate — replaying an auth ` +\n \"refresh is never safe (a stale rotation would trip reuse-detection and force-sign-out an \" +\n \"honest user); this entry predates the client's transient-mutation fix\",\n );\n void outbox\n .updateStatus(entry.clientId, entry.seq, \"failed\", {\n message: `mutation \"${entry.udfPath}\" dropped at hydrate: durable auth-refresh replay is never safe`,\n code: NON_REPLAYABLE_MUTATION_DROPPED,\n })\n .catch(() => {});\n return true;\n}\n\nexport interface OutboxDrainOptions {\n /** The Web Locks lock name — `helipod:outbox:<origin>:<deployment>`. */\n lockName: string;\n /** `undefined` → probe `navigator.locks`; `null` → force single-tab; an object → use it (tests). */\n locks?: OutboxLockManager | null;\n poisonPolicy?: PoisonPolicy;\n chunkSize?: number;\n intervalMs?: number;\n /** Injectable backoff (tests drive it deterministically); defaults to {@link computeDrainBackoff}. */\n backoffMs?: (attempts: number) => number;\n /** Fired once when `poisonPolicy: \"pause\"` halts the drain on a coded failure (surfacing). */\n onPause?: (info: { requestId: string; udfPath: string; code: string }) => void;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * The drain.\n * ------------------------------------------------------------------------------------------------- */\n\nexport class OutboxDrain {\n private readonly host: DrainHost;\n private readonly lockName: string;\n private readonly locksOption: OutboxLockManager | null | undefined;\n private readonly poisonPolicy: PoisonPolicy;\n private readonly chunkSize: number;\n private readonly intervalMs: number;\n private readonly backoffMs: (attempts: number) => number;\n private readonly onPause?: (info: { requestId: string; udfPath: string; code: string }) => void;\n\n private started = false;\n private leader = false;\n private stopped = false;\n private paused = false;\n private hydrated = false;\n /** The in-flight chunk: requestId → in-log entry. Non-null iff one unacked chunk is outstanding. */\n private active: Map<string, PendingMutation> | null = null;\n /** Re-entrancy guard around `maybeDrainNext`'s `await whenBaselineAdopted()` (one flush at a time). */\n private flushScheduling = false;\n /** Consecutive transient-stop count — the backoff attempt number (reset on any forward progress). */\n private transientAttempts = 0;\n\n private readonly abort = new AbortController();\n private releaseLeadership?: () => void;\n private intervalTimer?: ReturnType<typeof setInterval>;\n private backoffTimer?: ReturnType<typeof setTimeout>;\n\n constructor(host: DrainHost, opts: OutboxDrainOptions) {\n this.host = host;\n this.lockName = opts.lockName;\n this.locksOption = opts.locks;\n this.poisonPolicy = opts.poisonPolicy ?? \"skip\";\n this.chunkSize = opts.chunkSize ?? DEFAULT_DRAIN_CHUNK_SIZE;\n this.intervalMs = opts.intervalMs ?? DEFAULT_DRAIN_INTERVAL_MS;\n this.backoffMs = opts.backoffMs ?? ((attempts) => computeDrainBackoff(attempts));\n this.onPause = opts.onPause;\n }\n\n /** Acquire leadership (Web Locks, or single-tab fallback) and, once leader, hydrate + drain. Safe\n * to call once; a no-op afterward. */\n start(): void {\n if (this.started) return;\n this.started = true;\n const locks = this.locksOption === undefined ? probeLockManager() : this.locksOption ?? undefined;\n if (!locks) {\n // Single-tab: assume leadership on a microtask (so construction returns before hydrate runs).\n queueMicrotask(() => void this.becomeLeader());\n return;\n }\n // Hold the lock for the lifetime of `becomeLeader`'s promise (resolves on `stop()`). A pending\n // request the abort signal cancels (or any failure) simply means \"not leader\" — never a throw.\n void locks\n .request(this.lockName, { signal: this.abort.signal }, async () => {\n if (this.stopped) return;\n await this.becomeLeader();\n })\n .catch(() => {\n /* aborted before acquisition, or the lock request rejected — we are simply not the leader. */\n });\n }\n\n /** Stop cleanly (client close, or mid-drain lock loss). Releases leadership, cancels a pending\n * lock request, and clears every timer — the durable records make a successor leader safe. */\n stop(): void {\n if (this.stopped) return;\n this.stopped = true;\n this.leader = false;\n this.active = null;\n if (this.intervalTimer !== undefined) clearInterval(this.intervalTimer);\n if (this.backoffTimer !== undefined) clearTimeout(this.backoffTimer);\n this.intervalTimer = undefined;\n this.backoffTimer = undefined;\n this.abort.abort();\n this.releaseLeadership?.();\n }\n\n /** Wake the drain (enqueue / reconnect-after-baseline / an explicit nudge). Deferred to a\n * microtask so a synchronous settling frame (e.g. a `ConnectAck` emitted right after reopen)\n * always settles its entries BEFORE the drain re-reads the queue. */\n nudge(): void {\n if (this.stopped || !this.leader) return;\n queueMicrotask(() => void this.maybeDrainNext());\n }\n\n /** True iff `requestId` belongs to the drain's in-flight chunk — `client.ts` routes that unit's\n * `MutationResponse` here instead of down the direct-send path. */\n handles(requestId: string): boolean {\n return this.active?.has(requestId) ?? false;\n }\n\n /** The transport dropped (a reconnect-class close, NOT the client's `close()` — leadership and\n * the interval survive). The in-flight chunk's unresponded units will never get a response on\n * the new server session, so revert them to re-sendable and clear the chunk NOW — otherwise\n * `canDrain()`'s one-unacked invariant (`active === null`) would wedge the drain for the rest of\n * this tab session (no chunk would ever flush again, and every new mutation would queue behind\n * the stuck backlog until overflow). Called by `client.ts#onTransportClosed` BEFORE the\n * reconciler's S4 close rules run, so the reverted (`unsent`) units are simply retained by the\n * close disposition, promises pending, ready for the reconnect handshake + re-drain. A pending\n * transient-backoff timer is also cleared — the reconnect handshake re-drives the drain, and\n * `canDrain()` would no-op a stale timer against a closed transport anyway. */\n onTransportClosed(): void {\n this.revertActive();\n this.transientAttempts = 0;\n if (this.backoffTimer !== undefined) {\n clearTimeout(this.backoffTimer);\n this.backoffTimer = undefined;\n }\n }\n\n /** True while an unacked chunk is in flight — `client.ts#hasOutboxBacklog` counts it so a new\n * `mutation()` enqueues BEHIND the chunk instead of direct-sending ahead of it (the FIFO rule:\n * \"while the queue is non-empty, new mutations enqueue behind it\"). A chunk's units are\n * `inflight`, which the unsent/parked backlog scan alone would miss when the chunk consumed the\n * entire backlog. */\n get hasActiveChunk(): boolean {\n return this.active !== null;\n }\n\n /** @internal test/debug — the drain halted on a coded failure under `poisonPolicy: \"pause\"`. */\n get isPaused(): boolean {\n return this.paused;\n }\n\n /** @internal test/debug — the drain currently holds leadership. */\n get isLeader(): boolean {\n return this.leader;\n }\n\n /** Resume a `pause`d drain (T5 owns the app-facing retry surface; this is the mechanism). */\n resume(): void {\n if (!this.paused) return;\n this.paused = false;\n this.transientAttempts = 0;\n this.nudge();\n }\n\n private async becomeLeader(): Promise<void> {\n if (this.stopped) return;\n this.leader = true;\n await this.hydrateOnce();\n if (this.stopped) return;\n // A reload with a durable backlog fires the first-connect handshake (arms the S4 swap + lets\n // the server classify held entries); a fresh empty queue defers to the normal reopen handshake\n // so an empty-outbox client is byte-identical to before this task (no spurious Connect frame).\n if (this.host.transportOpen() && this.host.drainable().length > 0) this.host.ensureInitialHandshake();\n this.startInterval();\n void this.maybeDrainNext();\n // Hold leadership until `stop()`; resolving this promise releases the Web Lock.\n await new Promise<void>((resolve) => {\n this.releaseLeadership = resolve;\n });\n }\n\n private startInterval(): void {\n if (this.intervalTimer !== undefined || this.intervalMs <= 0) return;\n this.intervalTimer = setInterval(() => void this.maybeDrainNext(), this.intervalMs);\n (this.intervalTimer as { unref?: () => void }).unref?.();\n }\n\n /** Load the durable queue into the log (once), under recorded ids, then prune dead meta rows.\n * Only STILL-ACTIVE entries (`unsent`/`inflight`/`parked`) are hydrated into the log — a `failed`\n * entry left behind by a prior session is a terminal, accessor-only record (surfaced via\n * `pendingMutations()`/the constructor's `refireDurableFailures` R9 scan) and must never be\n * resurrected here as a fresh `unsent` log entry: `host.addHydrated` unconditionally stamps\n * `status: \"unsent\"` regardless of the persisted status, so hydrating a `failed` row verbatim\n * would render a phantom optimistic row for an already-dead mutation AND make it drainable again\n * (a resend of something the server/R9 already settled), on top of double-firing R9 alongside the\n * constructor's unconditional resume scan. Mirrors the same filter `mirrorFromStore`'s cross-tab\n * backstop applies (`client.ts`). */\n private async hydrateOnce(): Promise<void> {\n if (this.hydrated) return;\n this.hydrated = true;\n const { entries } = await this.host.outbox.loadAll();\n for (const e of entries) {\n if (e.status !== \"unsent\" && e.status !== \"inflight\" && e.status !== \"parked\") continue;\n // Defense-in-depth (see `dropIfNonReplayable`'s doc): DROP rather than resend — settle, don't\n // execute. Never added to the drainable log in the first place.\n if (dropIfNonReplayable(this.host.outbox, e)) continue;\n this.host.addHydrated(e);\n }\n await this.pruneDeadMeta();\n }\n\n /** Delete meta rows for clientIds with no live entries and that aren't the current session's — the\n * T1-flagged unbounded-tiny-rows gap (one dead row accrues per prior tab-session + every reset).\n * Capability-gated: a minimal `OutboxStorage` (a bare test double) may omit the two optional\n * methods, in which case pruning is simply skipped. */\n private async pruneDeadMeta(): Promise<void> {\n const list = this.host.outbox.listMetaClientIds;\n const del = this.host.outbox.deleteMeta;\n if (!list || !del) return;\n let ids: string[];\n try {\n ids = await list.call(this.host.outbox);\n } catch {\n return;\n }\n const current = this.host.currentClientId();\n const live = new Set<string>();\n for (const e of this.host.drainable()) if (e.clientId !== undefined) live.add(e.clientId);\n for (const id of ids) {\n if (id === current || live.has(id)) continue;\n try {\n await del.call(this.host.outbox, id);\n } catch {\n /* best-effort — a failed prune is never a correctness problem. */\n }\n }\n }\n\n private canDrain(): boolean {\n return (\n this.leader &&\n !this.stopped &&\n !this.paused &&\n this.active === null &&\n this.host.transportOpen() &&\n this.host.isArmed() &&\n this.host.drainable().length > 0\n );\n }\n\n /** Flush the next chunk when eligible. The `flushScheduling` guard + the synchronous `this.active`\n * set (in `flushChunk`) keep exactly one chunk in flight across the `await` yield. */\n private async maybeDrainNext(): Promise<void> {\n if (this.flushScheduling || !this.canDrain()) return;\n this.flushScheduling = true;\n try {\n await this.host.whenBaselineAdopted();\n if (!this.canDrain()) return;\n this.flushChunk();\n } finally {\n this.flushScheduling = false;\n }\n }\n\n private flushChunk(): void {\n const all = this.host.drainable();\n const currentFingerprint = this.host.currentFingerprint();\n const chunk: MutationBatchEntry[] = [];\n const map = new Map<string, PendingMutation>();\n let settledIdentityFailure = false;\n\n for (const entry of all) {\n if (chunk.length >= this.chunkSize) break;\n // Identity gate (hazard 9): a mutation queued under a different auth identity must never flush.\n if (entry.identityFingerprint !== undefined && entry.identityFingerprint !== currentFingerprint) {\n console.error(\n `[helipod] outbox: dropping mutation \"${entry.udfPath}\" — it was queued under a different ` +\n `identity than the current session (${OFFLINE_IDENTITY_CHANGED})`,\n );\n this.host.settleTerminal(\n entry.requestId,\n OFFLINE_IDENTITY_CHANGED,\n `mutation \"${entry.udfPath}\" dropped: the session identity changed since it was queued`,\n );\n settledIdentityFailure = true;\n continue;\n }\n this.host.setStatus(entry, \"inflight\");\n map.set(entry.requestId, entry);\n chunk.push(this.host.batchEntry(entry));\n }\n\n if (chunk.length === 0) {\n // Everything eligible was identity-failed (and settled); re-evaluate in case more remain.\n if (settledIdentityFailure) this.nudge();\n return;\n }\n\n this.active = map;\n this.host.sendBatch(chunk);\n }\n\n /** Route one unit's `MutationResponse` (only ever called for a requestId in the active chunk). */\n onResponse(msg: MutationResponse): void {\n const active = this.active;\n const entry = active?.get(msg.requestId);\n if (!active || !entry) return;\n\n if (msg.success) {\n active.delete(msg.requestId);\n const value = this.resolveResponseValue(msg);\n // `msg.replayed` distinguishes a historical resend (predates this session's baseline) from a\n // genuine first-ever apply THIS session (fresh `ts`) — the host routes the layer-drop by it.\n this.host.settleApplied(msg.requestId, value, msg.replayed === true, msg.ts);\n this.onForwardProgress();\n return;\n }\n\n if (msg.code !== undefined) {\n // Coded (terminal, server-recorded) failure.\n if (this.poisonPolicy === \"pause\") {\n this.paused = true;\n console.error(\n `[helipod] outbox drain PAUSED on a coded failure of \"${entry.udfPath}\" (${msg.code}); ` +\n `poisonPolicy=\"pause\" — the queue is halted until resumed`,\n );\n this.onPause?.({ requestId: msg.requestId, udfPath: entry.udfPath, code: msg.code });\n // Halt: leave the poisoned entry (and the rest of the chunk) queued, revert to re-sendable.\n this.revertActive();\n return;\n }\n // Skip-and-record (default): settle terminally and CONTINUE (the server already recorded it).\n active.delete(msg.requestId);\n this.host.settleTerminal(msg.requestId, msg.code, `mutation \"${entry.udfPath}\" failed`);\n this.onForwardProgress();\n return;\n }\n\n // Codeless (transient/infra) failure → the server STOPPED the batch here: this unit and every\n // unit after it (which got NO response) stay queued and re-send after a backoff, FROM this unit.\n this.transientAttempts++;\n this.revertActive();\n const delay = this.backoffMs(this.transientAttempts);\n this.backoffTimer = setTimeout(() => {\n this.backoffTimer = undefined;\n void this.maybeDrainNext();\n }, delay);\n (this.backoffTimer as { unref?: () => void }).unref?.();\n }\n\n private resolveResponseValue(msg: Extract<MutationResponse, { success: true }>): Value | null {\n if (msg.valueMissing) return null;\n return msg.value !== undefined ? jsonToConvex(msg.value as JSONValue) : null;\n }\n\n /** A unit settled (applied or coded-terminal): if the chunk is now empty, advance to the next. */\n private onForwardProgress(): void {\n this.transientAttempts = 0;\n if (this.active && this.active.size === 0) {\n this.active = null;\n this.nudge();\n }\n }\n\n /** Revert every still-in-flight entry of the active chunk to `unsent` (re-sendable) and clear the\n * chunk — used by the transient-stop and pause paths. The units that got no response are exactly\n * those still in `active`. */\n private revertActive(): void {\n if (!this.active) return;\n for (const entry of this.active.values()) this.host.setStatus(entry, \"unsent\");\n this.active = null;\n }\n}\n","/**\n * The `Connect` resume handshake's pure computation (verdict §(c) event 6, §(e)) — `held` (every\n * durable `(clientId, seq)` still awaiting a verdict) and `ackedThrough` (the contiguous\n * settled-prefix per clientId, for server-side retention pruning), plus the wire message itself.\n *\n * Extracted from `client.ts` (T3) so a SECOND caller with no `HelipodClient` can send the SAME\n * handshake shape — the headless drain (`headless-drain.ts`, the Background Sync seam). A Service\n * Worker has no live in-memory `MutationLog`, only the durable `OutboxStorage` rows, so\n * {@link outboxHeldFromStore} computes `held` straight from those instead of a live\n * `PendingMutation` log ({@link outboxHeldFromLog}, `HelipodClient`'s own source). Both feed the\n * SAME {@link outboxAckedThrough}/{@link buildConnectMessage} — the wire shape is identical either\n * way, only the source of `held` differs.\n */\nimport type { ClientMessage, ClientMutationRef } from \"@helipod/sync\";\nimport type { PendingMutation } from \"./mutation-log\";\nimport type { OutboxEntry } from \"./outbox-storage\";\n\n/** A durable entry is `held` (presented to the server for classification) while it's genuinely\n * unsettled: `unsent` (queued, never sent), `inflight` (sent, no response — outcome unknowable on\n * a dropped connection), or `parked` (closed with the S4 swap armed, awaiting a future drain).\n * Presenting `unsent`/`inflight` too is harmless — the server classifies them `unknown` and the\n * caller (re)sends them; only `completed`/`failed` (a settled fate) are never held. */\nconst HELD_STATUSES: ReadonlySet<string> = new Set([\"unsent\", \"inflight\", \"parked\"]);\n\n/** `held` from a LIVE `HelipodClient`'s in-memory log — every entry with a recorded `(clientId,\n * seq)` whose status is still unsettled. */\nexport function outboxHeldFromLog(entries: Iterable<PendingMutation>): ClientMutationRef[] {\n const refs: ClientMutationRef[] = [];\n for (const e of entries) {\n if (e.clientId !== undefined && e.seq !== undefined && HELD_STATUSES.has(e.status.type)) {\n refs.push({ clientId: e.clientId, seq: e.seq });\n }\n }\n return refs;\n}\n\n/** `held` from a store-only host (the headless drain — no live log, only persisted rows) — the same\n * three statuses, read straight off the persisted `OutboxEntry.status` (identical strings, identical\n * meaning, to the live `PendingMutation.status.type` above). */\nexport function outboxHeldFromStore(entries: Iterable<OutboxEntry>): ClientMutationRef[] {\n const refs: ClientMutationRef[] = [];\n for (const e of entries) {\n if (HELD_STATUSES.has(e.status)) refs.push({ clientId: e.clientId, seq: e.seq });\n }\n return refs;\n}\n\n/** The highest CONTIGUOUS settled-prefix seq per clientId (verdict §(c) Retention / spec decision\n * 3). Under the FIFO one-unacked-chunk drain a seq can never settle past an unsettled earlier one,\n * so for each clientId the settled prefix is exactly `(lowest still-held seq) - 1`; a clientId\n * whose lowest held seq is 0 has acked nothing and is omitted. */\nexport function outboxAckedThrough(held: ClientMutationRef[]): ClientMutationRef[] {\n const lowestHeld = new Map<string, number>();\n for (const ref of held) {\n const cur = lowestHeld.get(ref.clientId);\n if (cur === undefined || ref.seq < cur) lowestHeld.set(ref.clientId, ref.seq);\n }\n const acked: ClientMutationRef[] = [];\n for (const [clientId, minSeq] of lowestHeld) {\n if (minSeq > 0) acked.push({ clientId, seq: minSeq - 1 });\n }\n return acked;\n}\n\n/** Build the `Connect` resume handshake wire message. `sessionId` is a fresh per-connect id (the\n * server routes the handshake by the transport-level session, so this field is only\n * informational); `clientId` is likewise informational server-side — `handleConnect` classifies\n * purely off each `held`/`ackedThrough` entry's OWN `clientId`/`seq` pair, never the top-level\n * field — so a caller whose held set spans several prior tab-sessions' clientIds (the headless\n * drain) can safely omit it. */\nexport function buildConnectMessage(sessionId: string, clientId: string | undefined, held: ClientMutationRef[]): Extract<ClientMessage, { type: \"Connect\" }> {\n return {\n type: \"Connect\",\n sessionId,\n ...(clientId !== undefined ? { clientId } : {}),\n held,\n ackedThrough: outboxAckedThrough(held),\n // DLR Stage 2a: advertise by-id diff support. The server records this on any `Connect` (even a\n // resume-handshake one) and sends `QueryDiff` only to a session that advertised it.\n supportsQueryDiff: true,\n };\n}\n","/**\n * Shared identity-fingerprint primitives for the durable outbox (verdict §(d) hazard 9 / spec\n * §(k)7). `client.ts`'s live `setAuth`/`setSessionFingerprint` and `headless-drain.ts`'s one-shot\n * `drainOutboxOnce` must compute the EXACT SAME `identityFingerprint` for the same underlying\n * identity, or the two never agree — a durable entry stamped by a live tab under a managed\n * session's `sessionFingerprintKey` hash would otherwise look \"foreign\" to the headless drain's own\n * differently-formatted hash and terminal-fail with `OFFLINE_IDENTITY_CHANGED` even though nothing\n * about the identity actually changed. One shared module, not two hand-synced copies, is how that's\n * kept impossible by construction.\n */\n\n/** SHA-256 hex digest of `input`. */\nexport async function sha256Hex(input: string): Promise<string> {\n const bytes = new TextEncoder().encode(input);\n const digest = await crypto.subtle.digest(\"SHA-256\", bytes);\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/** The string a managed `createAuthClient` session's outbox fingerprint hashes (never the raw\n * `sessionId` alone) — `client.ts#setSessionFingerprint` and `headless-drain.ts`'s `getSessionId`\n * option both route through this ONE function so the \"session:\" prefix convention can never drift\n * between the two call sites (spec decision 9). */\nexport function sessionFingerprintKey(sessionId: string): string {\n return `session:${sessionId}`;\n}\n","/**\n * `HelipodClient` — the reactive client, now a **Gated Ledger** (verdict §(b)-(c)). It manages\n * query subscriptions (deduped by path+args), applies the version-bracketed sync protocol, and\n * layers optimistic updates over a serializable pending log:\n *\n * - S1 `MutationLog` — one serializable entry per unconfirmed mutation (`./mutation-log`).\n * - S2 `LayeredQueryStore` — per-subscription `serverValue` (server ingest) vs `composedValue`\n * (server base + surviving optimistic layers replayed on top); what listeners see (`./layered-store`).\n * - S3 `Reconciler` — the ONE chokepoint every state change routes through (`./reconcile`).\n * - S4 `DeliveryPolicy` — close rules; NO layer crosses a session (`./delivery-policy`).\n *\n * Promise resolution is at `MutationResponse` (D3) — today's timing, an explicit divergence from\n * convex-js's gate-time resolution. A one-shot `query()` returns the **composed** view (D15).\n */\nimport {\n versionsEqual,\n INITIAL_VERSION,\n type ClientMessage,\n type ClientMutationVerdict,\n type ServerMessage,\n type StateVersion,\n} from \"@helipod/sync\";\nimport { convexToJson, jsonToConvex, type JSONValue, type Value } from \"@helipod/values\";\nimport { getFunctionPath, type AnyFunctionRef, type FunctionReference } from \"./api\";\nimport type { AnyFunctionReference, FunctionArgs, FunctionReturnType } from \"./function-types\";\nimport type { ClientTransport } from \"./transport\";\nimport { LayeredQueryStore, queryHash, type Listener, type OptimisticUpdate, type QueryErrorListener, type QueryListener } from \"./layered-store\";\nimport { Reconciler } from \"./reconcile\";\nimport { MutationUndeliveredError } from \"./delivery-policy\";\nimport type { PendingMutation } from \"./mutation-log\";\nimport { isDevMode, type OptimisticLocalStore, type OptimisticUpdateFn } from \"./optimistic-store\";\nimport {\n DEFAULT_OUTBOX_MAX_QUEUE_SIZE,\n OUTBOX_VERSION,\n OfflineClientResetError,\n OutboxOverflowError,\n defaultMintClientId,\n mintIdentity,\n type OutboxEntry,\n type OutboxEntryError,\n type OutboxEntryStatus,\n type OutboxStorage,\n} from \"./outbox-storage\";\nimport { OutboxDrain, dropIfNonReplayable, type DrainHost, type OutboxLockManager, type PoisonPolicy } from \"./outbox-drain\";\nimport { buildConnectMessage, outboxHeldFromLog } from \"./connect-handshake\";\nimport { sha256Hex, sessionFingerprintKey } from \"./identity-fingerprint\";\nimport type { MutationBatchEntry } from \"@helipod/sync\";\n\nexport type { QueryListener, QueryErrorListener };\n\n/** Passed to the `onClientReset` callback (verdict §(d) Retention) when the server disowns this\n * client's mutation history on `ConnectAck{known: false}`. `unsentReEnqueued` counts the `unsent`\n * entries carried forward under the fresh clientId + NEW seqs; `parkedRejected` counts the\n * in-flight-at-disconnect entries rejected loudly with `OfflineClientResetError`. */\nexport interface ClientResetInfo {\n oldClientId: string | undefined;\n newClientId: string;\n unsentReEnqueued: number;\n parkedRejected: number;\n}\n\n/** T5 (R9): the `onMutationFailed` callback's payload — a terminal, server-recorded verdict for a\n * durable outbox entry the CURRENT session may have no live promise awaiter for (a hydrated\n * cross-reload entry, a retried one, or one discovered already-failed at construction — \"resume\"). */\nexport interface MutationFailedInfo {\n clientId: string;\n seq: number;\n udfPath: string;\n error: OutboxEntryError;\n}\n\n/** T5 (R9): one row of `client.pendingMutations()`/`usePendingMutations()` — a snapshot from the\n * DURABLE store (verdict §(d) \"Observability\"), not the in-memory reconciler log; `retry()`/\n * `dismiss()` are meaningful only when `status === \"failed\"` (a terminal, server-recorded verdict —\n * every other status is still in flight and simply isn't a `retry()`/`dismiss()` candidate) and are\n * harmless no-ops otherwise. */\nexport interface PendingMutationEntry {\n readonly clientId: string;\n readonly seq: number;\n readonly udfPath: string;\n readonly status: OutboxEntryStatus;\n readonly enqueuedAt: number;\n readonly error?: OutboxEntryError;\n /** Re-enqueue this FAILED entry under a fresh `(clientId, seq)` — \"never reuse a seq for a new\n * attempt\" (verdict §(b)): the old seq's durable record IS its terminal verdict. No-op unless\n * `status === \"failed\"`. */\n retry(): Promise<void>;\n /** Permanently remove this FAILED entry from the durable store without retrying. No-op unless\n * `status === \"failed\"`. */\n dismiss(): Promise<void>;\n}\n\n/** T5 (R9, hazard 2's client half): the queue-age/size advisory — cheap enough to poll before\n * surfacing a \"you have offline changes that may be lost soon\" banner ahead of Safari's 7-day\n * eviction cliff. `oldestEnqueuedAt`/`oldestAgeMs` are `undefined` for an empty (or unconfigured)\n * outbox. */\nexport interface PendingSummary {\n count: number;\n oldestEnqueuedAt: number | undefined;\n oldestAgeMs: number | undefined;\n}\n\n/** T5 (R9): the one method `usePendingMutations()`'s cross-tab nudge needs from `BroadcastChannel` —\n * a minimal, structurally-fakeable seam (the same probe-and-fallback discipline as\n * `OutboxLockManager`, `./outbox-drain`). Real `BroadcastChannel`s satisfy this structurally. */\nexport interface OutboxBroadcastLike {\n postMessage(message: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n close(): void;\n}\n\n/** T-crosstab (browser-ux spec Part A): the broadcast channel's payload becomes ADDITIVELY typed.\n * Today's bare `1` (\"the message IS the nudge\", see `notifyOutboxChange` below) stays a valid,\n * forward-compatible message forever — every listener still fires its `outboxChangeListeners` fan-out\n * on ANY payload shape first (`onmessage` below), unconditionally. These three shapes let a receiver\n * additionally MIRROR another tab's durable entries live, instead of merely re-reading on next poll:\n * - `enqueued` — posted after any durable-outbox-mutating write (append/dequeue/status change);\n * the receiver re-reads `loadAll()` and reconciles its mirrored set against it (the backstop).\n * - `settled` — posted by the drain leader right after an `applied` verdict; a mirroring tab holds\n * its layer `completed` and drops it only once ITS OWN feed observes `commitTs` (flicker-free).\n * - `failed` — posted by the drain leader right after a terminal verdict; a mirroring tab drops the\n * layer and fires its own R9 `onMutationFailed`/dev-loud default (no promise exists to reject).\n * A payload that isn't one of these three (including the legacy bare `1`, or anything malformed) is\n * simply not recognized by `isOutboxBroadcastMessage` below — nudge-only, mirrors nothing, throws\n * nothing. */\nexport type OutboxBroadcastMessage =\n | { kind: \"enqueued\" }\n | { kind: \"settled\"; clientId: string; seq: number; commitTs: number }\n | { kind: \"failed\"; clientId: string; seq: number; code?: string; message: string };\n\n/** Structural guard for `OutboxBroadcastMessage` — deliberately shallow (only `kind` is checked):\n * the caller (`onCrossTabSettle`) already tolerates a missing/wrong-typed `clientId`/`seq` by\n * simply finding no matching entry (a strict field-by-field validator would just be more code for\n * the same outcome), and the whole typed-dispatch call is wrapped in try/catch regardless (hazard\n * (d): \"a malformed typed payload must never break the nudge contract\"). */\nfunction isOutboxBroadcastMessage(data: unknown): data is OutboxBroadcastMessage {\n if (typeof data !== \"object\" || data === null || !(\"kind\" in data)) return false;\n const kind = (data as { kind: unknown }).kind;\n return kind === \"enqueued\" || kind === \"settled\" || kind === \"failed\";\n}\n\n/** Probe the ambient `BroadcastChannel` global — absent in most Node/vitest runtimes and in\n * private-mode Safari without a same-origin partition; returns `undefined` there, in which case\n * cross-tab observability degrades to same-instance-only (still fully reactive within one tab).\n * Wraps (rather than returns) the real channel: the DOM `BroadcastChannel.onmessage` setter's\n * parameter type is the full `MessageEvent`, which isn't structurally assignable to this seam's\n * minimal `{ data: unknown }` shape — the wrapper is the adapter, not a cast. */\nfunction probeBroadcastChannel(name: string): OutboxBroadcastLike | undefined {\n if (typeof BroadcastChannel === \"undefined\") return undefined;\n const channel = new BroadcastChannel(name);\n let closed = false;\n const wrapper: OutboxBroadcastLike = {\n // A `postMessage` racing an in-flight write-behind `.then()` against `close()` (e.g. a client\n // torn down mid-test, or an app unmounting while an append is still resolving) must NEVER throw\n // out from under `notifyOutboxChange()` — `closed` makes this a harmless no-op instead of the\n // DOM's `InvalidStateError`.\n postMessage: (message) => {\n if (!closed) channel.postMessage(message);\n },\n onmessage: null,\n close: () => {\n closed = true;\n channel.close();\n },\n };\n channel.onmessage = (ev) => wrapper.onmessage?.({ data: ev.data });\n return wrapper;\n}\n\nlet entropyCounter = 0;\nfunction makeEntropy(): string {\n return `${Date.now().toString(36)}-${(entropyCounter++).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport class HelipodClient {\n private readonly transport: ClientTransport;\n private version: StateVersion = { ...INITIAL_VERSION };\n private resyncing = false;\n private closed = false;\n // DLR Stage 3: the observed-ts frontier as of the moment BEFORE the current close. `closeSession`\n // resets `reconciler.maxObservedTs` to 0 on every transport close (reconcile.ts ~line 320, \"the\n // ts-gate is only sound over one monotone feed\") — so on a genuine reconnect, `maxObservedTs` is\n // already 0 by the time `resync()` runs and echoing it alone would defeat the resume watermark.\n // Captured in `onTransportClosed()` BEFORE `closeSession()` runs; `resync()` echoes\n // `Math.max(resumeSinceTs, reconciler.maxObservedTs)` so a real reconnect (observedTs reset to 0)\n // uses this snapshot, while a same-session drift resync (observedTs intact, no close in between)\n // uses the live value instead.\n private resumeSinceTs = 0;\n private readonly store = new LayeredQueryStore();\n private readonly reconciler: Reconciler;\n /** Mutation promise callbacks, keyed by requestId — resolved/rejected here; layers live in the log. */\n private readonly pendingMutationCallbacks = new Map<string, { resolve: (v: Value) => void; reject: (e: Error) => void }>();\n private readonly pendingActions = new Map<string, { resolve: (v: Value) => void; reject: (e: Error) => void }>();\n private readonly broadcastListeners = new Set<(topic: string, event: Value) => void>();\n private readonly disposeTransport: () => void;\n private readonly disposeClose: () => void;\n private readonly disposeReopen?: () => void;\n private nextQueryId = 1;\n private nextRequestId = 1;\n /** The last token passed to `setAuth` (T6: replayed on reconnect). Unset until `setAuth` is\n * first called — a transport that never had auth set never sends a spurious `SetAuth` on reopen. */\n private hasSetAuth = false;\n private lastAuthToken: string | null = null;\n /** Absent unless `opts.outbox` is configured — a client constructed without it behaves exactly\n * as before this seam existed (`outbox-storage.ts`'s file doc: \"never touches this file's\n * runtime branches that matter\"). */\n private readonly outbox?: OutboxStorage;\n /** Resolves once this tab-session's clientId is durably persisted (`mintIdentity`,\n * `outbox-storage.ts`) — ALWAYS a fresh clientId, never one reused from a prior session. Public\n * contract for tests/direct inspection; `mutation()` itself never awaits this (see\n * `outboxClientId`/`outboxNextSeq` below — the synchronous counterparts it actually reads). */\n private readonly outboxIdentity?: Promise<{ clientId: string; nextSeq: number }>;\n /** This tab-session's clientId, minted SYNCHRONOUSLY at construction (Task 2) — `mutation()` must\n * stay fully synchronous (T1's open concern), so it cannot await `outboxIdentity`'s async\n * `getMeta`/`setMeta` round-trip. Fed into `mintIdentity` via `opts.mintClientId` below so the\n * durable meta row names this SAME id. Set once, iff `opts.outbox` is configured; never reused\n * across a reload (a fresh `HelipodClient` always mints again). */\n private outboxClientId?: string;\n /** In-memory serial `seq` counter for `outboxClientId` (verdict §(d): \"seqs minted serially\n * in-memory per tab\"). Starts at 0 synchronously; `outboxIdentity`'s resolution only ever\n * reconciles it UPWARD (never re-hands-out a seq already allocated locally) for the\n * astronomically-unlikely colliding-clientId case `mintIdentity` itself guards against. */\n private outboxNextSeq = 0;\n /** Monotonic per-tab counter for `OutboxEntry.order` — the drain's (T4) FIFO key across the\n * WHOLE shared queue (every clientId/tab). Seeded from wall-clock time so multiple tabs sharing\n * one outbox interleave in roughly chronological order; strictly increasing per call within\n * this tab regardless of clock resolution. Cross-tab total ordering is a best-effort aid to the\n * drain's efficiency, NOT a correctness requirement — \"locks are efficiency; correctness is the\n * records\" (verdict §(d) \"Drain\"). */\n private outboxOrderCounter = 0;\n /** Cache of `identityFingerprint` (SHA-256 hex of the last `SetAuth` token, or `\"anon\"` for\n * none/empty) — see `setAuth()` below and spec §(k)7. Stamped synchronously onto every entry;\n * computed asynchronously (SubtleCrypto) whenever `setAuth` is called with a real token. */\n private outboxFingerprint = \"anon\";\n /** When true, a managed `createAuthClient` owns the outbox fingerprint (derived from the stable\n * `sessionId`, not the rotating token) — `setAuth`'s token-hash recompute is suppressed so\n * rotation never orphans queued offline mutations mid-drain (spec decision 9). The raw\n * `setAuth(token)` path (no `createAuthClient`) leaves this false and keeps token-hash\n * fingerprinting byte-for-byte unchanged. */\n private sessionFingerprintActive = false;\n /** The `session:<sessionId>` key whose SHA-256 is the active managed fingerprint — guards a stale\n * async digest from overwriting a newer session's fingerprint. */\n private sessionFingerprintKey: string | null = null;\n /** The S4 swap's capability flag (verdict §(d) \"S4 swap, feature-detected\") — flipped by\n * `setOutboxArmed()`, which T3's Connect handshake calls once a `ConnectAck` proves server-side\n * receipt dedup exists for this session. Defaults `false`: today's fail-fast, byte-for-byte,\n * whether or not an outbox is configured. */\n private outboxArmed = false;\n private readonly outboxMaxQueueSize: number;\n /** The last `ConnectAck.deploymentId` seen — the same-timeline proof stamp (verdict §(g) hazard\n * 15's client half). Surfaced via `getOutboxDeploymentId()`; also written into the durable meta\n * row so a future reload can compare timelines. Undefined until the first `ConnectAck`. */\n private outboxDeploymentId?: string;\n /** App callback fired once whenever a `ConnectAck{known: false}` resets this client's identity\n * (verdict §(d) Retention). Optional constructor config. */\n private readonly onClientResetCallback?: (info: ClientResetInfo) => void;\n /** True while the Connect handshake is waiting for the first post-`Connect` baseline Transition\n * to be ADOPTED through S3 (verdict §(d) / spec decision 5 — \"a NEW await\"). While true, the\n * drop rule for `applied` cross-session entries is DEFERRED (queued in `outboxPendingDrops`) and\n * `whenBaselineAdopted()` (T4's drain gate) stays pending. */\n private outboxAwaitingBaseline = false;\n /** requestIds whose `applied`-verdict layer drop is deferred until the baseline is adopted (so the\n * drop is flicker-free — the baseline already renders the effect). Drained by `markBaselineAdopted`. */\n private outboxPendingDrops: string[] = [];\n /** Resolvers for in-flight `whenBaselineAdopted()` promises — settled together when the baseline\n * Transition adopts (or immediately, when a reopen had no live subscriptions to re-baseline). */\n private outboxBaselineResolvers: Array<() => void> = [];\n /** Whether a `Connect` handshake has already gone out on the CURRENT connection (reset at close).\n * Guards against a double-handshake when both the reopen path and the drain's first-connect path\n * could fire — the drain's `ensureInitialHandshake()` is a no-op once a reopen already sent one. */\n private outboxConnectSent = false;\n /** DLR Stage 2a — whether this connection has advertised `supportsQueryDiff` (reset at close). A\n * non-outbox client has no resume `Connect` to piggyback the capability on (an outbox client's\n * `buildConnectMessage` carries it), so it sends a minimal capability-only `Connect` before its\n * first subscribe. Without this the server never records the capability and every by-id sub falls\n * back to RERUN — correct, but the diff path never engages. */\n private diffCapabilitySent = false;\n /** The drain (Task 4) — the Web Locks leader that turns the durable queue into exactly-once server\n * effects. Present iff `opts.outbox` is configured; started at construction. */\n private readonly outboxDrain?: OutboxDrain;\n\n /* -------------------------------------------------------------------------------------------\n * T5 — the `optimisticUpdates` registry, R9 observability.\n * ------------------------------------------------------------------------------------------- */\n\n /** T5: the registry `mutation()` NEVER consults — only `addHydratedEntry` does, at hydrate time\n * (verdict §(d): \"call-site closure wins for the live call; the registry is consulted only at\n * hydrate\"). Plain string-keyed: a generated `UdfPathOf<Api>` union (`@helipod/codegen`) narrows\n * the caller's OWN object-literal keys; this package never imports that generated type. */\n private readonly optimisticUpdates: Partial<Record<string, OptimisticUpdateFn>>;\n /** udfPaths already warned for a registry miss at hydrate — \"one warn per udfPath\" (spec §(k)6),\n * not once per missed ENTRY (a stale backlog of the same unregistered udfPath warns exactly once). */\n private readonly optimisticUpdateMissWarned = new Set<string>();\n /** T5 (R9): fired for a terminal durable failure with no live promise awaiter THIS session (a\n * hydrated/retried entry, or one discovered already-failed at construction — \"resume\"). Never\n * fired for a failure a live `mutation()` caller's own rejected promise already delivered\n * (Lunora's `hadAwaiter` — no double notification for one failure). */\n private readonly onMutationFailedCallback?: (info: MutationFailedInfo) => void;\n /** T5 (R9): same-instance listeners for \"the durable outbox changed\" — `usePendingMutations()`'s\n * re-read trigger. Fired locally on every outbox-mutating op AND on an incoming cross-tab\n * `outboxBroadcast` message (unified into one path — a listener never needs to know which). */\n private readonly outboxChangeListeners = new Set<() => void>();\n /** T5 (R9): the cross-tab nudge — `undefined` when no outbox is configured or the probe/injected\n * option resolved to nothing (single-tab observability still works via `outboxChangeListeners`). */\n private readonly outboxBroadcast?: OutboxBroadcastLike;\n /** T-crosstab: serializes `mirrorFromStore()` — a second call arriving while one is already\n * in-flight (a rapid burst of `enqueued` broadcasts) sets this bit instead of racing a second\n * `loadAll()`; the in-flight run loops once more on completion so the caller's freshest read is\n * never dropped. */\n private mirrorInFlight = false;\n private mirrorRerun = false;\n\n constructor(\n transport: ClientTransport,\n opts: {\n gateTimeoutMs?: number;\n outbox?: OutboxStorage;\n outboxMaxQueueSize?: number;\n onClientReset?: (info: ClientResetInfo) => void;\n /** How a coded (terminal, server-recorded) mutation failure is handled during the drain\n * (verdict §(c) R5) — `\"skip\"` (default: skip-and-record + continue) or `\"pause\"` (halt). */\n poisonPolicy?: PoisonPolicy;\n /** The Web Locks manager for the drain leader — `undefined` probes `navigator.locks`, `null`\n * forces single-tab, an object is used directly (tests inject a fake). */\n outboxLocks?: OutboxLockManager | null;\n /** Distinguishes the drain's lock name per deployment (`helipod:outbox:<origin>:<deployment>`);\n * defaults to `\"default\"`. */\n outboxDeployment?: string;\n /** The drain's interval-nudge period (verdict §(d): never `navigator.onLine`). */\n outboxDrainIntervalMs?: number;\n /** The drain's `MutationBatch` chunk size (default 50). */\n outboxChunkSize?: number;\n /** Injectable backoff for the drain's codeless-retry path (tests drive it deterministically). */\n outboxBackoffMs?: (attempts: number) => number;\n /** Fired once when `poisonPolicy: \"pause\"` halts the drain (surfacing). */\n onOutboxPause?: (info: { requestId: string; udfPath: string; code: string }) => void;\n /** T5: the durable-outbox registry — consulted ONLY when a durable entry is hydrated after a\n * reload (never for a live call). Plain string-keyed here; a generated `UdfPathOf<Api>`\n * (`@helipod/codegen`) narrows an app's own object literal at the call site. */\n optimisticUpdates?: Partial<Record<string, OptimisticUpdateFn>>;\n /** T5 (R9): fired for a terminal durable failure with no live promise awaiter this session. */\n onMutationFailed?: (info: MutationFailedInfo) => void;\n /** T5 (R9): the cross-tab nudge for `usePendingMutations()` — `undefined` probes the ambient\n * `BroadcastChannel`, `null` disables it (single-tab observability only), an object is used\n * directly (tests inject a fake). */\n outboxBroadcast?: OutboxBroadcastLike | null;\n } = {},\n ) {\n this.transport = transport;\n // DLR Stage 2a: a by-id `QueryDiff` whose checksum diverges triggers `resync()`. A full resync\n // (re-subscribe everything) is an acceptable scoped-enough recovery for 2a — since by-id diffs\n // are trivially correct this path should never fire; a per-query resync is a 2b refinement.\n this.reconciler = new Reconciler(this.store, { gateTimeoutMs: opts.gateTimeoutMs, onDrift: () => this.resync() });\n this.outbox = opts.outbox;\n this.onClientResetCallback = opts.onClientReset;\n this.outboxMaxQueueSize = opts.outboxMaxQueueSize ?? DEFAULT_OUTBOX_MAX_QUEUE_SIZE;\n this.optimisticUpdates = opts.optimisticUpdates ?? {};\n this.onMutationFailedCallback = opts.onMutationFailed;\n if (opts.outbox) {\n this.outboxClientId = defaultMintClientId();\n this.outboxIdentity = mintIdentity(opts.outbox, { mintClientId: () => this.outboxClientId! }).then((id) => {\n this.outboxNextSeq = Math.max(this.outboxNextSeq, id.nextSeq);\n return id;\n });\n // `this.outboxIdentity` is returned verbatim to `getOutboxIdentity()` callers (who may attach\n // their own handler, or never call it at all) — attaching a SEPARATE catch here (on the same\n // promise; a harmless fan-out, not a value consumption) guarantees it is never left unhandled\n // regardless of whether an external caller ever awaits it. A `mintIdentity` failure (the\n // durable meta-row write behind a fail-stopped outbox) has no mutation record to attach to —\n // floors to the same observability path as any other meta-only durable write.\n this.outboxIdentity.catch((err: unknown) => this.handleOutboxWriteError(\"mintIdentity\", this.outboxClientId, undefined, undefined, err));\n this.outboxDrain = new OutboxDrain(this.makeDrainHost(), {\n lockName: `helipod:outbox:${this.originTag()}:${opts.outboxDeployment ?? \"default\"}`,\n locks: opts.outboxLocks,\n poisonPolicy: opts.poisonPolicy,\n chunkSize: opts.outboxChunkSize,\n intervalMs: opts.outboxDrainIntervalMs,\n backoffMs: opts.outboxBackoffMs,\n onPause: opts.onOutboxPause,\n });\n this.outboxBroadcast =\n opts.outboxBroadcast === null ? undefined : (opts.outboxBroadcast ?? probeBroadcastChannel(`helipod:outbox:${this.originTag()}:${opts.outboxDeployment ?? \"default\"}:pending`));\n if (this.outboxBroadcast) {\n this.outboxBroadcast.onmessage = (event) => {\n // Keep the unconditional accessor fan-out FIRST — every existing/legacy listener nudges\n // on ANY message, exactly as before this typed dispatch existed.\n for (const l of this.outboxChangeListeners) l();\n // The typed path is IN ADDITION, and must never break the nudge contract above: a\n // malformed/foreign payload floors to console, never throws out of `onmessage`.\n try {\n this.handleOutboxBroadcastMessage(event.data);\n } catch (err) {\n if (isDevMode()) console.error(\"[helipod] outbox: error handling a cross-tab broadcast message\", err);\n }\n };\n }\n // R9 \"resume\" refire: scan the durable store for ALREADY-failed entries left behind (a prior\n // session that ended before the app ever surfaced them) — trivially \"no awaiter\" (nothing has\n // called `mutation()` yet this session), so every one refires unconditionally.\n void this.refireDurableFailures();\n // I-1 (browser-ux whole-branch review): a tab constructed AFTER another tab already durably\n // enqueued has NO path to those existing entries otherwise — `mirrorFromStore` is normally only\n // driven by an incoming `enqueued` broadcast (see `handleOutboxBroadcastMessage` above), and\n // `hydrateOnce` only runs inside `OutboxDrain#becomeLeader` (a live first tab holds the Web\n // Locks lock for its whole lifetime, so a second tab may never become leader while it's alive).\n // Run it once, unconditionally, at construction — idempotent by design, the same composition\n // `mirrorFromStore`'s doc above already relies on: `addHydratedEntry`'s (clientId, seq) dedup,\n // the active-status filter, and the `completed`-status skip all make a redundant/early call a\n // no-op rather than a hazard. Tolerates running before `ConnectAck`/baseline — like the\n // broadcast-triggered call, it only touches the reconciler log, never the wire. Same\n // fire-and-forget + observability-floor catch as the broadcast path.\n void this.mirrorFromStore().catch((err: unknown) => this.handleOutboxWriteError(\"mirrorFromStore\", undefined, undefined, undefined, err));\n }\n this.disposeTransport = transport.onMessage((msg) => this.onServerMessage(msg));\n this.disposeClose = transport.onClose(() => this.onTransportClosed());\n this.disposeReopen = transport.onReopen?.(() => this.onTransportReopened());\n // Start the drain AFTER the message/close hooks are wired (it may hydrate + handshake at once).\n this.outboxDrain?.start();\n }\n\n /** The origin component of the drain's Web Locks name — `location.origin` in a browser, a stable\n * fallback elsewhere (Node/SSR share one origin; correctness is the records, not the lock). */\n private originTag(): string {\n const loc = (globalThis as { location?: { origin?: string } }).location;\n return loc?.origin ?? \"app\";\n }\n\n /** @internal This tab-session's durable outbox identity, or `undefined` when no `outbox` was\n * configured. Exposed for direct testing of the identity-mint behavior; `mutation()` itself\n * reads the synchronous `outboxClientId`/`outboxNextSeq` counterparts, never this promise\n * (see the field doc above `outboxClientId`). */\n getOutboxIdentity(): Promise<{ clientId: string; nextSeq: number }> | undefined {\n return this.outboxIdentity;\n }\n\n /** @internal T3's Connect handshake calls this once a `ConnectAck` proves server-side receipt\n * dedup exists for this session — see verdict §(d) \"S4 swap, feature-detected\". Before that (no\n * outbox configured, a fresh/pre-handshake session, or an old server that never sends\n * `ConnectAck`), `close()` behaves exactly as it always has: today's fail-fast, byte-for-byte. */\n setOutboxArmed(armed: boolean): void {\n this.outboxArmed = armed;\n }\n\n /**\n * Subscribe to a reactive query. `onUpdate` fires with the latest **composed** value (immediately\n * if cached). `onError` (optional) fires if the query's handler throws server-side — otherwise a\n * failing query is logged and leaves the last known value in place.\n *\n * Two overloads bridge T3/T5's type reconciliation (`api.ts`'s `AnyFunctionRef` doc): a\n * codegen-generated ref types `args`/`onUpdate`'s value from its declared `__args`/`__returns`;\n * this package's own untyped `{ __path }` ref or a raw string path fall back to the pre-existing\n * `Record<string, Value>`/`Value` shape (an explicit `T` still overrides, as before).\n */\n subscribe<Q extends AnyFunctionReference<any, any>>(\n ref: Q,\n args: FunctionArgs<Q>,\n onUpdate: (value: FunctionReturnType<Q>) => void,\n onError?: QueryErrorListener,\n ): () => void;\n subscribe(\n ref: FunctionReference | string,\n args: Record<string, Value> | undefined,\n onUpdate: QueryListener,\n onError?: QueryErrorListener,\n ): () => void;\n subscribe(\n ref: AnyFunctionRef,\n args: Record<string, Value> = {},\n onUpdate: QueryListener,\n onError?: QueryErrorListener,\n ): () => void {\n const path = getFunctionPath(ref);\n const argsJson = convexToJson(args as Value);\n const hash = queryHash(path, argsJson);\n\n let sub = this.store.byHash.get(hash);\n if (!sub) {\n const queryId = this.nextQueryId++;\n sub = this.store.create(queryId, path, argsJson, hash);\n this.maybeSendDiffCapability(); // must precede the first ModifyQuerySet (see the method doc)\n this.transport.send({ type: \"ModifyQuerySet\", add: [{ queryId, udfPath: path, args: argsJson }], remove: [] });\n }\n const listener: Listener = { onUpdate, onError };\n sub.listeners.add(listener);\n // Cached first delivery serves the COMPOSED view (server base + any optimistic layer).\n if (sub.composedValue !== undefined) onUpdate(sub.composedValue);\n\n return () => {\n const s = this.store.byHash.get(hash);\n if (!s) return;\n s.listeners.delete(listener);\n if (s.listeners.size === 0) {\n this.transport.send({ type: \"ModifyQuerySet\", add: [], remove: [s.queryId] });\n this.store.remove(hash);\n }\n };\n }\n\n /** One-shot read: resolves with the first **composed** value (D15) — a one-shot read can return\n * speculative data — or rejects if the query throws; then unsubscribes. */\n query<Q extends AnyFunctionReference<any, any>>(ref: Q, args?: FunctionArgs<Q>): Promise<FunctionReturnType<Q>>;\n query(ref: FunctionReference | string, args?: Record<string, Value>): Promise<Value>;\n query(ref: AnyFunctionRef, args: Record<string, Value> = {}): Promise<Value> {\n return new Promise((resolve, reject) => {\n // Overload dispatch needs a concrete match; `ref`/`args` are already the resolved runtime\n // shape here (the outer overloads did the caller-facing type-checking).\n const unsubscribe = this.subscribe(\n ref as FunctionReference | string,\n args,\n (value) => {\n resolve(value);\n // Defer the reference: a cached first delivery fires this synchronously inside\n // `subscribe()`, before `unsubscribe` is assigned (TDZ) — an arrow reads it later.\n queueMicrotask(() => unsubscribe());\n },\n (error) => {\n reject(new Error(error));\n queueMicrotask(() => unsubscribe());\n },\n );\n });\n }\n\n /**\n * Run a mutation; resolves with its return value at `MutationResponse` (D3), or rejects with its\n * error. With `{ optimisticUpdate }`, the closure runs synchronously against a writeable composed\n * view before the mutation is sent (instant UI); if it throws, `mutation` throws **synchronously**\n * and nothing is sent. The optimistic layer is dropped on observed inclusion, never on the ack.\n *\n * The typed overload's `optimisticUpdate` is typed against the public `OptimisticLocalStore`\n * (`Q`'s declared `__args`) — sound because `Reconciler.invokeUpdate` (`reconcile.ts`) ALWAYS\n * enriches the raw internal view into an `OptimisticLocalStore` before calling `entry.update`,\n * regardless of entry point; the cast to the internal `OptimisticUpdate` shape below is safe for\n * exactly that reason.\n *\n * `{ transient: true }` (internal escape hatch, e.g. `auth-client.ts`'s refresh call) skips the\n * durable outbox entirely for THIS call — wire-send-only, normal promise semantics — even when an\n * `outbox` is configured. It exists for mutations that must never be durably replayed after a\n * reload (an auth-refresh call: replaying a stale refresh token blind, with no live awaiter for\n * the mint result, trips reuse-detection and force-signs-out an honest user). A non-outbox client\n * is unaffected either way (`useOutbox` is already false whenever `this.outbox` is unset).\n */\n mutation<Q extends AnyFunctionReference<any, any>>(\n ref: Q,\n args?: FunctionArgs<Q>,\n opts?: { optimisticUpdate?: (store: OptimisticLocalStore, args: FunctionArgs<Q>) => void; transient?: boolean },\n ): Promise<FunctionReturnType<Q>>;\n mutation(\n ref: FunctionReference | string,\n args?: Record<string, Value>,\n opts?: { optimisticUpdate?: OptimisticUpdate; transient?: boolean },\n ): Promise<Value>;\n mutation(\n ref: AnyFunctionRef,\n args: Record<string, Value> = {},\n opts: { optimisticUpdate?: OptimisticUpdate; transient?: boolean } = {},\n ): Promise<Value> {\n const path = getFunctionPath(ref);\n // Encodability triage (verdict §(d) \"Drain\", applied at enqueue time too): an unencodable\n // `args` throws HERE, synchronously — before any requestId/seq/entry exists — so a bad call\n // never occupies a durable outbox slot (a seq, once minted, is never reused).\n const argsJson = convexToJson(args as Value);\n // `transient: true` opts THIS call out of the durable outbox entirely — see the method doc.\n const useOutbox = this.outbox !== undefined && opts.transient !== true;\n\n if (useOutbox && this.outboxQueueDepth() >= this.outboxMaxQueueSize) {\n // Overflow: reject the NEW enqueue, coded (verdict §(d) \"Enqueue\") — nothing was created,\n // no seq was consumed, no optimistic layer was touched.\n return Promise.reject(new OutboxOverflowError());\n }\n\n const requestId = String(this.nextRequestId++);\n const entry: PendingMutation = {\n requestId,\n udfPath: path,\n args: argsJson,\n update: opts.optimisticUpdate,\n seed: { entropy: makeEntropy(), now: Date.now() },\n touched: new Set(),\n status: { type: \"unsent\" },\n };\n if (useOutbox) {\n // Stamped synchronously — the durable-outbox identity (verdict §(d) \"Identity\"/\"Enqueue\").\n // Carried on the wire whenever an outbox is configured, not only once the S4 swap is armed\n // (see `mutationMessage` below) — \"for park-safety... exactly as today otherwise\".\n entry.clientId = this.outboxClientId;\n entry.seq = this.outboxNextSeq++;\n entry.order = this.nextOutboxOrder();\n entry.identityFingerprint = this.outboxFingerprint;\n entry.enqueuedAt = Date.now();\n }\n // \"While the queue is non-empty, new mutations enqueue behind it; when empty, live sends go\n // direct\" (verdict §(d) \"Enqueue\") — computed BEFORE `initiate()` adds this entry to the log,\n // so it only ever sees OTHER entries' backlog.\n const queueBusy = useOutbox && this.hasOutboxBacklog();\n\n // Event 1 — apply at initiation. A throwing updater rethrows here, synchronously, before any\n // promise is created or anything is sent.\n this.reconciler.initiate(entry);\n\n return new Promise<Value>((resolve, reject) => {\n this.pendingMutationCallbacks.set(requestId, { resolve, reject });\n if (this.closed || queueBusy) {\n // Offline, or FIFO behind an already-queued backlog: retain as `unsent` for a flush. The\n // promise stays pending.\n entry.status = { type: \"unsent\" };\n } else {\n entry.status = { type: \"inflight\" };\n this.transport.send(this.mutationMessage(entry));\n }\n if (useOutbox) {\n // Write-behind: durably append WITHOUT awaiting — \"the send never waits for it\" (verdict\n // §(d) \"Enqueue\"). `entry.durable` flips once this resolves; `delivery-policy.ts`'s\n // `closeDisposition` reads it at close (\"park eligibility requires durability\"). A rejected\n // append (e.g. a fail-stopped `fsOutbox` after a disk error) must NOT become an unhandled\n // rejection — see `handleOutboxWriteError`.\n void this.outbox!\n .append(this.toOutboxEntry(entry))\n .then(() => {\n entry.durable = true;\n // Now durable → drain-eligible: wake the drain (wake on enqueue, verdict §(d)).\n this.outboxDrain?.nudge();\n this.notifyOutboxChange(); // T5 (R9): usePendingMutations()'s re-read trigger.\n })\n .catch((err: unknown) => this.handleOutboxWriteError(\"append\", entry.clientId, entry.seq, entry.udfPath, err));\n }\n });\n }\n\n /** The wire `Mutation` message for `entry` — carries `(clientId, seq)` whenever an outbox is\n * configured (park-safety, verdict §(d)), and OMITS the fields entirely (not merely `undefined`)\n * when it isn't, so a client with no `outbox` sends exactly today's shape, byte-for-byte. */\n private mutationMessage(entry: PendingMutation): ClientMessage {\n return {\n type: \"Mutation\",\n requestId: entry.requestId,\n udfPath: entry.udfPath,\n args: entry.args,\n ...(entry.clientId !== undefined ? { clientId: entry.clientId, seq: entry.seq! } : {}),\n };\n }\n\n /** The persisted `OutboxStorage` twin of `entry` — only ever called when `this.outbox` (and thus\n * `entry.clientId`/`seq`/`order`/`enqueuedAt`) is set. */\n private toOutboxEntry(entry: PendingMutation): OutboxEntry {\n return {\n clientId: entry.clientId!,\n seq: entry.seq!,\n requestId: entry.requestId,\n udfPath: entry.udfPath,\n args: entry.args,\n seed: entry.seed,\n order: entry.order!,\n status: entry.status.type === \"unsent\" ? \"unsent\" : \"inflight\",\n identityFingerprint: entry.identityFingerprint,\n outboxVersion: OUTBOX_VERSION,\n enqueuedAt: entry.enqueuedAt!,\n };\n }\n\n /** True while any OTHER entry is `unsent` (queued for a flush) or `parked` (queued for a future\n * drain) — the FIFO-preserving gate a new mutation enqueues behind (verdict §(d) \"Enqueue\").\n * A drain chunk in flight also counts (its units are `inflight`, which the scan alone would miss\n * when the chunk consumed the whole backlog) — otherwise a mutation issued mid-chunk would\n * direct-send AHEAD of a still-unsettled older unit, breaking the FIFO promise if the chunk\n * transient-stops and re-sends. Plain live in-flight direct-sends deliberately do NOT count:\n * \"when empty, live sends go direct and concurrent\" (T2's scoping, unchanged). */\n private hasOutboxBacklog(): boolean {\n if (this.outboxDrain?.hasActiveChunk) return true;\n for (const e of this.reconciler.entries()) {\n if (e.status.type === \"unsent\" || e.status.type === \"parked\") return true;\n }\n return false;\n }\n\n /** Count of outbox-tracked entries not yet fully settled (excludes `completed` — already acked,\n * held only for the ts-gate) — the overflow cap's occupancy (verdict §(d) \"Enqueue\": \"bounded,\n * default 1000\"). */\n private outboxQueueDepth(): number {\n let n = 0;\n for (const e of this.reconciler.entries()) {\n if (e.clientId !== undefined && e.status.type !== \"completed\") n++;\n }\n return n;\n }\n\n /** Monotonic `OutboxEntry.order` allocator — see the `outboxOrderCounter` field doc. */\n private nextOutboxOrder(): number {\n const now = Date.now();\n this.outboxOrderCounter = this.outboxOrderCounter >= now ? this.outboxOrderCounter + 1 : now;\n return this.outboxOrderCounter;\n }\n\n /** Run an action; resolves with its return value (or rejects with its error). Not reactive — an action has no subscription. */\n action<Q extends AnyFunctionReference<any, any>>(ref: Q, args?: FunctionArgs<Q>): Promise<FunctionReturnType<Q>>;\n action(ref: FunctionReference | string, args?: Record<string, Value>): Promise<Value>;\n action(ref: AnyFunctionRef, args: Record<string, Value> = {}): Promise<Value> {\n const requestId = String(this.nextRequestId++);\n return new Promise<Value>((resolve, reject) => {\n this.pendingActions.set(requestId, { resolve, reject });\n this.transport.send({ type: \"Action\", requestId, udfPath: getFunctionPath(ref), args: convexToJson(args as Value) });\n });\n }\n\n /** Set (or clear) the session identity for this connection; the server re-runs subscriptions under it. */\n setAuth(token: string | null): void {\n this.hasSetAuth = true;\n this.lastAuthToken = token;\n this.transport.send({ type: \"SetAuth\", token });\n if (this.outbox && !this.sessionFingerprintActive) {\n // `identityFingerprint` cache (verdict §(d) hazard 9 / spec §(k)7): SHA-256 of the token, or\n // \"anon\" for none/empty — computed here (async, SubtleCrypto) so `mutation()` can stamp the\n // cached value synchronously. Guarded against a stale resolution racing a LATER setAuth call.\n // Suppressed when a managed auth client owns the fingerprint (derived from sessionId instead —\n // see `setSessionFingerprint`), so token rotation never re-fingerprints queued mutations.\n if (!token) {\n this.outboxFingerprint = \"anon\";\n } else {\n const forToken = token;\n void sha256Hex(forToken).then((hex) => {\n if (this.lastAuthToken === forToken) this.outboxFingerprint = hex;\n });\n }\n }\n }\n\n /** Managed-session fingerprinting (spec decision 9): a `createAuthClient` calls this so the durable\n * outbox's `identityFingerprint` derives from the STABLE `sessionId`, not the rotating access\n * token — otherwise a rotation mid-drain would orphan queued offline mutations under a new\n * fingerprint. Pass `null` to hand the fingerprint back to the raw `setAuth` token-hash path\n * (e.g. on sign-out). No-op when no outbox is configured. */\n setSessionFingerprint(sessionId: string | null): void {\n this.sessionFingerprintActive = sessionId !== null;\n if (sessionId === null) {\n this.sessionFingerprintKey = null;\n // Fall back to the current token's fingerprint (or \"anon\"): re-run setAuth's cache path.\n if (this.outbox) {\n const token = this.lastAuthToken;\n if (!token) this.outboxFingerprint = \"anon\";\n else void sha256Hex(token).then((hex) => { if (this.lastAuthToken === token && !this.sessionFingerprintActive) this.outboxFingerprint = hex; });\n }\n return;\n }\n if (!this.outbox) return;\n const key = sessionFingerprintKey(sessionId);\n this.sessionFingerprintKey = key;\n void sha256Hex(key).then((hex) => { if (this.sessionFingerprintKey === key) this.outboxFingerprint = hex; });\n }\n\n /** Publish an ephemeral event (presence/typing) — bypasses the engine. */\n publishEphemeral(topic: string, event: Value): void {\n this.transport.send({ type: \"EphemeralPublish\", topic, event: convexToJson(event) });\n }\n\n /** Listen for ephemeral broadcasts (presence/typing) from other clients. */\n onBroadcast(listener: (topic: string, event: Value) => void): () => void {\n this.broadcastListeners.add(listener);\n return () => this.broadcastListeners.delete(listener);\n }\n\n close(): void {\n this.outboxDrain?.stop();\n this.outboxBroadcast?.close();\n this.disposeTransport();\n this.disposeClose();\n this.disposeReopen?.();\n this.transport.close();\n this.onTransportClosed();\n }\n\n /** @internal test/debug only — the observed-inclusion frontier (resets to 0 at close). */\n get __maxObservedTs(): number {\n return this.reconciler.maxObservedTs;\n }\n\n /** @internal test/debug only — the live pending-mutation log, in requestId order. */\n get __pending(): readonly PendingMutation[] {\n return this.reconciler.entries();\n }\n\n /** @internal test/debug only — the current `identityFingerprint` cache (see `setAuth`); polling\n * this (rather than calling `mutation()` repeatedly, which consumes seqs) is how a test waits\n * out the async SHA-256 digest without depending on a fixed tick count. */\n get __outboxFingerprint(): string {\n return this.outboxFingerprint;\n }\n\n private onServerMessage(msg: ServerMessage): void {\n switch (msg.type) {\n case \"Transition\": {\n // While resyncing, adopt the next transition as the new baseline (its modifications are the\n // full re-subscribed results) regardless of its start version. Layers are NOT blanket-dropped\n // (same session, ts still monotone) — the gate still drops any covered `completed` layer.\n if (this.resyncing) {\n this.reconciler.ingestTransition(msg.modifications, msg.endVersion.ts);\n this.version = msg.endVersion;\n this.resyncing = false;\n // T3: this adopted Transition IS the post-Connect baseline the drop rule + T4's drain\n // await — fire the deferred drops and release `whenBaselineAdopted()` waiters.\n if (this.outboxAwaitingBaseline) this.markBaselineAdopted();\n return;\n }\n // Version-bracket guard: a non-contiguous start means a frame was dropped. Do NOT deliver the\n // (post-gap) values — resync from scratch instead, preserving correctness.\n if (!versionsEqual(msg.startVersion, this.version)) {\n this.resync();\n return;\n }\n this.reconciler.ingestTransition(msg.modifications, msg.endVersion.ts);\n this.version = msg.endVersion;\n // First-connect baseline adoption (Task 4): unlike a reopen (whose baseline adopts in the\n // `resyncing` branch above), a fresh first connect's baseline is just its first contiguous\n // Transition — so release the drain's `whenBaselineAdopted()` gate + fire deferred drops\n // here. Only ever true while a first-connect handshake is in flight; a no-op otherwise.\n if (this.outboxAwaitingBaseline) this.markBaselineAdopted();\n return;\n }\n case \"MutationResponse\": {\n // A response for a unit the drain (Task 4) is awaiting routes to the drain's state machine\n // (per-unit resolution, transient-stop, backoff) — NOT the direct-send path below.\n if (this.outboxDrain?.handles(msg.requestId)) {\n this.outboxDrain.onResponse(msg);\n return;\n }\n // Capture the outbox identity BEFORE the settling event removes the entry from the log —\n // dequeue-on-success/settle needs its recorded `(clientId, seq)` (T3, verdict §(f) AC1.2).\n const entry = this.reconciler.getEntry(msg.requestId);\n const pending = this.pendingMutationCallbacks.get(msg.requestId);\n const hadAwaiter = pending !== undefined;\n this.pendingMutationCallbacks.delete(msg.requestId);\n if (msg.success) {\n // `value` is optional on the wire (a Receipted Outbox replay-ack with `valueMissing`\n // omits it); coalesce to null. Full replay handling is Plan B — this keeps today's shape.\n pending?.resolve(jsonToConvex(msg.value ?? null)); // D3: resolve now\n this.reconciler.onMutationSuccess(msg.requestId, msg.ts);\n // A successful response means this durable entry's fate is known — dequeue it so it never\n // resends. (No-op when no outbox is configured.)\n this.dequeueOutboxEntry(entry);\n } else {\n pending?.reject(this.mutationError(msg.error, msg.code));\n this.reconciler.onMutationFailure(msg.requestId);\n // R9: MARK failed (persist for the tray) instead of dequeuing — a retryable follow-up is a\n // FRESH seq (verdict §(d) `retry()`), never a resurrection of this record. `hadAwaiter` is\n // essentially always true on this direct-send path (the caller's own promise already\n // learned of the failure via `pending.reject` above) — the refire/dev-loud path exists for\n // the rare case a caller's promise had no live handler by the time this settles.\n if (entry?.clientId !== undefined && entry.seq !== undefined) {\n this.outboxMarkFailed(entry.clientId, entry.seq, msg.error, msg.code);\n }\n if (!hadAwaiter) this.notifyMutationFailed(entry?.clientId, entry?.seq, entry?.udfPath ?? \"unknown\", { message: msg.error, code: msg.code });\n }\n return;\n }\n case \"ConnectAck\":\n this.handleConnectAck(msg);\n return;\n case \"ActionResponse\": {\n const pending = this.pendingActions.get(msg.requestId);\n if (pending) {\n this.pendingActions.delete(msg.requestId);\n if (msg.success) pending.resolve(jsonToConvex(msg.value));\n else pending.reject(new Error(msg.error));\n }\n return;\n }\n case \"Broadcast\": {\n const event = jsonToConvex(msg.event);\n for (const listener of this.broadcastListeners) listener(msg.topic, event);\n return;\n }\n default:\n return;\n }\n }\n\n /** A frame was missed: reset and re-subscribe all live queries; adopt the server's next state. */\n private resync(): void {\n this.resyncing = true;\n this.version = { ...INITIAL_VERSION };\n const subs = [...this.store.byId.values()];\n if (subs.length === 0) {\n this.resyncing = false;\n return;\n }\n this.transport.send({\n type: \"ModifyQuerySet\",\n // `resultHash` (subscription resume): echoed ONLY for a sub that was actually delivered a\n // base value and still has its fingerprint on hand — a failed sub (`serverValue` never set),\n // a never-answered sub, or one whose last `QueryUpdated` had no `hash` (old server) echoes\n // nothing, falling through to today's full-send byte-for-byte. Note this condition doesn't\n // check \"currently failed\" — a sub that failed AFTER a prior success still has a retained\n // `serverValue`/`lastHash` from that success and still echoes it here. That's sound: the\n // server only replies `QueryUnchanged` if the FRESH re-run (against live, current state)\n // hashes equal to the echoed base — i.e. the query has recovered back to that exact value —\n // in which case `QueryUnchanged` renders exactly what a full `QueryUpdated` send would have\n // delivered. Any other outcome (still failing, or recovered to a different value) arrives as\n // a normal `QueryFailed`/`QueryUpdated` modification, not `QueryUnchanged`.\n add: subs.map((s) => ({\n queryId: s.queryId,\n udfPath: s.path,\n args: s.args,\n ...(s.answered && s.serverValue !== undefined && s.lastHash !== undefined ? { resultHash: s.lastHash } : {}),\n // DLR Stage 3: echo the client's observed-inclusion frontier so the server can skip the\n // re-run entirely when nothing touched this query's read-set since. Fresh `subscribe()`\n // never sets this — only a resume resubscribe has a meaningful watermark to echo. `Math.max`\n // with `resumeSinceTs` (the pre-close snapshot) covers both resume paths: a real transport\n // close resets `reconciler.maxObservedTs` to 0 (see `resumeSinceTs`'s own comment), so the\n // snapshot wins there; a same-session drift resync never closed, so `maxObservedTs` is still\n // live and wins instead.\n sinceTs: Math.max(this.resumeSinceTs, this.reconciler.maxObservedTs),\n })),\n remove: [],\n });\n }\n\n private onTransportClosed(): void {\n this.closed = true;\n this.outboxConnectSent = false; // a fresh connection needs a fresh Connect handshake.\n this.diffCapabilitySent = false; // ...and must re-advertise the diff capability (new server session).\n // BEFORE the S4 close rules: revert the drain's in-flight chunk (its unresponded units will\n // never get a response on the new server session) so `closeSession` below sees them as plain\n // `unsent` retained entries, and the drain's one-unacked invariant (`active === null`) can't\n // wedge the rest of this tab session (see `OutboxDrain#onTransportClosed`). Leadership and the\n // interval nudge survive a reconnect-class close; only `close()` stops the drain.\n this.outboxDrain?.onTransportClosed();\n // DLR Stage 3: snapshot the observed-ts frontier BEFORE `closeSession()` resets it to 0, so the\n // next `resync()` can still echo a real watermark on reconnect (see `resumeSinceTs`'s own comment).\n this.resumeSinceTs = this.reconciler.maxObservedTs;\n // S4 close rules: unsent retained; inflight/completed layers drop; frontier resets. Task 2's\n // park swap (armed + durable) is folded in here via `this.outboxArmed` — `rejectedInflight`\n // already excludes anything that parked instead; a parked entry's promise stays pending in\n // `pendingMutationCallbacks`, untouched, ready for a future drain (T4) to settle.\n const { rejectedInflight, parked } = this.reconciler.closeSession(this.outboxArmed);\n for (const rid of rejectedInflight) {\n const pending = this.pendingMutationCallbacks.get(rid);\n this.pendingMutationCallbacks.delete(rid);\n pending?.reject(new MutationUndeliveredError());\n }\n // T5 (R9): persist the park transition too — otherwise a parked entry's durable `status` stays\n // frozen at whatever it was appended/last flushed as, misleading `pendingMutations()`'s tray.\n for (const rid of parked) {\n const entry = this.reconciler.getEntry(rid);\n if (entry?.clientId !== undefined && entry.seq !== undefined) this.outboxUpdateStatus(entry.clientId, entry.seq, \"parked\");\n }\n // Actions have no layer — their outcome is simply unknown on a dropped socket.\n for (const [, pending] of this.pendingActions) pending.reject(new Error(\"connection closed\"));\n this.pendingActions.clear();\n }\n\n /**\n * T6: the transport reconnected (a fresh session — the server has no state for it). Order is\n * load-bearing (verdict §(c) event 6): `SetAuth` replay first (the server re-runs subscriptions\n * under the right identity), THEN resubscribe every live query (the existing resync path — it\n * adopts the reply as a fresh baseline regardless of its start version), THEN flush every\n * `unsent` mutation FIFO — each transitions `unsent` -> `inflight` reusing its ORIGINAL\n * `requestId` (never re-minted), so the promise created at `mutation()` call time stays the one\n * that resolves when the new session's `MutationResponse` arrives.\n */\n private onTransportReopened(): void {\n this.closed = false;\n if (this.hasSetAuth) this.transport.send({ type: \"SetAuth\", token: this.lastAuthToken });\n this.maybeSendDiffCapability(); // re-advertise before resync's resubscribes (fresh server session)\n this.resync();\n if (this.outbox) {\n // T3: for a durable-outbox client the naive unsent flush is REPLACED by the `Connect` resume\n // handshake. `Connect` re-proves capability (its `ConnectAck` arms the S4 park swap) and lets\n // the server classify every held `(clientId, seq)`; the actual FIFO resend of `unknown`/parked\n // entries is T4's drain, which awaits `whenBaselineAdopted()` before sending. Held entries are\n // NOT flushed directly here — that would bypass the dedup handshake and re-order the FIFO.\n this.initiateHandshake(this.resyncing);\n this.outboxDrain?.nudge(); // wake on reconnect-after-baseline.\n return;\n }\n for (const entry of this.reconciler.unsentInOrder()) {\n entry.status = { type: \"inflight\" };\n this.transport.send(this.mutationMessage(entry));\n }\n }\n\n /** Send the `Connect` resume handshake once per connection (idempotent via `outboxConnectSent`),\n * arming the baseline await. Shared by the reopen path and the drain's first-connect path (Task 4\n * / T3 handoff #1: a fresh-client-first-connect after reload has no reopen event, so the drain\n * triggers the same handshake on becoming leader with a durable backlog). */\n private initiateHandshake(expectTransition: boolean): void {\n if (this.outboxConnectSent || this.closed || !this.outbox) return;\n this.outboxConnectSent = true;\n this.beginBaselineAwait(expectTransition);\n this.sendConnect();\n }\n\n /** True iff at least one live subscription has NOT yet received its first server reply\n * (`!sub.answered` — set by `ingestTransition` on EITHER outcome, `QueryUpdated` or `QueryFailed`).\n * The drain's first-connect `ensureInitialHandshake` gate (T4 bug fix, later widened to cover the\n * failed-query shape too — re-review FIX 2): a subscription created (and answered) BEFORE the\n * drain's async hydrate finishes has already consumed its one-shot Transition by the time the\n * handshake arms — waiting for ANOTHER one that will never come on a quiet deployment would starve\n * `whenBaselineAdopted()` (and so the drain) forever. Only a subscription still awaiting its first\n * reply guarantees a future Transition is actually coming — that's the one worth waiting for.\n * (Deliberately NOT `sub.serverValue === undefined`: a `QueryFailed` reply never sets `serverValue`\n * — there's no base to render — so that check misclassified an already-answered failed query as\n * still-undelivered and reproduced the same deadlock via the failed-query path.) */\n private hasUndeliveredSubscription(): boolean {\n for (const sub of this.store.byId.values()) {\n if (!sub.answered) return true;\n }\n return false;\n }\n\n /* ---------------------------------------------------------------------------------------------\n * T3 — the Connect resume handshake, verdict settlement, the baseline-gated drop rule, and reset.\n * ------------------------------------------------------------------------------------------- */\n\n /** @internal T4's drain gate. Resolves once the first post-`Connect` baseline Transition has been\n * adopted through S3 (verdict §(d) / spec decision 5). Resolves immediately when no handshake is\n * in flight (nothing to await) or when a reopen had no live subscriptions to re-baseline. */\n whenBaselineAdopted(): Promise<void> {\n if (!this.outboxAwaitingBaseline) return Promise.resolve();\n return new Promise<void>((resolve) => this.outboxBaselineResolvers.push(resolve));\n }\n\n /** @internal test/debug — the last `ConnectAck.deploymentId` (the same-timeline proof stamp), or\n * `undefined` before any handshake completed. */\n getOutboxDeploymentId(): string | undefined {\n return this.outboxDeploymentId;\n }\n\n /** @internal test/debug — whether the S4 park swap is armed (a `ConnectAck` has proven dedup). */\n get __outboxArmed(): boolean {\n return this.outboxArmed;\n }\n\n /** Begin awaiting the post-`Connect` baseline. `expectTransition` is true iff a baseline Transition\n * is actually coming — for a reopen, `this.resyncing` (set iff `resync()` re-subscribed live\n * queries); for a first connect, whether any live subscription is still awaiting its first\n * delivery (`hasUndeliveredSubscription()` — NOT merely whether one exists: a subscription\n * created and already answered before the handshake armed has nothing left to wait for). With\n * nothing pending, there is no baseline frame coming and adoption is immediate. */\n private beginBaselineAwait(expectTransition: boolean): void {\n this.outboxAwaitingBaseline = expectTransition;\n if (!expectTransition) this.markBaselineAdopted();\n }\n\n /** The baseline Transition adopted (or there was none to await): fire every deferred `applied`\n * layer drop (each flicker-free now — the baseline renders the effect), release the drain gate,\n * and wake the drain (reconnect-after-baseline). */\n private markBaselineAdopted(): void {\n this.outboxAwaitingBaseline = false;\n for (const rid of this.outboxPendingDrops.splice(0)) this.reconciler.onVerdictAfterBaseline(rid);\n const resolvers = this.outboxBaselineResolvers.splice(0);\n for (const resolve of resolvers) resolve();\n this.outboxDrain?.nudge();\n }\n\n /** Send the `Connect` resume handshake: this tab-session's clientId, the `held` durable entries\n * (every not-yet-settled `(clientId, seq)` in the log — the server classifies each into\n * `ConnectAck.results`), and `ackedThrough` (the contiguous settled-prefix per clientId, for\n * server-side retention pruning). Delegates the pure computation to `./connect-handshake` — the\n * SAME shared module the headless drain (`headless-drain.ts`) builds its own `Connect` from. */\n private sendConnect(): void {\n const held = outboxHeldFromLog(this.reconciler.entries());\n this.transport.send(buildConnectMessage(makeEntropy(), this.outboxClientId!, held));\n }\n\n /** DLR Stage 2a — advertise by-id diff support once per connection for a NON-outbox client (an\n * outbox client's resume `Connect` already carries the flag). A capability-only `Connect` (no\n * `clientId`/`held`/`ackedThrough`) is the reserved server no-op path — it records the capability\n * and sends no `ConnectAck` — so it never interferes with the outbox handshake or backpressure.\n * Sent BEFORE the first `ModifyQuerySet` (fresh connect) and before `resync()` (reopen) so the\n * server has the capability recorded when it decides a by-id sub's initial answer; the ordered\n * transport guarantees delivery order. Even if it somehow arrived late the diff path self-heals\n * (a pre-capability RERUN answer, then diffs resume), but sending it first keeps the common path\n * on the diff answer from the very first subscribe. */\n private maybeSendDiffCapability(): void {\n if (this.outbox || this.diffCapabilitySent || this.closed) return;\n this.diffCapabilitySent = true;\n this.transport.send({ type: \"Connect\", sessionId: makeEntropy(), supportsQueryDiff: true });\n }\n\n /** Process a `ConnectAck` (verdict §(e)): the capability proof arms the S4 park swap; the\n * deploymentId is surfaced + persisted; `known: false` triggers `onClientReset`; otherwise each\n * classified `held` seq is settled (`applied`/`failed`/`stale` terminal; `unknown` left for the\n * drain). */\n private handleConnectAck(msg: Extract<ServerMessage, { type: \"ConnectAck\" }>): void {\n // The ConnectAck itself is the capability proof — arm regardless of `known` (the server speaks\n // the dedup protocol either way; a reset still wants future closes to park under the fresh id).\n this.outboxArmed = true;\n this.outboxDeploymentId = msg.deploymentId;\n if (this.outbox && this.outboxClientId !== undefined) {\n // Stamp the timeline onto the current clientId's meta row (best-effort, fire-and-forget). No\n // mutation record to attach a failure to (this is a meta-only write) — the dev-loud\n // console.error floor in `handleOutboxWriteError` applies.\n void this.outbox\n .setMeta(this.outboxClientId, { nextSeq: this.outboxNextSeq, deployment: msg.deploymentId })\n .catch((err: unknown) => this.handleOutboxWriteError(\"setMeta\", this.outboxClientId, undefined, undefined, err));\n }\n if (!msg.known) {\n void this.onClientReset().then(() => this.outboxDrain?.nudge());\n return;\n }\n for (const v of msg.results) this.settleVerdict(v);\n // The handshake proved dedup + classified `held`; wake the drain to (re)send any `unknown` seqs.\n this.outboxDrain?.nudge();\n }\n\n /** Settle one classified `held` seq from a `ConnectAck` (or, later, a drain replay-ack). */\n private settleVerdict(v: ClientMutationVerdict): void {\n const entry = this.findOutboxEntry(v.clientId, v.seq);\n switch (v.verdict) {\n case \"applied\": {\n // Resolve the awaiting promise (a parked entry from THIS session still has one; a hydrated\n // cross-reload entry has none — `valueMissing` is tolerated everywhere) with the recorded\n // value, dequeue the durable record, and drop its layer once the baseline is adopted.\n // Unconditional is sound HERE (unlike a drain response, see `drainSettleApplied`): a\n // ConnectAck verdict is always classifying a seq from a PRIOR connect — by construction its\n // commit predates this session's `Connect`, hence the baseline — never a fresh same-session\n // apply, so there is no flicker risk to gate against.\n const value = v.valueMissing ? null : jsonToConvex(v.value ?? null);\n if (entry) this.resolvePending(entry.requestId, value);\n this.outboxDequeue(v.clientId, v.seq);\n if (entry) this.dropAfterBaseline(entry.requestId);\n break;\n }\n case \"failed\": {\n // R9: MARK failed (persist, don't dequeue) instead of removing — \"failed entries persist\n // until dismissed/retried\". `hadAwaiter` gates the `onMutationFailed` refire (never a double\n // notification for a failure the entry's own live promise already delivered THIS session).\n const hadAwaiter = entry ? this.pendingMutationCallbacks.has(entry.requestId) : false;\n const message = `mutation \"${entry?.udfPath ?? \"unknown\"}\" failed`;\n if (entry) this.rejectPending(entry.requestId, this.mutationError(message, v.code));\n this.outboxMarkFailed(v.clientId, v.seq, message, v.code);\n if (entry) this.reconciler.onMutationFailure(entry.requestId);\n if (!hadAwaiter) this.notifyMutationFailed(v.clientId, v.seq, entry?.udfPath ?? \"unknown\", { message, code: v.code });\n break;\n }\n case \"stale\": {\n const hadAwaiter = entry ? this.pendingMutationCallbacks.has(entry.requestId) : false;\n const message = \"mutation disowned (STALE_CLIENT)\";\n const code = v.code ?? \"STALE_CLIENT\";\n if (entry) this.rejectPending(entry.requestId, this.mutationError(message, code));\n this.outboxMarkFailed(v.clientId, v.seq, message, code);\n if (entry) this.reconciler.onMutationFailure(entry.requestId);\n if (!hadAwaiter) this.notifyMutationFailed(v.clientId, v.seq, entry?.udfPath ?? \"unknown\", { message, code });\n break;\n }\n case \"unknown\":\n // Never seen by the server — remains in the log for T4's drain to (re)send under its seq.\n break;\n }\n }\n\n /** `known: false` — the server disowned this client's history (verdict §(d) Retention). Re-mint a\n * fresh clientId + meta; re-enqueue every `unsent` entry under the new clientId + NEW seqs (never\n * applied, so safe); reject every `parked` entry LOUDLY (in-flight-at-disconnect, no server dedup\n * → a blind resend could double-apply); fire the `onClientReset` callback. */\n private async onClientReset(): Promise<void> {\n const oldClientId = this.outboxClientId;\n const fresh = defaultMintClientId();\n this.outboxClientId = fresh;\n this.outboxNextSeq = 0;\n\n let parkedRejected = 0;\n let unsentReEnqueued = 0;\n // Snapshot first — the loop rejects (mutates promise maps) and re-stamps entries.\n for (const entry of [...this.reconciler.entries()]) {\n // The durable store is keyed by each entry's RECORDED `(clientId, seq)` — for an entry\n // hydrated from a prior session that is the PRIOR session's clientId, not this session's\n // `oldClientId`. Targeting `oldClientId` here made the dequeue/markFailed below a silent\n // no-op for every hydrated entry (its record lingered in the store forever, so\n // `pendingMutations()` never drained to empty after a reset) — the fsOutbox E2E found it.\n const recordedClientId = entry.clientId ?? oldClientId;\n if (entry.status.type === \"parked\") {\n // R9: MARK failed (persist for the tray) rather than dequeue — a deliberate user-initiated\n // `retry()` under a FRESH `(clientId, seq)` is safe even though a blind auto-resend under the\n // OLD identity would not be (the reason it rejects loudly in the first place).\n const hadAwaiter = this.pendingMutationCallbacks.has(entry.requestId);\n const resetMessage = \"the server disowned this client's mutation history (swept/foreign timeline)\";\n if (recordedClientId !== undefined && entry.seq !== undefined) {\n this.outboxMarkFailed(recordedClientId, entry.seq, resetMessage, \"OFFLINE_CLIENT_RESET\");\n }\n this.rejectPending(entry.requestId, new OfflineClientResetError());\n this.reconciler.onMutationFailure(entry.requestId); // remove from the log (no layer to roll back)\n if (!hadAwaiter) this.notifyMutationFailed(recordedClientId, entry.seq, entry.udfPath, { message: resetMessage, code: \"OFFLINE_CLIENT_RESET\" });\n parkedRejected++;\n } else if (entry.status.type === \"unsent\") {\n // Re-key onto the fresh identity under a brand-new seq; the old durable record is dropped.\n if (recordedClientId !== undefined && entry.seq !== undefined) this.outboxDequeue(recordedClientId, entry.seq);\n entry.clientId = fresh;\n entry.seq = this.outboxNextSeq++;\n entry.order = this.nextOutboxOrder();\n this.outboxAppend(entry);\n unsentReEnqueued++;\n }\n }\n\n if (this.outbox) {\n await mintIdentity(this.outbox, { mintClientId: () => fresh, deployment: this.outboxDeploymentId });\n // The fresh meta row must reflect the seqs already re-handed-out to `unsent` entries above.\n await this.outbox.setMeta(fresh, { nextSeq: this.outboxNextSeq, deployment: this.outboxDeploymentId });\n }\n\n this.onClientResetCallback?.({ oldClientId, newClientId: fresh, unsentReEnqueued, parkedRejected });\n }\n\n /** The in-memory log entry with this recorded `(clientId, seq)`, or `undefined`. */\n private findOutboxEntry(clientId: string, seq: number): PendingMutation | undefined {\n for (const e of this.reconciler.entries()) {\n if (e.clientId === clientId && e.seq === seq) return e;\n }\n return undefined;\n }\n\n /** Resolve a pending mutation promise by requestId (no-op if it already settled / has no awaiter). */\n private resolvePending(requestId: string, value: Value): void {\n const pending = this.pendingMutationCallbacks.get(requestId);\n this.pendingMutationCallbacks.delete(requestId);\n pending?.resolve(value);\n }\n\n /** Reject a pending mutation promise by requestId (no-op if it already settled / has no awaiter). */\n private rejectPending(requestId: string, error: Error): void {\n const pending = this.pendingMutationCallbacks.get(requestId);\n this.pendingMutationCallbacks.delete(requestId);\n pending?.reject(error);\n }\n\n /** Drop an `applied` cross-session entry's layer — deferred until the baseline is adopted (so the\n * drop is flicker-free), or immediately if it already has. */\n private dropAfterBaseline(requestId: string): void {\n if (this.outboxAwaitingBaseline) this.outboxPendingDrops.push(requestId);\n else this.reconciler.onVerdictAfterBaseline(requestId);\n }\n\n /** Dequeue a settled durable entry from the outbox store (no-op without an outbox / clientId). */\n private dequeueOutboxEntry(entry: PendingMutation | undefined): void {\n if (entry?.clientId !== undefined && entry.seq !== undefined) this.outboxDequeue(entry.clientId, entry.seq);\n }\n\n /** An `Error` carrying the server's terminal verdict `code` (STALE_CLIENT, an app error code) so\n * the drain's coded-vs-codeless retry policy (T4) and apps can key off it. */\n private mutationError(message: string, code?: string): Error {\n const err = new Error(message);\n if (code !== undefined) (err as Error & { code?: string }).code = code;\n return err;\n }\n\n /* ---------------------------------------------------------------------------------------------\n * Task 4 — the drain host. These bind the drain's `DrainHost` seam to the client's private state\n * so the T3 settlement primitives (`resolvePending`/`rejectPending`, `dequeue`, the drop rule) are\n * REUSED by the drain, not forked (verdict §(d) \"Drain\").\n * ------------------------------------------------------------------------------------------- */\n\n /** @internal test/debug — the live drain (Task 4), or `undefined` without an outbox. */\n get __outboxDrain(): OutboxDrain | undefined {\n return this.outboxDrain;\n }\n\n private makeDrainHost(): DrainHost {\n return {\n outbox: this.outbox!,\n currentClientId: () => this.outboxClientId,\n currentFingerprint: () => this.outboxFingerprint,\n transportOpen: () => !this.closed,\n isArmed: () => this.outboxArmed,\n drainable: () => this.drainableEntries(),\n addHydrated: (entry) => this.addHydratedEntry(entry),\n ensureInitialHandshake: () => this.initiateHandshake(this.hasUndeliveredSubscription()),\n setStatus: (entry, status) => {\n entry.status = { type: status };\n // T5 (R9): persist the transition too — `pendingMutations()` reads the DURABLE record, so\n // without this its `status` would stay frozen at whatever `append()` first wrote.\n if (entry.clientId !== undefined && entry.seq !== undefined) this.outboxUpdateStatus(entry.clientId, entry.seq, status);\n },\n batchEntry: (entry) => this.drainBatchEntry(entry),\n sendBatch: (entries) => this.transport.send({ type: \"MutationBatch\", entries }),\n settleApplied: (requestId, value, replayed, ts) => this.drainSettleApplied(requestId, value, replayed, ts),\n settleTerminal: (requestId, code, message) => this.drainSettleTerminal(requestId, code, message),\n whenBaselineAdopted: () => this.whenBaselineAdopted(),\n };\n }\n\n /** Drain-eligible entries: durable, recorded `(clientId, seq)`, still `unsent`/`parked`, FIFO by\n * the persisted `order`. Excludes `inflight` (a live direct-send or an in-flight chunk unit) and\n * `completed`. */\n private drainableEntries(): PendingMutation[] {\n return this.reconciler\n .entries()\n .filter(\n (e) =>\n e.clientId !== undefined &&\n e.seq !== undefined &&\n e.durable === true &&\n (e.status.type === \"unsent\" || e.status.type === \"parked\"),\n )\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n }\n\n /** Add a hydrated durable entry into the log under a FRESH requestId (the persisted requestId was\n * session-correlation only; a fresh one avoids colliding with this session's requestId counter).\n * Idempotent by `(clientId, seq)` — a direct-send this session already tracks is not re-added.\n * T5: `entry.update` is populated from the `optimisticUpdates` registry (hydrate-only lookup — a\n * live call-site closure is never in play here, there IS no live call site for a cross-reload\n * entry); a registry miss is layerless (no `update` at all), a clean drop under the baseline-gated\n * drop rule exactly as T4 shipped it. */\n private addHydratedEntry(e: OutboxEntry): void {\n // Defense-in-depth (see `dropIfNonReplayable`'s doc): the SAME check `OutboxDrain#hydrateOnce`\n // applies, repeated here because this is ALSO the chokepoint `mirrorFromStore` (the T-crosstab\n // backstop) routes through directly, bypassing `hydrateOnce` entirely.\n if (dropIfNonReplayable(this.outbox!, e)) return;\n for (const existing of this.reconciler.entries()) {\n if (existing.clientId === e.clientId && existing.seq === e.seq) return;\n }\n // Keep the order counter ahead of every hydrated (past-session) order so new mutations this\n // session sort strictly AFTER the hydrated backlog — FIFO across the reload boundary.\n this.outboxOrderCounter = Math.max(this.outboxOrderCounter, e.order);\n const entry: PendingMutation = {\n requestId: String(this.nextRequestId++),\n udfPath: e.udfPath,\n args: e.args,\n seed: e.seed,\n touched: new Set(),\n status: { type: \"unsent\" },\n clientId: e.clientId,\n seq: e.seq,\n order: e.order,\n identityFingerprint: e.identityFingerprint,\n enqueuedAt: e.enqueuedAt,\n durable: true,\n update: this.lookupHydratedUpdate(e.udfPath),\n };\n // `addHydrated` (not `initiate`): a throwing registered updater here is ordinary replay-drop\n // collateral (warned + dropped), never rethrown — there is no synchronous caller on the hydrate\n // path to propagate it to (see `reconcile.ts#addHydrated`'s doc).\n this.reconciler.addHydrated(entry);\n }\n\n /** T5: the registry lookup `addHydratedEntry` makes — hydrate-time ONLY (verdict §(d): \"the\n * registry is consulted at hydrate only\"). A miss warns ONCE per udfPath (not per entry — a\n * backlog of many unregistered entries for the same udfPath warns once) and returns `undefined`:\n * the entry still drains fine, only its optimistic rendering is skipped (spec §(k)6). */\n private lookupHydratedUpdate(udfPath: string): OptimisticUpdate | undefined {\n const fn = this.optimisticUpdates[udfPath];\n if (fn) return fn as unknown as OptimisticUpdate;\n if (!this.optimisticUpdateMissWarned.has(udfPath)) {\n this.optimisticUpdateMissWarned.add(udfPath);\n console.warn(\n `[helipod] outbox: no optimisticUpdates registered for \"${udfPath}\" — a hydrated cross-reload ` +\n `mutation for it will drain without an optimistic layer (rendering only; the mutation itself is unaffected)`,\n );\n }\n return undefined;\n }\n\n /* ---------------------------------------------------------------------------------------------\n * T-crosstab (browser-ux spec Part A) — live cross-tab rendering. Extends the hydrate machinery\n * above (`addHydratedEntry`/`lookupHydratedUpdate`) with live callers driven by the broadcast\n * channel, instead of its one construction-time caller (`OutboxDrain#hydrateOnce`).\n * ------------------------------------------------------------------------------------------- */\n\n /** The one predicate distinguishing a MIRRORED entry (another tab-session's durable append, or\n * this tab's own past-session hydrate — either way, no live promise) from an entry THIS instance\n * itself initiated live (hazard (a), \"own-tab discrimination\"): durable AND no\n * `pendingMutationCallbacks` registered for its `requestId`. A live `mutation()` caller has a\n * callback registered only UNTIL its own wire response settles it — resolved/rejected callbacks\n * are deleted immediately (`resolvePending`/`rejectPending`), well before a `completed` layer's\n * gate gets a chance to drop it. So a live caller's entry looks exactly like a true mirror\n * (`isMirroredEntry` returns `true`) for the whole post-ack, still-gated window — this predicate\n * alone does NOT distinguish \"own tab, post-ack\" from \"another tab's mirror\"; it is the\n * `status.type === \"completed\"` skip in `mirrorFromStore`'s backstop pass that closes that gap\n * (a `completed` layer is owned by its gate, never force-settled by this predicate or the store).\n * Used both by the dispatch methods below and the doc comment on `mirrorFromStore`'s backstop pass\n * — a single documented helper, never inlined twice (brief hazard (a)). */\n private isMirroredEntry(entry: PendingMutation): boolean {\n return entry.durable === true && !this.pendingMutationCallbacks.has(entry.requestId);\n }\n\n /** Handle an incoming (already-fan-out'd) broadcast payload — the typed half of `onmessage`. A\n * payload that doesn't match `OutboxBroadcastMessage` (the legacy bare `1`, or anything else) is\n * simply not recognized: the accessor nudge already fired in the caller, nothing else happens. */\n private handleOutboxBroadcastMessage(data: unknown): void {\n if (!this.outbox || !isOutboxBroadcastMessage(data)) return;\n if (data.kind === \"enqueued\") {\n // `mirrorFromStore` is fire-and-forget (never awaited by this synchronous handler) — a\n // rejection (e.g. a fail-stopped `fsOutbox`'s `OutboxClosedError` after a disk error) must\n // route to the SAME observability floor every other durable-outbox write rejection uses,\n // rather than becoming an unhandled promise rejection on every subsequent broadcast (several\n // Node/Electron hosts treat that as fatal by default — see `handleOutboxWriteError`'s doc).\n // Meta-only (no `(clientId, seq)` to attach to): floors straight to the dev-loud console.\n void this.mirrorFromStore().catch((err: unknown) => this.handleOutboxWriteError(\"mirrorFromStore\", undefined, undefined, undefined, err));\n } else {\n this.onCrossTabSettle(data);\n }\n }\n\n /** Re-read the durable store and reconcile this tab's mirrored set against it — the `enqueued`\n * broadcast's handler, and the missed-message backstop (spec Part A \"Rules\"): a mirrored entry\n * absent from the fresh snapshot (settled/failed elsewhere, whose OWN targeted broadcast this tab\n * never received) drops via the same `dropAfterBaseline` one-pass rule the verdict-after-baseline\n * path uses. Serialized on `mirrorInFlight` — a second call arriving mid-read sets `mirrorRerun`\n * and is folded into one more pass after the current read finishes, rather than racing a second\n * `loadAll()` against it.\n *\n * Only STILL-ACTIVE entries (`unsent`/`inflight`/`parked`) are (re-)hydrated — a `failed` (or a\n * stray `completed`) entry is a terminal, accessor-only record (`pendingMutations()` already\n * surfaces it) and must never be resurrected as a fresh `unsent` optimistic layer. Without this\n * guard, a tab that itself once owned a now-terminally-failed entry would keep reviving its OWN\n * dead record on every subsequent `enqueued` broadcast (this store never dequeues a `failed`\n * entry — R9 \"persists until dismissed/retried\") — and, being `durable` with no live callback,\n * that revived entry would then match `isMirroredEntry`, making the tab react to an UNRELATED\n * later `settled`/`failed` broadcast that merely happens to name the same `(clientId, seq)`.\n *\n * Two review-fixed hazards in the reconcile loop below (both stem from the SAME root cause: the\n * store's presence/absence is not always the authority for a mirrored layer's fate):\n * - a `completed` mirrored layer (this tab already got a targeted `settled` broadcast and is\n * holding it gated until ITS OWN feed observes `commitTs`, per `onCrossTabSettle`) is SKIPPED\n * entirely here, never force-dropped merely because the store record is absent. The leader's\n * own `drainSettleApplied` dequeues the record right after posting `settled`, and THAT\n * dequeue's `{kind:\"enqueued\"}` follow-up broadcast is exactly what drives this backstop pass —\n * so a `completed` entry being store-absent is the ordinary, expected case, not a missed\n * message. Force-dropping it here would race ahead of this tab's own gate (a flicker the gate\n * exists to prevent) — CRITICAL. The same skip also protects THIS tab's own just-acked live\n * mutation: its `pendingMutationCallbacks` entry is deleted the moment its wire response\n * settles it (see `isMirroredEntry`'s doc — the callback does NOT survive the whole gated\n * window, only until the response arrives), so during that gated window it is\n * indistinguishable from a true mirror to `isMirroredEntry` and would otherwise be dropped by a\n * totally unrelated tab's `enqueued` broadcast.\n * - the \"is this mirror still active\" check now reads from ACTIVE-status (`unsent`/`inflight`/\n * `parked`) entries only, not \"any entry present in the store\" — a mirror whose backing record\n * flipped to `failed` (R9 never dequeues a failure) is present-in-store but no longer active;\n * treating presence alone as \"still live\" would leave a permanent phantom optimistic row behind\n * a missed `failed` broadcast. Such an entry is instead settled failed right here (same effect\n * as `onCrossTabSettle`'s `failed` branch — mark failed + fire R9), using the terminal verdict\n * already recorded on the store row itself. */\n private async mirrorFromStore(): Promise<void> {\n if (!this.outbox) return;\n if (this.mirrorInFlight) {\n this.mirrorRerun = true;\n return;\n }\n this.mirrorInFlight = true;\n try {\n do {\n this.mirrorRerun = false;\n const { entries } = await this.outbox.loadAll();\n const activeLive = new Set<string>();\n const byKey = new Map<string, OutboxEntry>();\n for (const e of entries) {\n const key = `${e.clientId}:${e.seq}`;\n byKey.set(key, e);\n if (e.status === \"unsent\" || e.status === \"inflight\" || e.status === \"parked\") {\n activeLive.add(key);\n // idempotent — addHydratedEntry's own (clientId, seq) dedup.\n this.addHydratedEntry(e);\n }\n }\n for (const entry of this.reconciler.entries()) {\n if (!this.isMirroredEntry(entry)) continue; // never touch an own-live entry here.\n if (entry.clientId === undefined || entry.seq === undefined) continue;\n if (entry.status.type === \"completed\") continue; // gate-owned — see doc above.\n const key = `${entry.clientId}:${entry.seq}`;\n if (activeLive.has(key)) continue; // still active in the store — nothing to reconcile.\n const stored = byKey.get(key);\n if (stored?.status === \"failed\") {\n this.reconciler.onMutationFailure(entry.requestId);\n this.notifyMutationFailed(entry.clientId, entry.seq, entry.udfPath, stored.error ?? { message: `mutation \"${entry.udfPath}\" failed` });\n } else {\n this.dropAfterBaseline(entry.requestId); // genuinely absent — the original missed-settle drop.\n }\n }\n } while (this.mirrorRerun);\n } finally {\n this.mirrorInFlight = false;\n }\n }\n\n /** Handle a targeted `settled`/`failed` broadcast — the leader's flicker-free fast path (Part A's\n * normal route; `mirrorFromStore`'s backstop above is the fallback for a missed message). Ignored\n * entirely for an entry this tab doesn't know about, or one it initiated live itself (hazard (a)).\n * Never touches the durable store — the leader (whichever tab settled it) already wrote that;\n * this only updates THIS tab's in-memory reconciler layer + R9 observability. */\n private onCrossTabSettle(msg: Extract<OutboxBroadcastMessage, { kind: \"settled\" | \"failed\" }>): void {\n const entry = this.findOutboxEntry(msg.clientId, msg.seq);\n if (!entry || !this.isMirroredEntry(entry)) return;\n if (msg.kind === \"settled\") {\n // The exact same same-session gate `drainSettleApplied`'s fresh-apply branch uses — hold\n // `completed` until THIS tab's own feed observes `commitTs` (flicker-free, never drop-on-ack).\n this.reconciler.onMutationSuccess(entry.requestId, msg.commitTs);\n } else {\n // The terminal-settle shape minus the promise reject — no promise exists for a mirror.\n this.reconciler.onMutationFailure(entry.requestId);\n this.notifyMutationFailed(entry.clientId, entry.seq, entry.udfPath, { message: msg.message, code: msg.code });\n }\n }\n\n private drainBatchEntry(entry: PendingMutation): MutationBatchEntry {\n return { requestId: entry.requestId, udfPath: entry.udfPath, args: entry.args, clientId: entry.clientId, seq: entry.seq };\n }\n\n /** applied settlement for a drained unit — resolve the awaiting promise (if any) and dequeue the\n * durable record ALWAYS; then route the layer drop by `replayed` (T4 review fix — the ungated\n * fresh-apply drop):\n * - `replayed: true` — a resend whose commit predates this session's `Connect`, reusing the same\n * primitive `settleVerdict`'s `applied` case uses (T3's unconditional baseline-gated drop).\n * Drop-soundness (T3 watch item, scoped to replays ONLY): the drop is gated on baseline\n * adoption, not on the replay's carried commitTs — the entry's commit necessarily predates this\n * session's `Connect`, so it predates the baseline's read snapshot and the baseline already\n * renders the effect. Historical-ts-vs-current-base is therefore still covered; the drop is\n * flicker-free by the same one-pass rule as T3's handshake.\n * - a FRESH apply (`replayed` absent/false — this session's OWN first execution, a genuinely new\n * `ts`) — the argument above does NOT apply: nothing proves this commit predates the baseline,\n * so an unconditional drop here would remove a still-rendered layer before its authoritative row\n * ever appears (a flicker). Instead this routes through the normal same-session gate,\n * `onMutationSuccess` (the response `ts`) — the exact same shipped no-flicker discipline the\n * direct-send path uses at `MutationResponse` (see the `case \"MutationResponse\"` handler above):\n * hold the layer `completed` until this client's own reactive feed observes `ts`.\n *\n * T-crosstab: AFTER the local settle above, the leader (this tab, if it holds the drain lock)\n * also posts a targeted `settled` broadcast so a tab MIRRORING this same `(clientId, seq)` gets\n * the flicker-free fast path instead of waiting for its own next `enqueued`-triggered backstop\n * read. Posted only when the entry carries a durable `(clientId, seq)` — a plain non-outbox\n * mutation has nothing for another tab to have mirrored in the first place.\n */\n private drainSettleApplied(requestId: string, value: Value | null, replayed: boolean, ts: number | undefined): void {\n const entry = this.reconciler.getEntry(requestId);\n this.resolvePending(requestId, value);\n if (entry?.clientId !== undefined && entry.seq !== undefined) this.outboxDequeue(entry.clientId, entry.seq);\n if (!entry) return;\n if (replayed) this.dropAfterBaseline(requestId);\n else this.reconciler.onMutationSuccess(requestId, ts);\n if (entry.clientId !== undefined && entry.seq !== undefined) {\n this.outboxBroadcast?.postMessage({ kind: \"settled\", clientId: entry.clientId, seq: entry.seq, commitTs: ts ?? 0 } satisfies OutboxBroadcastMessage);\n }\n }\n\n /** Terminal settlement for a drained unit (a coded server verdict, or the identity gate) — reject\n * the awaiting promise (coded), MARK the durable record `\"failed\"` (R9: never dequeue a terminal\n * failure — it persists until dismissed/retried), drop the layer, and (T5) refire `onMutationFailed`\n * / the dev-loud default when nothing awaited this failure this session. T-crosstab: AFTER all of\n * the above, also posts a targeted `failed` broadcast (same durability gate as `drainSettleApplied`\n * above) — a mirroring tab's own `onCrossTabSettle` fires ITS OWN `onMutationFailed`/dev-loud\n * default; it never double-delivers THIS tab's own notification above. */\n private drainSettleTerminal(requestId: string, code: string | undefined, message: string): void {\n const entry = this.reconciler.getEntry(requestId);\n const hadAwaiter = this.pendingMutationCallbacks.has(requestId);\n this.rejectPending(requestId, this.mutationError(message, code));\n if (entry?.clientId !== undefined && entry.seq !== undefined) this.outboxMarkFailed(entry.clientId, entry.seq, message, code);\n if (entry) this.reconciler.onMutationFailure(requestId);\n if (!hadAwaiter) this.notifyMutationFailed(entry?.clientId, entry?.seq, entry?.udfPath ?? \"unknown\", { message, code });\n if (entry?.clientId !== undefined && entry.seq !== undefined) {\n this.outboxBroadcast?.postMessage({ kind: \"failed\", clientId: entry.clientId, seq: entry.seq, code, message } satisfies OutboxBroadcastMessage);\n }\n }\n\n /* ---------------------------------------------------------------------------------------------\n * T5 — R9 observability: `pendingMutations()`/`usePendingMutations()`, `pendingSummary()`,\n * `onMutationFailed` refire, the dev-mode loud default, and the outbox-change notification bus\n * every durable-mutating operation above funnels through.\n * ------------------------------------------------------------------------------------------- */\n\n /** A snapshot of the durable outbox — `usePendingMutations()`'s underlying read. `[]` without an\n * outbox configured (verdict §(d) R9). Each row's `retry()`/`dismiss()` close over the entry as\n * read HERE (no extra storage round-trip — `retry()` needs `args`/`seed`/`identityFingerprint`,\n * all captured already). */\n async pendingMutations(): Promise<PendingMutationEntry[]> {\n if (!this.outbox) return [];\n const { entries } = await this.outbox.loadAll();\n return entries.map((e) => this.toPendingMutationEntry(e));\n }\n\n /** T5 (R9, hazard 2's client half): count + oldest-age advisory over the durable queue — cheap\n * enough to poll for a \"your offline changes may be lost soon\" banner ahead of a storage cliff\n * (Safari's 7-day eviction). `{count: 0, oldestEnqueuedAt: undefined, oldestAgeMs: undefined}`\n * without an outbox configured, or with an empty one. */\n async pendingSummary(): Promise<PendingSummary> {\n if (!this.outbox) return { count: 0, oldestEnqueuedAt: undefined, oldestAgeMs: undefined };\n const { entries } = await this.outbox.loadAll();\n if (entries.length === 0) return { count: 0, oldestEnqueuedAt: undefined, oldestAgeMs: undefined };\n let oldest = entries[0]!.enqueuedAt;\n for (const e of entries) if (e.enqueuedAt < oldest) oldest = e.enqueuedAt;\n return { count: entries.length, oldestEnqueuedAt: oldest, oldestAgeMs: Date.now() - oldest };\n }\n\n private toPendingMutationEntry(e: OutboxEntry): PendingMutationEntry {\n return {\n clientId: e.clientId,\n seq: e.seq,\n udfPath: e.udfPath,\n status: e.status,\n enqueuedAt: e.enqueuedAt,\n error: e.error,\n retry: () => this.retryOutboxEntry(e),\n dismiss: () => this.dismissOutboxEntry(e),\n };\n }\n\n /** `entry.retry()` (R9): a FAILED entry only — everything else is a harmless no-op (verdict §(b):\n * \"never reuse a seq for a new attempt\"). Dequeues the OLD (failed-verdict) durable record and\n * builds a brand-new `PendingMutation` — fresh requestId/seq/order, the CURRENT session's identity\n * fingerprint (a fair shot even if identity rotated since the original failure), reconstructed\n * exactly like a hydrated entry (same udfPath/args/seed; the registry is consulted — there is no\n * live call-site closure for a retry either). No live promise is registered: like a hydrated\n * entry, its eventual outcome surfaces via `usePendingMutations()`/`onMutationFailed`, never a\n * returned `Promise<Value>` (the durable record outlives any promise). */\n private async retryOutboxEntry(e: OutboxEntry): Promise<void> {\n if (!this.outbox || e.status !== \"failed\" || this.outboxClientId === undefined) return;\n const requestId = String(this.nextRequestId++);\n const entry: PendingMutation = {\n requestId,\n udfPath: e.udfPath,\n args: e.args,\n seed: e.seed,\n touched: new Set(),\n status: { type: \"unsent\" },\n clientId: this.outboxClientId,\n seq: this.outboxNextSeq++,\n order: this.nextOutboxOrder(),\n identityFingerprint: this.outboxFingerprint,\n enqueuedAt: Date.now(),\n durable: false,\n update: this.lookupHydratedUpdate(e.udfPath),\n };\n this.reconciler.addHydrated(entry);\n this.outboxAppend(entry);\n this.outboxDequeue(e.clientId, e.seq);\n this.outboxDrain?.nudge();\n }\n\n /** `entry.dismiss()` (R9): a FAILED entry only — permanently forget it without retrying. */\n private async dismissOutboxEntry(e: OutboxEntry): Promise<void> {\n if (!this.outbox || e.status !== \"failed\") return;\n this.outboxDequeue(e.clientId, e.seq);\n }\n\n /** T5 (R9): subscribe to \"the durable outbox changed\" — `usePendingMutations()`'s re-read trigger.\n * Fires on every local outbox-mutating op AND on an incoming cross-tab `outboxBroadcast` message. */\n onOutboxChange(listener: () => void): () => void {\n this.outboxChangeListeners.add(listener);\n return () => this.outboxChangeListeners.delete(listener);\n }\n\n private notifyOutboxChange(): void {\n for (const l of this.outboxChangeListeners) l();\n // T-crosstab: was the payload-irrelevant bare `1` (\"the message IS the nudge\"); now additively\n // typed as `{kind: \"enqueued\"}` — every OTHER tab's receiver re-reads `loadAll()` and reconciles\n // its mirrored set against it (both the live-render path and the missed-message backstop).\n this.outboxBroadcast?.postMessage({ kind: \"enqueued\" } satisfies OutboxBroadcastMessage);\n }\n\n /** Every durable-mutating outbox call site funnels through these four wrappers (instead of a bare\n * `this.outbox?.xxx(...)`) so `notifyOutboxChange()` — and a rejection's route through\n * `handleOutboxWriteError` — is never missed at a new call site. */\n private outboxAppend(entry: PendingMutation): void {\n if (!this.outbox) return;\n void this.outbox\n .append(this.toOutboxEntry(entry))\n .then(() => {\n entry.durable = true; // write-behind confirmed — mirrors `mutation()`'s own append `.then()`.\n this.outboxDrain?.nudge();\n this.notifyOutboxChange();\n })\n .catch((err: unknown) => this.handleOutboxWriteError(\"append\", entry.clientId, entry.seq, entry.udfPath, err));\n }\n\n private outboxDequeue(clientId: string, seq: number): void {\n if (!this.outbox) return;\n void this.outbox\n .dequeue(clientId, seq)\n .then(() => this.notifyOutboxChange())\n .catch((err: unknown) => this.handleOutboxWriteError(\"dequeue\", clientId, seq, undefined, err));\n }\n\n private outboxUpdateStatus(clientId: string, seq: number, status: OutboxEntryStatus): void {\n if (!this.outbox) return;\n void this.outbox\n .updateStatus(clientId, seq, status)\n .then(() => this.notifyOutboxChange())\n .catch((err: unknown) => this.handleOutboxWriteError(\"updateStatus\", clientId, seq, undefined, err));\n }\n\n /** Record a terminal failure DURABLY (`status: \"failed\"` + the error) instead of dequeuing — R9:\n * \"failed entries persist until dismissed/retried\". */\n private outboxMarkFailed(clientId: string, seq: number, message: string, code: string | undefined): void {\n if (!this.outbox) return;\n void this.outbox\n .updateStatus(clientId, seq, \"failed\", { message, code })\n .then(() => this.notifyOutboxChange())\n .catch((err: unknown) => this.handleOutboxWriteError(\"updateStatus\", clientId, seq, undefined, err));\n }\n\n /** Routes a rejected fire-and-forget durable-outbox write (append/updateStatus/dequeue/setMeta —\n * never awaited by its caller, per the write-behind contract) to observability instead of letting\n * it become an unhandled promise rejection, which several Node/Electron hosts treat as fatal by\n * default (a `fsOutbox` that has fail-stopped after a disk error rejects EVERY subsequent op —\n * see `outbox-fs.ts`'s `OutboxClosedError`). When the write carries a `(clientId, seq)` — every\n * case except a meta-only write — it routes through the SAME R9 channel as any other terminal\n * mutation failure (`onMutationFailed`, or `notifyMutationFailed`'s own dev-mode loud\n * `console.error` default). With no such record to attach the failure to (a meta write), a\n * dev-loud `console.error` is the floor — NEVER swallowed silently either way. */\n private handleOutboxWriteError(op: string, clientId: string | undefined, seq: number | undefined, udfPath: string | undefined, err: unknown): void {\n const message = err instanceof Error ? err.message : String(err);\n const code = typeof err === \"object\" && err !== null && \"code\" in err ? String((err as { code: unknown }).code) : undefined;\n if (clientId !== undefined && seq !== undefined) {\n this.notifyMutationFailed(clientId, seq, udfPath ?? \"unknown\", { message: `durable outbox ${op} failed: ${message}`, code });\n } else if (isDevMode()) {\n console.error(`[helipod] durable outbox ${op} failed (clientId=${clientId ?? \"unknown\"}):`, err);\n }\n }\n\n /** T5 (R9): fire `onMutationFailed` for a terminal durable failure with NO live promise awaiter\n * this session (`hadAwaiter` already checked by every call site) — or, absent a registered\n * handler, the dev-mode loud `console.error` default (spec-review: \"the five-line courtesy\" no\n * position shipped). A no-op for a non-outbox-tracked entry (`clientId`/`seq` undefined) — R9 is\n * entirely a durable-outbox concern. */\n private notifyMutationFailed(clientId: string | undefined, seq: number | undefined, udfPath: string, error: OutboxEntryError): void {\n if (clientId === undefined || seq === undefined) return;\n if (this.onMutationFailedCallback) {\n this.onMutationFailedCallback({ clientId, seq, udfPath, error });\n } else if (isDevMode()) {\n console.error(\n `[helipod] outbox: mutation \"${udfPath}\" (clientId=${clientId}, seq=${seq}) failed terminally` +\n `${error.code ? ` (${error.code})` : \"\"} with no onMutationFailed handler registered: ${error.message}`,\n );\n }\n }\n\n /** R9 \"resume\" refire (constructor-only, verdict §(d) Observability: \"`onMutationFailed` refires\n * from durable records on resume\"): a fresh `HelipodClient` instance has made zero `mutation()`\n * calls yet, so EVERY already-`\"failed\"` durable record found here is trivially \"no live awaiter\" —\n * Lunora's `hadAwaiter` check is unconditionally false at this point, no gating needed. */\n private async refireDurableFailures(): Promise<void> {\n if (!this.outbox) return;\n const { entries } = await this.outbox.loadAll();\n for (const e of entries) {\n if (e.status === \"failed\") {\n this.notifyMutationFailed(e.clientId, e.seq, e.udfPath, e.error ?? { message: `mutation \"${e.udfPath}\" failed` });\n }\n }\n }\n}\n","/**\n * S2 — the `LayeredQueryStore`. It splits what was a single `value` slot per subscription into a\n * **`serverValue`** (written only by server ingest) and a **`composedValue`** (= ordered replay of\n * the surviving optimistic updates over that base — what listeners and the cached first delivery\n * see). Change detection is by **reference inequality**, exactly as convex-js does.\n *\n * The **byte-identity invariant** (the no-re-render binding): when NO surviving update touches a\n * query, its `composedValue` must be the *same reference* as its `serverValue`, not merely\n * deep-equal — so a client with zero optimistic updates behaves reference-for-reference like the\n * pre-slice client and triggers no extra renders.\n *\n * `recompose` is the sole place composed values are (re)built. It is transactional per-updater:\n * an updater that throws mid-run contributes NOTHING (its buffered writes are discarded) and is\n * reported for the caller to drop — a mid-rebuild throw never leaves the composed store half-built.\n */\nimport { convexToJson, jsonToConvex, type JSONValue, type Value } from \"@helipod/values\";\nimport { applyChanges, driftChecksum, type Change, type RowVersion } from \"@helipod/sync\";\nimport { base64ToBytes, compareKeyBytes } from \"@helipod/index-key-codec\";\nimport { getFunctionPath, type FunctionReference } from \"./api\";\nimport type { PendingMutation } from \"./mutation-log\";\n\n/** The query identity hash — `path + \":\" + JSON.stringify(argsJson)` (the existing `client.ts` hash). */\nexport function queryHash(path: string, argsJson: JSONValue): string {\n return `${path}:${JSON.stringify(argsJson)}`;\n}\n\n/**\n * DLR Stage 2a — render a by-id DIFFABLE query's value from its keyed row-map: the sole (0-or-1)\n * entry's row decoded to a `Value`, or `undefined` when the map is empty (a `db.get(id)` returning\n * `null` renders as no value, exactly as the RERUN path does today). `jsonToConvex` mints a fresh\n * object every call, so each apply yields a distinct-by-reference value — the identity `recompose`\n * needs to fire listeners.\n */\nfunction renderByIdValue(rows: Map<string, RowVersion>): Value | undefined {\n for (const rv of rows.values()) return jsonToConvex(rv.row) as Value; // first (and only) entry\n return undefined;\n}\n\n/**\n * DLR Stage 2b — render a DIFFABLE_RANGE query's value from its keyed row-map: every row sorted by\n * `orderKey` (byte comparison via `compareKeyBytes`, matching the engine's own index-key ordering),\n * reversed for `orderDir === \"desc\"`. Always an array, never `undefined` — an empty range is `[]`,\n * mirroring the RERUN path's own \"a range query's value is a list\" contract. `orderKey` decodes via\n * `base64ToBytes` (`@helipod/index-key-codec`), the exact decoder-half of the codec the server's\n * `orderKeyFor` (`commit-differ.ts`) encodes through — never hand-roll a second base64 codec, or the\n * client's sort order can silently diverge from the server's. Mints a fresh array every call (never\n * mutates/reuses a prior render), so `recompose`'s reference-inequality check fires listeners.\n */\nfunction renderRangeValue(rows: Map<string, RowVersion>, orderDir: \"asc\" | \"desc\"): Value {\n const entries = [...rows.values()].sort((a, b) =>\n compareKeyBytes(base64ToBytes(a.orderKey ?? \"\"), base64ToBytes(b.orderKey ?? \"\")),\n );\n if (orderDir === \"desc\") entries.reverse();\n return entries.map((e) => jsonToConvex(e.row)) as Value;\n}\n\n/**\n * DLR Stage 2c — render a DIFFABLE_PAGE (`.paginate()`) query's value from its keyed row-map: the\n * `.paginate()`-shaped `{ page, nextCursor, hasMore, scanCapped }` result, with `page` sorted exactly\n * as `renderRangeValue` sorts a plain range (same `orderKey` byte-comparison decode). The pagination\n * metadata (`nextCursor`/`hasMore`/`scanCapped`) is NOT re-derived from the row-map — it is PINNED at\n * the last reset (`applyDiff`'s `reset` param) and carried through unchanged on every incremental\n * diff, so an in-bounds row add/remove reactively grows/shrinks `page` without silently moving the\n * cursor or flipping `hasMore` out from under the caller (that only happens on the NEXT explicit\n * reset — a fresh subscribe or a resync). Mints a fresh object every call (never mutates/reuses a\n * prior render), so `recompose`'s reference-inequality check fires listeners.\n */\nfunction renderPageValue(\n rows: Map<string, RowVersion>,\n orderDir: \"asc\" | \"desc\",\n meta: { nextCursor: string | null; hasMore: boolean; scanCapped: boolean },\n): Value {\n return {\n page: renderRangeValue(rows, orderDir),\n nextCursor: meta.nextCursor,\n hasMore: meta.hasMore,\n scanCapped: meta.scanCapped,\n } as Value;\n}\n\n/**\n * The writeable view of composed state an optimistic updater receives. Reads see the composed\n * state as built so far (prior layers + this updater's own writes); writes stack on top.\n *\n * T5 layers the *public typed* `OptimisticLocalStore` (with `placeholderId(table)`/`now()` derived\n * from the entry `seed`, and dev-mode `Object.freeze` on `getQuery` results) on top of this\n * internal contract — this slice implements the read/write view; those three are additive.\n */\nexport interface OptimisticStoreView {\n getQuery(ref: FunctionReference | string, args?: Record<string, Value>): Value | undefined;\n setQuery(ref: FunctionReference | string, args: Record<string, Value>, value: Value | undefined): void;\n getAllQueries(ref: FunctionReference | string): Array<{ args: Record<string, Value>; value: Value | undefined }>;\n}\n\n/** The optimistic update closure. `store` is a writeable composed view; `args` is the mutation's args. */\nexport type OptimisticUpdate = (store: OptimisticStoreView, args: Value) => void;\n\nexport type QueryListener = (value: Value) => void;\n/** Fires when a subscribed query throws server-side (its handler errored). */\nexport type QueryErrorListener = (error: string) => void;\n\nexport interface Listener {\n onUpdate: QueryListener;\n onError?: QueryErrorListener;\n}\n\nexport interface Subscription {\n queryId: number;\n path: string;\n args: JSONValue;\n hash: string;\n /** The authoritative base — written only by server ingest. */\n serverValue: Value | undefined;\n /** `serverValue` with surviving optimistic updates replayed on top (=== `serverValue` when none). */\n composedValue: Value | undefined;\n /** Has this subscription received ITS FIRST server reply yet — either outcome, `QueryUpdated` OR\n * `QueryFailed`. Distinct from `serverValue !== undefined`: a `QueryFailed` answer never sets\n * `serverValue` (there is no base to render), but it IS a delivered reply — nothing further is\n * coming for it on a quiet deployment. `hasUndeliveredSubscription()` (client.ts) keys off this,\n * not `serverValue`, so a failed-but-answered subscription doesn't wedge the outbox drain's\n * baseline await waiting for a Transition that will never arrive (re-review FIX 2).\n * `QueryUnchanged` (subscription resume) also sets this — it IS a delivered reply too, see\n * `markUnchanged`. */\n answered: boolean;\n /** Subscription resume (2025-11-28, widened DLR 2b Task 10 to cover diffable subs): the server-\n * minted fingerprint of `serverValue` — a `\"sha256:\" + hex` string stored verbatim from the most\n * recent `QueryUpdated.hash` OR a diffable sub's reset `QueryDiff.hash` (set by `reconcile.ts`,\n * never by `LayeredQueryStore.applyDiff` itself — see that method's ownership note). Echoed back\n * as `resultHash` on resubscribe (`client.ts#resync`) so the server can reply `QueryUnchanged`\n * instead of resending the full value/reset. Cleared whenever there is nothing current to echo: a\n * hash-less `QueryUpdated` (old server, or wire-compat hand-construction), or an INCREMENTAL\n * `QueryDiff` (the value just changed and carries no fingerprint) — an old-server session, or a\n * session mid-diff, must never echo a stale hash. Distinct from the query-identity `hash` field\n * above (`path + args` — never changes for a given subscription); this one is a RESULT fingerprint\n * that changes every time the value does.\n */\n lastHash?: string;\n /** DLR Stage 2a — the by-id materialized cache. For a DIFFABLE query the authoritative base is\n * this keyed row-map (`docId -> {row, ts}`); `serverValue` is re-DERIVED from it on every\n * `applyDiff` (for by-id: the sole entry's row, or `undefined` when empty). Absent for a RERUN\n * query (`serverValue` is set directly by `setServerValue`). Held per-subscription so the client\n * can compute the drift checksum and apply incremental `QueryDiff`s over the running map. */\n diffRows?: Map<string, RowVersion>;\n /** DLR Stage 2b/2c — which shape to render `diffRows` as: `\"byid\"` (sole entry's row, or\n * `undefined`), `\"range\"` (every row sorted by `orderKey`, always an array), or `\"page\"` (2c —\n * `{ page, nextCursor, hasMore, scanCapped }`, `page` sorted the same way `\"range\"` is). Set from a\n * reset descriptor's `mode` (`applyDiff`'s `reset` param); undefined for a non-diffable (RERUN)\n * sub, or before a diffable sub's first `applyDiff` call. Defaults to by-id rendering when unset,\n * so a by-id sub (whose resets are always `reset: true`, never an object) never needs to set it. */\n renderMode?: \"byid\" | \"range\" | \"page\";\n /** DLR Stage 2b — the range/page sub's sort direction, set alongside `renderMode` from a\n * range/page reset's `orderDir`. Unused for `renderMode === \"byid\"`. */\n orderDir?: \"asc\" | \"desc\";\n /** DLR Stage 2c — a page sub's pagination metadata, set alongside `renderMode`/`orderDir` from a\n * page reset's `nextCursor`/`hasMore`/`scanCapped`. PINNED across incremental diffs — see\n * `renderPageValue`'s doc for why it is not re-derived from the row-map on every apply. Unused for\n * any other `renderMode`. */\n pageMeta?: { nextCursor: string | null; hasMore: boolean; scanCapped: boolean };\n listeners: Set<Listener>;\n}\n\n/** An updater that threw during replay — the caller drops the entry and warns. */\nexport interface ReplayDrop {\n requestId: string;\n error: unknown;\n}\n\nexport class LayeredQueryStore {\n readonly byHash = new Map<string, Subscription>();\n readonly byId = new Map<number, Subscription>();\n\n create(queryId: number, path: string, args: JSONValue, hash: string): Subscription {\n const sub: Subscription = {\n queryId,\n path,\n args,\n hash,\n serverValue: undefined,\n composedValue: undefined,\n answered: false,\n lastHash: undefined,\n listeners: new Set(),\n };\n this.byHash.set(hash, sub);\n this.byId.set(queryId, sub);\n return sub;\n }\n\n remove(hash: string): void {\n const sub = this.byHash.get(hash);\n if (!sub) return;\n this.byHash.delete(hash);\n this.byId.delete(sub.queryId);\n }\n\n /** Set a subscription's authoritative base (from a `QueryUpdated` modification). Does NOT fire —\n * `recompose` owns all listener firing so the base+drop+rebuild happen as one atomic frame.\n * `hash` is the server-minted result fingerprint carried on the same modification — stored\n * verbatim (undefined clears `lastHash`, e.g. an old server that never sends `hash`).\n *\n * DLR Stage 2b (Finding 2): a `QueryUpdated` is the ONLY way a DIFFABLE (range/by-id) sub ever\n * reaches this method — the server sends a full RERUN answer instead of a `QueryDiff` only on a\n * RERUN-fallback (a `SetAuth` identity switch drops the server-side row-map; a fleet-forwarded\n * RERUN does the same), never in steady state (steady state for a diffable sub is `QueryDiff`).\n * So revert the sub to a plain RERUN-rendered sub here: drop the PRIOR identity's `diffRows` and\n * `renderMode`. Otherwise a subsequent incremental `QueryDiff` would merge onto the stale previous-\n * identity row-map and `renderRangeValue` the prior user's rows for ~1 RTT until drift-resync heals\n * — a transient cross-identity leak on an auth-scoped range query. Cleared → a later incremental\n * diff instead hits `reconcile.ts`'s uninitialized-render-mode guard (→ resync), and a later\n * `QueryDiff` reset re-establishes `renderMode`/`diffRows` cleanly. The immediate frame renders\n * `value` directly (recompose reads `serverValue`, never `diffRows`), so no stale row is shown. */\n setServerValue(sub: Subscription, value: Value | undefined, hash?: string): void {\n sub.serverValue = value;\n sub.lastHash = hash;\n sub.answered = true;\n sub.diffRows = undefined;\n sub.renderMode = undefined;\n sub.orderDir = undefined;\n sub.pageMeta = undefined;\n }\n\n /** Mark a subscription as having received its first reply WITHOUT a base value (from a\n * `QueryFailed` modification — there is no server row to render, only an error). See the\n * `answered` field doc for why this is distinct from `setServerValue`. */\n markAnswered(sub: Subscription): void {\n sub.answered = true;\n }\n\n /** Ingest a `QueryUnchanged` modification (subscription resume, design 2025-11-28): the fresh\n * server-side re-run's hash matched what this session echoed, so there is no new value on the\n * wire — `serverValue`/`lastHash`/`composedValue` all stay exactly as they were. It still counts\n * as a delivered reply (`answered = true`, same as `setServerValue`/`markAnswered`) — see the\n * `answered` field doc. The caller (`reconcile.ts`) is responsible for passing this sub's `hash`\n * into `recompose`'s `forceNotify` set — see that method's doc for why a `QueryUnchanged` must\n * still fire listeners to introduce no new observable difference for app code. */\n markUnchanged(sub: Subscription): void {\n sub.answered = true;\n }\n\n /**\n * DLR Stage 2a/2b — apply a `QueryDiff`'s changes to a DIFFABLE query's keyed row-map, re-derive\n * the rendered `serverValue` as a FRESH reference (so `recompose`'s reference-inequality check\n * fires listeners), and report whether the client-recomputed drift checksum diverged from the\n * server's. The caller (the reconciler) triggers a scoped resync on `drift === true`.\n *\n * `reset` (2b — widened from a bare `true`) governs where the diff applies FROM and how the\n * result renders:\n * - `undefined` (an incremental diff): merges `changes` onto the RUNNING `sub.diffRows` map —\n * `sub.renderMode`/`orderDir` are left exactly as they were from the last reset.\n * - `true` (a by-id reset): the classic 2a reset — `sub.renderMode = \"byid\"`.\n * - `{ mode, orderDir }` (a range reset, or a future non-byid mode): `sub.renderMode = mode`,\n * `sub.orderDir = orderDir`.\n * Both reset forms rebuild from an EMPTY map, never merge onto `sub.diffRows` — a reset is a full\n * re-baseline (the initial subscribe answer, or a range resync after a drift/table-invalidation),\n * so a row that's no longer in the result set must disappear, not linger from the prior baseline.\n * `applyChanges` is copy-on-write, so `diffRows` becomes a new Map each apply — the client never\n * mutates a map a listener may still hold.\n *\n * `lastHash` OWNERSHIP (DLR 2b Task 10): this method does NOT touch `sub.lastHash` — the caller\n * (`reconcile.ts#ingestTransition`'s `QueryDiff` arm) owns it, since only the caller knows whether\n * this call was a RESET (carries the server's resume fingerprint, `mod.hash`, to store) or an\n * INCREMENTAL diff (no fingerprint — the caller clears it instead). Leaving it here would mean\n * either always clearing it (breaking resume for every diffable sub, the Task 10 regression this\n * fixes) or reaching into wire-message fields (`mod.hash`) this store-level method doesn't receive.\n *\n * NOTE ON AN UNINITIALIZED `renderMode` (DLR 2b Task 10 residual): this method does NOT itself\n * guard against an INCREMENTAL diff (`reset === undefined`) arriving while `renderMode` has never\n * been established — it can't distinguish that from the ordinary \"first-ever call, reset omitted\"\n * shorthand several of this file's own unit tests use to mean \"build from an empty baseline\" (a\n * pattern that predates `reset`'s widening and has no way here to tell \"legitimately fresh\" apart\n * from \"already answered by something else, never diff-initialized\"). The caller\n * (`reconcile.ts#ingestTransition`) owns that distinction instead — it has `sub.answered`'s\n * PRE-CALL value on hand, which this method's own post-call mutation of `sub.answered` would\n * otherwise erase, and doing it there also means the store-level surface tested here stays exactly\n * as it always was.\n */\n applyDiff(\n sub: Subscription,\n changes: readonly Change[],\n checksum: string,\n reset?:\n | true\n | { mode: \"byid\" | \"range\"; orderDir?: \"asc\" | \"desc\" }\n | { mode: \"page\"; orderDir: \"asc\" | \"desc\"; nextCursor: string | null; hasMore: boolean; scanCapped: boolean },\n ): { drift: boolean } {\n if (reset === true) {\n sub.renderMode = \"byid\";\n } else if (reset) {\n sub.renderMode = reset.mode;\n sub.orderDir = reset.orderDir;\n if (reset.mode === \"page\") {\n sub.pageMeta = { nextCursor: reset.nextCursor, hasMore: reset.hasMore, scanCapped: reset.scanCapped };\n }\n }\n // `reset === undefined` (an incremental diff): `renderMode`/`orderDir`/`pageMeta` are left exactly\n // as they were from the last reset — a page sub's pagination metadata is PINNED across incremental\n // diffs (see `renderPageValue`'s doc), not re-derived here.\n const base = reset !== undefined ? new Map<string, RowVersion>() : (sub.diffRows ?? new Map<string, RowVersion>());\n const next = applyChanges(base, changes);\n sub.diffRows = next;\n sub.serverValue =\n sub.renderMode === \"page\"\n ? renderPageValue(next, sub.orderDir ?? \"asc\", sub.pageMeta!)\n : sub.renderMode === \"range\"\n ? renderRangeValue(next, sub.orderDir ?? \"asc\")\n : renderByIdValue(next);\n sub.answered = true;\n return { drift: driftChecksum(next) !== checksum };\n }\n\n private serverValueOf(hash: string): Value | undefined {\n return this.byHash.get(hash)?.serverValue;\n }\n\n /**\n * Rebuild every subscription's `composedValue` = replay the surviving updates (in `requestId`\n * order) over the current `serverValue`s, then fire listeners wherever the composed value's\n * reference changed. Transactional per-updater: a throwing updater is buffered-and-discarded and\n * returned as a `ReplayDrop` (the caller removes it from the log); the rebuild always completes.\n *\n * @param entries surviving pending mutations, in replay order\n * @param invokeUpdate runs one entry's updater against the view (kept in the reconciler so the\n * entry `seed` can feed `placeholderId()`/`now()` when T5 wires them)\n * @param forceNotify subscription `hash`es (subscription resume) to fire listeners for even when\n * the composed reference didn't change — a `QueryUnchanged` ingest has no new\n * value to swap in (`serverValue` is retained as-is), yet this store has NEVER\n * done content-based dedup for a plain `QueryUpdated` either: `setServerValue`\n * unconditionally overwrites `serverValue`, and — because `jsonToConvex` mints\n * a fresh object on every decode — the reference-inequality check below ALWAYS\n * fires for a content-identical `QueryUpdated` today (verified in\n * `test/resume-client.test.ts`'s Step-1 comment). `forceNotify` reproduces\n * that same always-fires behavior for `QueryUnchanged`, so it introduces no new\n * observable difference for app code. One nuance worth flagging: a\n * `QueryUnchanged` notify hands listeners the RETAINED `serverValue` reference\n * (nothing new was decoded), whereas a value-equal `QueryUpdated` today mints a\n * fresh object every time — so a reference-equality consumer (e.g. React's\n * `useState`, which bails out of a re-render when the new state === the old\n * state) may legitimately skip one redundant re-render on this path that it\n * wouldn't skip on an equivalent full send. Content identity is hash-proven\n * either way; only the object identity differs.\n */\n recompose(\n entries: Iterable<PendingMutation>,\n invokeUpdate: (entry: PendingMutation, view: OptimisticStoreView) => void,\n forceNotify?: ReadonlySet<string>,\n ): ReplayDrop[] {\n // Committed overlay: hash -> value, accumulated across updaters in order. Absent hash === base.\n const overlay = new Map<string, Value | undefined>();\n const dropped: ReplayDrop[] = [];\n\n for (const entry of entries) {\n if (!entry.update) continue;\n const local = new Map<string, Value | undefined>(); // this updater's writes, isolated until it succeeds\n const touched = new Set<string>();\n const read = (hash: string): Value | undefined =>\n local.has(hash) ? local.get(hash) : overlay.has(hash) ? overlay.get(hash) : this.serverValueOf(hash);\n const view: OptimisticStoreView = {\n getQuery: (ref, args = {}) => read(queryHash(getFunctionPath(ref), convexToJson(args as Value))),\n setQuery: (ref, args, value) => {\n const h = queryHash(getFunctionPath(ref), convexToJson(args as Value));\n local.set(h, value);\n touched.add(h);\n },\n getAllQueries: (ref) => {\n const path = getFunctionPath(ref);\n const out: Array<{ args: Record<string, Value>; value: Value | undefined }> = [];\n for (const s of this.byHash.values()) {\n if (s.path === path) out.push({ args: jsonToConvex(s.args) as Record<string, Value>, value: read(s.hash) });\n }\n return out;\n },\n };\n try {\n invokeUpdate(entry, view);\n } catch (error) {\n dropped.push({ requestId: entry.requestId, error }); // local writes discarded — overlay untouched\n continue;\n }\n for (const h of touched) overlay.set(h, local.get(h));\n entry.touched = touched;\n }\n\n // Apply composed values + fire on reference change. Untouched subs get the SAME serverValue\n // reference (byte-identity invariant), so they never spuriously fire — UNLESS `forceNotify`\n // names them (a `QueryUnchanged` resume, matching today's always-fires `QueryUpdated` behavior).\n for (const sub of this.byHash.values()) {\n const next = overlay.has(sub.hash) ? overlay.get(sub.hash) : sub.serverValue;\n if (next !== sub.composedValue || forceNotify?.has(sub.hash)) {\n sub.composedValue = next;\n if (next !== undefined) {\n for (const l of sub.listeners) l.onUpdate(next);\n }\n }\n }\n return dropped;\n }\n}\n","/**\n * S3 — the reconcile chokepoint. EVERY state change flows through this one object: mutation\n * initiation, server ingest (Transition), mutation resolution (success/failure), the gate-timeout\n * valve, and transport close. It is the sole place optimistic layers are applied, dropped, or\n * replayed. The gate predicate is isolated behind `versionCoversCommit` so the sharded-frontier\n * future (lmid-shape identity confirmation, verdict §(g)) changes one predicate, not the reconciler.\n *\n * The reconciler owns the `MutationLog` (S1), the `maxObservedTs` frontier, and the per-entry gate\n * timers; it drives the `LayeredQueryStore` (S2) and consults the `DeliveryPolicy` (S4) at close.\n * It does NOT own promise callbacks or the transport — the client resolves/rejects promises and\n * decides send-vs-unsent, calling into the reconciler for all layer bookkeeping.\n */\nimport { jsonToConvex } from \"@helipod/values\";\nimport type { StateModification } from \"@helipod/sync\";\nimport type { LayeredQueryStore, OptimisticStoreView } from \"./layered-store\";\nimport { MutationLog, type PendingMutation } from \"./mutation-log\";\nimport { closeDisposition } from \"./delivery-policy\";\nimport { createOptimisticLocalStore } from \"./optimistic-store\";\n\n/**\n * The gate predicate (v1). A `completed` layer is safe to drop once this client's own reactive feed\n * has observed a ts at or beyond the mutation's commit — \"drop on observed inclusion, never on the\n * ack alone\". Guarded `commitTs > 0` so a leaked `0`/absent commitTs can never falsely gate.\n */\nexport function versionCoversCommit(maxObservedTs: number, commitTs: number): boolean {\n return commitTs <= maxObservedTs && commitTs > 0;\n}\n\n/** Result of a `MutationResponse` — the client already resolved the promise; this handles the layer. */\nexport interface CloseResult {\n /** `inflight` request ids whose promises the client must reject with `MutationUndeliveredError`. */\n rejectedInflight: string[];\n /** `inflight` request ids that PARKED instead (Task 2's S4 swap, armed + durable) — their\n * promise stays pending; the client must NOT reject (or resolve) them here. */\n parked: string[];\n}\n\nconst DEFAULT_GATE_TIMEOUT_MS = 10_000;\n\nexport class Reconciler {\n readonly log = new MutationLog();\n private observedTs = 0;\n private readonly gateTimeoutMs: number;\n private readonly timers = new Map<string, ReturnType<typeof setTimeout>>();\n\n /** DLR Stage 2a — invoked when a `QueryDiff`'s client-recomputed drift checksum diverges from the\n * server's. The client wires this to a scoped resync (`client.ts` passes `() => this.resync()`).\n * For by-id the diff is trivially correct so this should never fire in normal operation; it is the\n * safety net that 2b's harder differ inherits already proven. */\n private readonly onDrift?: (queryId: number) => void;\n\n constructor(\n private readonly store: LayeredQueryStore,\n opts: { gateTimeoutMs?: number; onDrift?: (queryId: number) => void } = {},\n ) {\n this.gateTimeoutMs = opts.gateTimeoutMs ?? DEFAULT_GATE_TIMEOUT_MS;\n this.onDrift = opts.onDrift;\n }\n\n /** Max `endVersion.ts` observed this session (reset on close). Exposed for tests. */\n get maxObservedTs(): number {\n return this.observedTs;\n }\n\n entries(): PendingMutation[] {\n return this.log.entriesInOrder();\n }\n\n /** T6: `unsent` entries in FIFO (requestId/insertion) order — flushed on transport reopen. */\n unsentInOrder(): PendingMutation[] {\n return this.log.entriesInOrder().filter((e) => e.status.type === \"unsent\");\n }\n\n /** T3: the log entry for `requestId` (or `undefined`). Lets `client.ts` read an outbox entry's\n * recorded `(clientId, seq)` for dequeue-on-success BEFORE a settling event removes it from the\n * log. Read-only — all mutation still flows through the events below. */\n getEntry(requestId: string): PendingMutation | undefined {\n return this.log.get(requestId);\n }\n\n /**\n * The drop-on-verdict-after-baseline rule (T3, verdict §(d) \"Reload and rendering — the fork,\n * decided\"). A CROSS-SESSION entry whose recorded verdict is `applied` (learned via the `Connect`\n * handshake's `ConnectAck` or a drain replay-ack) drops its optimistic layer once the reconnect\n * baseline Transition has been ADOPTED. Sound because the entry's commit necessarily predates\n * this session's `Connect`, hence predates the baseline's read snapshot — so the baseline already\n * renders the effect; removing the (registry-rebuilt, T5) layer in the SAME one-pass `rebuild()`\n * is flicker-free (the authoritative rows are already in the base, so the composed view never\n * blinks the row away). The rule is deliberately **layer-agnostic**: the entry may hold no layer\n * at all — a parked entry (its `update` was cleared at close), a plain non-optimistic mutation,\n * or (until T5 ships the registry) any hydrated entry — in which case this is a clean removal.\n *\n * The CALLER (`client.ts`) is responsible for gating the call on baseline adoption; this method\n * assumes that gate has already passed. `versionCoversCommit` (the same-session gate predicate)\n * is intentionally untouched — same-session entries still drop on observed inclusion, never here.\n */\n onVerdictAfterBaseline(requestId: string): void {\n if (!this.log.get(requestId)) return;\n this.log.delete(requestId);\n this.clearTimer(requestId);\n this.rebuild();\n }\n\n /**\n * T5: add a HYDRATED (cross-reload) durable entry to the log — `client.ts#addHydratedEntry`'s\n * counterpart to `initiate()` for a live call-site mutation. `entry.update` may already be set\n * (a registry hit — `client.ts` looks it up BEFORE calling this). Unlike `initiate()`, where the\n * OWN entry's throw is rethrown synchronously to the `mutation()` caller, a REGISTERED updater\n * that throws here is ordinary replay-drop collateral — warned and dropped via the normal\n * `rebuild()` path, never rethrown (there is no synchronous caller on the hydrate path to\n * propagate to; the entry still drains fine, only its rendering is lost). An entry with no\n * `update` (no registry hit, or the registry simply wasn't configured) is added without a\n * recompose pass at all — a plain layerless entry, exactly T4's pre-registry behavior.\n */\n addHydrated(entry: PendingMutation): void {\n this.log.add(entry);\n if (entry.update) this.rebuild();\n }\n\n private invokeUpdate = (entry: PendingMutation, view: OptimisticStoreView): void => {\n // T5: enrich the raw view into the typed OptimisticLocalStore (placeholderId()/now()/dev-freeze,\n // derived from `entry.seed`) before invoking. `entry.update`'s declared param type is the\n // internal `OptimisticStoreView` (a subset of `OptimisticLocalStore`'s surface); a real\n // `PendingMutation.update` created via `useMutation(...).withOptimisticUpdate(...)` is actually\n // typed against `OptimisticLocalStore` and cast down at that boundary (react.tsx) — this is the\n // runtime guarantee that makes that cast sound: every invocation, from every entry point,\n // always receives the fully-enriched store.\n const store = createOptimisticLocalStore(view, entry.seed);\n entry.update!(store, jsonToConvex(entry.args));\n };\n\n /** Rebuild composed values; drop + warn any entry whose updater threw during replay.\n * `forceNotify` (subscription resume) forwards straight to `store.recompose` — see its doc. */\n private rebuild(forceNotify?: ReadonlySet<string>): void {\n const dropped = this.store.recompose(this.log.entriesInOrder(), this.invokeUpdate, forceNotify);\n for (const d of dropped) {\n const path = this.log.get(d.requestId)?.udfPath ?? d.requestId;\n this.log.delete(d.requestId);\n this.clearTimer(d.requestId);\n console.warn(`[helipod] optimistic update for \"${path}\" threw during replay; dropping its pending layer`, d.error);\n }\n }\n\n /**\n * Event 1 — mutation initiation. Add the entry, replay all surviving updates over the current\n * base. If THIS entry's updater throws, it is removed and the error rethrown **synchronously** so\n * the caller sends nothing (a prior entry throwing is contained + warned, not rethrown).\n */\n initiate(entry: PendingMutation): void {\n this.log.add(entry);\n if (!entry.update) return; // plain mutation — no layer to build\n const dropped = this.store.recompose(this.log.entriesInOrder(), this.invokeUpdate);\n let ownError: { error: unknown } | undefined;\n for (const d of dropped) {\n this.log.delete(d.requestId);\n this.clearTimer(d.requestId);\n if (d.requestId === entry.requestId) {\n ownError = { error: d.error };\n } else {\n const path = this.log.get(d.requestId)?.udfPath ?? d.requestId;\n console.warn(`[helipod] optimistic update for \"${path}\" threw during replay; dropping its pending layer`, d.error);\n }\n }\n if (ownError) throw ownError.error;\n }\n\n /**\n * Event 2 — a contiguous (or resync-adopted) Transition, applied as ONE synchronous pass:\n * advance the frontier, apply modifications to the base, drop every gated `completed` layer, then\n * rebuild composed. The frame where a layer disappears is the same frame its authoritative rows\n * appear — the no-flicker guarantee. An empty (`modifications: []`) ts-advancing Transition (T2)\n * flows through here with zero special-casing: the loop simply does no base writes.\n */\n ingestTransition(modifications: StateModification[], endTs: number): void {\n this.observedTs = Math.max(this.observedTs, endTs);\n // Subscription resume: `hash`es of subs answered via `QueryUnchanged` this pass — forwarded to\n // `rebuild`/`recompose` so they still fire listeners despite no reference change (see\n // `LayeredQueryStore.recompose`'s `forceNotify` doc).\n let unchanged: Set<string> | undefined;\n for (const mod of modifications) {\n if (mod.type === \"QueryUpdated\") {\n const sub = this.store.byId.get(mod.queryId);\n if (sub) this.store.setServerValue(sub, jsonToConvex(mod.value), mod.hash);\n } else if (mod.type === \"QueryFailed\") {\n const sub = this.store.byId.get(mod.queryId);\n if (sub) {\n this.store.markAnswered(sub);\n console.error(`[helipod] query \"${sub.path}\" failed: ${mod.error}`);\n for (const l of sub.listeners) l.onError?.(mod.error);\n }\n } else if (mod.type === \"QueryUnchanged\") {\n const sub = this.store.byId.get(mod.queryId);\n if (sub) {\n this.store.markUnchanged(sub);\n (unchanged ??= new Set()).add(sub.hash);\n }\n } else if (mod.type === \"QueryDiff\") {\n // DLR Stage 2a/2b: a DIFFABLE (by-id or range) sub's answer — a reset (initial subscribe, or\n // a resync) or an incremental diff. `applyDiff` mutates the sub's row-map and re-derives\n // `serverValue` (a fresh reference, so the `rebuild` below fires listeners) per `mod.reset`'s\n // mode (by-id sole-row vs. range sorted array); a checksum mismatch triggers a scoped resync.\n const sub = this.store.byId.get(mod.queryId);\n if (sub) {\n // UNINITIALIZED-RENDER-MODE GUARD (DLR 2b Task 10 residual): an INCREMENTAL diff\n // (`mod.reset === undefined`) arriving for a sub that was already ANSWERED by something\n // OTHER than a reset (`sub.answered` true, but `sub.renderMode` never established) is\n // treated exactly like a checksum-drift resync, WITHOUT calling `applyDiff` at all — so\n // `serverValue`/`diffRows` are left untouched rather than silently mis-rendered (e.g. a\n // range sub falling back to `applyDiff`'s by-id single-row default). This is reachable even\n // though the server only ever emits an incremental diff for an already-registered\n // subscription: a `QueryUnchanged` RESUME answer (Task 10) seeds the SERVER's own row-map\n // (ready to diff going forward) without teaching the CLIENT anything about render shape —\n // and a client whose retained cross-session baseline for this query predates diff-\n // capability entirely (its subscribe was answered `QueryUpdated` in an earlier session,\n // before this connection ever advertised `supportsQueryDiff`) has no `renderMode` to resume\n // from. Checked BEFORE `sub.answered` gets read anywhere else this pass (see below), and\n // gated on `sub.answered`'s PRE-diff value so a genuinely fresh, never-yet-answered sub\n // (this subscription's first-ever reply) is unaffected — that can only be a real reset\n // (`mod.reset` defined) on the actual wire, so this branch never fires for it regardless.\n if (mod.reset === undefined && sub.renderMode === undefined && sub.answered) {\n console.error(`[helipod] query \"${sub.path}\" incremental diff arrived with no established render mode; resyncing`);\n sub.lastHash = undefined;\n this.onDrift?.(mod.queryId);\n } else {\n const { drift } = this.store.applyDiff(sub, mod.changes, mod.checksum, mod.reset);\n // DLR 2b Task 10 — resume fingerprint ownership: a RESET carries the server's `hashValue`\n // fingerprint of the fresh baseline (same contract `QueryUpdated.hash` already has) —\n // store it so a later resubscribe can echo it back for a `QueryUnchanged` resume. An\n // INCREMENTAL diff (`mod.reset === undefined`) has no such fingerprint (the server never\n // computes one for a single write's delta) and, more importantly, the value just CHANGED\n // — any hash on file is now stale, so it must be cleared rather than left dangling.\n // Echoing nothing on the next resubscribe safely forces a full reset; echoing a stale\n // hash could never happen here (undefined never matches), but clearing it is the honest,\n // cheap-to-reason-about invariant.\n sub.lastHash = mod.reset !== undefined ? mod.hash : undefined;\n if (drift) {\n console.error(`[helipod] query \"${sub.path}\" diff checksum mismatch; resyncing`);\n this.onDrift?.(mod.queryId);\n }\n }\n }\n }\n // QueryRemoved: keep the last known base.\n }\n for (const entry of this.log.entriesInOrder()) {\n if (entry.status.type === \"completed\" && versionCoversCommit(this.observedTs, entry.status.commitTs)) {\n this.log.delete(entry.requestId);\n this.clearTimer(entry.requestId);\n }\n }\n this.rebuild(unchanged);\n }\n\n /**\n * Event 3 — `MutationResponse` success carrying `ts` (W1). The client already resolved the\n * promise (D3). Here: drop now if there is nothing to protect (no updater / nothing touched), or\n * the gate is already covered, or `ts` is missing/≤0 (accept one-frame flicker over a wedge —\n * the server-side `commitTs > 0` assertion makes this unreachable); otherwise hold the layer as\n * `completed` and arm the gate timer.\n */\n onMutationSuccess(requestId: string, ts: number | undefined): void {\n const entry = this.log.get(requestId);\n if (!entry) return; // already dropped (e.g. replay-throw) — promise handled by the client\n if (!entry.update || entry.touched.size === 0) {\n this.log.delete(requestId);\n return; // nothing rendered — no rebuild needed\n }\n if (ts === undefined || ts <= 0) {\n console.warn(`[helipod] mutation \"${entry.udfPath}\" acked with no usable commitTs (ts=${ts}); dropping its layer now`);\n this.log.delete(requestId);\n this.rebuild();\n return;\n }\n if (versionCoversCommit(this.observedTs, ts)) {\n this.log.delete(requestId);\n this.rebuild();\n return;\n }\n entry.status = { type: \"completed\", commitTs: ts, completedAt: Date.now() };\n this.armGateTimer(requestId);\n }\n\n /** Event 4 — `MutationResponse` failure. The client rejected the promise; drop the layer + rebuild. */\n onMutationFailure(requestId: string): void {\n if (!this.log.get(requestId)) return;\n this.log.delete(requestId);\n this.clearTimer(requestId);\n this.rebuild();\n }\n\n /**\n * Event 6 — transport close (S4). `unsent` retained; `inflight`/`completed` layers drop; the\n * frontier resets; composed rebuilds over the retained set. Returns the `inflight` ids the client\n * must reject with `MutationUndeliveredError`. NO layer crosses a session.\n *\n * Task 2 extends this with the S4 park swap: when `armed` (a `ConnectAck` has proven server-side\n * dedup — T3 sets it via `client.ts#setOutboxArmed`) AND an `inflight` entry's durable append has\n * already committed (`entry.durable`), it PARKS instead of rejecting — its `status` flips to\n * `\"parked\"` and its `update` closure is cleared (the layer still drops, via the normal\n * `rebuild()` below: `recompose` skips any entry with no `update`, the same mechanism a plain\n * non-optimistic mutation already relies on) but the entry itself STAYS in the log, ready for a\n * future drain (T4) to resend under its recorded `(clientId, seq)`. Every other `drop`ped id\n * (rejected-inflight, completed) is still fully removed from the log, exactly as before.\n */\n closeSession(armed = false): CloseResult {\n const disp = closeDisposition(this.log.entriesInOrder(), { armed });\n const parkedIds = new Set(disp.park);\n for (const rid of disp.park) {\n const entry = this.log.get(rid);\n if (entry) {\n entry.status = { type: \"parked\" };\n entry.update = undefined; // layer drops — unchanged rule (verdict §(d)); entry itself stays\n }\n }\n for (const rid of disp.drop) {\n if (parkedIds.has(rid)) continue; // parked entries are NOT removed from the log\n this.log.delete(rid);\n this.clearTimer(rid);\n }\n this.observedTs = 0; // reset with the session — the ts-gate is only sound over one monotone feed\n this.rebuild();\n return { rejectedInflight: disp.reject, parked: disp.park };\n }\n\n private armGateTimer(requestId: string): void {\n this.clearTimer(requestId);\n // Event 5 — the gate-timeout valve: no wrong guess and no lost frame can wedge a layer forever.\n const timer = setTimeout(() => {\n this.timers.delete(requestId);\n const entry = this.log.get(requestId);\n if (!entry || entry.status.type !== \"completed\") return;\n console.warn(`[helipod] mutation \"${entry.udfPath}\" layer not confirmed within ${this.gateTimeoutMs}ms; dropping it`);\n this.log.delete(requestId);\n this.rebuild();\n }, this.gateTimeoutMs);\n // Don't keep the process alive for a pending gate timer (Node).\n (timer as { unref?: () => void }).unref?.();\n this.timers.set(requestId, timer);\n }\n\n private clearTimer(requestId: string): void {\n const timer = this.timers.get(requestId);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.timers.delete(requestId);\n }\n }\n}\n","/**\n * S1 — the `MutationLog`. One entry per unconfirmed mutation, held in a Map whose iteration order\n * is insertion order — which, because `requestId`s are assigned by a monotonically-incrementing\n * counter (`client.ts`), IS the \"requestId order\" the reconciler replays surviving updates in\n * (verdict §(c) event 2d). The entry carries the **serializable triple** `(requestId, udfPath,\n * args)` from day one (verdict §(b)'s A1 amendment) so \"the log is what a durable outbox later\n * persists\" is true instead of aspirational — the durable-offline slice backs S1 with IndexedDB\n * without reshaping this record.\n */\nimport type { JSONValue } from \"@helipod/values\";\nimport type { OptimisticUpdate } from \"./layered-store\";\n\n/** A single unconfirmed mutation and its optimistic effect. Verbatim from verdict §(b). */\nexport interface PendingMutation {\n /** Client-local id; rides the existing wire `requestId` field. Opaque string (kept opaque so a\n * future durable outbox can choose uuid vs monotone clientSeq without reshaping this record). */\n requestId: string;\n udfPath: string;\n /** `(requestId, udfPath, args)` = the serializable triple. */\n args: JSONValue;\n /** The optimistic update closure. Looked up at replay, never serialized. Absent for a plain\n * (non-optimistic) mutation — such an entry holds no layer and only tracks in-flight status. */\n update?: OptimisticUpdate;\n /** Fixed at creation — feeds the deterministic `placeholderId()`/`now()` an updater calls (D2);\n * the SAME seed is reused on every replay so minted ids/timestamps are stable. (T5 consumes it.) */\n seed: { entropy: string; now: number };\n /** Query hashes the updater wrote to on its most recent run — which subscriptions its layer covers. */\n touched: Set<string>;\n status:\n | { type: \"unsent\" } // queued, never hit the wire — safe to (re)send\n | { type: \"inflight\" } // sent, no response — outcome unknowable on disconnect\n | { type: \"completed\"; commitTs: number; completedAt: number } // acked; layer held for the gate\n | { type: \"parked\" }; // T2: closed with a durable append + the S4 swap armed — awaits a future drain\n /**\n * Durable-outbox identity (verdict §(d), Task 2) — present ONLY when this `HelipodClient` was\n * constructed with a durable `outbox`; a client without one never sets any of the fields below,\n * so `entriesInOrder()` and the wire `Mutation` shape stay byte-identical to before this task for\n * that path. `clientId`/`seq` ride the wire `Mutation`/`MutationBatchEntry` (`clientId`/`seq`,\n * `@helipod/sync`'s `protocol.ts`) whenever an outbox is configured — carried for park-safety\n * on every send, not just once the S4 swap is armed (see `client.ts#mutationMessage`).\n */\n clientId?: string;\n seq?: number;\n /** Global position across the WHOLE shared durable queue (every clientId sharing one outbox) —\n * the drain's FIFO key (T4 consumes it; T2 only assigns it). */\n order?: number;\n /** SHA-256 of the last `SetAuth` token (\"anon\" for none/empty) — stamped synchronously from a\n * cache `client.ts#setAuth` computes asynchronously (spec §(k)7: SubtleCrypto is async, enqueue\n * is sync, so the digest is computed ahead of time and merely read here). */\n identityFingerprint?: string;\n enqueuedAt?: number;\n /** Flips `true` once this entry's `OutboxStorage.append()` has resolved. \"Park eligibility\n * requires durability\" (verdict §(d)): `delivery-policy.ts#closeDisposition` only parks an\n * `inflight` entry when this is `true` — a still-in-flight (unconfirmed) append rejects with\n * `MutationUndeliveredError` exactly as before this task existed. Always left falsy when no\n * outbox is configured. */\n durable?: boolean;\n}\n\n/** The ordered set of unconfirmed mutations. Insertion order == requestId order (see file doc). */\nexport class MutationLog {\n private readonly entries = new Map<string, PendingMutation>();\n\n add(entry: PendingMutation): void {\n this.entries.set(entry.requestId, entry);\n }\n\n get(requestId: string): PendingMutation | undefined {\n return this.entries.get(requestId);\n }\n\n delete(requestId: string): boolean {\n return this.entries.delete(requestId);\n }\n\n /** All entries in requestId (insertion) order — the replay order. */\n entriesInOrder(): PendingMutation[] {\n return [...this.entries.values()];\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n"],"mappings":"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","names":["convexToJson","jsonToConvex","jsonToConvex","jsonToConvex","jsonToConvex","jsonToConvex","convexToJson","jsonToConvex"]}
|