@doync/client 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/adapter.cjs +1 -1
- package/dist/adapter.d.cts +1 -1
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.js +1 -1
- package/dist/{client-CR5SoP5D.cjs → client-AGRXHAJy.cjs} +6 -6
- package/dist/{client-OAVaC3pt.d.ts → client-BZWglwNl.d.cts} +42 -7
- package/dist/{client-OPt8Axyp.d.cts.map → client-BZWglwNl.d.cts.map} +1 -1
- package/dist/client-Dphx71Qk.js +16 -0
- package/dist/client-Dphx71Qk.js.map +1 -0
- package/dist/{client-OPt8Axyp.d.cts → client-tAY8RnNM.d.ts} +42 -7
- package/dist/{client-OAVaC3pt.d.ts.map → client-tAY8RnNM.d.ts.map} +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/internal.cjs +1 -1
- package/dist/internal.d.cts +2 -2
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/package.json +3 -3
- package/src/engine.ts +91 -6
- package/src/index.ts +2 -0
- package/src/internal.ts +4 -0
- package/dist/client-C97IzM56.js +0 -16
- package/dist/client-C97IzM56.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-Dphx71Qk.js","names":["#listeners","#status","#raw","#generation","migration0000","migration0001","migration0002","#listeners","#decodedFrom","#lastRows","#seeded","#project","#lastStatus","#seed","#held","#unwire","#db","#schema","#mutations","#socket","#generateId","#now","#connectionListeners","#onSchemaEvent","#schemaListeners","#promises","#keyed","#recoveredKeys","#rejected","#optimisticFailed","#instanceInfo","#desired","#onceRequests","#onceBuffers","#localReads","#warmReads","#errors","#chainQueue","#gatedViews","#ctx","#token","#userId","#boot","#onMessage","#onOpen","#lastPongAt","#setConnected","#onSeamStatus","#connectedClock","#clientId","#cookie","#bundleVersion","#schemaEvent","#wipeStore","#forgetStore","#appliedVersion","#durableInt","#nextMutationId","#emitSchemaEvent","#pending","#ensurePromise","#runMembershipGc","#openOverlay","#chainBusy","#finishChainStep","#runExclusive","#drainOrFlush","#flushGatedViews","#overlayOpen","#replayFrom","#overlayTables","#replayBody","#closeOverlay","#tx","#failBody","#releaseBody","#makePromise","#exclusive","#applyMutate","#rebase","#settleInitialApply","#settleClient","#notifyChange","#connected","#sendPush","#dropPending","#settleServer","#halted","#retainSubscription","#releaseSubscription","#retainPreload","#heldAtFor","#sideWrite","#desiredQuery","#setPhase","#retainDesire","#initialCompute","#stampLastRelease","#warmRead","#persistConnectedClock","#gcEligibleInstances","#breakStaleInstance","#nextOnceSeq","#retainLocal","#releaseLocal","#sendHandshake","#onPong","#setNeedsAuth","#needsAuth","#onSchemaSkew","#onSchemaDirective","#clearTransientSchemaEvent","#pokeBuffer","#applyPoke","#handleReject","#handleOnceEnd","#wipeAndResync","#failInstances","#failUnconfirmed","#doApplyPoke","#applyPut","#applyDel","#applyPks","#promoteConfirmed","#tableFor","#retractRow","#halt","#applySchemaDirective","#allConsumerTables","#forgetAndReset","#sideWritePref","#rejectAllPending","#resetToFreshClient","#rebootstrapAfterReset","#notifySchema","#seamStatus","#transitionConnection","#liveReads","#decode","#startNetwork","#drop","#snapshot","#ensureOpen","#serverPromiseWithResolvers","#disposed","#disposeTimer","#started","g"],"sources":["../src/raw-read.ts","../src/migrate.ts","../src/replica/meta.ts","../src/replica/db/0000_replica_engine_v0.sql?raw","../src/replica/db/0001_release_stamps.sql?raw","../src/replica/db/0002_prefs_table.sql?raw","../src/replica/db/meta/_journal.json","../src/replica/track.ts","../src/replica/stamps.ts","../src/replica/index.ts","../src/engine.ts","../src/client.ts"],"sourcesContent":["import type { SqlValue } from '@doync/core'\n\nimport type { ViewStatus } from './engine'\n\n/**\n * A resolved statement a raw read executes verbatim — the engine's INTERNAL\n * currency (ADR-0021). Distinct from the wire's `DesiredQuery` (`{name, args}`,\n * ADR-0016): the statement never leaves the device — the Mirror re-resolves the\n * registered callback under its own verified ctx (ADR-0018).\n */\nexport interface LocalStatement {\n readonly sql: string\n readonly params: readonly SqlValue[]\n}\n\n/**\n * What a {@link RawRead} needs from its host, injected at construction\n * (ADR-0023: raw reads receive their narrow needs and know nothing of the\n * engine). `run` executes the statement over the live replica; `readSet`\n * answers the subscription's server-confirmed Read-set for targeted\n * re-projection, or `undefined` before the ack / for a Local read. Both are\n * plain accessors so a five-line fake makes the read unit-testable.\n */\nexport interface RawReadHost {\n run(statement: LocalStatement): Record<string, SqlValue>[]\n readSet(): ReadonlySet<string> | undefined\n}\n\ntype AnyRow = Record<string, SqlValue>\n\n/**\n * One shared reactive read per Query instance (ADR-0023 share-raw): executes\n * the statement and dedups on RAW rows. No decoder here — identity excludes\n * decode/one (ADR-0021), so identical SQL flavors share this read and each\n * handle decodes (Q4). generation ticks on real change.\n *\n * Raw rows (scalars / JSON TEXT) equal cheaply; decoded nested objects cannot.\n * Status (#104) is stable until the visible status moves; pending→acked is\n * invisible.\n */\nexport class RawRead {\n #raw: AnyRow[] = []\n /** Bumped on real change — handles' decode-memo key. */\n #generation = 0\n readonly #listeners = new Set<() => void>()\n #status: ViewStatus\n\n constructor(\n private readonly host: RawReadHost,\n readonly statement: LocalStatement,\n /** The Query instance identity; `undefined` for a Local read. */\n readonly identity: string | undefined,\n /** Seed status: phase, or complete for Local. */\n initialStatus: ViewStatus,\n ) {\n this.#status = initialStatus\n }\n\n /** Subscription Read-set for targeted re-projection (via host). */\n get readSet(): ReadonlySet<string> | undefined {\n return this.identity === undefined ? undefined : this.host.readSet()\n }\n\n status(): ViewStatus {\n return this.#status\n }\n\n updateStatus(next: ViewStatus): boolean {\n if (\n this.#status.status === next.status &&\n this.#status.error === next.error\n )\n return false\n this.#status = next\n return true\n }\n\n /** Re-run statement; true iff raw rows moved. */\n recompute(): boolean {\n const rows = this.host.run(this.statement)\n if (rowsEqual(rows, this.#raw)) return false\n this.#raw = rows\n this.#generation += 1\n return true\n }\n\n /** Undecoded rows (handles decode against generation). */\n rawRows(): readonly AnyRow[] {\n return this.#raw\n }\n\n get generation(): number {\n return this.#generation\n }\n\n onChange(listener: () => void): () => void {\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\n }\n\n notify(): void {\n for (const listener of this.#listeners) listener()\n }\n}\n\n/**\n * Scalar equality on raw SQL rows — keeps `current()` stable between changes\n * (useSyncExternalStore).\n */\nexport function rowsEqual(a: readonly AnyRow[], b: readonly AnyRow[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n const ra = a[i] as AnyRow\n const rb = b[i] as AnyRow\n const ka = Object.keys(ra)\n if (ka.length !== Object.keys(rb).length) return false\n for (const k of ka) {\n if (!valuesEqual(ra[k] ?? null, rb[k] ?? null)) return false\n }\n }\n return true\n}\n\n/** SqlValue equality: === for scalars, byte-wise BLOBs (ADR-0010). */\nfunction valuesEqual(a: SqlValue, b: SqlValue): boolean {\n if (a === b) return true\n if (a instanceof ArrayBuffer && b instanceof ArrayBuffer) {\n if (a.byteLength !== b.byteLength) return false\n const va = new Uint8Array(a)\n const vb = new Uint8Array(b)\n for (let i = 0; i < va.length; i++) if (va[i] !== vb[i]) return false\n return true\n }\n return false\n}\n","import type { DoyncSchema } from '@doync/core'\n\nimport {\n parse,\n parseAll,\n SQLiteParserError,\n type Statement,\n unparse,\n} from '@doync/sqlite-parser'\n\n/**\n * Parser-backed SQL classification for client DDL-only bundled-track replay\n * (ADR-0020 / ADR-0006). Same AST as the server conversation, narrower API: 1.\n * split migrations keeping trigger BEGIN…END whole; 2. ddl vs trigger (skip —\n * apply state, never enforce; ADR-0009/0020) vs backfill DML (skip — data via\n * feed; ADR-0006). Fails closed on unparseable input.\n */\n\n/** How the client treats one already-split migration statement. */\nexport type ClientStatementKind = 'ddl' | 'trigger' | 'dml' | 'pragma' | 'other'\n\n/** Statement kinds recorded as replayable schema DDL (shape, not triggers). */\nconst DDL_KINDS = new Set<Statement['kind']>([\n 'STMT_CREATE_TABLE',\n 'STMT_CREATE_INDEX',\n 'STMT_CREATE_VIEW',\n 'STMT_CREATE_VTABLE',\n 'STMT_DROP',\n 'STMT_ALTER',\n])\n\n/**\n * Statement kinds that read or write rows. Backfill DML is never replayed on\n * the client — data arrives through the feed (ADR-0006).\n */\nconst DML_KINDS = new Set<Statement['kind']>([\n 'STMT_INSERT',\n 'STMT_UPDATE',\n 'STMT_DELETE',\n 'STMT_SELECT',\n 'COMPOUND_SELECT',\n])\n\n/**\n * Parse one statement, or `null` when the input is unparseable or empty. Only a\n * parser error maps to `null`; anything else propagates. `parse` validates the\n * whole input and returns the FIRST statement (extras are dropped), so a string\n * that begins with a valid statement classifies by that statement.\n */\nfunction parseFirstOrNull(statement: string): Statement | null {\n try {\n return parse(statement)\n } catch (error) {\n if (error instanceof SQLiteParserError) return null\n throw error\n }\n}\n\n/**\n * Parse a whole script into its statements, or `null` on a parser error (empty\n * input yields an empty list, never `null`). Only a parser error maps to\n * `null`; anything else propagates — the one place the parse-failure decision\n * lives.\n */\nfunction parseAllOrNull(sql: string): Statement[] | null {\n try {\n return parseAll(sql)\n } catch (error) {\n if (error instanceof SQLiteParserError) return null\n throw error\n }\n}\n\n/** Map a parsed statement to the client's kind bucket. */\nfunction classOf(statement: Statement): ClientStatementKind {\n // Triggers first — clients never install/drop them (ADR-0009/0020).\n if (statement.kind === 'STMT_CREATE_TRIGGER') return 'trigger'\n if (statement.kind === 'STMT_DROP' && statement.target === 'TRIGGER') {\n return 'trigger'\n }\n if (statement.kind === 'STMT_PRAGMA') return 'pragma'\n // Remaining DDL is shape-replayable (CREATE TRIGGER not in DDL_KINDS).\n if (DDL_KINDS.has(statement.kind)) return 'ddl'\n if (DML_KINDS.has(statement.kind)) return 'dml'\n return 'other'\n}\n\n/**\n * Split a migration's SQL into its top-level statements. The parser understands\n * statement structure, so a `CREATE TRIGGER … BEGIN … END` body's internal\n * semicolons stay inside one statement — a migration may mix a trigger with\n * surrounding DDL without mis-splitting. Each statement is returned as its\n * canonical unparse (semantically identical to the source); statements that are\n * empty (whitespace/comments only) yield nothing.\n *\n * Fails closed: an unparseable script throws a clear error rather than applying\n * a partial or mis-read migration. Trusted input only in the sense that the\n * consumer authored the migrations — the parse itself validates their syntax.\n */\nexport function splitClientStatements(sql: string): string[] {\n const statements = parseAllOrNull(sql)\n if (statements === null) {\n throw new Error(\n 'could not parse the SQL — a statement has a syntax error; fix it and retry',\n )\n }\n return statements.map((statement) => unparse(statement))\n}\n\n/**\n * Classify ONE already-split statement by its parsed kind. `CREATE TRIGGER` and\n * `DROP TRIGGER` are `'trigger'` (the client neither creates nor drops triggers\n * — it runs none, ADR-0009); every other shape-changing DDL is `'ddl'`\n * (replayed); the row read/write verbs are `'dml'` (backfill, never replayed on\n * the client); `PRAGMA` and anything unrecognized/unparseable land in their own\n * buckets.\n */\nexport function classifyClientStatement(\n statement: string,\n): ClientStatementKind {\n const parsed = parseFirstOrNull(statement)\n return parsed === null ? 'other' : classOf(parsed)\n}\n\n/**\n * The non-trigger DDL statements of the bundled migrations in the half-open\n * range `(fromVersion, toVersion]` (0-based migration indices `fromVersion …\n * toVersion − 1`), in order — the replayable shape for the client's DDL-only\n * bundled-track replay (ADR-0020). Trigger DDL is skipped (ADR-0009) and\n * backfill DML never runs on the client (ADR-0006). Passing `fromVersion = 0`\n * yields the full track up to `toVersion` (a fresh replica); a higher\n * `fromVersion` yields just the newly-appended migrations (a mid-session\n * catch-up).\n */\nexport function bundledSchemaDdl(\n schema: DoyncSchema,\n fromVersion: number,\n toVersion: number,\n): string[] {\n const ddl: string[] = []\n for (let version = fromVersion; version < toVersion; version += 1) {\n const migration = schema.migrations[version]\n if (migration === undefined) {\n // Dense track — hole or past-end means corrupt/truncated; fail loud.\n throw new Error(\n `doync: bundled migration track has a hole at version ${version} ` +\n `(requested range ${fromVersion}..${toVersion}, but the track holds ` +\n `${schema.migrations.length}) — the schema is corrupt or truncated`,\n )\n }\n for (const statement of splitClientStatements(migration.sql)) {\n if (classifyClientStatement(statement) === 'ddl') ddl.push(statement)\n }\n }\n return ddl\n}\n","import type { SqlValue } from '@doync/core'\n\nimport type { LocalDb } from '../port'\n\n/**\n * Read one `__doync_meta` value, or `null` if unset. Columns are `k`/`v`\n * (ADR-0024).\n */\nexport function readMeta(db: LocalDb, key: string): string | null {\n const row = db.exec<{ v: SqlValue }>(\n `SELECT v FROM __doync_meta WHERE k = ?`,\n key,\n )[0]\n if (row === undefined || row.v === null) return null\n return String(row.v)\n}\n\n/** Upsert one `__doync_meta` value. Columns are `k`/`v` (ADR-0024). */\nexport function writeMeta(db: LocalDb, key: string, value: string): void {\n db.exec(\n `INSERT INTO __doync_meta (k, v) VALUES (?, ?)\n ON CONFLICT (k) DO UPDATE SET v = excluded.v`,\n key,\n value,\n )\n}\n\n/**\n * Read one `__doync_prefs` value, or `null` if unset. Columns are `k`/`v`\n * (ADR-0024).\n */\nexport function readPref(db: LocalDb, key: string): string | null {\n const row = db.exec<{ v: SqlValue }>(\n `SELECT v FROM __doync_prefs WHERE k = ?`,\n key,\n )[0]\n if (row === undefined || row.v === null) return null\n return String(row.v)\n}\n\n/** Upsert one `__doync_prefs` value. Columns are `k`/`v` (ADR-0024). */\nexport function writePref(db: LocalDb, key: string, value: string): void {\n db.exec(\n `INSERT INTO __doync_prefs (k, v) VALUES (?, ?)\n ON CONFLICT (k) DO UPDATE SET v = excluded.v`,\n key,\n value,\n )\n}\n","export default \"CREATE TABLE `__doync_membership` (\\n\\t`instance` text NOT NULL,\\n\\t`level` integer NOT NULL,\\n\\t`tbl` text NOT NULL,\\n\\t`pk` text NOT NULL,\\n\\tPRIMARY KEY(`instance`, `level`, `pk`)\\n) WITHOUT ROWID;\\n--> statement-breakpoint\\nCREATE INDEX `__doync_membership_by_row` ON `__doync_membership` (`tbl`,`pk`);--> statement-breakpoint\\nCREATE TABLE `__doync_meta` (\\n\\t`k` text PRIMARY KEY NOT NULL,\\n\\t`v` blob\\n) WITHOUT ROWID;\\n--> statement-breakpoint\\nCREATE TABLE `__doync_pending` (\\n\\t`mutation_id` integer PRIMARY KEY NOT NULL,\\n\\t`name` text NOT NULL,\\n\\t`args` text NOT NULL,\\n\\t`idem_key` text\\n);\\n\"","export default \"CREATE TABLE `__doync_release_stamp` (\\n\\t`instance` text PRIMARY KEY NOT NULL,\\n\\t`cookie` integer NOT NULL,\\n\\t`released_at` integer NOT NULL,\\n\\t`ttl_ms` integer NOT NULL\\n) WITHOUT ROWID;\\n\"","export default \"CREATE TABLE `__doync_prefs` (\\n\\t`k` text PRIMARY KEY NOT NULL,\\n\\t`v` blob\\n) WITHOUT ROWID;\\n--> statement-breakpoint\\nINSERT INTO `__doync_prefs` (`k`, `v`)\\n\\tSELECT `k`, `v` FROM `__doync_meta` WHERE `k` = 'logout_behavior';\\n--> statement-breakpoint\\nDELETE FROM `__doync_meta` WHERE `k` = 'logout_behavior';\\n\"","","import type { LocalDb } from '../port'\n\n/**\n * Replica engine track + LocalDb twin of server `runEngineTrack` (ADR-0024 /\n * #159). Drizzle migrations under `./db/` via `*.sql?raw` (#156); journal by\n * idx; split on statement-breakpoint. Ledger `__doync_engine` and\n * `__doync_prefs` survive wipe; pending + clientId do not (ADR-0037).\n */\nimport migration0000 from './db/0000_replica_engine_v0.sql?raw'\nimport migration0001 from './db/0001_release_stamps.sql?raw'\nimport migration0002 from './db/0002_prefs_table.sql?raw'\nimport journal from './db/meta/_journal.json'\nimport { readPref, writePref } from './meta'\n\n/** One ordered engine-track step: the statements of one migration file. */\nexport type EngineTrackStep = readonly string[]\n\n/**\n * Drizzle-kit journal shape (min fields we consume). Kept local so the client\n * does not depend on drizzle-kit at runtime.\n */\ntype Journal = {\n readonly entries: readonly {\n readonly idx: number\n readonly tag: string\n }[]\n}\n\n/** Migration tag → raw SQL text, filled by the raw imports above. */\nconst MIGRATION_SQL: Readonly<Record<string, string>> = {\n '0000_replica_engine_v0': migration0000,\n '0001_release_stamps': migration0001,\n '0002_prefs_table': migration0002,\n}\n\n/**\n * Assembled track: journal order, each migration split on drizzle's breakpoint\n * marker. Empty statements (trailing markers) drop out.\n */\nexport const REPLICA_ENGINE_TRACK: readonly EngineTrackStep[] = (\n journal as Journal\n).entries\n .slice()\n .sort((a, b) => a.idx - b.idx)\n .map((entry) => {\n const sql = MIGRATION_SQL[entry.tag]\n if (sql === undefined) {\n throw new Error(\n `doync: replica engine track missing sql for journal tag ${JSON.stringify(entry.tag)}`,\n )\n }\n return sql\n .split(/-->\\s*statement-breakpoint/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n })\n\n/** Outcome of applying (or rebuilding) the replica engine track. */\nexport type EngineTrackResult = {\n /**\n * True when ledger was above bundled length: engine tables wiped/rebuilt,\n * preferences restored best-effort. Caller finishes consumer wipe-and-resync\n * (drop pending, mint fresh clientId — ADR-0037).\n */\n readonly rolledBack: boolean\n}\n\n/** Unversioned ledger bootstrap — created before any track step. */\nfunction ensureEngineLedger(db: LocalDb): void {\n db.exec(\n `CREATE TABLE IF NOT EXISTS __doync_engine (\n k TEXT PRIMARY KEY,\n v\n ) WITHOUT ROWID`,\n )\n}\n\n/**\n * Apply every track step from `fromVersion` (0-based exclusive start of the\n * next step) up to the track length, recording `track_version` after each.\n */\nfunction applyTrackFrom(db: LocalDb, fromVersion: number): void {\n let version = fromVersion\n while (version < REPLICA_ENGINE_TRACK.length) {\n const step = REPLICA_ENGINE_TRACK[version]\n if (step === undefined) break\n const next = version + 1\n for (const statement of step) {\n db.exec(statement)\n }\n db.exec(\n `INSERT INTO __doync_engine (k, v) VALUES ('track_version', ?)\n ON CONFLICT (k) DO UPDATE SET v = excluded.v`,\n next,\n )\n version = next\n }\n}\n\n/**\n * LocalDb twin of the server's `runEngineTrack` (packages/server/src/boot.ts):\n *\n * 1. Create `__doync_engine (k, v)` unversioned (`CREATE TABLE IF NOT EXISTS`).\n * 2. Read `track_version` (absent → 0).\n * 3. Rollback tripwire: version above bundled length → `console.warn`, rebuild\n * engine tables from scratch (preferences restored best-effort; pending and\n * clientId go with the consumer wipe the caller finishes — ADR-0037), return\n * `{ rolledBack: true }`. NOT a throw (Origin-only).\n * 4. Else apply every step above the ledger in order, record the new version after\n * each step.\n */\nexport function runReplicaEngineTrack(db: LocalDb): EngineTrackResult {\n ensureEngineLedger(db)\n const recorded = db.exec<{ v: string | number | null }>(\n `SELECT v FROM __doync_engine WHERE k = 'track_version'`,\n )\n let version = recorded.length === 0 ? 0 : Number(recorded[0]?.v)\n if (!Number.isFinite(version) || version < 0) version = 0\n\n if (version > REPLICA_ENGINE_TRACK.length) {\n console.warn(\n `doync: replica engine track_version ${version} is above bundled track ` +\n `length ${REPLICA_ENGINE_TRACK.length} — wiping engine tables and ` +\n `rebuilding (code rollback against a newer database). Consumer state ` +\n `will resync through wipe-and-resync.`,\n )\n rebuildEngineTables(db)\n return { rolledBack: true }\n }\n try {\n applyTrackFrom(db, version)\n } catch (error) {\n console.warn(\n `doync: replica engine track failed at version ${version} — wiping ` +\n `engine tables and rebuilding (code rollback against a newer database). ` +\n `Consumer state will resync through wipe-and-resync.`,\n error,\n )\n rebuildEngineTables(db)\n return { rolledBack: true }\n }\n return { rolledBack: false }\n}\n\n/**\n * Wipe and re-apply engine track (tripwire). Preferences are restored\n * best-effort; pending and clientId are not — the caller's consumer\n * wipe-and-resync finishes the fresh-client half (ADR-0037).\n */\nfunction rebuildEngineTables(db: LocalDb): void {\n let logoutBehavior: string | null = null\n try {\n // Prefs arrived later in the track than meta/pending, so a replica rolled\n // back below that migration cannot hold one.\n logoutBehavior = readPref(db, 'logout_behavior')\n } catch {\n // No prefs table at this track position — nothing to carry forward.\n }\n\n db.exec(`DROP TABLE IF EXISTS __doync_release_stamp`)\n db.exec(`DROP TABLE IF EXISTS __doync_membership`)\n db.exec(`DROP TABLE IF EXISTS __doync_prefs`)\n db.exec(`DROP TABLE IF EXISTS __doync_meta`)\n db.exec(`DROP TABLE IF EXISTS __doync_pending`)\n db.exec(`DROP TABLE IF EXISTS __doync_engine`)\n\n ensureEngineLedger(db)\n applyTrackFrom(db, 0)\n\n if (logoutBehavior !== null) writePref(db, 'logout_behavior', logoutBehavior)\n}\n","import type { LocalDb } from '../port'\n\n/**\n * Durable release stamp for one parked Query instance (ADR-0014 client half /\n * closeio/doync#224). Shared infrastructure for `heldAt` (#225) and membership\n * GC (#226) — package-internal, no public consumer surface.\n *\n * Lifecycle: written at last-release; deleted on re-desire and on an ADR-0028\n * warmth-break; wiped with memberships on resync/forget (a stamp must never\n * outlive the memberships it vouches for).\n */\nexport type ReleaseStamp = {\n readonly instance: string\n /** Client scalar cookie at last-release — the future `heldAt` claim. */\n readonly cookie: number\n /** Release moment in connected-clock units (ms). */\n readonly releasedAt: number\n /**\n * Resolved grace in ms — the Subscription's declared ttl, or the internal\n * default (mirrors the server's DEFAULT_QUERY_TTL_MS; no public knob).\n */\n readonly ttlMs: number\n}\n\n/**\n * Fallback when a Subscription declares no ttl — mirrors server\n * DEFAULT_QUERY_TTL_MS (30m). Not a public knob; duplicated so client does not\n * depend on server.\n */\nexport const DEFAULT_RELEASE_TTL_MS: number = 30 * 60 * 1000\n\n/** Resolve the stamp's recorded ttl: declared value, else the internal default. */\nexport function resolveReleaseTtlMs(declared: number | undefined): number {\n return declared === undefined ? DEFAULT_RELEASE_TTL_MS : declared\n}\n\n/** Upsert one stamp (at most one per instance; a re-release overwrites). */\nexport function upsertReleaseStamp(db: LocalDb, stamp: ReleaseStamp): void {\n db.exec(\n `INSERT INTO __doync_release_stamp (instance, cookie, released_at, ttl_ms)\n VALUES (?, ?, ?, ?)\n ON CONFLICT (instance) DO UPDATE SET\n cookie = excluded.cookie,\n released_at = excluded.released_at,\n ttl_ms = excluded.ttl_ms`,\n stamp.instance,\n stamp.cookie,\n stamp.releasedAt,\n stamp.ttlMs,\n )\n}\n\n/** Delete the stamp for one instance (no-op when absent). */\nexport function deleteReleaseStamp(db: LocalDb, instance: string): void {\n db.exec(`DELETE FROM __doync_release_stamp WHERE instance = ?`, instance)\n}\n\n/** Drop every stamp — paired with membership wipe on resync/forget. */\nexport function clearReleaseStamps(db: LocalDb): void {\n db.exec(`DELETE FROM __doync_release_stamp`)\n}\n\ntype StampRow = {\n instance: string | number\n cookie: number | string\n released_at: number | string\n ttl_ms: number | string\n}\n\nfunction rowToStamp(row: StampRow): ReleaseStamp {\n return {\n instance: String(row.instance),\n cookie: Number(row.cookie),\n releasedAt: Number(row.released_at),\n ttlMs: Number(row.ttl_ms),\n }\n}\n\n/** Read one stamp by instance identity, or `null` when none. */\nexport function readReleaseStamp(\n db: LocalDb,\n instance: string,\n): ReleaseStamp | null {\n const row = db.exec<StampRow>(\n `SELECT instance, cookie, released_at, ttl_ms\n FROM __doync_release_stamp WHERE instance = ?`,\n instance,\n )[0]\n return row === undefined ? null : rowToStamp(row)\n}\n\n/**\n * Enumerate every parked stamp (closeio/doync#226 hygiene GC walks this). Order\n * is unspecified.\n */\nexport function listReleaseStamps(db: LocalDb): ReleaseStamp[] {\n return db\n .exec<StampRow>(\n `SELECT instance, cookie, released_at, ttl_ms FROM __doync_release_stamp`,\n )\n .map(rowToStamp)\n}\n","import type { DoyncSchema, SqlValue, TableSchema } from '@doync/core'\n\nimport {\n declaredSchemaVersion,\n decodeImageValue,\n decodePkValue,\n isInternalTable,\n isSqlIdentifier,\n tablePrimaryKey,\n} from '@doync/core/internal'\n\nimport type { LocalDb } from '../port'\n\nimport { bundledSchemaDdl } from '../migrate'\nimport { readMeta, writeMeta } from './meta'\nimport { runReplicaEngineTrack } from './track'\n\nexport { readMeta, readPref, writeMeta, writePref } from './meta'\nexport {\n clearReleaseStamps,\n DEFAULT_RELEASE_TTL_MS,\n deleteReleaseStamp,\n listReleaseStamps,\n readReleaseStamp,\n resolveReleaseTtlMs,\n upsertReleaseStamp,\n type ReleaseStamp,\n} from './stamps'\nexport {\n REPLICA_ENGINE_TRACK,\n runReplicaEngineTrack,\n type EngineTrackResult,\n type EngineTrackStep,\n} from './track'\n\n/**\n * The replica-side helpers the engine uses to apply patches to the consumer's\n * local tables (ADR-0013/0019). The client holds a REAL partial-Mirror replica:\n * whole rows land in the consumer's own tables so Local reads and subscription\n * snapshots run ordinary SQL over them. These mirror the server's `sql.ts`\n * pk/image plumbing, kept minimal and client-local (the server module is not a\n * shared export).\n */\n\n/** Quote a validated identifier for interpolation (never user text). */\nexport function quoteIdent(name: string): string {\n if (!isSqlIdentifier(name)) {\n throw new Error(\n `doync: refusing to quote non-identifier ${JSON.stringify(name)}`,\n )\n }\n return `\"${name}\"`\n}\n\n/**\n * Parse a canonical serialized primary key (ADR-0003: a JSON array of the pk\n * column values in key order, BLOB parts as hex-tagged objects) into bound\n * values, in the table's key order. The `pk` on a wire `Patch` is exactly this\n * form.\n */\nexport function parsePk(pk: string, table: TableSchema): SqlValue[] {\n const parts: unknown = JSON.parse(pk)\n const key = tablePrimaryKey(table)\n if (!Array.isArray(parts) || parts.length !== key.length) {\n throw new Error(\n `doync: malformed pk ${pk} for table ${table.name} (expected ${key.length} key part(s))`,\n )\n }\n return parts.map((part) => decodePkValue(part))\n}\n\n/** `pk_a = ? AND pk_b = ?` over a table's primary-key columns, in key order. */\nexport function pkWhereClause(table: TableSchema): string {\n return tablePrimaryKey(table)\n .map((column) => `${quoteIdent(column)} = ?`)\n .join(' AND ')\n}\n\n/**\n * Decode a wire `PutPatch.image` (a codec-tagged row object, ADR-0010) into a\n * parallel columns/values pair for an INSERT — the same one decode path the\n * server's apply uses. A tagged blob becomes an `ArrayBuffer`; a scalar passes\n * through; column names are validated as SQL identifiers before interpolation.\n */\nexport function decodePatchImage(\n table: TableSchema,\n image: Record<string, unknown>,\n): { columns: string[]; values: SqlValue[] } {\n const columns = Object.keys(image)\n for (const column of columns) {\n if (!isSqlIdentifier(column)) {\n throw new Error(\n `doync: patch image for table ${table.name} has an invalid column name ${JSON.stringify(column)}`,\n )\n }\n }\n return {\n columns,\n values: columns.map((column) => decodeImageValue(image[column])),\n }\n}\n\n/** Look a synced table up by name; throw if the schema does not declare it. */\nexport function requireTable(schema: DoyncSchema, name: string): TableSchema {\n const table = schema.tables.find((t) => t.name === name)\n if (table === undefined) {\n throw new Error(\n `doync: no synced table named ${JSON.stringify(name)} in the client schema`,\n )\n }\n return table\n}\n\n/**\n * Apply the Replica engine track via `__doync_engine` ledger (ADR-0024 / #159).\n * LocalDb twin of server `runEngineTrack`. Returns whether rollback tripwire\n * fired (caller finishes consumer wipe-and-resync). Named `createEngineTables`\n * for stable call sites.\n */\nexport function createEngineTables(db: LocalDb): {\n rolledBack: boolean\n} {\n return runReplicaEngineTrack(db)\n}\n\n/**\n * Boot replica shape via DDL-only bundled track (ADR-0020). Fresh client is a\n * partial Mirror of the same migrations (triggers skipped, FKs off ADR-0009;\n * backfill skipped ADR-0006).\n *\n * - Fresh (no schema_version): full bundle track.\n * - Persisted: leave recorded shape; new migrations arrive mid-session as\n * `schema` directives ({@link applyBundledMigrations}), not at boot.\n */\nexport function replayMigrations(db: LocalDb, schema: DoyncSchema): void {\n if (readMeta(db, 'schema_version') !== null) return\n applyBundledMigrations(db, schema, 0, declaredSchemaVersion(schema))\n}\n\n/**\n * Apply the bundled track's non-trigger DDL for the migrations in\n * `(fromVersion, toVersion]` to the replica and record `toVersion` as the\n * applied `schema_version`. The single primitive behind both fresh boot replay\n * ({@link replayMigrations} with `fromVersion = 0`) and a mid-session catch-up\n * (`fromVersion =` the currently applied version). The DDL text is the client's\n * OWN bundle (never wire-carried — ADR-0020); a `CREATE TABLE` from a fresh\n * base and an `ALTER TABLE … ADD COLUMN` catch-up both keep any rows already\n * present (SQLite ADD COLUMN preserves rows). Throws if a statement fails — the\n * caller turns a failed local migration into wipe-and-resync (ADR-0020).\n */\nexport function applyBundledMigrations(\n db: LocalDb,\n schema: DoyncSchema,\n fromVersion: number,\n toVersion: number,\n): void {\n for (const ddl of bundledSchemaDdl(schema, fromVersion, toVersion)) {\n db.execBatch(ddl)\n }\n writeMeta(db, 'schema_version', String(toVersion))\n}\n\n/**\n * Drop every consumer (synced) table (ADR-0020's wipe fallback): the first step\n * of wipe-replica-and-resync, run with foreign keys already off so no cascade\n * fires and drop order is irrelevant (ADR-0009). `__doync_`-prefixed engine\n * tables are left alone here; the caller drops pending / sync meta and mints a\n * fresh clientId (ADR-0037). The `__doync_engine` ledger and `__doync_prefs`\n * survive.\n */\nexport function dropConsumerTables(db: LocalDb, schema: DoyncSchema): void {\n for (const table of schema.tables) {\n db.exec(`DROP TABLE IF EXISTS ${quoteIdent(table.name)}`)\n }\n}\n\n/** Assert the schema declares no `__doync_`-prefixed synced table (ADR-0007). */\nexport function assertNoReservedTables(schema: DoyncSchema): void {\n for (const table of schema.tables) {\n if (isInternalTable(table.name)) {\n throw new Error(\n `doync: synced table ${table.name} uses the reserved __doync_ prefix`,\n )\n }\n }\n}\n","import type {\n AuthContext,\n BoundQuery,\n DoyncSchema,\n MutationDefinition,\n MutationExec,\n RowDecoder,\n SqlValue,\n} from '@doync/core'\nimport type {\n DesiredQuery,\n InstanceInfo,\n OnceEndMessage,\n Patch,\n PksPatch,\n SchemaDirectiveMessage,\n SchemaSkewMessage,\n ServerMessage,\n} from '@doync/core/internal'\n\nimport { isBoundQuery } from '@doync/core/internal'\nimport {\n declaredSchemaVersion,\n decodeArgs,\n decodeImageValue,\n encodeArgs,\n isInternalTable,\n prepareArgs,\n} from '@doync/core/internal'\n\nimport type { LocalDb } from './port'\nimport type { SeamStatus, SyncSocket } from './socket'\n\nimport { type LocalStatement, RawRead } from './raw-read'\nimport {\n applyBundledMigrations,\n assertNoReservedTables,\n clearReleaseStamps,\n createEngineTables,\n decodePatchImage,\n deleteReleaseStamp,\n dropConsumerTables,\n listReleaseStamps,\n parsePk,\n pkWhereClause,\n quoteIdent,\n readMeta,\n readReleaseStamp,\n type ReleaseStamp,\n replayMigrations,\n requireTable,\n resolveReleaseTtlMs,\n upsertReleaseStamp,\n writeMeta,\n writePref,\n} from './replica'\n\n/**\n * What `mutate()` returns: `client` settles when the optimistic body applies\n * locally (rejects if the body throws or args fail validation); `server`\n * settles when the Origin confirms or rejects the mutation. Render off\n * `client`; await `server` when you need authoritative confirmation.\n */\nexport interface MutationResult {\n readonly client: Promise<void>\n readonly server: Promise<void>\n}\n\n// ADR-0022; closeio/doync#135/#139 (durable per-identity logout retention).\n/**\n * What happens to this identity's local store on logout: `keep` leaves it so\n * unsynced writes await the next login (default); `forget` erases it\n * (shared-computer / privacy). Set at construction or via\n * {@link DoyncClient.setLogoutBehavior}.\n */\nexport type LogoutBehavior = 'keep' | 'forget'\n\n/**\n * The `__doync_prefs` key the durable {@link LogoutBehavior} lives under\n * (closeio/doync#135) — shared by the direct engine's\n * {@link ClientEngine.setLogoutBehavior} / `createClient` write and the web DB\n * worker's per-identity write + boot read, so the ends can never drift.\n */\nexport const __LOGOUT_BEHAVIOR_PREF_KEY = 'logout_behavior'\n\n/**\n * The `__doync_meta` key the Client's durable connected-time counter lives\n * under (ADR-0014 client-half addendum / closeio/doync#223). Blob-typed\n * store-as-bound: no engine-track migration. Package-internal — release stamps\n * (#224) and membership GC read it through the engine, not this key.\n */\nexport const __CONNECTED_CLOCK_META_KEY = 'connected_clock'\n\n// ADR-0020/0022 (schema-skew + recovery surface).\n/**\n * Schema / recovery states the UI can show as a banner:\n *\n * - `reload` — client bundle cannot understand the server's shape; reload the app\n * after deploying a matching client. Terminal until reload.\n * - `server-behind` — client is ahead of a mid-deploy Mirror; the client backs\n * off and re-handshakes automatically.\n * - `resync` — local sync state wiped and rebuilding as a fresh Client under the\n * same identity (pending writes dropped, new clientId). Transient; clears\n * when sync resumes.\n * - `reaped` — same fresh-client rebuild as `resync`, but the Origin forgot this\n * Client (gone past the client-state lifetime). Transient; clears when sync\n * resumes. Distinct so the app can say \"you were away too long\" rather than\n * \"data refreshed\".\n * - `forget` — local store erased (including identity); boots as a fresh client.\n * Transient; clears when sync resumes.\n */\nexport type SchemaEventKind =\n | 'reload'\n | 'server-behind'\n | 'resync'\n | 'reaped'\n | 'forget'\n\n/**\n * One schema-status transition: `kind` plus a human-readable `message` for\n * diagnostics (not a control signal). `null` on the client means nominal.\n */\nexport interface SchemaEvent {\n readonly kind: SchemaEventKind\n /** Human-readable reason (diagnostics; never a control signal). */\n readonly message: string\n}\n\n/** Options for one `mutate()` call. */\nexport interface MutationOptions {\n /**\n * Idempotency key: a second `mutate()` with a key already in flight (or\n * already settled) returns the same `{client, server}` pair and enqueues\n * nothing, so a retried submit never double-writes.\n */\n readonly key?: string\n}\n\n// ADR-0021 addendum; closeio/doync#104 (honest View status lifecycle).\n/**\n * Whether a view's rows have been server-confirmed:\n *\n * - `unknown` — local answer only (fresh subscribe, skip, or reconnect). Rows may\n * already be present; this speaks to confirmation, not emptiness.\n * - `complete` — server has confirmed this subscription's rows up to the current\n * sync point. Empty results can still be `complete`.\n * - `error` — the Mirror could not honor the subscribe; detail on\n * {@link ViewStatus.error}.\n */\nexport type QueryStatus = 'unknown' | 'complete' | 'error'\n\n/**\n * A view's status snapshot. The object reference is stable until the visible\n * status changes, so it is safe for `useSyncExternalStore`.\n */\nexport interface ViewStatus {\n readonly status: QueryStatus\n /** Subscribe-failure detail; present only when `status` is `error`. */\n readonly error?: Error\n}\n\n// closeio/doync#102/#105/#136 (connection status surface).\n/**\n * Client ↔ Mirror connection state (backing for `useConnectionStatus`):\n *\n * - `connecting` — a (re)connect attempt is in flight\n * - `connected` — socket open, sync live\n * - `disconnected` — dropped and backing off\n * - `error` — transport error\n * - `needs-auth` — Mirror rejected auth; refresh credentials before sync resumes\n */\nexport type ConnectionStatus =\n | 'connecting'\n | 'connected'\n | 'disconnected'\n | 'error'\n | 'needs-auth'\n\n// ADR-0019/0021/0023; closeio/doync#104/#120/#131/#137 (desire layer, Warm pool, status snapshots).\n/**\n * A live, shared handle on a query's rows — what `subscribe()` and `local()`\n * return, and what `useQuery` renders from.\n *\n * `current()` returns the latest rows; the array reference only changes when\n * the rows do, so it is safe for `useSyncExternalStore`. `onChange` fires when\n * the rows or the visible status move; re-read both `current()` and `status()`\n * in the listener. A view keeps serving its last rows through disconnects and\n * errors — check `status()` to tell fresh from stale.\n *\n * Lifecycle: creating a view is free and owns nothing. Call `retain()` when\n * your component mounts, `release()` when it unmounts (both idempotent). Views\n * for the same query share one subscription automatically.\n */\nexport interface View<\n Row extends Record<string, unknown> = Record<string, SqlValue>,\n> {\n current(): readonly Row[]\n onChange(listener: () => void): () => void\n /**\n * Take ownership of this handle (call from a mount effect, never during\n * render). Idempotent per handle.\n */\n retain(): void\n /**\n * Drop ownership (call from unmount cleanup). The last release unsubscribes\n * upstream; this handle keeps serving its last snapshot. Idempotent.\n */\n release(): void\n /** Latest status snapshot. Moves ride the same `onChange` as row changes. */\n status(): ViewStatus\n /**\n * `true` when the query is one-row (`` sql.one`…` `` / Drizzle `findFirst`),\n * so `useQuery` unwraps to `Row | undefined`. `false` for multi-row;\n * `undefined` when one-ness is not yet known.\n */\n readonly one?: boolean\n}\n\n// ADR-0012/0021/0023 (Once = cache-and-network, no ongoing subscription).\n/**\n * A one-shot cache-and-network read — what `once()` returns and `useQueryOnce`\n * renders from. `current()` is the local cache immediately; `server` resolves\n * with the Mirror's answer (and updates the snapshot). Not reactive to later\n * local writes. Call `dispose()` when done; a quick remount within a short\n * grace keeps the same promise and in-flight request (StrictMode-safe).\n */\nexport interface OnceView<\n Row extends Record<string, unknown> = Record<string, SqlValue>,\n> {\n current(): readonly Row[]\n onChange(listener: () => void): () => void\n dispose(): void\n /** Resolves with the server's answer (network half of cache-and-network). */\n readonly server: Promise<readonly Row[]>\n}\n\n/**\n * The client call surface: `subscribe` / `once` / `local` / `mutate`, plus\n * connection and schema status. Platform adapters (`@doync/web`,\n * `@doync/mobile`) implement this; `@doync/react` hooks adapt over it.\n *\n * Query-taking methods accept a {@link BoundQuery} (from a registered query\n * call) or {@link FalsyQuery} (\"no query\"). `options.skip` is also supported.\n */\nexport interface DoyncClient {\n /**\n * Live subscription for a bound query (`queries.foo(args)`). Falsy or\n * `options.skip` yields an inert view (empty rows, status `unknown`).\n */\n subscribe<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n options?: SubscribeOptions,\n ): View<Row>\n /**\n * One-shot cache-and-network read for a bound query. Falsy never starts a\n * network half.\n */\n once<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n ): OnceView<Row>\n /**\n * Live local-only read of raw SQL against the replica (no server\n * subscription). Same retain/release lifecycle as {@link subscribe}.\n */\n local<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n sql: string,\n ...params: SqlValue[]\n ): View<Row>\n /**\n * Start a subscription and hold its view before a component needs it. The\n * hold is released automatically after a short grace window, when its signal\n * aborts, or when the returned handle is released. An already-aborted signal\n * returns an inert handle without starting the query.\n */\n warmup<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n options?: WarmupOptions,\n ): WarmupHandle\n /**\n * Apply a registered mutation optimistically and push it to the Origin. Pass\n * the {@link MutationDefinition} from your mutations tree; args are\n * type-checked from the definition. Returns {@link MutationResult}.\n */\n mutate<Args = unknown>(\n mutation: MutationDefinition<Args>,\n args: Args,\n options?: MutationOptions,\n ): MutationResult\n /** Current schema/recovery state, or `null` when nominal. */\n readonly schemaStatus: SchemaEvent | null\n /**\n * Subscribe to schema-status transitions (including a clear back to nominal).\n * Returns the unsubscribe function.\n */\n onSchemaChange(listener: () => void): () => void\n /**\n * Rebuild sync state as a fresh Client under the same identity: drop pending\n * writes, mint a new clientId, wipe the replica, and rebootstrap. Destructive\n * to unsent work — not a harmless refresh. Optional on the interface —\n * platform clients implement it.\n */\n resync(): void\n /**\n * Erase this identity's local data (replica, pendings, identity). Privacy /\n * logout-forget path. Optional on the interface — platform clients implement\n * it.\n */\n forget(identity?: string): void\n /**\n * Durably set {@link LogoutBehavior} at runtime (e.g. a \"remember me\"\n * checkbox). Survives restart. Optional — platform clients implement it.\n */\n setLogoutBehavior?(behavior: LogoutBehavior): void\n /**\n * Authenticated identity, or `null` when anonymous. Updated by auth refresh;\n * never derived from a bearer token by the library.\n */\n readonly userId: string | null\n /**\n * Warm the replica for a query without creating a local view — rows flow in\n * for other queries that read the same tables. Falsy yields a no-op handle.\n * Call `cleanup()` to release (often never, for a session-long preload).\n */\n preload<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n options?: PreloadOptions,\n ): PreloadHandle\n /** Current {@link ConnectionStatus} to the Mirror. */\n readonly connectionStatus: ConnectionStatus\n /** Subscribe to connection-status transitions. Returns the unsubscribe. */\n onConnectionChange(listener: () => void): () => void\n}\n\n/**\n * Falsy \"no query\" on subscribe / once / preload / warmup: `false | null |\n * undefined`. Lets `cond && query(args)` and optional-prop patterns\n * type-check.\n */\nexport type FalsyQuery = false | null | undefined\n\n/** Per-subscribe options. */\nexport interface SubscribeOptions {\n /**\n * How long after unmount the server keeps this subscription warm, in ms of\n * connected time. Absent ⇒ server default (clamped to its ceiling).\n */\n readonly ttl?: number\n /**\n * Skip the subscribe: empty rows, status `unknown`, no network. Use for\n * conditional queries under React's unconditional-hooks rule.\n */\n readonly skip?: boolean\n}\n\n/** Options for {@link DoyncClient.preload}. */\nexport interface PreloadOptions {\n /**\n * Connected-time grace after cleanup before the server drops the warm\n * subscription, in ms. Absent ⇒ server default.\n */\n readonly ttl?: number\n}\n\n/** Options accepted by `warmup()`. */\nexport interface WarmupOptions {\n /** Connected-time grace after the query is released. */\n readonly ttl?: number\n /** Release the warmup hold when the signal aborts. */\n readonly signal?: AbortSignal\n}\n\n/** Handle returned by `warmup()`. */\nexport interface WarmupHandle {\n /** Release the warmup hold. Idempotent. */\n release(): void\n}\n\n/** Handle returned by {@link DoyncClient.preload}. */\nexport interface PreloadHandle {\n /**\n * Release the preload. Idempotent; typically unused for a session-long\n * preload.\n */\n cleanup(): void\n}\n\nexport interface ClientEngineConfig {\n /**\n * The synchronous local-DB port (ADR-0019): node:sqlite in tests, wa-sqlite\n * on the web.\n */\n readonly db: LocalDb\n /**\n * The consumer's synced schema — the shape source replayed for the replica\n * (ADR-0020).\n */\n readonly schema: DoyncSchema\n /**\n * Named mutation bodies resolved on `push` — deterministic, DB-only\n * (ADR-0017/0021).\n */\n readonly mutations: Record<string, MutationDefinition>\n /** The Mirror socket seam (ADR-0016). */\n readonly socket: SyncSocket\n /**\n * The asserted projected auth context (ADR-0018 addendum / closeio/doync#167)\n * queries and mutation bodies read. Consumer-owned; the library ships no\n * decode. `null` when anonymous.\n */\n readonly ctx?: AuthContext\n /**\n * Transport credential PRESENTED as `connect{jwt}` (ADR-0016/0018) — the\n * Mirror resolves token-first. Auth must ride the HANDSHAKE, not only the\n * WebSocket upgrade's Cookie header: the socket lives in the SharedWorker and\n * can OUTLIVE a login. The engine NEVER derives identity from this token;\n * {@link userId} and {@link ctx} are the asserted siblings.\n */\n readonly token?: string\n /**\n * The ASSERTED authenticated identity (closeio/doync#167) — drives\n * {@link ClientEngine.userId}. `null` / omitted = anonymous. Never derived\n * from {@link token}.\n */\n readonly userId?: string | null\n /**\n * The durable clientId (ADR-0019's respawn-double-apply trap): reused from\n * `__doync_meta` when present, else this value, else generated. Provide it to\n * pin identity across engine restarts over the same DB.\n */\n readonly clientId?: string\n /**\n * ClientId generator when none is stored/provided (default:\n * `crypto.randomUUID`). Test-only pin — production paths always provide\n * {@link clientId} or accept a random UUID.\n */\n readonly generateId?: () => string\n /**\n * Consume schema-state transitions (ADR-0020): a stale-client or above-bundle\n * skew (`reload`), a client-ahead skew (`server-behind`), or a\n * post-failed-migration wipe (`resync`). The UI layer reloads the app on\n * `reload` and can surface a retry banner on `server-behind`; the engine\n * drives the recovery itself (re-handshake / wipe). The current state is also\n * readable synchronously via {@link ClientEngine.schemaStatus}.\n */\n readonly onSchemaEvent?: (event: SchemaEvent) => void\n /**\n * Wall-clock source for the connected-time counter (ADR-0014 client half /\n * closeio/doync#223). Defaults to `Date.now`. Injectable so Seam A tests pin\n * pong deltas without real timers; package-internal — never a public\n * surface.\n */\n readonly now?: () => number\n}\n\n/**\n * Durable pending mutation mirrored from `__doync_pending`. `args` = live\n * canonicalized JS for replay (#230). `wireArgs` = reserved-key form in the\n * queue and on push (ADR-0031 / #231). Reload decodes wire -> live.\n */\ninterface PendingMutation {\n readonly mutationId: number\n readonly name: string\n readonly args: unknown\n readonly wireArgs: unknown\n readonly key: string | undefined\n}\n\n/** Resolvers for one mutation's `{client, server}` pair. */\ninterface PendingPromise {\n readonly pair: MutationResult\n resolveClient: () => void\n rejectClient: (error: unknown) => void\n resolveServer: () => void\n rejectServer: (error: unknown) => void\n clientSettled: boolean\n serverSettled: boolean\n}\n\nconst OPTIMISTIC_SAVEPOINT = 'doync_optimistic'\nconst BODY_SAVEPOINT = 'doync_body'\n\n/**\n * Client engine (ADR-0019): synchronous local-DB port + socket seam +\n * optimistic Layer 1.\n *\n * Durable base (consumer tables, `__doync_membership`, `__doync_meta`) stays\n * committed. One held SAVEPOINT ({@link OPTIMISTIC_SAVEPOINT}) layers pending\n * bodies in order; same-connection reads see that uncommitted state. Durable\n * `__doync_pending` is written only while the overlay is closed, so an overlay\n * crash loses the guess and boot replays from the queue. Every durable write\n * closes the overlay (`ROLLBACK TO` → `RELEASE`), commits outside it, then\n * reopens and replays survivors — overlay always equals current pendings on\n * current base.\n *\n * Rebase, never merge: a poke rolls the guess off, applies authoritative\n * patches (puts before dels; row delete only when last membership drops), drops\n * rejected-then-acked pendings, persists cookie only on pokeEnd, then replays\n * survivors. A rejected write vanishes by not replaying.\n */\n/**\n * Internal confirmation phase for a desired Subscription (closeio/doync#104),\n * finer than visible {@link QueryStatus}:\n *\n * - `pending` — subscribe/connect sent, no ack yet (visible `unknown`)\n * - `acked` — `subscribeAck` seen; waiting for a `pokeEnd` that names this\n * instance in `confirms` (#114) (visible `unknown`)\n * - `complete` — that hydration/reactivation pokeEnd confirmed it\n * - `error` — Mirror framed a subscribe failure for this instance\n *\n * `pending`↔`acked` share visible `unknown`, so that step neither swaps status\n * references nor notifies.\n */\ntype SubPhase = 'pending' | 'acked' | 'complete' | 'error'\n\n/**\n * Mount declaration shared by `subscribe`, `preload`, and the desire layer:\n * wire name+args, declared ttl, and the resolved local statement.\n */\ninterface SubscriptionSpec {\n readonly name: string\n readonly args: unknown\n readonly ttl: number | undefined\n readonly statement: LocalStatement\n}\n\n/**\n * One desired Subscription (refcounted), keyed by statement identity. Shared by\n * live subscribes and `preload` (preload = desire with no View). Wire `heldAt`\n * is NOT stored — re-derived from live membership + release stamps on every\n * (re)send (ADR-0029) so reconnect attests what the replica holds NOW.\n */\ninterface DesiredEntry {\n readonly name: string\n readonly args: unknown\n /**\n * Widest ttl any retain declared (#102 ratchet — see `#retainDesire`; release\n * never lowers it).\n */\n ttl: number | undefined\n /**\n * Retained holders only (ADR-0023: commit owns): retained Views + preloads.\n * Handle creation does not count. 0→1 sends `subscribe`; last release sends\n * `unsubscribe` immediately (#120/Q2 — Warm pool is paint budget, not wire\n * lifetime).\n */\n count: number\n /**\n * Shared reactive read for this instance (ADR-0023 share-raw). `null` for\n * preload-only (no local execution — #104).\n */\n read: RawRead | null\n /** Confirmation phase driving View {@link QueryStatus} (#104). */\n phase: SubPhase\n /** Subscribe-failure detail when `phase` is `error` (#104). */\n error: Error | undefined\n}\n\n/**\n * Local-read slot (sql+params key): shared RawRead + retain count. Same retain\n * / warm lifecycle as {@link DesiredEntry}, no wire half.\n */\ninterface LocalReadEntry {\n count: number\n read: RawRead | null\n}\n\n/**\n * View handle from `subscribe()`/`local()` (CONTEXT.md; ADR-0023). Creation is\n * pure compute; `retain()` is first ownership (0→1 sends wire `subscribe`).\n * Last `release()` unsubscribes immediately (#120/Q2) and parks the read in the\n * Warm pool for one tick.\n *\n * Stale-but-stable (Q1): after Warm eviction the handle keeps last rows/status\n * until a re-retain's read notifies fresh ones. `onChange` listeners re-wire\n * onto the recreated read so the handle never observes the gap.\n */\nclass ViewHandle implements View<Record<string, unknown>> {\n /** Whether THIS handle currently retains (idempotent; #120). */\n #held = false\n #unwire: (() => void) | null = null\n readonly #listeners = new Set<() => void>()\n #lastRows: readonly Record<string, unknown>[] = EMPTY_ROWS\n #lastStatus: ViewStatus = UNKNOWN_STATUS\n #seeded = false\n /**\n * Decode memo (Q4): read identity + generation that produced `#lastRows`\n * (recreated reads restart generations).\n */\n #decodedFrom: { read: RawRead; generation: number } | null = null\n\n constructor(\n /**\n * Retain/release/peek for the shared read; `compute` is pure discardable\n * seed.\n */\n private readonly lifecycle: {\n retain(): RawRead\n release(): void\n peek(): RawRead | null\n /**\n * Pure creation-time compute (ADR-0023/Q3): run statement + decode, no\n * shared state. Caller snapshot-gates mid-replay (torn overlay → empty\n * seed; commit-time read supplies truth).\n */\n compute(): {\n rows: readonly Record<string, unknown>[]\n status: ViewStatus\n }\n },\n /**\n * Per-handle decode (Q4): never keyed by statement — two flavors with\n * identical SQL share one RawRead and decode here, memoized on generation.\n */\n private readonly decode: RowDecoder | undefined,\n readonly one: boolean | undefined,\n ) {}\n\n /** Decode shared raw rows with this handle's flavor, memoized on generation. */\n #project(read: RawRead): readonly Record<string, unknown>[] {\n if (\n this.#decodedFrom?.read !== read ||\n this.#decodedFrom.generation !== read.generation\n ) {\n const raw = read.rawRows()\n this.#lastRows = this.decode\n ? this.decode(raw as readonly Record<string, SqlValue>[])\n : raw\n this.#decodedFrom = { read, generation: read.generation }\n }\n return this.#lastRows\n }\n\n /**\n * Lazy first-paint seed (Q1/Q3): prefer the live shared read (another retain\n * or Warm pool), else pure `compute` once. Discarded renders that never read\n * stay zero-cost; retain-then-read shares the live read's execution.\n */\n #seed(): void {\n if (this.#seeded) return\n this.#seeded = true\n const read = this.lifecycle.peek()\n if (read !== null) {\n this.#project(read)\n this.#lastStatus = read.status()\n } else {\n const seed = this.lifecycle.compute()\n this.#lastRows = seed.rows\n this.#lastStatus = seed.status\n }\n }\n\n current(): readonly Record<string, unknown>[] {\n const read = this.lifecycle.peek()\n if (read !== null && read.generation > 0) {\n this.#seeded = true\n return this.#project(read)\n }\n this.#seed()\n return this.#lastRows\n }\n\n status(): ViewStatus {\n const read = this.lifecycle.peek()\n if (read !== null) {\n this.#seeded = true\n this.#lastStatus = read.status()\n return this.#lastStatus\n }\n this.#seed()\n return this.#lastStatus\n }\n\n onChange(listener: () => void): () => void {\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\n }\n\n retain(): void {\n if (this.#held) return\n this.#held = true\n this.#seeded = true\n const read = this.lifecycle.retain()\n // One forwarder per handle onto the (possibly recreated) shared read.\n this.#unwire = read.onChange(() => {\n this.#project(read)\n this.#lastStatus = read.status()\n for (const l of this.#listeners) l()\n })\n // Re-project when the live read is newer than our snapshot. Generation 0\n // (gated initial compute) holds nothing fresher than any seed — must not\n // overwrite; its first real recompute notifies via the wire above (Q1:\n // never blank, only move forward).\n if (\n (read.generation > 0 &&\n (this.#decodedFrom?.read !== read ||\n this.#decodedFrom.generation !== read.generation)) ||\n read.status() !== this.#lastStatus\n ) {\n if (read.generation > 0) this.#project(read)\n this.#lastStatus = read.status()\n for (const l of this.#listeners) l()\n }\n }\n\n release(): void {\n if (!this.#held) return\n this.#held = false\n this.#unwire?.()\n this.#unwire = null\n this.lifecycle.release()\n }\n}\n\nexport class ClientEngine implements DoyncClient {\n readonly #db: LocalDb\n readonly #schema: DoyncSchema\n readonly #mutations: Record<string, MutationDefinition>\n readonly #socket: SyncSocket\n /**\n * Projected auth ctx (ADR-0018) for queries/bodies. Updated by\n * {@link updateAuth} on same-user refresh (#102). `null` when anonymous.\n */\n #ctx: AuthContext\n /**\n * Bearer for every handshake (ADR-0016/0018). Updated by {@link updateAuth} so\n * refresh rides in-band update AND the next reconnect (#102).\n */\n #token: string | undefined\n /**\n * Asserted identity (#104 / #167) from construction / {@link updateAuth}.\n * Never derived from {@link #token}.\n */\n #userId: string | null = null\n\n #clientId!: string\n /**\n * ClientId mint (config `generateId` or {@link defaultGenerateID}). Kept so\n * `forget()` and corrupt-meta heal can mint mid-session, not only at boot.\n */\n readonly #generateId: () => string\n /** Durable next-mutation-id high-water mark (ADR-0019). */\n #nextMutationId = 1\n /** Origin-commit cookie; advances only on pokeEnd. */\n #cookie: number | null = null\n #connected = false\n /**\n * Connected-time ms (ADR-0014 client / #223): pong deltas while connected;\n * frozen offline. Currency for release stamps and membership GC. Memory\n * first; piggybacks durable rebases (naive meta write would join the overlay\n * and roll back — #139). Lost ticks ⇒ clock runs SLOW (safe).\n */\n #connectedClock = 0\n /**\n * Wall time of previous pong on THIS connection (`null` until first pong).\n * Re-anchored on open/close so disconnect gaps add nothing.\n */\n #lastPongAt: number | null = null\n /** Wall clock for pong deltas (injectable in tests). */\n readonly #now: () => number\n /**\n * Sticky auth failure (#136, ADR-0018): set on framed `unauthorized` (engine\n * inference — seam never reports it). Sticky across backoff until a\n * substantive frame proves a refreshed handshake (same as shared-hub.ts).\n * Outranks seam report and live `open` in {@link connectionStatus}.\n */\n #needsAuth = false\n /**\n * Last optional seam transient (#136), or `null` if never reported / cleared\n * by live open. Surfaces in {@link connectionStatus} only when neither open\n * nor needs-auth wins.\n */\n #seamStatus: SeamStatus | null = null\n #overlayOpen = false\n /**\n * Connection-status subscribers (#105): notified when visible\n * {@link connectionStatus} flips.\n */\n readonly #connectionListeners = new Set<() => void>()\n\n /** Bundled schema version — handshake skew currency (ADR-0020). */\n #bundleVersion = 0\n /**\n * Replica data schema version in `__doync_meta`. Fresh → `#bundleVersion`;\n * persists until mid-session `schema` directives advance it.\n */\n #appliedVersion = 0\n /** Current schema-handling state, or `null` when nominal (ADR-0020). */\n #schemaEvent: SchemaEvent | null = null\n readonly #onSchemaEvent: ((event: SchemaEvent) => void) | undefined\n /**\n * Schema-state subscribers (#89): every transition including silent clear.\n * Config `onSchemaEvent` fires only on NEW state (clears emit nothing), so a\n * banner on that alone sticks after recovery — hooks use this +\n * `schemaStatus`.\n */\n readonly #schemaListeners = new Set<() => void>()\n /**\n * After `reload` skew: stop handshakes and frame apply until app reload\n * (ADR-0020).\n */\n #halted = false\n\n /** Durable pending queue, ascending mutation id. */\n #pending: PendingMutation[] = []\n /** `{client, server}` resolvers by mutation id. */\n readonly #promises = new Map<number, PendingPromise>()\n /** Idempotency key → result pair (ADR-0019 retry-once). */\n readonly #keyed = new Map<string, MutationResult>()\n /**\n * Keys reloaded from durable queue at boot (ADR-0019): pairs are orphaned (no\n * caller awaits; settlement swallowed). A DB_FAILOVER retry on a respawned\n * engine must treat client phase as already applied.\n */\n readonly #recoveredKeys = new Set<string>()\n /** Mutids dropped by `pokeReject` — never resolved by a later lmid. */\n readonly #rejected = new Set<number>()\n /** Mutids whose body threw in the latest optimistic replay. */\n readonly #optimisticFailed = new Map<number, unknown>()\n\n /** SubscribeAck metadata (Read-set + Level→table). */\n readonly #instanceInfo = new Map<string, InstanceInfo>()\n /**\n * Desired set by statement identity. `heldAt` re-derived on send from\n * membership + stamps (ADR-0029), not stored.\n */\n readonly #desired = new Map<string, DesiredEntry>()\n /**\n * In-flight Once (ADR-0012) by client id: view + wire `{name, args}` for\n * reconnect re-issue (Once is not durable).\n */\n readonly #onceRequests = new Map<\n string,\n { view: OnceViewImpl; name: string; args: unknown }\n >()\n /**\n * Once answer accumulation (ADR-0012): start → parts → end. Feeds OnceView,\n * never replica/cookie.\n */\n readonly #onceBuffers = new Map<string, Record<string, unknown>[]>()\n #nextOnceSeq = 1\n /**\n * Local shared raw reads (ADR-0019: re-run on any local commit, unhinted).\n * Separate from {@link DesiredEntry} so each reactive read has one owner\n * (ADR-0023 one-count).\n */\n readonly #localReads = new Map<string, LocalReadEntry>()\n /**\n * Warm pool (CONTEXT.md; ADR-0023): last-release reads kept one tick outside\n * `#desired` (desired = handshake truth — #120). Re-retain reclaims rows and\n * re-sends `subscribe`; wire `unsubscribe` already fired (Q2).\n */\n readonly #warmReads = new Map<\n string,\n { read: RawRead; timer: ReturnType<typeof setTimeout> }\n >()\n /**\n * Consumer tables written by the current overlay. Overlay rollback removes\n * those optimistic rows — rebase must re-project them even without a patch or\n * survivor rewrite (else phantoms of rejected/acked-away guesses).\n */\n #overlayTables = new Set<string>()\n\n /** Patches between pokeStart and pokeEnd; `null` when idle. */\n #pokeBuffer: Patch[] | null = null\n /** Framed protocol errors — never silent (ADR-0016). */\n readonly #errors: string[] = []\n\n /**\n * Exclusive chain (ADR-0019 addendum). True while an overlay-touching op runs\n * and STAYS true across an awaited body so concurrent JS sees busy. Sync ops\n * set/clear without yield — observers only see true during async replay.\n * Serialization latch ({@link #chainQueue}) and snapshot gate (defer new view\n * initial reads). All-sync bodies never queue or gate.\n */\n #chainBusy = false\n /** Ops deferred behind in-flight async replay (FIFO). */\n readonly #chainQueue: ChainStep[] = []\n /**\n * Views whose initial recompute was gated mid-async-replay (torn overlay).\n * Flushed when the chain idles.\n */\n readonly #gatedViews = new Set<RawRead>()\n\n constructor(config: ClientEngineConfig) {\n this.#db = config.db\n this.#schema = config.schema\n this.#mutations = config.mutations\n this.#socket = config.socket\n this.#ctx = config.ctx ?? null\n this.#token = config.token\n // Asserted identity (#167) — never from the token.\n this.#userId = config.userId ?? null\n this.#onSchemaEvent = config.onSchemaEvent\n this.#generateId = config.generateId ?? defaultGenerateID\n this.#now = config.now ?? Date.now\n this.#boot(config)\n this.#socket.setHandlers({\n message: (message) => this.#onMessage(message),\n open: () => this.#onOpen(),\n close: () => {\n // Freeze connected clock across the gap (ADR-0014 / #223): clear pong\n // anchor so the first post-open pong contributes nothing.\n this.#lastPongAt = null\n this.#setConnected(false)\n },\n // Optional seam status (#136): reconnecting adapters feed connecting/error.\n status: (status) => this.#onSeamStatus(status),\n })\n }\n\n /**\n * In-memory connected-time ms (ADR-0014 / #223). Package-internal for release\n * stamps (#224) and membership GC — not on {@link DoyncClient}.\n */\n get connectedClock(): number {\n return this.#connectedClock\n }\n\n /**\n * Durable release stamp for one instance, or `null` (ADR-0014 / #224).\n * Package-internal for `heldAt` (#225) and GC (#226).\n */\n releaseStamp(instance: string): ReleaseStamp | null {\n return readReleaseStamp(this.#db, instance)\n }\n\n /**\n * Every parked release stamp (#226 hygiene GC). Package-internal; order\n * unspecified.\n */\n releaseStamps(): ReleaseStamp[] {\n return listReleaseStamps(this.#db)\n }\n\n /** Durable clientId (stable across restarts on the same DB). */\n get clientId(): string {\n return this.#clientId\n }\n\n /**\n * Asserted `userId` from construction / {@link updateAuth}, or `null` (#104 /\n * #167). Never derived from a bearer (ADR-0018) — cookie sessions assert\n * `userId` with no token.\n */\n get userId(): string | null {\n return this.#userId\n }\n\n /** Scalar cookie held now (`null` on first sight). */\n get cookie(): number | null {\n return this.#cookie\n }\n\n /** Framed protocol errors (`error` frames, stray poke parts). */\n get errors(): readonly string[] {\n return this.#errors\n }\n\n /**\n * Idempotency keys recovered at boot (ADR-0019). A DB_FAILOVER retry on a\n * respawned engine gets the recovered pair whose orphaned `client` never\n * settles for it — treat client phase as already applied.\n */\n get recoveredKeys(): ReadonlySet<string> {\n return this.#recoveredKeys\n }\n\n /** Bundled schema version (ADR-0020) — declared on every handshake for skew. */\n get schemaVersion(): number {\n return this.#bundleVersion\n }\n\n /**\n * Schema-handling state, or `null` when nominal (ADR-0020). Sync counterpart\n * to {@link ClientEngineConfig.onSchemaEvent} for reload/retry UI.\n */\n get schemaStatus(): SchemaEvent | null {\n return this.#schemaEvent\n }\n\n // --- boot / durable state -------------------------------------------------\n\n #boot(config: ClientEngineConfig): void {\n const db = this.#db\n // Clients apply state, never enforce (ADR-0009/0020).\n db.exec('PRAGMA foreign_keys = OFF')\n assertNoReservedTables(this.#schema)\n // Engine track via `__doync_engine` ledger (ADR-0024). Ledger-above-bundle\n // rebuilds engine tables and flags consumer wipe-and-resync below.\n const { rolledBack } = createEngineTables(db)\n if (rolledBack) {\n // Consumer half of wipe-and-resync. Boot mints a fresh clientId because\n // the stored one goes with the rest of the cookie/cursor state. The\n // bundle version is passed explicitly — `#bundleVersion` is assigned\n // below, after the track settles.\n this.#wipeStore(db, declaredSchemaVersion(this.#schema))\n this.#cookie = null\n } else {\n replayMigrations(db, this.#schema)\n }\n this.#bundleVersion = declaredSchemaVersion(this.#schema)\n\n // Corrupt-meta heal (ADR-0022): non-integer durable counters cannot boot\n // safely (NaN would ride wire as cookie/lmid or reissue mutids — ADR-0019\n // next-id trap; corrupt mut counter makes pending untrustworthy). Loud\n // forget-shaped heal to a fresh first-sight base, then mint clientId /\n // empty queue below. Emit the reset only after that state exists.\n const corruptKey = corruptMetaKey(db)\n if (corruptKey !== null) {\n // Silent would look like data loss — name the corruption and consequence.\n console.error(\n `doync: durable meta \"${corruptKey}\" is corrupt — healing by ` +\n `FORGETTING the local store (fresh identity, full re-hydration). A ` +\n `corrupt mutation counter makes the pending queue untrustworthy, so ` +\n `unsynced writes are discarded rather than risk double-apply.`,\n )\n this.#forgetStore(db)\n }\n\n this.#appliedVersion = this.#durableInt(db, 'schema_version') ?? 0\n\n // Reuse durable clientId when present (respawn-double-apply trap). Heal\n // cleared it ⇒ mint fresh first-sight id.\n const stored = readMeta(db, 'client_id')\n this.#clientId = stored ?? config.clientId ?? this.#generateId()\n if (stored === null) writeMeta(db, 'client_id', this.#clientId)\n\n // Independent high-water mark (not MAX(pending)+1).\n this.#nextMutationId = this.#durableInt(db, 'next_mutation_id') ?? 1\n this.#cookie = this.#durableInt(db, 'cookie')\n // Connected clock (ADR-0014 / #223): resume last persisted reading. Lost\n // unpersisted ticks ⇒ slow (safe). Lenient outside forget heal — corrupt\n // clock must not cost identity/pending (never rides wire); zero matches\n // crash-loss direction.\n const storedClock = Number(readMeta(db, __CONNECTED_CLOCK_META_KEY))\n if (Number.isInteger(storedClock) && storedClock >= 0) {\n this.#connectedClock = storedClock\n } else {\n if (readMeta(db, __CONNECTED_CLOCK_META_KEY) !== null) {\n console.warn(\n 'doync: durable connected-clock reading is corrupt — resetting to 0 (clock runs slow; GC defers, server full-hydrates)',\n )\n }\n this.#connectedClock = 0\n }\n this.#lastPongAt = null\n\n // Emit corrupt-meta forget only once clientId/cookie are initialized.\n if (corruptKey !== null) {\n this.#emitSchemaEvent(\n 'forget',\n `corrupt durable meta \"${corruptKey}\" — forgot the local store and ` +\n `reset to a fresh identity`,\n )\n }\n\n // Reload durable pending; decode reserved-key JSON (ADR-0031 / #231) so\n // replay sees live ArrayBuffers matching the original optimistic bind.\n this.#pending = db\n .exec<{\n mutation_id: SqlValue\n name: SqlValue\n args: SqlValue\n idem_key: SqlValue\n }>(\n `SELECT mutation_id, name, args, idem_key FROM __doync_pending ORDER BY mutation_id ASC`,\n )\n .map((row) => {\n const wireArgs = JSON.parse(String(row.args)) as unknown\n return {\n mutationId: Number(row.mutation_id),\n name: String(row.name),\n wireArgs,\n args: decodeArgs(wireArgs, 'mutation args'),\n key: row.idem_key === null ? undefined : String(row.idem_key),\n }\n })\n for (const p of this.#pending) {\n // Orphan promises: swallow settlement (no caller awaits reloaded pendings).\n const promise = this.#ensurePromise(p.mutationId, true)\n if (p.key !== undefined) {\n this.#keyed.set(p.key, promise.pair)\n this.#recoveredKeys.add(p.key)\n }\n }\n // Membership GC at boot (ADR-0014 / #226): aged stamped releases only.\n // Defer stampless until in-session (`#desired` live) — at boot every\n // instance looks undesired, so stampless would wipe live memberships that\n // only survived process death / DB-worker failover. Before overlay opens\n // (plain autocommit). Clock piggybacks only when the sweep has work.\n this.#runMembershipGc({ includeStampless: false })\n\n // Discard bootstrap writes before opening the overlay.\n db.drainWrittenTables()\n // Initial overlay is usually sync. Async durable pending cannot be awaited\n // in the constructor — hold chain busy (gate reads) until it lands.\n const opened = this.#openOverlay()\n if (isThenable(opened)) {\n this.#chainBusy = true\n opened.then(\n () => this.#finishChainStep(),\n () => this.#finishChainStep(),\n )\n }\n }\n\n /**\n * Durable integer meta (ADR-0015/0019). `null` when unset (caller default;\n * cookie may stay null). Present-but-corrupt throws — never silent NaN on the\n * wire (ADR-0019 next-id trap).\n *\n * At boot the throw is unreachable: {@link corruptMetaKey} + ADR-0022 heal\n * clear the store first. Kept as fail-loud tripwire for values the heal\n * missed (invariant break).\n */\n #durableInt(db: LocalDb, key: string): number | null {\n const raw = readMeta(db, key)\n if (raw === null) return null\n const n = Number(raw)\n if (!Number.isInteger(n)) {\n throw new Error(\n `doync: durable meta \"${key}\" is corrupt (${JSON.stringify(raw)} → ` +\n `${n}) — a non-integer must never ride the wire (ADR-0019 next-id ` +\n `trap); the boot-time corrupt-meta heal should have caught this`,\n )\n }\n return n\n }\n\n // --- the exclusive chain (ADR-0019 addendum) ------------------------------\n\n /**\n * Run one overlay-touching op on the exclusive chain. Idle ⇒ run now (sync\n * path stays fully sync). Busy ⇒ queue until prior finishes so mutate /\n * rebase / poke never interleave and an awaited body only defers its paint.\n */\n #exclusive(work: ChainStep): void {\n if (this.#chainBusy) {\n this.#chainQueue.push(work)\n return\n }\n this.#chainBusy = true\n this.#runExclusive(work)\n }\n\n #runExclusive(work: ChainStep): void {\n let running: Awaitable<void>\n try {\n running = work()\n } catch (error) {\n // Sync throw: free chain and rethrow (same surface as pre-chain code).\n this.#chainBusy = false\n this.#drainOrFlush()\n throw error\n }\n if (isThenable(running)) {\n running.then(\n () => this.#finishChainStep(),\n (error: unknown) => {\n // Async rejection cannot reach the sync caller — frame it (ADR-0016)\n // and free the chain so the engine is not wedged.\n this.#errors.push(\n `doync: exclusive-chain step rejected — ${errorMessage(error)}`,\n )\n this.#finishChainStep()\n },\n )\n } else {\n this.#finishChainStep()\n }\n }\n\n #finishChainStep(): void {\n this.#chainBusy = false\n this.#drainOrFlush()\n }\n\n /** Next queued op, or flush gated views when idle. */\n #drainOrFlush(): void {\n const next = this.#chainQueue.shift()\n if (next !== undefined) {\n this.#chainBusy = true\n this.#runExclusive(next)\n return\n }\n this.#flushGatedViews()\n }\n\n /**\n * Recompute views gated during replay on the settled overlay; notify movers.\n * No-op on the common all-sync path.\n */\n #flushGatedViews(): void {\n if (this.#gatedViews.size === 0) return\n const gated = [...this.#gatedViews]\n this.#gatedViews.clear()\n for (const view of gated) if (view.recompute()) view.notify()\n }\n\n /**\n * New view initial recompute: now, or deferred while overlay is torn\n * (snapshot gating).\n */\n #initialCompute(view: RawRead): void {\n if (this.#chainBusy) this.#gatedViews.add(view)\n else view.recompute()\n }\n\n // --- the optimistic overlay ----------------------------------------------\n\n /**\n * Open held savepoint and replay pendings onto base; record `#overlayTables`.\n * Clears/drains the write-table accumulator so priors never leak. Sync when\n * every body is sync; returns a Promise once a body awaits (caller awaits\n * settle — exclusive chain, ADR-0019).\n */\n #openOverlay(): Awaitable<void> {\n this.#db.drainWrittenTables()\n this.#db.exec(`SAVEPOINT ${OPTIMISTIC_SAVEPOINT}`)\n this.#overlayOpen = true\n this.#optimisticFailed.clear()\n return thenMaybe(this.#replayFrom(0), () => {\n this.#overlayTables = consumerTables(this.#db.drainWrittenTables())\n })\n }\n\n /**\n * Replay pendings from `index` in order. Stays sync across sync bodies; an\n * await then tail-recurses so ordering is strict without microtask tax.\n */\n #replayFrom(index: number): Awaitable<void> {\n for (let i = index; i < this.#pending.length; i++) {\n const running = this.#replayBody(this.#pending[i] as PendingMutation)\n if (isThenable(running))\n return running.then(() => this.#replayFrom(i + 1))\n }\n }\n\n /** Roll overlay to base and release (autocommit). */\n #closeOverlay(): void {\n if (!this.#overlayOpen) return\n this.#db.exec(`ROLLBACK TO ${OPTIMISTIC_SAVEPOINT}`)\n this.#db.exec(`RELEASE ${OPTIMISTIC_SAVEPOINT}`)\n this.#overlayOpen = false\n }\n\n /**\n * Rebase envelope for every durable change (mutate, poke, reject): close\n * overlay, run `durable` in a committed base transaction (outside savepoint),\n * reopen with survivor replay. Returns union of tables removed by rollback,\n * written by durable, and written by new overlay — so pure disappearances\n * still re-project.\n *\n * `durable` is sync; only survivor replay may await. Exclusive chain holds\n * other ops until overlay is rebuilt.\n */\n #rebase(durable: () => void): Awaitable<Set<string>> {\n const removed = this.#overlayTables\n this.#closeOverlay()\n this.#db.drainWrittenTables()\n this.#db.exec('BEGIN')\n try {\n durable()\n this.#db.exec('COMMIT')\n } catch (error) {\n // Leave DB clean for caller recovery (wipe-and-resync): roll durable body,\n // restore overlay, then rethrow.\n this.#db.exec('ROLLBACK')\n return thenMaybe(this.#openOverlay(), () => {\n throw error\n })\n }\n const applied = consumerTables(this.#db.drainWrittenTables())\n return thenMaybe(\n this.#openOverlay(),\n () => new Set([...removed, ...applied, ...this.#overlayTables]),\n )\n }\n\n /**\n * Replay one pending body in a nested savepoint so a throw rolls back only\n * itself and survivors still apply. Failure lands in `#optimisticFailed` for\n * `client` reject; caller decides drop (initial apply / body-throw contract)\n * vs keep (later rebase — Origin decides). Async bodies release the savepoint\n * only after settle.\n */\n #replayBody(p: PendingMutation): Awaitable<void> {\n const db = this.#db\n db.exec(`SAVEPOINT ${BODY_SAVEPOINT}`)\n let running: Awaitable<void>\n try {\n const entry = this.#mutations[p.name]\n if (entry === undefined) throw new Error(`unknown mutation \"${p.name}\"`)\n running = entry.body({ args: p.args, ctx: this.#ctx, sql: this.#tx() })\n } catch (error) {\n this.#failBody(p.mutationId, error)\n return\n }\n if (isThenable(running)) {\n return running.then(\n () => this.#releaseBody(p.mutationId),\n (error: unknown) => this.#failBody(p.mutationId, error),\n )\n }\n this.#releaseBody(p.mutationId)\n }\n\n /** Clean body: release savepoint, clear prior failure. */\n #releaseBody(id: number): void {\n this.#db.exec(`RELEASE ${BODY_SAVEPOINT}`)\n this.#optimisticFailed.delete(id)\n }\n\n /** Thrown body: roll savepoint back and record the error. */\n #failBody(id: number, error: unknown): void {\n this.#db.exec(`ROLLBACK TO ${BODY_SAVEPOINT}`)\n this.#db.exec(`RELEASE ${BODY_SAVEPOINT}`)\n this.#optimisticFailed.set(id, error)\n }\n\n /** Mutation-body write surface (exec returns rows, ADR-0021). */\n #tx(): MutationExec {\n // Booleans canonicalize to 1/0 at the seam — the LocalDb port speaks\n // SqlValue only (SQLite has no boolean affinity).\n return {\n exec: (query, ...params) =>\n this.#db.exec(\n query,\n ...params.map((p) => (typeof p === 'boolean' ? (p ? 1 : 0) : p)),\n ),\n }\n }\n\n /**\n * `push` frame (ADR-0016): `{name, args}` + durable id, never the optimistic\n * result. `wireArgs` is reserved-key encoded at enqueue/reload (ADR-0031 /\n * #231) so clean-apply and reconnect outbox share one blob.\n */\n #sendPush(p: PendingMutation): void {\n this.#socket.send({\n type: 'push',\n clientId: this.#clientId,\n mutationId: p.mutationId,\n name: p.name,\n args: p.wireArgs,\n })\n }\n\n // --- mutate ---------------------------------------------------------------\n\n mutate<Args = unknown>(\n mutation: MutationDefinition<Args>,\n args: Args,\n options?: MutationOptions,\n ): MutationResult {\n const key = options?.key\n if (key !== undefined) {\n const existing = this.#keyed.get(key)\n if (existing !== undefined) return existing\n }\n\n // Definition is a typed name stamp only (ADR-0021 / #161): resolve body +\n // validator from THIS engine's registry. Passed object body is never run.\n const name = mutation.name\n if (name === undefined || name === '') {\n return settledRejection(\n new Error(\n 'doync: mutate requires a registered mutation — wrap it in defineMutations(...) so it carries a dotted name',\n ),\n )\n }\n\n const entry = this.#mutations[name]\n if (entry === undefined) {\n return settledRejection(new Error(`doync: unknown mutation \"${name}\"`))\n }\n let liveArgs: unknown\n let wireArgs: unknown\n try {\n // The one args-preparation path (ADR-0031), shared with the Origin push\n // seam: guard, canonicalize, validate through the definition's schema.\n // Throw is sync fail-fast: nothing enqueued, `client` rejects. Encode\n // the post-schema value so wire and durable queue match what the\n // optimistic body saw (validators assumed pass-through / idempotent).\n liveArgs = prepareArgs(entry.args, args, 'mutation args')\n wireArgs = encodeArgs(liveArgs, 'mutation args')\n } catch (error) {\n return settledRejection(error)\n }\n\n // Pair returned sync; mutid allocated ON THE CHAIN in `#applyMutate` so\n // mutate-during-replay only defers its paint, fail-fast throws consume no\n // id, and the Origin cursor never sees a gap.\n const promise = this.#makePromise(false)\n if (key !== undefined) this.#keyed.set(key, promise.pair)\n this.#exclusive(() =>\n this.#applyMutate({ name, args: liveArgs, wireArgs, key, promise }),\n )\n return promise.pair\n }\n\n /**\n * Optimistic apply on the exclusive chain (ADR-0019): allocate id, durable\n * queue row + counter outside the savepoint, rebase all pendings into a fresh\n * overlay, settle `client`. Counter rolls back via `#dropPending` on\n * fail-fast so only pushed mutids are consumed.\n */\n #applyMutate(request: {\n name: string\n args: unknown\n wireArgs: unknown\n key: string | undefined\n promise: PendingPromise\n }): Awaitable<void> {\n const db = this.#db\n const id = this.#nextMutationId\n this.#promises.set(id, request.promise)\n // Memory: live args for replay. Durable: encoded wire (ADR-0031 / #231).\n const record: PendingMutation = {\n mutationId: id,\n name: request.name,\n args: request.args,\n wireArgs: request.wireArgs,\n key: request.key,\n }\n const touched = this.#rebase(() => {\n db.exec(\n `INSERT INTO __doync_pending (mutation_id, name, args, idem_key) VALUES (?, ?, ?, ?)`,\n id,\n record.name,\n JSON.stringify(record.wireArgs ?? null),\n record.key ?? null,\n )\n this.#nextMutationId = id + 1\n writeMeta(db, 'next_mutation_id', String(this.#nextMutationId))\n this.#pending.push(record)\n })\n return thenMaybe(touched, (t) => this.#settleInitialApply(record, t))\n }\n\n /**\n * Settle first optimistic apply (ADR-0019/0021). Clean ⇒ resolve `client`,\n * re-project, PUSH (`{name, args}` only). Thrown body ⇒ client-body-throw\n * (#103): drop pending (never pushed; body savepoint already rolled back),\n * reject both promises (`server` can never ack a never-pushed mut).\n */\n #settleInitialApply(\n record: PendingMutation,\n touched: Set<string>,\n ): Awaitable<void> {\n const id = record.mutationId\n if (!this.#optimisticFailed.has(id)) {\n this.#settleClient(id, null)\n this.#notifyChange(touched)\n if (this.#connected) this.#sendPush(record)\n return\n }\n const failure = this.#optimisticFailed.get(id)\n const pair = this.#promises.get(id)?.pair\n // Release idempotency key so a same-key retry re-evaluates (condition may\n // have cleared) instead of returning the cached rejection.\n if (record.key !== undefined) this.#keyed.delete(record.key)\n // Never-pushed ⇒ roll high-water so Origin cursor sees no gap.\n return thenMaybe(this.#dropPending(id, true), (dropped) => {\n this.#settleClient(id, failure)\n this.#settleServer(id, failure)\n // Swallow unhandled rejection if consumer awaits only `client`.\n pair?.client.catch(() => {})\n pair?.server.catch(() => {})\n this.#notifyChange(new Set([...touched, ...dropped]))\n })\n }\n\n /**\n * Drop one pending and rebase survivors (ADR-0019). Shared by body-throw\n * (`rollbackCounter`) and server reject. `rollbackCounter` only on fail-fast\n * (id never pushed); server reject consumes the id (Origin advanced).\n */\n #dropPending(id: number, rollbackCounter = false): Awaitable<Set<string>> {\n const db = this.#db\n return this.#rebase(() => {\n db.exec(`DELETE FROM __doync_pending WHERE mutation_id = ?`, id)\n this.#pending = this.#pending.filter((p) => p.mutationId !== id)\n if (rollbackCounter) {\n this.#nextMutationId = id\n writeMeta(db, 'next_mutation_id', String(id))\n }\n })\n }\n\n // --- auth (ADR-0018, closeio/doync#102) -----------------------------------\n\n /**\n * Adopt a refreshed SAME-USER identity (#167). When connected with a bearer,\n * send in-band `updateAuth` (ADR-0018 / #85) so the Mirror extends auth\n * without reconnect when userId matches and `issuedAt` is newer. Identity\n * CHANGE (different `userId`, incl. anon↔user) is topology-level replica swap\n * — SharedWorker routes only same-user refreshes here.\n *\n * Token is stored for the next reconnect (#92: surviving SharedWorker must\n * not reuse a stale token). `ctx`/`userId` asserted, never derived. Halted\n * engines never emit. Pass `ctx: null` for anonymous.\n */\n updateAuth(\n token: string | null | undefined,\n ctx?: AuthContext,\n userId?: string | null,\n ): void {\n this.#token = token ?? undefined\n if (userId !== undefined) this.#userId = userId\n if (ctx !== undefined) this.#ctx = ctx\n if (\n this.#connected &&\n !this.#halted &&\n typeof token === 'string' &&\n token !== ''\n ) {\n this.#socket.send({ type: 'updateAuth', jwt: token })\n }\n }\n\n // --- reactive reads -------------------------------------------------------\n\n /**\n * Subscribe to a registered query (ADR-0021): resolve under args+ctx,\n * register upstream, return a reactive {@link View}. Local snapshots run\n * resolved SQL over base+overlay (RYOW). Re-project only when a local change\n * touches the instance's server Read-set.\n *\n * Bound form only (ADR-0027 / #200): {@link BoundQuery} or falsy →\n * SkippedView. `options.skip` is also an inert View with no desire.\n */\n subscribe<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n options?: SubscribeOptions,\n ): View<Row> {\n // Falsy ⇒ inert SkippedView, no desire/wire (ADR-0027). Distinct from\n // `options.skip` on a real bound query.\n if (isFalsyQuery(query)) {\n return new SkippedView() as unknown as View<Row>\n }\n const {\n query: leaf,\n args,\n options: opts,\n } = normalizeQuerySurface('subscribe', query, options)\n // Skipped (#104): no desire, no re-execution. Un-skip is a fresh subscribe\n // at the hook (#105), never in-place. Not keyed — holds no resources.\n if (opts?.skip) {\n return new SkippedView() as unknown as View<Row>\n }\n const name = leaf.name as string\n // Pure compute (ADR-0023/Q3): mint handle only. `retain()` is ownership.\n // Resolve here (engine consumption), never at BoundQuery bind (ADR-0027).\n const resolved = leaf.resolve({ args, ctx: this.#ctx })\n const statement: LocalStatement = {\n sql: resolved.sql,\n params: resolved.params,\n }\n const identity = resolved.identity\n const spec = { name, args, ttl: opts?.ttl, statement }\n const decode = resolved.decode\n const one = resolved.one ?? false\n return new ViewHandle(\n {\n peek: () => this.#desired.get(identity)?.read ?? null,\n retain: () => this.#retainSubscription(identity, spec),\n release: () => this.#releaseSubscription(identity),\n compute: () => {\n // Snapshot-gated like once(): mid-replay → empty seed.\n if (this.#chainBusy) {\n return { rows: EMPTY_ROWS, status: UNKNOWN_STATUS }\n }\n const raw = this.#db.exec(statement.sql, ...statement.params)\n const entry = this.#desired.get(identity)\n return {\n rows: decode ? decode(raw) : raw,\n status: phaseToStatus(entry?.phase ?? 'pending', entry?.error),\n }\n },\n },\n decode,\n one,\n ) as unknown as View<Row>\n }\n\n /**\n * Start a subscription and hold its view before a component needs it. The\n * hold is released automatically after a short grace window, when its signal\n * aborts, or when the returned handle is released. An already-aborted signal\n * returns an inert handle without starting the query.\n */\n warmup<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n options?: WarmupOptions,\n ): WarmupHandle {\n if (isFalsyQuery(query) || options?.signal?.aborted) return NOOP_WARMUP\n return warmupHold(\n this.subscribe(\n query,\n options === undefined ? undefined : { ttl: options.ttl },\n ),\n options?.signal,\n )\n }\n\n /**\n * Warm the replica without materializing a View (#104, ADR-0019/0021):\n * register upstream so pokes hydrate rows other queries read, at zero local\n * recompute of the preload statement. Refcounts the same desired instance as\n * a live subscribe (byte-identical statement). `{cleanup}` releases into TTL\n * grace (ADR-0014). Bound form only (ADR-0027 / #200); falsy → no-op handle.\n */\n preload<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n options?: PreloadOptions,\n ): PreloadHandle {\n if (isFalsyQuery(query)) {\n return NOOP_PRELOAD\n }\n const {\n query: leaf,\n args,\n options: opts,\n } = normalizeQuerySurface('preload', query, options)\n const name = leaf.name as string\n const resolved = leaf.resolve({ args, ctx: this.#ctx })\n const identity = resolved.identity\n this.#retainPreload(identity, {\n name,\n args,\n ttl: opts?.ttl,\n statement: { sql: resolved.sql, params: resolved.params },\n })\n let released = false\n return {\n cleanup: () => {\n // Idempotent — double cleanup must not under-count a shared instance.\n if (released) return\n released = true\n this.#releaseSubscription(identity)\n },\n }\n }\n\n /**\n * Desire half of retain (subscribe AND preload): 0→1 entry + wire\n * `subscribe`, error-phase retry (#104/#114), ttl ratchet (#102, deviation\n * below).\n */\n #retainDesire(identity: string, spec: SubscriptionSpec): DesiredEntry {\n let entry = this.#desired.get(identity)\n if (entry === undefined) {\n // Capture heldAt BEFORE marking desired / dropping the stamp (ADR-0029 /\n // #225). `#heldAtFor` prefers desired+live → current cookie; doing those\n // first would attest the advanced cookie and trip the server's ahead\n // verdict against the parked baseline.\n const heldAt = this.#heldAtFor(identity)\n entry = {\n name: spec.name,\n args: spec.args,\n ttl: spec.ttl,\n count: 0,\n read: null,\n phase: 'pending',\n error: undefined,\n }\n this.#desired.set(identity, entry)\n // Drop parked stamp — only released instances keep one (ADR-0014 / #224).\n // Via `#sideWrite` so the delete doesn't join the open overlay (#139).\n this.#sideWrite(() => deleteReleaseStamp(this.#db, identity))\n if (this.#connected) {\n this.#socket.send({\n type: 'subscribe',\n queries: [this.#desiredQuery(identity, { heldAt })],\n })\n }\n } else if (entry.phase === 'error' && this.#connected) {\n // Fresh mount retries a failed subscribe (#setPhase notifies holders).\n this.#setPhase(identity, 'pending')\n this.#socket.send({\n type: 'subscribe',\n queries: [this.#desiredQuery(identity)],\n })\n } else if (\n spec.ttl !== undefined &&\n (entry.ttl === undefined || spec.ttl > entry.ttl)\n ) {\n // Raised ttl (#102): widen Mirror grace with a cheap re-send (ADR-0015).\n // DELIBERATE ratchet (max-of-EVER, not max-of-live) — lowering would need\n // per-holder ttl tracking. Cost: wider storage-only grace after a high-ttl\n // mount leaves (ADR-0014, 24h server clamp); wire lifetime (Q2) unchanged.\n entry.ttl = spec.ttl\n if (this.#connected) {\n this.#socket.send({\n type: 'subscribe',\n queries: [this.#desiredQuery(identity)],\n })\n }\n }\n entry.count += 1\n return entry\n }\n\n /**\n * Commit ownership for one subscription: {@link #retainDesire} plus the shared\n * {@link RawRead} (reclaim warm, else build and catch up).\n */\n #retainSubscription(identity: string, spec: SubscriptionSpec): RawRead {\n const entry = this.#retainDesire(identity, spec)\n if (entry.read === null) {\n // Prefer warm reclaim (zero re-exec); else build the shared read.\n const warm = this.#warmReads.get(identity)\n if (warm !== undefined) {\n clearTimeout(warm.timer)\n this.#warmReads.delete(identity)\n entry.read = warm.read\n // Parked reads miss fan-out; catch up on reclaim. rowsEqual dedups so\n // unchanged → one SELECT, no notify; handles only move forward (Q1).\n this.#initialCompute(warm.read)\n } else {\n const read = new RawRead(\n {\n run: (st) => this.#db.exec(st.sql, ...st.params),\n readSet: () => {\n const info = this.#instanceInfo.get(identity)\n return info === undefined ? undefined : new Set(info.readSet)\n },\n },\n spec.statement,\n identity,\n phaseToStatus(entry.phase, entry.error),\n )\n entry.read = read\n this.#initialCompute(read)\n }\n }\n return entry.read\n }\n\n /**\n * Preload ownership (#104): same desire as subscribe (shared instance on\n * byte-identical statements), no local read — poke hydrates. Cleanup is\n * ordinary release.\n */\n #retainPreload(identity: string, spec: SubscriptionSpec): void {\n this.#retainDesire(identity, spec)\n }\n\n /**\n * Drop one desire ref (#104, #120). Last release removes the entry and, when\n * connected, sends `unsubscribe` (name+args; Mirror re-resolves under its\n * ctx). Mirror moves into ADR-0014 TTL grace (out of Sweep, membership warm)\n * so a re-subscribe is pk-list catch-up. Non-last release only decrements.\n *\n * Wire fires immediately on last drop — no client linger (#120/Q2). Warm pool\n * is ONLY local paint budget (StrictMode / hop-back); Mirror TTL is the\n * durable warmth. Re-retain within the tick re-sends `subscribe` (cheap via\n * #225 `heldAt`).\n *\n * Offline/halted (`#connected` false): drop desire silently, queue nothing —\n * next handshake rebuilds from {@link #desired}, which already omits this\n * instance. Racing an already-inactive Mirror sub is a no-op server-side.\n */\n #releaseSubscription(identity: string): void {\n const entry = this.#desired.get(identity)\n if (entry === undefined || --entry.count > 0) return\n // Leave declared truth immediately; wire unsubscribes NOW (Q2); rows → Warm.\n this.#desired.delete(identity)\n if (this.#connected) {\n this.#socket.send({\n type: 'unsubscribe',\n queries: [\n {\n name: entry.name,\n args:\n entry.args === undefined\n ? undefined\n : encodeArgs(entry.args, 'query args'),\n },\n ],\n })\n }\n // Stamp park position with the unsubscribe (ADR-0014 / #224) for heldAt\n // (#225) and GC (#226). No cookie ⇒ no stamp. Must not join the optimistic\n // SAVEPOINT (#139 / #223) — use the stamp envelope (also piggybacks clock).\n this.#stampLastRelease(identity, entry.ttl)\n if (entry.read !== null) this.#warmRead(identity, entry.read)\n }\n\n /**\n * Last-release stamp (ADR-0014 / #224). No-ops without a scalar cookie. Same\n * envelope runs membership GC (#226) and persists the connected clock (#223)\n * — GC may delete fully-orphaned rows, so notify the touched set (not the\n * meta-only {@link #sideWrite} path).\n */\n #stampLastRelease(identity: string, declaredTtl: number | undefined): void {\n const cookie = this.#cookie\n if (cookie === null) return\n const stamp: ReleaseStamp = {\n instance: identity,\n cookie,\n releasedAt: this.#connectedClock,\n ttlMs: resolveReleaseTtlMs(declaredTtl),\n }\n this.#exclusive(() =>\n thenMaybe(\n this.#rebase(() => {\n upsertReleaseStamp(this.#db, stamp)\n // After stamp: non-positive ttl self-collects; aged/stampless siblings\n // ride the same write (`#desired` live ⇒ stampless of undesired is\n // safe). Clock always piggybacks on a stamp write (#223).\n this.#runMembershipGc({ includeStampless: true })\n this.#persistConnectedClock()\n }),\n (touched) => this.#notifyChange(touched),\n ),\n )\n }\n\n /**\n * Persist connected-clock reading (ADR-0014 / #223). Caller provides the\n * durable envelope. Never a naive standalone writeMeta (#139).\n */\n #persistConnectedClock(): void {\n writeMeta(\n this.#db,\n __CONNECTED_CLOCK_META_KEY,\n String(this.#connectedClock),\n )\n }\n\n /**\n * Membership GC (ADR-0014 / #226) via ADR-0028 {@link #breakStaleInstance}\n * (memberships + stamp + fully-orphaned rows).\n *\n * Eligible when not desired, and either: stamp with `ttlMs <= 0`; stamp age\n * `connectedClock - releasedAt > ttlMs`; or no stamp at all when\n * `includeStampless` (in-session only — boot `#desired` is empty, stampless\n * would wipe live failover survivors). Hygiene only under ADR-0029 (early ⇒\n * full hydrate; late ⇒ server already forgot). Inside a durable envelope;\n * piggybacks the clock when it breaks anything.\n */\n #runMembershipGc(opts: { includeStampless: boolean }): void {\n const victims = this.#gcEligibleInstances(opts.includeStampless)\n if (victims.length === 0) return\n for (const instance of victims) this.#breakStaleInstance(instance)\n this.#persistConnectedClock()\n }\n\n /** Instances eligible for membership GC this sweep (order unspecified). */\n #gcEligibleInstances(includeStampless: boolean): string[] {\n const desired = this.#desired\n const stamps = listReleaseStamps(this.#db)\n const stamped = new Set(stamps.map((s) => s.instance))\n const eligible: string[] = []\n\n // Stampless of un-desired — only with a live desired set (see GC doc).\n if (includeStampless) {\n const held = this.#db.exec<{ instance: SqlValue }>(\n `SELECT DISTINCT instance FROM __doync_membership`,\n )\n for (const row of held) {\n const instance = String(row.instance)\n if (desired.has(instance) || stamped.has(instance)) continue\n eligible.push(instance)\n }\n }\n\n // Stamped releases past (or non-positive) ttl.\n const clock = this.#connectedClock\n for (const stamp of stamps) {\n if (desired.has(stamp.instance)) continue\n if (stamp.ttlMs <= 0 || clock - stamp.releasedAt > stamp.ttlMs) {\n eligible.push(stamp.instance)\n }\n }\n return eligible\n }\n\n /**\n * Park a released read in the Warm pool (CONTEXT.md; ADR-0023) for one policy\n * tick ({@link WARM_POOL_TICK_MS}) — re-retain reclaims without re-exec\n * (StrictMode, j/k hops). Wire half already fired.\n */\n #warmRead(key: string, read: RawRead): void {\n const prior = this.#warmReads.get(key)\n if (prior !== undefined) clearTimeout(prior.timer)\n this.#warmReads.set(key, {\n read,\n timer: setTimeout(() => {\n const parked = this.#warmReads.get(key)\n if (parked === undefined || parked.read !== read) return\n this.#warmReads.delete(key)\n this.#gatedViews.delete(read)\n }, WARM_POOL_TICK_MS),\n })\n }\n\n /**\n * Once (ADR-0012 / ADR-0021): cache-and-network. Local replica first (real\n * SQL `[]` when empty); Mirror executes once under its ctx. Local\n * exec/decode/shape errors throw at call (#141). Leaves no\n * Subscription/CVR/Membership. Bound form only (ADR-0027 / #200); falsy never\n * starts the network half.\n */\n once<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n query: BoundQuery<Row, boolean> | FalsyQuery,\n ): OnceView<Row> {\n if (isFalsyQuery(query)) {\n return new SkippedOnceView() as unknown as OnceView<Row>\n }\n const { query: leaf, args } = normalizeQuerySurface('once', query)\n const name = leaf.name as string\n const resolved = leaf.resolve({ args, ctx: this.#ctx })\n // Cache half; gate only mid-async-replay (torn overlay). Fail-loud.\n let cached: readonly Record<string, unknown>[]\n if (this.#chainBusy) {\n cached = []\n } else {\n const raw = this.#db.exec(resolved.sql, ...resolved.params)\n cached = resolved.decode ? resolved.decode(raw) : raw\n }\n const id = `once-${this.#nextOnceSeq++}`\n // Decode also applies to the server answer. Pure construction (Q3/Q7):\n // network on first use; dispose deferred one Warm tick for StrictMode.\n // Encode wire args once (ADR-0031 / #230) for reconnect re-issue.\n const wireArgs =\n args === undefined ? undefined : encodeArgs(args, 'query args')\n const view = new OnceViewImpl(cached, resolved.decode, {\n start: () => {\n this.#onceRequests.set(id, { view, name, args: wireArgs })\n if (this.#connected) {\n this.#socket.send({ type: 'once', id, name, args: wireArgs })\n }\n },\n drop: () => {\n this.#onceRequests.delete(id)\n this.#onceBuffers.delete(id)\n },\n })\n return view as unknown as OnceView<Row>\n }\n\n /**\n * Local read (ADR-0019/0021): arbitrary SQL over the replica, never upstream;\n * re-run on any local commit (unhinted).\n */\n local<Row extends Record<string, unknown> = Record<string, SqlValue>>(\n sql: string,\n ...params: SqlValue[]\n ): View<Row> {\n // Pure compute twin of subscribe (ADR-0023/Q3). Always `complete` — local\n // answer is authoritative (#104).\n const key = `local:${sql}\\u0000${JSON.stringify(params)}`\n const statement: LocalStatement = { sql, params }\n return new ViewHandle(\n {\n peek: () => this.#localReads.get(key)?.read ?? null,\n retain: () => this.#retainLocal(key, statement),\n release: () => this.#releaseLocal(key),\n compute: () => {\n if (this.#chainBusy) {\n return { rows: EMPTY_ROWS, status: UNKNOWN_STATUS }\n }\n return {\n rows: this.#db.exec(statement.sql, ...statement.params),\n status: COMPLETE_STATUS,\n }\n },\n },\n undefined,\n undefined,\n ) as unknown as View<Row>\n }\n\n /** Local-read retain (subscription twin, no wire). */\n #retainLocal(key: string, statement: LocalStatement): RawRead {\n let entry = this.#localReads.get(key)\n if (entry === undefined) {\n entry = { count: 0, read: null }\n this.#localReads.set(key, entry)\n }\n entry.count += 1\n if (entry.read === null) {\n const warm = this.#warmReads.get(key)\n if (warm !== undefined) {\n clearTimeout(warm.timer)\n this.#warmReads.delete(key)\n entry.read = warm.read\n // Catch up across the parked window (see the subscription reclaim).\n this.#initialCompute(warm.read)\n } else {\n const read = new RawRead(\n {\n run: (st) => this.#db.exec(st.sql, ...st.params),\n readSet: () => undefined,\n },\n statement,\n undefined,\n COMPLETE_STATUS,\n )\n entry.read = read\n this.#initialCompute(read)\n }\n }\n return entry.read\n }\n\n #releaseLocal(key: string): void {\n const entry = this.#localReads.get(key)\n if (entry === undefined || --entry.count > 0) return\n this.#localReads.delete(key)\n if (entry.read !== null) this.#warmRead(key, entry.read)\n }\n\n // --- socket handlers ------------------------------------------------------\n\n #onOpen(): void {\n // Reload skew: never re-handshake until the app reloads.\n if (this.#halted) return\n // First pong after (re)connect contributes zero (ADR-0014 / #223).\n this.#lastPongAt = null\n this.#setConnected(true)\n this.#sendHandshake()\n }\n\n /**\n * Connected-time tick on keepalive pong (ADR-0014 / #223): elapsed wall time\n * since the previous pong of THIS connection; first pong only re-anchors.\n * Memory-only — durable persist rides the next poke apply.\n */\n #onPong(): void {\n const now = this.#now()\n if (this.#lastPongAt !== null) {\n const delta = now - this.#lastPongAt\n // Never count backward Wall-clock skew.\n if (delta > 0) this.#connectedClock += delta\n }\n this.#lastPongAt = now\n }\n\n /**\n * Handshake + durable outbox in order (ADR-0016). `connect` carries\n * `schemaVersion` (ADR-0020) and cookie; server dedups re-pushes by lmid.\n * Used on (re)connect and post-wipe resync.\n */\n #sendHandshake(): void {\n // Every (re)connect must reconfirm session: desired → `pending` (#104).\n // First connect is a no-op notify for already-pending; reconnect drops\n // `complete` back to unknown until ack + poke.\n for (const identity of this.#desired.keys()) {\n this.#setPhase(identity, 'pending')\n }\n this.#socket.send({\n type: 'connect',\n clientId: this.#clientId,\n // Bearer on every handshake (ADR-0016/0018) — not only the upgrade cookie\n // a surviving SharedWorker may have captured pre-login (#92).\n ...(this.#token !== undefined ? { jwt: this.#token } : {}),\n cookie: this.#cookie,\n // heldAt from memberships + stamps (ADR-0029); no memberships → no claim.\n desiredQueries: [...this.#desired.keys()].map((id) =>\n this.#desiredQuery(id),\n ),\n schemaVersion: this.#bundleVersion,\n })\n for (const p of this.#pending) this.#sendPush(p)\n // Once is not durable (ADR-0012): re-ask; cache holds until answer. Fresh\n // onceStart resets a mid-stream partial buffer.\n for (const [id, once] of this.#onceRequests) {\n this.#socket.send({\n type: 'once',\n id,\n name: once.name,\n args: once.args,\n })\n }\n }\n\n /**\n * Wire `DesiredQuery` (ADR-0029 / #225). Optional `opts.heldAt` for re-desire\n * that must capture the claim BEFORE stamp drop / becoming desired — else\n * `#heldAtFor` would treat a warm remount as live at the advanced cookie.\n * Present `opts` with `heldAt === undefined` is a real absence (do not\n * re-derive).\n *\n * Args reserved-key encoded here (ADR-0031 / #230) so every subscribe path\n * shares one choke point; in-memory desire keeps live values for resolve.\n */\n #desiredQuery(\n identity: string,\n opts?: { readonly heldAt: number | undefined },\n ): DesiredQuery {\n const entry = this.#desired.get(identity)\n // opts present ⇒ caller-derived (before desired/stamp mutations).\n const heldAt = opts !== undefined ? opts.heldAt : this.#heldAtFor(identity)\n return {\n name: entry?.name ?? identity,\n args:\n entry?.args === undefined\n ? undefined\n : encodeArgs(entry.args, 'query args'),\n ...(entry?.ttl !== undefined ? { ttl: entry.ttl } : {}),\n ...(heldAt !== undefined ? { heldAt } : {}),\n }\n }\n\n /**\n * HeldAt (ADR-0029): no memberships → absent; desired → current cookie; stamp\n * → park cookie; memberships without stamp and not desired → absent\n * (crash-gap). After the membership gate: desired first, then stamp. Callers\n * mutating desired capture first and pass override to {@link #desiredQuery}.\n */\n #heldAtFor(identity: string): number | undefined {\n const hasMembership =\n this.#db.exec(\n `SELECT 1 FROM __doync_membership WHERE instance = ? LIMIT 1`,\n identity,\n ).length > 0\n if (!hasMembership) return undefined\n if (this.#desired.has(identity)) {\n return this.#cookie ?? undefined\n }\n const stamp = readReleaseStamp(this.#db, identity)\n if (stamp !== null) return stamp.cookie\n // Crash-gap / pre-feature: honest silence → server full-hydrates.\n return undefined\n }\n\n #onMessage(message: ServerMessage): void {\n // Reload skew: ignore all but framed errors (new-shape frames would corrupt).\n if (this.#halted) {\n if (message.type === 'error') this.#errors.push(message.message)\n return\n }\n // Keepalive pong (ADR-0014 / #223): ticks clock; not handshake proof (never\n // clears needs-auth; hub parity). Before auth inference.\n if (message.type === 'pong') {\n this.#onPong()\n return\n }\n // Auth inference (#136, ADR-0018; hub parity): `unauthorized` => sticky\n // needs-auth; any substantive later frame clears it. Framed `error` does\n // not. Engine-owned — seam never reports needs-auth.\n if (message.type === 'unauthorized') {\n this.#errors.push(message.message)\n this.#setNeedsAuth(true)\n return\n }\n // Bidirectional Pin (ADR-0016 addendum / closeio/doync#400): a poke\n // addressed to another Client is not handshake proof and never opens the\n // buffer. Trailing part/end hit the existing no-open-poke arms.\n if (message.type === 'pokeStart' && message.clientId !== this.#clientId) {\n this.#errors.push(\n `doync: pokeStart addressed to client ${message.clientId}, this engine is ${this.#clientId}`,\n )\n return\n }\n if (this.#needsAuth && message.type !== 'error') {\n this.#setNeedsAuth(false)\n }\n switch (message.type) {\n case 'schemaSkew':\n this.#onSchemaSkew(message)\n break\n case 'schema':\n this.#onSchemaDirective(message)\n break\n case 'subscribeAck':\n // Ack proves handshake accepted — clear transient back-off / resync.\n this.#clearTransientSchemaEvent()\n for (const info of message.instances) {\n this.#instanceInfo.set(info.instance, info)\n // Half of complete (#104): -> acked until hydration pokeEnd. Clears\n // prior error; never regresses already-complete.\n const entry = this.#desired.get(info.instance)\n if (entry !== undefined && entry.phase !== 'complete') {\n this.#setPhase(info.instance, 'acked')\n }\n }\n break\n case 'pokeStart':\n this.#clearTransientSchemaEvent()\n this.#pokeBuffer = []\n break\n case 'pokePart':\n if (this.#pokeBuffer === null) {\n this.#errors.push('doync: pokePart arrived with no open poke')\n } else {\n for (const patch of message.patches) this.#pokeBuffer.push(patch)\n }\n break\n case 'pokeEnd':\n if (this.#pokeBuffer === null) {\n this.#errors.push('doync: pokeEnd arrived with no open poke')\n } else {\n this.#applyPoke(\n message.cookie,\n message.lastMutationId,\n message.confirms,\n )\n }\n break\n case 'pokeReject':\n this.#handleReject(message.mutationId, message.error, message.reason)\n break\n case 'onceStart':\n // Fresh Once accumulator (ADR-0012); reconnect re-issue resets partial.\n this.#onceBuffers.set(message.id, [])\n break\n case 'oncePart': {\n const buffer = this.#onceBuffers.get(message.id)\n if (buffer === undefined) {\n this.#errors.push('doync: oncePart arrived with no open once')\n } else {\n for (const row of message.rows) buffer.push(row)\n }\n break\n }\n case 'onceEnd':\n this.#handleOnceEnd(message)\n break\n case 'resyncRequired':\n // heldAt-ahead tripwire (ADR-0029 / #225): resync self-heal as a fresh\n // client. First-class frame, not phrase-matched.\n this.#errors.push(message.message)\n this.#exclusive(() =>\n this.#wipeAndResync(\n `heldAt ahead of vouched baseline (instance ${message.instance}: heldAt=${message.heldAt} baseline=${message.baseline}) — ${message.message}`,\n ),\n )\n break\n case 'error':\n this.#errors.push(message.message)\n // Cookie-above-head (ADR-0020 addendum): wipe/redeploy makes every\n // returning cookie above head — heal via wipe-and-resync as a fresh\n // client, not a permanent subscription error.\n if (isCookieAboveHeadError(message.message)) {\n this.#exclusive(() =>\n this.#wipeAndResync(\n `cookie above the Origin head (server reset) — ${message.message}`,\n ),\n )\n break\n }\n // Named instances -> exact fail (#114); else coarse tar unconfirmed.\n if (message.instances !== undefined) {\n this.#failInstances(message.instances, message.message)\n } else {\n this.#failUnconfirmed(message.message)\n }\n break\n default:\n // Unknown ServerMessage type => Mirror ahead of bundle — surface loud.\n this.#errors.push(\n `doync: unknown ServerMessage type ${String(\n (message as { type?: unknown }).type,\n )}`,\n )\n }\n }\n\n /**\n * Fail exactly the instances the Mirror named on an `error` frame (#114).\n * Skips already-complete/dropped; later ack recovers. Fallback for un-named\n * errors is {@link #failUnconfirmed}.\n */\n #failInstances(instances: readonly string[], message: string): void {\n const error = new Error(message)\n for (const identity of instances) {\n const entry = this.#desired.get(identity)\n if (\n entry !== undefined &&\n (entry.phase === 'pending' || entry.phase === 'acked')\n ) {\n this.#setPhase(identity, 'error', error)\n }\n }\n }\n\n /**\n * Un-attributable framed `error` -> every pending/acked instance (#104/#114).\n * Complete untouched; later ack recovers. Prefer {@link #failInstances}.\n */\n #failUnconfirmed(message: string): void {\n const error = new Error(message)\n for (const [identity, entry] of this.#desired) {\n if (entry.phase === 'pending' || entry.phase === 'acked') {\n this.#setPhase(identity, 'error', error)\n }\n }\n }\n\n /**\n * OnceEnd (ADR-0012): deliver accumulated parts to {@link OnceViewImpl} or\n * reject on framed error. No cookie/lmid. Stray onceEnd (disposed / already\n * settled) is benign — drop it.\n */\n #handleOnceEnd(message: OnceEndMessage): void {\n const buffered = this.#onceBuffers.get(message.id) ?? []\n this.#onceBuffers.delete(message.id)\n const pending = this.#onceRequests.get(message.id)\n if (pending === undefined) return\n this.#onceRequests.delete(message.id)\n if (message.error !== undefined) {\n pending.view.settleError(new Error(message.error))\n return\n }\n pending.view.deliver(buffered.map(decodeOnceRow))\n }\n\n // --- rebase ---------------------------------------------------------------\n\n /**\n * PokeEnd apply (ADR-0013/0016/0019). Capture buffer sync (next poke may\n * reset it), then exclusive chain so poke never rolls an overlay mid-await.\n * Rolls overlay, applies patches (puts before dels), drops rejected-then-\n * acked, persists cookie with batch, replays survivors.\n */\n #applyPoke(\n cookie: number,\n lastMutationId: number | null,\n confirms: readonly string[] | undefined,\n ): void {\n const patches = this.#pokeBuffer ?? []\n this.#pokeBuffer = null\n this.#exclusive(() =>\n this.#doApplyPoke(patches, cookie, lastMutationId, confirms),\n )\n }\n\n #doApplyPoke(\n patches: readonly Patch[],\n cookie: number,\n lastMutationId: number | null,\n confirms: readonly string[] | undefined,\n ): Awaitable<void> {\n const db = this.#db\n const acked: number[] = []\n\n const touched = this.#rebase(() => {\n // Puts before dels/prunes (ADR-0013) — row moving instances never flaps.\n for (const patch of patches) if (patch.op === 'put') this.#applyPut(patch)\n for (const patch of patches) if (patch.op === 'del') this.#applyDel(patch)\n // pks (ADR-0013/0015): after puts, prune held rows absent from the list\n // (cookie-filtered / warm reactivation). Same EXISTS refcount as dels.\n for (const patch of patches) if (patch.op === 'pks') this.#applyPks(patch)\n\n // Drop rejected-then-acked (order): rejected already gone; lmid settles\n // only non-rejected; rejected ids covered by lmid stay rejected.\n if (lastMutationId !== null) {\n for (const p of this.#pending) {\n if (\n p.mutationId <= lastMutationId &&\n !this.#rejected.has(p.mutationId)\n ) {\n acked.push(p.mutationId)\n }\n }\n for (const id of acked) {\n db.exec(`DELETE FROM __doync_pending WHERE mutation_id = ?`, id)\n }\n }\n\n // Cookie advances only here, with the batch (ADR-0016).\n this.#cookie = cookie\n writeMeta(db, 'cookie', String(cookie))\n // Connected clock on same txn (ADR-0014 / #223); crash => slow (safe).\n this.#persistConnectedClock()\n this.#pending = this.#pending.filter((p) => !acked.includes(p.mutationId))\n })\n\n return thenMaybe(touched, (t) => {\n for (const id of acked) this.#settleServer(id, null)\n for (const id of acked) this.#rejected.delete(id)\n this.#notifyChange(t)\n // Other half of complete (#104/#114): promote named `confirms` after\n // rows (rows-then-status). Empty hydration still completes.\n this.#promoteConfirmed(confirms)\n })\n }\n\n /**\n * Promote hydration/reactivation `confirms` to `complete` (#114). Marker-\n * only — a Sweep poke with no/empty confirms promotes nothing (#114 race).\n * Only `acked` promotes; pending waits for ack; already-complete is no-op.\n */\n #promoteConfirmed(confirms: readonly string[] | undefined): void {\n if (confirms === undefined) return\n for (const identity of confirms) {\n const entry = this.#desired.get(identity)\n if (entry !== undefined && entry.phase === 'acked') {\n this.#setPhase(identity, 'complete')\n }\n }\n }\n\n /** Apply one put (ADR-0013): membership + upsert (delete-by-pk then insert). */\n #applyPut(patch: Extract<Patch, { op: 'put' }>): void {\n const db = this.#db\n const table = this.#tableFor(patch.instance, patch.level)\n db.exec(\n `INSERT INTO __doync_membership (instance, level, tbl, pk) VALUES (?, ?, ?, ?)\n ON CONFLICT (instance, level, pk) DO UPDATE SET tbl = excluded.tbl`,\n patch.instance,\n patch.level,\n table.name,\n patch.pk,\n )\n const pkValues = parsePk(patch.pk, table)\n db.exec(\n `DELETE FROM ${quoteIdent(table.name)} WHERE ${pkWhereClause(table)}`,\n ...pkValues,\n )\n const { columns, values } = decodePatchImage(table, patch.image)\n db.exec(\n `INSERT INTO ${quoteIdent(table.name)} (${columns.map(quoteIdent).join(', ')})\n VALUES (${columns.map(() => '?').join(', ')})`,\n ...values,\n )\n }\n\n /**\n * Apply one del (ADR-0013): drop membership; delete row only when last ref is\n * gone (EXISTS).\n */\n #applyDel(patch: Extract<Patch, { op: 'del' }>): void {\n this.#retractRow(patch.instance, patch.level, patch.pk)\n }\n\n /**\n * Apply one pks patch (ADR-0013/0015): complete pk-list for (instance,\n * level). Prune held rows absent from the list via the same EXISTS retract as\n * del.\n */\n #applyPks(patch: PksPatch): void {\n const keep = new Set(patch.pks)\n const held = this.#db.exec<{ pk: SqlValue }>(\n `SELECT pk FROM __doync_membership WHERE instance = ? AND level = ?`,\n patch.instance,\n patch.level,\n )\n for (const row of held) {\n const pk = String(row.pk)\n if (!keep.has(pk)) this.#retractRow(patch.instance, patch.level, pk)\n }\n }\n\n /**\n * Retract one membership (ADR-0013/0028): drop ref; delete row when no\n * DESIRED holder remains (#215). Released holders must not pin\n * authoritatively retracted rows. Only-stale remaining => delete row and\n * {@link #breakStaleInstance} so next subscribe omits heldAt.\n */\n #retractRow(instance: string, level: number, pk: string): void {\n const db = this.#db\n const table = this.#tableFor(instance, level)\n db.exec(\n `DELETE FROM __doync_membership WHERE instance = ? AND level = ? AND pk = ?`,\n instance,\n level,\n pk,\n )\n const holders = db.exec<{ instance: string }>(\n `SELECT DISTINCT instance FROM __doync_membership WHERE tbl = ? AND pk = ?`,\n table.name,\n pk,\n )\n // Desired holder keeps the row (ADR-0013 EXISTS).\n if (holders.some((h) => this.#desired.has(String(h.instance)))) return\n db.exec(\n `DELETE FROM ${quoteIdent(table.name)} WHERE ${pkWhereClause(table)}`,\n ...parsePk(pk, table),\n )\n // Only stale holders: retraction outranks warmth (ADR-0028).\n for (const h of holders) this.#breakStaleInstance(String(h.instance))\n }\n\n /**\n * Break stale (undesired) warmth (ADR-0028): drop all memberships and orphan\n * rows. Authoritative retraction proved parked state stale; clearing\n * memberships makes next subscribe's heldAt honestly absent (full rehydrate).\n * Rows still referenced elsewhere survive.\n */\n #breakStaleInstance(instance: string): void {\n const db = this.#db\n const refs = db.exec<{ tbl: string; pk: string }>(\n `SELECT DISTINCT tbl, pk FROM __doync_membership WHERE instance = ?`,\n instance,\n )\n db.exec(`DELETE FROM __doync_membership WHERE instance = ?`, instance)\n // Drop stamp with memberships (ADR-0028 / #224) so next subscribe cannot\n // claim a stale heldAt. Caller supplies durable envelope (poke apply or GC).\n deleteReleaseStamp(db, instance)\n for (const ref of refs) {\n const tblName = String(ref.tbl)\n const pk = String(ref.pk)\n const stillReferenced = db.exec(\n `SELECT 1 FROM __doync_membership WHERE tbl = ? AND pk = ? LIMIT 1`,\n tblName,\n pk,\n )\n if (stillReferenced.length === 0) {\n const table = requireTable(this.#schema, tblName)\n db.exec(\n `DELETE FROM ${quoteIdent(table.name)} WHERE ${pkWhereClause(table)}`,\n ...parsePk(pk, table),\n )\n }\n }\n }\n\n #tableFor(instance: string, level: number): ReturnType<typeof requireTable> {\n const info = this.#instanceInfo.get(instance)\n if (info === undefined) {\n throw new Error(\n `doync: patch for unknown instance ${instance} — a subscribeAck must precede its hydration poke`,\n )\n }\n const levelInfo = info.levels.find((l) => l.level === level)\n if (levelInfo === undefined) {\n throw new Error(\n `doync: instance ${instance} has no Level ${level} in its subscribeAck`,\n )\n }\n return requireTable(this.#schema, levelInfo.table)\n }\n\n /**\n * Rejection (ADR-0016/0019): mark rejected immediately (lmid cannot resolve\n * it even under race), then exclusive-chain drop pending, reject `server`,\n * re-project (optimistic effect vanishes).\n *\n * `reason: 'reaped'` (ADR-0015 / ADR-0037) is recovery, not a per-mutation\n * failure: the Origin has no cursor for this Client, so every pending is\n * dropped as a fresh-client heal — first-class tag, never phrase-matched.\n */\n #handleReject(mutationId: number, error: string, reason?: 'reaped'): void {\n this.#rejected.add(mutationId)\n if (reason === 'reaped') {\n const dropped = this.#pending.length\n this.#errors.push(error)\n this.#exclusive(() =>\n this.#wipeAndResync(\n `server forgot this client (reaped) — dropped ${dropped} pending write${dropped === 1 ? '' : 's'} — ${error}`,\n 'reaped',\n ),\n )\n return\n }\n this.#exclusive(() =>\n thenMaybe(this.#dropPending(mutationId), (touched) => {\n this.#settleServer(mutationId, new Error(error))\n this.#notifyChange(touched)\n }),\n )\n }\n\n // --- schema handling (ADR-0020) -------------------------------------------\n\n /**\n * Schema-skew handshake answer (ADR-0020, reject-don't-project): -\n * `client-stale` — bundle behind Mirror; emit reload and HALT. -\n * `client-ahead` — mid-deploy Mirror; back off via socket reconnect (adapter\n * owns timer); clears on later successful handshake.\n */\n #onSchemaSkew(message: SchemaSkewMessage): void {\n const server =\n message.serverVersion === undefined\n ? ''\n : ` (server v${message.serverVersion})`\n if (message.reason === 'client-stale') {\n this.#emitSchemaEvent(\n 'reload',\n `schema client-stale: bundle v${this.#bundleVersion} is behind the Mirror${server} — reload for the new bundle`,\n )\n this.#halt()\n } else {\n this.#emitSchemaEvent(\n 'server-behind',\n `schema client-ahead: bundle v${this.#bundleVersion} is ahead of the Mirror${server} — backing off until it deploys`,\n )\n this.#setConnected(false)\n this.#socket.reconnect()\n }\n }\n\n /**\n * Mid-session `schema` directive (ADR-0020): apply OWN bundled DDL at this\n * feed position (never wire SQL).\n *\n * - Version > bundle → reload + HALT\n * - Version <= applied → idempotent no-op (redelivery)\n * - Else apply `(applied, version]`; failure → wipe-and-resync\n */\n #onSchemaDirective(message: SchemaDirectiveMessage): void {\n const version = message.version\n if (version > this.#bundleVersion) {\n this.#emitSchemaEvent(\n 'reload',\n `schema directive v${version} is above the bundled track v${this.#bundleVersion} — reload for the new bundle`,\n )\n this.#halt()\n return\n }\n if (version <= this.#appliedVersion) return\n // Schema apply on exclusive chain like every overlay touching op.\n this.#exclusive(() => this.#applySchemaDirective(version))\n }\n\n #applySchemaDirective(version: number): Awaitable<void> {\n const failReason = (error: unknown): string =>\n `local migration to schema v${version} failed: ${errorMessage(error)}`\n let touched: Awaitable<Set<string>>\n try {\n touched = this.#rebase(() => {\n applyBundledMigrations(\n this.#db,\n this.#schema,\n this.#appliedVersion,\n version,\n )\n })\n } catch (error) {\n // Sync migration failure (ADR-0020): #rebase left a clean handle.\n return this.#wipeAndResync(failReason(error))\n }\n return thenMaybeCatch(\n touched,\n (t) => {\n this.#appliedVersion = version\n // Shape change can touch every read — re-project all consumer tables.\n this.#notifyChange(new Set([...t, ...this.#allConsumerTables()]))\n },\n // Async migration failure: same wipe via rejected rebase promise.\n (error) => this.#wipeAndResync(failReason(error)),\n )\n }\n\n // --- recovery surface (ADR-0022 / ADR-0037) --------------------------------\n\n /**\n * Rebuild sync state as a fresh Client under the same identity (ADR-0022 /\n * ADR-0037 resync): drop pending writes, mint a new clientId, wipe replica /\n * memberships / cookie, rebootstrap. Same core as failed-migration,\n * cookie-above-head, and heldAt-ahead heals ({@link #wipeAndResync}).\n * Exclusive chain; offline rebootstrap rides next reconnect. Preferences\n * survive (`__doync_prefs`).\n */\n resync(): void {\n this.#exclusive(() =>\n this.#wipeAndResync('resync() requested by the consumer'),\n )\n }\n\n /**\n * Erase this identity's local data (ADR-0022 forget): wipe replica,\n * memberships, pending, meta, and preferences, then fresh first-sight under a\n * new clientId. Erases WHO, not just WHAT. Awaiters of forgotten writes are\n * rejected. Topology can erase the identity's database file on active forget\n * (#134); this resets the current store. Exclusive chain.\n */\n forget(): void {\n this.#exclusive(() =>\n this.#forgetAndReset('forget() requested by the consumer'),\n )\n }\n\n // --- durable meta side-writes (ADR-0022, closeio/doync#139) ---------------\n\n /**\n * Durably set {@link LogoutBehavior} at runtime ({@link\n * DoyncClient.setLogoutBehavior}). Via {@link #sideWritePref} so it commits\n * outside the optimistic overlay (#135 / ADR-0019). Direct engine stores for\n * RN; web DB worker reports to hub.\n */\n setLogoutBehavior(behavior: LogoutBehavior): void {\n this.#sideWritePref(__LOGOUT_BEHAVIOR_PREF_KEY, behavior)\n }\n\n /**\n * Durable `__doync_prefs` side write via rebase envelope (#139). For low-\n * frequency runtime preferences ({@link setLogoutBehavior}). High-frequency\n * values (connected clock #223) stay in memory and piggyback poke apply —\n * full rebase per tick is wrong. Exclusive chain + {@link #rebase} so the\n * write commits on the base, not the overlay SAVEPOINT. Pref-only → discard\n * touched set.\n */\n #sideWritePref(key: string, value: string): void {\n this.#sideWrite(() => writePref(this.#db, key, value))\n }\n\n /**\n * Durable side-body through overlay envelope (#139/#223/#224): exclusive +\n * {@link #rebase}. For meta and release stamps. Must NOT nest inside an\n * in-flight durable body (self-queue); those exec SQL on `this.#db` directly\n * (see `#breakStaleInstance`).\n */\n #sideWrite(body: () => void): void {\n this.#exclusive(() => thenMaybe(this.#rebase(body), () => {}))\n }\n\n /**\n * Shared fresh-client store wipe for Resync, Forget, and the boot-time\n * ledger-rollback heal (ADR-0037): drop consumer tables, memberships, stamps,\n * pending queue, and all sync meta; re-replay the bundled track from empty.\n * Preferences (`__doync_prefs`) and the engine ledger stay. Caller mints the\n * fresh identity. FKs already off (ADR-0009).\n *\n * `bundleVersion` defaults to the engine's, and is passed explicitly from\n * boot, which runs before that field is assigned.\n */\n #wipeStore(db: LocalDb, bundleVersion = this.#bundleVersion): void {\n dropConsumerTables(db, this.#schema)\n db.exec('DELETE FROM __doync_membership')\n // Wipe stamps with memberships (ADR-0014 / #224); table shape stays.\n clearReleaseStamps(db)\n db.exec('DELETE FROM __doync_pending')\n // All sync meta gone — cookie, clientId, next-mutid, connected clock.\n db.exec('DELETE FROM __doync_meta')\n // Full bundled track from empty (re-records schema_version).\n applyBundledMigrations(db, this.#schema, 0, bundleVersion)\n }\n\n /**\n * Store-erase core for forget / corrupt-meta heal: shared wipe plus\n * preferences. Caller mints identity. Topology may also erase the database\n * file.\n */\n #forgetStore(db: LocalDb): void {\n this.#wipeStore(db)\n db.exec('DELETE FROM __doync_prefs')\n }\n\n /**\n * Shared memory reset after a store wipe: reject awaiters, mint a fresh\n * clientId, empty the pending queue, null the cookie, reset the mutation\n * counter and connected clock.\n */\n #resetToFreshClient(dropError: Error): void {\n const db = this.#db\n this.#rejectAllPending(dropError)\n this.#clientId = this.#generateId()\n writeMeta(db, 'client_id', this.#clientId)\n this.#cookie = null\n this.#nextMutationId = 1\n this.#connectedClock = 0\n this.#lastPongAt = null\n this.#pending = []\n }\n\n /**\n * Mid-session forget (ADR-0022): erase store (including preferences), mint\n * fresh identity, re-handshake (or next reconnect). Topology may also erase\n * the identity's database file.\n */\n #forgetAndReset(reason: string): Awaitable<void> {\n const db = this.#db\n const touched = this.#rebase(() => {\n this.#forgetStore(db)\n this.#resetToFreshClient(\n new Error(`doync: ${reason} — pending writes were forgotten`),\n )\n })\n return this.#rebootstrapAfterReset(\n touched,\n 'forget',\n `forgot the local store — ${reason}`,\n )\n }\n\n /**\n * Rebootstrap tail for {@link #wipeAndResync} and {@link #forgetAndReset}: mark\n * fresh, clear instance info, emit event, drop-and-redial when live so the\n * new clientId rides the upgrade pin (ADR-0016), re-project all consumer\n * tables. Offline tanks until the next connect (no live socket to drop).\n */\n #rebootstrapAfterReset(\n touched: Awaitable<Set<string>>,\n kind: SchemaEventKind,\n message: string,\n ): Awaitable<void> {\n return thenMaybe(touched, (t) => {\n this.#appliedVersion = this.#bundleVersion\n this.#instanceInfo.clear()\n this.#emitSchemaEvent(kind, message)\n // Fresh clientId cannot ride an already-pinned socket (ADR-0016 upgrade\n // pin). Drop and redial so the next open re-handshakes as the new Client\n // — same posture as client-ahead skew backoff.\n if (this.#connected) {\n this.#setConnected(false)\n this.#socket.reconnect()\n }\n this.#notifyChange(new Set([...t, ...this.#allConsumerTables()]))\n })\n }\n\n /**\n * Reject every outstanding mutation promise (resync/forget discard their\n * queue). Idempotent; orphaneds swallow — frees in-flight callers only.\n */\n #rejectAllPending(error: Error): void {\n // Safe: Map iteration skips removed keys; settleServer deletes when done.\n for (const id of this.#promises.keys()) {\n this.#settleClient(id, error)\n this.#settleServer(id, error)\n }\n this.#keyed.clear()\n this.#recoveredKeys.clear()\n this.#rejected.clear()\n this.#optimisticFailed.clear()\n }\n\n /**\n * Wipe-replica-and-resync core (ADR-0020 / ADR-0037) for the resync verb,\n * failed migration, cookie-above-head, heldAt-ahead, and client-state reap:\n * drop consumer tables, pending writes, and sync meta; mint a fresh clientId;\n * re-replay the bundle; rebootstrap. Preferences (`__doync_prefs`) and the\n * `__doync_engine` ledger survive. A clientId the Origin has never seen\n * starts its mutation counter at 1 by construction. `kind` is `reaped` when\n * the Origin forgot the Client so the consumer can distinguish that from an\n * ordinary repair; the default is `resync`.\n */\n #wipeAndResync(\n reason: string,\n kind: 'resync' | 'reaped' = 'resync',\n ): Awaitable<void> {\n const db = this.#db\n const touched = this.#rebase(() => {\n this.#wipeStore(db)\n this.#resetToFreshClient(\n new Error(`doync: ${reason} — pending writes were dropped`),\n )\n })\n return this.#rebootstrapAfterReset(\n touched,\n kind,\n kind === 'reaped'\n ? `client state was reaped — ${reason}`\n : `wiped and resyncing — ${reason}`,\n )\n }\n\n /** Every consumer table name (untargeted re-projection set). */\n #allConsumerTables(): string[] {\n return this.#schema.tables.map((t) => t.name)\n }\n\n /**\n * Schema-state transitions including silent clear (#89) so hooks re-read\n * {@link schemaStatus} and banners don't stick after recovery.\n */\n onSchemaChange(listener: () => void): () => void {\n this.#schemaListeners.add(listener)\n return () => this.#schemaListeners.delete(listener)\n }\n\n /** Record a schema-state transition and notify listeners/callback. */\n #emitSchemaEvent(kind: SchemaEventKind, message: string): void {\n const event: SchemaEvent = { kind, message }\n this.#schemaEvent = event\n this.#onSchemaEvent?.(event)\n this.#notifySchema()\n }\n\n /**\n * Clear transient schema state once sync resumes (ack/poke after\n * re-handshake): server-behind / resync / reaped / forget. `reload` is\n * terminal (halted ignores frames). Notifies subscribers; config callback has\n * no cleared kind.\n */\n #clearTransientSchemaEvent(): void {\n const kind = this.#schemaEvent?.kind\n if (\n kind === 'server-behind' ||\n kind === 'resync' ||\n kind === 'reaped' ||\n kind === 'forget'\n ) {\n this.#schemaEvent = null\n this.#notifySchema()\n }\n }\n\n /** Notify schema-state subscribers (set or clear). */\n #notifySchema(): void {\n for (const listener of this.#schemaListeners) listener()\n }\n\n /** Stop handshakes and frame apply until app reload (reload skew). */\n #halt(): void {\n this.#halted = true\n this.#setConnected(false)\n }\n\n // --- connection status (closeio/doync#105, #136) --------------------------\n\n /**\n * {@link ConnectionStatus} to the Mirror (#105/#136). Precedence: 1.\n * `needs-auth` — engine inference off framed `unauthorized` (ADR-0018),\n * sticky until a real frame proves the refreshed handshake; outranks seam and\n * even a reopened socket. 2. `connected` — live open outranks a stale seam\n * report. 3. Last {@link SeamStatus}, else `disconnected`.\n *\n * Full five states when the seam reports; degraded adapters (no seam) get\n * connected/disconnected (+ needs-auth). Parity with shared-hub.ts.\n */\n get connectionStatus(): ConnectionStatus {\n if (this.#needsAuth) return 'needs-auth'\n if (this.#connected) return 'connected'\n return this.#seamStatus ?? 'disconnected'\n }\n\n /** Subscribe to connection-status transitions (#105). */\n onConnectionChange(listener: () => void): () => void {\n this.#connectionListeners.add(listener)\n return () => this.#connectionListeners.delete(listener)\n }\n\n /**\n * Flip handshake-live (#136). Live open clears stale seam status; needs-auth\n * untouched (clears only on proven frame). {@link #transitionConnection}\n * notifies only on visible change.\n */\n #setConnected(value: boolean): void {\n this.#transitionConnection(() => {\n this.#connected = value\n if (value) this.#seamStatus = null\n })\n }\n\n /**\n * Seam transient (#136): connecting/error. Stored even while outranked so it\n * surfaces when higher precedence clears.\n */\n #onSeamStatus(status: SeamStatus): void {\n this.#transitionConnection(() => {\n this.#seamStatus = status\n })\n }\n\n /** Set/clear sticky auth-failure (#136, ADR-0018). */\n #setNeedsAuth(value: boolean): void {\n this.#transitionConnection(() => {\n this.#needsAuth = value\n })\n }\n\n /**\n * Mutate connection state; notify only when visible {@link connectionStatus}\n * changes (outranked inputs stay silent).\n */\n #transitionConnection(mutate: () => void): void {\n const before = this.connectionStatus\n mutate()\n if (this.connectionStatus === before) return\n for (const listener of this.#connectionListeners) listener()\n }\n\n // --- reactivity -----------------------------------------------------------\n\n /**\n * Re-project after a local change: subscription re-runs when Read-set\n * intersects touched consumer tables; Local (no Read-set) always re-runs.\n * Unchanged rows => no notify.\n *\n * Target TOUCHED TABLES (ADR-0019 delta 3), not patched instances — shared\n * replica means A's patch can change B's SQL over the same table.\n */\n #notifyChange(touched: ReadonlySet<string>): void {\n const consumerTouched = consumerTables(touched)\n // Overlay settled: flush views gated during the just-finished replay.\n // Re-entrant gating inside notify() is picked up by `#flushGatedViews`.\n const wasGated = new Set(this.#gatedViews)\n this.#gatedViews.clear()\n for (const read of this.#liveReads()) {\n const readSet = read.readSet\n const affected =\n wasGated.has(read) ||\n readSet === undefined ||\n [...readSet].some((t) => consumerTouched.has(t))\n if (affected && read.recompute()) read.notify()\n }\n }\n\n /** Every live reactive read: desired + local (ADR-0023). */\n *#liveReads(): IterableIterator<RawRead> {\n for (const entry of this.#desired.values()) {\n if (entry.read !== null) yield entry.read\n }\n for (const entry of this.#localReads.values()) {\n if (entry.read !== null) yield entry.read\n }\n }\n\n // --- status lifecycle (closeio/doync#104) ---------------------------------\n\n /**\n * Set desire phase and push visible {@link ViewStatus} (#104). No-op if\n * unknown. pending→acked is invisible (stable status, no notify).\n */\n #setPhase(identity: string, phase: SubPhase, error?: Error): void {\n const entry = this.#desired.get(identity)\n if (entry === undefined) return\n entry.phase = phase\n entry.error = error\n const next = phaseToStatus(phase, error)\n if (entry.read !== null && entry.read.updateStatus(next)) {\n entry.read.notify()\n }\n }\n\n // --- promise bookkeeping --------------------------------------------------\n\n #ensurePromise(id: number, orphan: boolean): PendingPromise {\n const existing = this.#promises.get(id)\n if (existing !== undefined) return existing\n const promise = this.#makePromise(orphan)\n this.#promises.set(id, promise)\n return promise\n }\n\n /**\n * Make `{client, server}` without id keying. mutate returns sync; id is\n * allocated on the chain so fail-fast consumes none.\n */\n #makePromise(orphan: boolean): PendingPromise {\n let resolveClient!: () => void\n let rejectClient!: (error: unknown) => void\n let resolveServer!: () => void\n let rejectServer!: (error: unknown) => void\n const client = new Promise<void>((res, rej) => {\n resolveClient = res\n rejectClient = rej\n })\n const server = new Promise<void>((res, rej) => {\n resolveServer = res\n rejectServer = rej\n })\n if (orphan) {\n // Orphan: swallow settlement (no caller in this process).\n client.catch(() => {})\n server.catch(() => {})\n }\n return {\n pair: { client, server },\n resolveClient,\n rejectClient,\n resolveServer,\n rejectServer,\n clientSettled: false,\n serverSettled: false,\n }\n }\n\n #settleClient(id: number, error: unknown): void {\n const promise = this.#promises.get(id)\n if (promise === undefined) {\n // Unknown id would hang a waiting mutate — surface instead of silent no-op.\n this.#errors.push(\n `doync: settleClient for unknown mutation ${id} — no pending promise (a mutate() awaiting it would hang)`,\n )\n return\n }\n if (promise.clientSettled) return\n promise.clientSettled = true\n if (error === null) promise.resolveClient()\n else promise.rejectClient(error)\n }\n\n #settleServer(id: number, error: unknown): void {\n const promise = this.#promises.get(id)\n if (promise === undefined) {\n this.#errors.push(\n `doync: settleServer for unknown mutation ${id} — no pending promise (a mutate() awaiting it would hang)`,\n )\n return\n }\n if (promise.serverSettled) return\n promise.serverSettled = true\n if (error === null) promise.resolveServer()\n else promise.rejectServer(error)\n if (promise.clientSettled) this.#promises.delete(id)\n }\n}\n\n/**\n * Sync-or-async overlay currency (ADR-0019 addendum): ops return T now or a\n * Promise. Chained via isThenable / thenMaybe without microtasking the all-\n * sync path.\n */\ntype Awaitable<T> = T | Promise<T>\n\n/** One overlay-touching step on the exclusive chain (ADR-0019 addendum). */\ntype ChainStep = () => Awaitable<void>\n\nfunction isThenable(value: unknown): value is Promise<unknown> {\n return value instanceof Promise\n}\n\n/** Continue with `fn` sync when already settled, else via Promise. */\nfunction thenMaybe<T, R>(\n value: Awaitable<T>,\n fn: (value: T) => Awaitable<R>,\n): Awaitable<R> {\n return value instanceof Promise ? value.then(fn) : fn(value)\n}\n\n/**\n * {@link thenMaybe} with async rejection continuation. Sync values only run\n * `onOk` (caller try/catch already handled sync throws).\n */\nfunction thenMaybeCatch<T, R>(\n value: Awaitable<T>,\n onOk: (value: T) => Awaitable<R>,\n onErr: (error: unknown) => Awaitable<R>,\n): Awaitable<R> {\n return value instanceof Promise ? value.then(onOk, onErr) : onOk(value)\n}\n\ntype AnyRow = Record<string, SqlValue>\n\n/**\n * Inert {@link View} for skipped subscribe (#104): no desire, no re-exec, empty\n * rows, status unknown, no-op retain/release. Not keyed. Un-skip = fresh\n * subscribe at the hook (#105).\n */\nclass SkippedView implements View<Record<string, unknown>> {\n current(): readonly Record<string, unknown>[] {\n return EMPTY_ROWS\n }\n\n status(): ViewStatus {\n return UNKNOWN_STATUS\n }\n\n onChange(): () => void {\n return () => {}\n }\n\n retain(): void {}\n\n release(): void {}\n}\n\n/**\n * In-flight Once handle (ADR-0012/0021): local cache first, server answer\n * later, `server` promise for hook status. Internal over {@link AnyRow}; typed\n * at boundary.\n */\nclass OnceViewImpl implements OnceView<Record<string, unknown>> {\n #snapshot: readonly Record<string, unknown>[]\n readonly #listeners = new Set<() => void>()\n #started = false\n #disposed = false\n #serverPromiseWithResolvers =\n Promise.withResolvers<readonly Record<string, unknown>[]>()\n #disposeTimer: ReturnType<typeof setTimeout> | null = null\n /** Nested-relation decode; applied to server answer too. */\n readonly #decode: RowDecoder | undefined\n readonly #startNetwork: () => void\n readonly #drop: () => void\n\n constructor(\n cached: readonly Record<string, unknown>[],\n decode: RowDecoder | undefined,\n hooks: { start: () => void; drop: () => void },\n ) {\n this.#snapshot = cached\n this.#decode = decode\n this.#startNetwork = hooks.start\n this.#drop = hooks.drop\n }\n\n current(): readonly Record<string, unknown>[] {\n // First read arms the network half (#91). StrictMode-discarded renders\n // never read (uSES after subscribe), so construction stays pure.\n this.#ensureOpen()\n return this.#snapshot\n }\n\n onChange(listener: () => void): () => void {\n this.#ensureOpen()\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\n }\n\n /** Lazily arm the network half (Q7 commit-owns); first onChange/server starts. */\n get server(): Promise<readonly Record<string, unknown>[]> {\n this.#ensureOpen()\n return this.#serverPromiseWithResolvers.promise\n }\n\n /** Server answer landed: decode, swap snapshot, resolve, notify. */\n deliver(rows: AnyRow[]): void {\n if (this.#disposed) return\n const decoded = this.#decode ? this.#decode(rows) : rows\n this.#snapshot = decoded\n this.#serverPromiseWithResolvers.resolve(decoded)\n for (const listener of this.#listeners) listener()\n }\n\n /** Mirror could not answer: reject `server`. */\n settleError(error: unknown): void {\n if (this.#disposed) return\n this.#serverPromiseWithResolvers.reject(error)\n }\n\n /**\n * Drop after one Warm tick. Idempotent; re-open within tick cancels\n * (StrictMode).\n */\n dispose(): void {\n if (this.#disposed) return\n if (this.#disposeTimer !== null) return\n this.#disposeTimer = setTimeout(() => {\n this.#disposeTimer = null\n if (this.#disposed) return\n this.#disposed = true\n this.#listeners.clear()\n this.#drop()\n }, WARM_POOL_TICK_MS)\n }\n\n #ensureOpen(): void {\n if (this.#disposed) return\n if (this.#disposeTimer !== null) {\n clearTimeout(this.#disposeTimer)\n this.#disposeTimer = null\n }\n if (this.#started) return\n this.#started = true\n this.#serverPromiseWithResolvers =\n Promise.withResolvers<readonly Record<string, unknown>[]>()\n // Swallow server rejection if caller never awaits (no unhandled rejection).\n this.#serverPromiseWithResolvers.promise.catch(() => {})\n this.#startNetwork()\n }\n}\n\n/**\n * `false | null | undefined` — the \"no query\" sentinel (ADR-0027 /\n * closeio/doync#195/#200). Exported so adapters reuse the same predicate the\n * direct engine uses rather than shadowing a three-literal check.\n */\nexport function isFalsyQuery(value: unknown): value is FalsyQuery {\n return value === false || value === null || value === undefined\n}\n\n/**\n * Bound-form surface normalize (ADR-0027 / closeio/doync#200): peel a\n * {@link BoundQuery} into `{leaf, args, options?}`, or reject a truthy non-bound\n * impostor with a surface-named error. An uncalled RegisteredQuery (a function)\n * hits \"did you forget to call it?\" — registration is a type-level fact, so the\n * legacy definition arm is gone.\n *\n * Exported from `@doync/client` so the web topology and mobile wrapper share\n * one surface check with the direct engine — no per-adapter copy of the\n * impostor wording.\n */\nexport function normalizeQuerySurface<Options = never>(\n surface: 'subscribe' | 'once' | 'preload',\n queryOrBound: unknown,\n options?: Options,\n): {\n query: BoundQuery['query']\n args: unknown\n options: Options | undefined\n} {\n if (isBoundQuery(queryOrBound)) {\n // Cast erases the bound leaf's `One` phantom — resolve/name are all the\n // engine needs from here on; Row/One flow from the public overloads only.\n return {\n query: queryOrBound.query,\n args: queryOrBound.args,\n options,\n }\n }\n throw querySurfaceImpostorError(surface, queryOrBound)\n}\n\n/**\n * Named impostor rejection for a truthy non-bound value on a query surface\n * (ADR-0027): one error shape per surface, with the received-a-function case\n * called out as the uncalled-query mistake.\n */\nfunction querySurfaceImpostorError(surface: string, value: unknown): Error {\n if (typeof value === 'function') {\n return new Error(\n `doync: client.${surface} expected a BoundQuery — received a function; did you forget to call it?`,\n )\n }\n return new Error(\n `doync: client.${surface} expected a BoundQuery, got ${describeImpostor(value)}`,\n )\n}\n\n/** Short runtime description for an impostor value (never dumps large objects). */\nfunction describeImpostor(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'an array'\n const type = typeof value\n if (type === 'object') {\n const kind = (value as { kind?: unknown }).kind\n if (typeof kind === 'string') return `an object with kind \"${kind}\"`\n return 'an object'\n }\n return type\n}\n\n/**\n * Inert {@link OnceView} for falsy Once (ADR-0027 / #195): no network, empty\n * cache, never-settling `server` (hooks map to skipped). Safe no-op methods.\n */\nclass SkippedOnceView implements OnceView<Record<string, unknown>> {\n current(): readonly Record<string, unknown>[] {\n return EMPTY_ROWS\n }\n\n onChange(): () => void {\n return () => {}\n }\n\n get server(): Promise<readonly Record<string, unknown>[]> {\n // Never settles — network never started; hook layer owns the skipped status.\n return new Promise(() => {})\n }\n\n dispose(): void {}\n}\n\n/** No-op {@link WarmupHandle} for warmup(falsy) (ADR-0027). */\nexport const NOOP_WARMUP: WarmupHandle = {\n release(): void {},\n}\n\n/**\n * Retain `view` as a warmup hold: released by the grace lapse, `signal` abort,\n * or the handle — idempotent, first wins. Shared by every client's `warmup` so\n * the three release paths cannot drift per platform.\n */\nexport function warmupHold(\n view: View<Record<string, unknown>>,\n signal?: AbortSignal,\n): WarmupHandle {\n view.retain()\n let timer: ReturnType<typeof setTimeout>\n let released = false\n const release = (): void => {\n if (released) return\n released = true\n clearTimeout(timer)\n signal?.removeEventListener('abort', release)\n view.release()\n }\n timer = setTimeout(release, WARMUP_GRACE_MS)\n unrefTimer(timer)\n signal?.addEventListener('abort', release, { once: true })\n return { release }\n}\n\n/** No-op {@link PreloadHandle} for preload(falsy) (ADR-0027). */\nconst NOOP_PRELOAD: PreloadHandle = {\n cleanup(): void {},\n}\n\n/** Decode a codec-encoded Once row (ADR-0010). */\nfunction decodeOnceRow(image: Record<string, unknown>): AnyRow {\n const row: AnyRow = {}\n for (const [column, value] of Object.entries(image)) {\n row[column] = decodeImageValue(value)\n }\n return row\n}\n\n/** Drop `__doync_`-internal tables from a set (ADR-0007). */\nfunction consumerTables(tables: ReadonlySet<string>): Set<string> {\n return new Set([...tables].filter((t) => !isInternalTable(t)))\n}\n\n/**\n * A `mutate()` whose failure is known synchronously (unknown name, bad args).\n * Exported for the web topology's tab-side `mutate`, which shares the\n * never-throw contract (#161).\n */\nexport function settledRejection(error: unknown): MutationResult {\n const client = Promise.reject(error)\n const server = Promise.reject(error)\n // The caller may await only one of the pair; swallow the other so a validation\n // error never surfaces as an unhandled rejection.\n client.catch(() => {})\n server.catch(() => {})\n return { client, server }\n}\n\n/** Message of a thrown value (Error or otherwise). */\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * Warm pool retention (CONTEXT.md; ADR-0023) — one knob (StrictMode /\n * soft-nav). Future count/time bounds change only this value's shape.\n */\nconst WARM_POOL_TICK_MS = 0\n/** Warmup ownership grace; callers can release sooner via signal or handle. */\nconst WARMUP_GRACE_MS = 15_000\n\n/** Release an event-loop reference when the runtime exposes `unref()`. */\nfunction unrefTimer(timer: ReturnType<typeof setTimeout>): void {\n if (typeof timer === 'object' && timer !== null && 'unref' in timer) {\n const unref = (timer as { unref?: () => void }).unref\n unref?.call(timer)\n }\n}\n\n/** Shared empty-rows snapshot for skipped/unseeded Views (stable). */\nconst EMPTY_ROWS: readonly Record<string, unknown>[] = Object.freeze([])\n/** Shared unknown/complete status snapshots (stable, #104). */\nconst UNKNOWN_STATUS: ViewStatus = Object.freeze({ status: 'unknown' })\nconst COMPLETE_STATUS: ViewStatus = Object.freeze({ status: 'complete' })\n\n/**\n * {@link SubPhase} -> visible {@link ViewStatus} (#104): pending/acked share\n * frozen unknown; complete uses frozen singleton; error is a fresh object.\n */\nfunction phaseToStatus(phase: SubPhase, error: Error | undefined): ViewStatus {\n if (phase === 'complete') return COMPLETE_STATUS\n if (phase === 'error') {\n return error === undefined\n ? { status: 'error' }\n : { status: 'error', error }\n }\n return UNKNOWN_STATUS\n}\n\nconst defaultGenerateID = (): string => {\n const g = globalThis as { crypto?: { randomUUID?: () => string } }\n const uuid = g.crypto?.randomUUID?.()\n if (uuid !== undefined) return uuid\n return `client-${Math.random().toString(16).slice(2)}-${Date.now().toString(16)}`\n}\n\n/**\n * Durable int meta keys for boot corruption scan (ADR-0022). Excludes connected\n * clock (#223 — never on wire; lenient zero reset, not forget-heal).\n */\nconst DURABLE_INT_META_KEYS = ['schema_version', 'next_mutation_id', 'cookie']\n\n/**\n * First durable-int meta key with a present non-integer, or null (ADR-0022).\n * Boot scan before store read — corrupt counter => loud forget-heal, not refuse\n * (#128 C6). Same keys `#durableInt` later reads.\n */\nfunction corruptMetaKey(db: LocalDb): string | null {\n for (const key of DURABLE_INT_META_KEYS) {\n const raw = readMeta(db, key)\n if (raw !== null && !Number.isInteger(Number(raw))) return key\n }\n return null\n}\n\n/**\n * Cookie-above-Origin-head framed error (ADR-0016/0020): keys on the harness-\n * pinned phrase; heal via automatic resync, not permanent sub error.\n */\nfunction isCookieAboveHeadError(message: string): boolean {\n return message.includes('above the Origin head')\n}\n","import type {\n AuthContext,\n AuthData,\n DoyncSchema,\n MutationTree,\n} from '@doync/core'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport { flattenMutations, validateStandardSchema } from '@doync/core/internal'\n\nimport type { DoyncClient, LogoutBehavior, SchemaEvent } from './engine'\nimport type { LocalDb } from './port'\nimport type { SyncSocket } from './socket'\n\nimport { __LOGOUT_BEHAVIOR_PREF_KEY, ClientEngine } from './engine'\nimport { createEngineTables, writePref } from './replica'\n\n// The durable logout-policy seam (ADR-0022) is owned by the engine (the layer\n// that owns `__doync_prefs` and the runtime {@link ClientEngine.setLogoutBehavior}).\n// Only LogoutBehavior is public; the pref key stays on `./internal`.\nexport type { LogoutBehavior } from './engine'\n\n/**\n * Options for {@link createClient}. Platform adapters (`@doync/web`,\n * `@doync/mobile`) fill these; app code rarely calls `createClient` directly.\n */\nexport interface CreateClientOptions<\n TAuthContext extends AuthContext = AuthContext,\n> {\n /** Synchronous local SQLite port (wa-sqlite / node:sqlite / op-sqlite). */\n readonly db: LocalDb\n /** Synced schema shared with Origin and Mirror. */\n readonly schema: DoyncSchema\n /**\n * Shared mutation tree from `defineMutations(shared)` — no server overrides\n * in the client bundle.\n */\n readonly mutations: MutationTree\n /** Transport to the Mirror. */\n readonly socket: SyncSocket\n /**\n * Whole client identity, or `null` for anonymous. `token` is optional inside\n * {@link AuthData} (cookie auth); `ctx` is required when authenticated.\n */\n readonly authData: AuthData<TAuthContext> | null\n /**\n * Standard Schema for `authData.ctx`. When present, construction validates\n * synchronously and throws on failure. Platform adapters pass the consumer's\n * shared `ctxValidationSchema`.\n */\n readonly ctxValidationSchema: StandardSchemaV1<unknown, TAuthContext>\n /** Observe schema/recovery transitions. */\n readonly onSchemaEvent?: (event: SchemaEvent) => void\n /**\n * Initial {@link LogoutBehavior}, written durably when provided. Omit to keep\n * a previously stored choice (a restart will not revert `forget` to `keep`).\n * Change later via {@link DoyncClient.setLogoutBehavior}.\n */\n readonly logoutBehavior?: LogoutBehavior\n}\n\n/**\n * Construct a {@link DoyncClient} from local DB, schema, shared mutations, and a\n * Mirror socket. App code normally goes through `@doync/web` / `@doync/mobile`\n * instead.\n */\nexport function createClient<TAuthContext extends AuthContext = AuthContext>(\n options: CreateClientOptions<TAuthContext>,\n): DoyncClient {\n return createClientEngine(options)\n}\n\n/**\n * {@link CreateClientOptions} for trusted internal construction: the web DB\n * worker receives tab-validated identity over the wire and cannot hold the\n * consumer's schema (a validator is a function), so the schema is optional here\n * — absent means the caller already validated.\n */\nexport type CreateClientEngineOptions<\n TAuthContext extends AuthContext = AuthContext,\n> = Omit<CreateClientOptions<TAuthContext>, 'ctxValidationSchema'> & {\n readonly ctxValidationSchema?: StandardSchemaV1<unknown, TAuthContext>\n /** Pin a durable client id (monorepo test/bench lanes only). */\n readonly clientId?: string\n}\n\n/**\n * Concrete-engine factory (ADR-0033 / closeio/doync#241). Same construction as\n * {@link createClient}, but typed as {@link ClientEngine} so platform adapters\n * (web DB-worker, mobile wrapper) can reach engine-only fields like `clientId`\n * without an unchecked downcast. Exported on `@doync/client/internal` only.\n */\nexport function createClientEngine<\n TAuthContext extends AuthContext = AuthContext,\n>(options: CreateClientEngineOptions<TAuthContext>): ClientEngine {\n // Logout policy must land before boot (ADR-0022 / #135): construction opens\n // the optimistic overlay (ADR-0019), so a later write on this connection would\n // join the savepoint and roll back. Pre-boot is autocommit.\n if (options.logoutBehavior !== undefined) {\n createEngineTables(options.db)\n writePref(options.db, __LOGOUT_BEHAVIOR_PREF_KEY, options.logoutBehavior)\n }\n const identity = resolveClientAuthData(\n options.authData,\n options.ctxValidationSchema,\n )\n return new ClientEngine({\n db: options.db,\n schema: options.schema,\n mutations: flattenMutations(options.mutations),\n socket: options.socket,\n // Asserted identity (ADR-0018 / #167): never derived.\n ctx: identity.ctx,\n token: identity.token,\n userId: identity.userId,\n clientId: options.clientId,\n onSchemaEvent: options.onSchemaEvent,\n })\n}\n\n/**\n * Validate and flatten `authData` for engine slots. When a schema is present\n * and identity is non-null, `ctx` is validated synchronously (throws at the\n * call site). Anonymous (`null`) yields `userId: null`, `ctx: null`, no token.\n */\nexport function resolveClientAuthData<TAuthContext extends AuthContext>(\n authData: AuthData<TAuthContext> | null,\n ctxValidationSchema?: StandardSchemaV1<unknown, TAuthContext>,\n): {\n userId: string | null\n token: string | undefined\n ctx: AuthContext\n} {\n if (authData === null) {\n return { userId: null, token: undefined, ctx: null }\n }\n let ctx: Record<string, unknown> = authData.ctx\n if (ctxValidationSchema !== undefined) {\n ctx = validateStandardSchema(\n ctxValidationSchema,\n authData.ctx,\n 'auth context',\n ) as Record<string, unknown>\n }\n return {\n userId: authData.userId,\n token: authData.token,\n ctx,\n }\n}\n"],"mappings":"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"}
|