@bitcobblers/wod-wiki-storage 0.39.2142 → 0.40.2144

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.
@@ -1,6 +1,8 @@
1
1
  import { S as SqlConnection, D as Database } from '../connection-DxuCLpRc.cjs';
2
2
  export { L as LibsqlClient, a as SqlRow, b as SqlStatement, c as SqlValue, l as libsqlDatabase } from '../connection-DxuCLpRc.cjs';
3
- import { a as StoreDef, R as RangeDTO, K as KeyDTO, e as DomainQueryResult, T as TxOp, I as IStorage, S as StoreName, b as IReadOnlyStore, c as StoreType, d as IReadWriteStore, D as DomainQuery, f as IStorageTransaction } from '../wire-CKJDK_Nq.cjs';
3
+ import { e as StoreDef, R as RangeDTO, K as KeyDTO, f as DomainQueryResult, T as TxOp, I as IStorage, S as StoreName, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, D as DomainQuery, d as IStorageTransaction } from '../wire-DGYxXt3I.cjs';
4
+ import { b as StoredSyncOp, S as SyncMutation } from '../sync-DG4Z-szj.cjs';
5
+ export { A as ApplySyncBatchOptions, R as RawSyncOp, c as SYNC_OPS_TABLE, d as SYNC_STATE_TABLE, a as SyncEntry, e as SyncTargetKind, f as applySyncBatch, g as fetchSyncOps, i as initSyncTarget, p as probeSyncTarget, r as readLegacyRows, h as readRawSyncOps } from '../sync-DG4Z-szj.cjs';
4
6
  import '@libsql/client';
5
7
  import '@bitcobblers/wod-wiki-core';
6
8
  import '@bitcobblers/wod-wiki-lang';
@@ -157,148 +159,78 @@ declare class BrowserTursoStorage extends SqlStorage {
157
159
  }
158
160
 
159
161
  /**
160
- * Turso backup/sync protocol — the local-first sync tables layered on top of
161
- * any libsql database. The browser data plane is always IndexedDB; a saved
162
- * Turso target is a backup/sync destination, never the read/write provider.
162
+ * Native-copy verification + migration finalize — the pure-SQL seam the CLI
163
+ * `wod db backup/restore/migrate` commands run over any libsql Database.
163
164
  *
164
- * Design: the remote holds ONE op log (`_wodwiki_sync_ops`) plus a state row
165
- * (`_wodwiki_sync_state`) carrying {schema, rev}. Every committed local
166
- * transaction group appends one idempotent op-group (uuid op_id,
167
- * INSERT-or-ignore per op); `rev` is assigned monotonically per NEWLY applied
168
- * group — a retried group is recognized by its first op id, consumes no rev
169
- * and appends nothing. Pulls replay ops with rev > cursor in deterministic
170
- * (rev, idx) order. Tombstones (delete/clear/wipe) are ordinary ops —
171
- * deletions propagate.
165
+ * - databaseFingerprint: canonical sha-256 over EVERY user table (schema
166
+ * SQL + sorted typed-cell rows + the sync state row), so a Turso native
167
+ * seed-copy can be proven row- and binary-identical (BLOB bytes are hex
168
+ * encoded — JSON.stringify(ArrayBuffer) would collapse to {}) before any
169
+ * credential is minted or paired. Rows are encoded as typed cells inside
170
+ * JSON arrays and SORTED, so framing is unambiguous and rowid/physical
171
+ * order differences after a native copy cannot produce a false mismatch.
172
+ * - assertDatabaseCopyMatches: interrupted-copy AND concurrent-source
173
+ * refusal — the source must be unchanged since the copy started and the
174
+ * copy must match that exact snapshot.
175
+ * - finalizeMigrationTarget: CAS that flips a migration target from the
176
+ * non-legitimate "constructing" marker (state schema 0, unpairable by
177
+ * engine refusal and by `wod db pair`) to the live schema in ONE
178
+ * transaction, only after full replay/pin/binary validation passed.
172
179
  *
173
- * Fetch atomicity: a pull window is a set of WHOLE revision groups. The rev
174
- * boundary is chosen with a bounded subquery, then every row of those revs is
175
- * returned — a group is never split, so the cursor never skips the rest of
176
- * a group (one pathologically large group can exceed the nominal row bound;
177
- * that is the unavoidable atomicity ceiling).
178
- *
179
- * ponytail: op-log replay pulls — a fresh browser replays the whole log
180
- * (O(history)) instead of reading per-store current-state tables. Upgrade
181
- * path: add per-store state tables rev-indexed by primary key and collapse
182
- * the log into them periodically; the wire format (op_id, rev, mutation)
183
- * stays unchanged.
184
- *
185
- * Never-downgrade rule: the state row's `schema` is the managing client's
186
- * DB_VERSION at init time. Uploads verify `schema` INSIDE the write
187
- * transaction (a stale probe must not append current payloads to an old or
188
- * newer remote); an older remote log is migrated forward by the caller
189
- * (transform payloads, never just rewrite the marker), a newer remote is
190
- * never rewritten — the client refuses and keeps working locally.
180
+ * No source is ever mutated by these helpers (reads + one guarded state-row
181
+ * UPDATE on the target). Fingerprint reads run inside one transaction so the
182
+ * enumeration and the rows come from one consistent snapshot.
191
183
  */
192
184
 
193
- declare const SYNC_OPS_TABLE = "_wodwiki_sync_ops";
194
- declare const SYNC_STATE_TABLE = "_wodwiki_sync_state";
195
- /** Pre-migration op log copies live here — the recoverable old source,
196
- * retained after a schema migration replaces the active log. */
197
- declare const SYNC_ARCHIVE_TABLE = "_wodwiki_sync_ops_archive";
198
- /** One committed local transaction group, as journaled in IndexedDB. */
199
- type SyncMutation = {
200
- op: 'put';
201
- store: StoreName;
202
- key: KeyDTO;
203
- value: unknown;
204
- } | {
205
- op: 'delete';
206
- store: StoreName;
207
- key: KeyDTO;
208
- } | {
209
- op: 'clear';
210
- store: StoreName;
211
- } | {
212
- op: 'wipe';
213
- };
214
- interface SyncEntry {
215
- /** Idempotency key — one uuid per committed local transaction group. */
216
- id: string;
217
- at: number;
218
- ops: SyncMutation[];
219
- }
220
- interface StoredSyncOp {
221
- opId: string;
222
- rev: number;
223
- idx: number;
224
- at: number;
225
- mutation: SyncMutation;
226
- }
227
- type SyncTargetKind =
228
- /** No tables at all (beyond libsql internals): fresh database. */
229
- 'empty'
230
- /** Tables exist but neither sync state nor a legacy schema guard — not
231
- * ours; initializing over unknown data is refused by the caller. */
232
- | {
233
- kind: 'unknown';
234
- }
235
- /** Legacy direct-Turso SqlStorage layout (`_wodwiki_schema` guard). */
236
- | {
237
- kind: 'legacy';
238
- schemaVersion: number;
239
- }
240
- /** This sync protocol's layout; payload = state row. */
241
- | {
242
- kind: 'current';
243
- schemaVersion: number;
244
- rev: number;
245
- };
246
- /** Classify the sync target WITHOUT mutating it. */
247
- declare function probeSyncTarget(db: Database): Promise<SyncTargetKind>;
248
- /** Create the sync tables + state row. Only for empty targets; existing
249
- * state (current, legacy or unknown) is never overwritten by init. */
250
- declare function initSyncTarget(db: Database, schemaVersion: number): Promise<void>;
251
- interface ApplySyncBatchOptions {
252
- /** Schema the caller verified at probe time — rechecked INSIDE the write
253
- * transaction so a schema change between probe and append aborts. */
254
- expectSchema?: number;
255
- /** Bump the state schema version in the SAME transaction (remote-schema
256
- * migrations append converted payloads + move the marker atomically). */
257
- toSchemaVersion?: number;
185
+ interface DatabaseFingerprint {
186
+ tables: number;
187
+ rows: number;
188
+ /** sha-256 hex over the canonical whole-database rendering. */
189
+ digest: string;
190
+ /** The sync protocol state row when this database carries that layout. */
191
+ sync: {
192
+ schema: number;
193
+ rev: number;
194
+ } | undefined;
258
195
  }
259
196
  /**
260
- * Append op-groups to the log in ONE SQL transaction, idempotently by
261
- * group: a group whose first op id already exists is skipped entirely and
262
- * consumes no rev (retries never duplicate, never allocate revisions).
263
- * Returns the number of NEW groups committed — ack semantics for the
264
- * uploader's watermark.
197
+ * Canonical fingerprint of every user table (sqlite_% / libsql_% internal
198
+ * tables excluded): per table the CREATE statement text plus its rows as
199
+ * typed-cell JSON arrays, SORTED so physical row order cannot influence the
200
+ * digest. Reads run inside one transaction for a consistent snapshot.
265
201
  */
266
- declare function applySyncBatch(db: Database, entries: SyncEntry[], opts?: ApplySyncBatchOptions): Promise<number>;
202
+ declare function databaseFingerprint(db: Database): Promise<DatabaseFingerprint>;
267
203
  /**
268
- * Replay window: ops with rev > afterRev, in deterministic commit order.
269
- * The window covers WHOLE revision groups: up to `limit` distinct revs are
270
- * chosen (bounded subquery), then every row of those revs is returned, so a
271
- * cursor advanced to the last returned rev never splits a group. A single
272
- * group larger than `limit` is returned whole — the atomicity ceiling.
204
+ * Prove a native copy: the copy must equal the exact source snapshot the
205
+ * caller fingerprinted BEFORE starting the copy (`before`), and the source
206
+ * must not have advanced meanwhile (writers were supposed to be retired).
207
+ * Returns the re-read source fingerprint.
273
208
  */
274
- declare function fetchSyncOps(db: Database, afterRev: number, limit: number): Promise<StoredSyncOp[]>;
209
+ declare function assertDatabaseCopyMatches(source: Database, copy: Database, before: DatabaseFingerprint): Promise<DatabaseFingerprint>;
275
210
  /**
276
- * Schema-migration replacement for the active op log, in ONE transaction:
277
- * every previous op is copied into the archive table (the recoverable old
278
- * source — nothing is destroyed), the active log is cleared, and the
279
- * converted snapshot groups become the entire replayable history at
280
- * rev 1..n with the schema marker moved forward atomically. Fresh browsers
281
- * replay converted payloads only, so pulls never touch legacy row shapes.
211
+ * CAS-finalize a migration target constructed at the non-legitimate marker
212
+ * schema 0: requires the state row to be exactly {schema: 0, rev:
213
+ * expectRev}, then publishes the live schema in the same transaction. A
214
+ * target that never reaches this call stays schema 0 — probes report
215
+ * current/0, which no client accepts and `wod db pair` refuses.
282
216
  */
283
- declare function replaceSyncLog(db: Database, entries: SyncEntry[], toSchemaVersion: number): Promise<void>;
284
- /** Migration-only raw replay: parsed op payloads WITHOUT store/key validation
285
- * (old logs reference era store names like `segments`). Only
286
- * migrateRemoteToCurrent consumes this — pulls use fetchSyncOps, which
287
- * validates every row against the current schema. Binary cells revive. */
288
- interface RawSyncOp {
289
- op: string;
290
- store: string;
291
- key: unknown;
292
- value?: unknown;
293
- }
294
- declare function readRawSyncOps(db: Database): Promise<RawSyncOp[]>;
217
+ declare function finalizeMigrationTarget(db: Database, toSchema: number, expectRev: number): Promise<void>;
218
+ /**
219
+ * Policy snapshot validation shared by `wod db migrate` (pre-write) and
220
+ * `wod db pair` (over the full replayed remote log): a user-scope snapshot
221
+ * is policy-compliant PUTS ONLY — any delete/clear/wipe means non-user
222
+ * policy operations leaked into the copy — and every session pin and note
223
+ * block pin must have its immutable block_revisions row present (missing
224
+ * pins fail the copy, they are never silently dropped).
225
+ */
226
+ declare function assertPolicySnapshot(ops: readonly SyncMutation[]): void;
295
227
  /**
296
- * Import rows from a legacy direct-Turso (SqlStorage) database into op-
297
- * groups. Reads the per-store JSON `data` columns directly — the legacy
298
- * schema guard may hold an older version SqlStorage refuses to open, so the
299
- * rows are lifted raw, never through SqlStorage. Missing tables contribute
300
- * no rows; nothing is written or deleted remotely.
228
+ * Pin closure over a LIVE op log (pair verification): unlike a conversion
229
+ * snapshot, an operational log legitimately contains delete/clear/wipe
230
+ * tombstones, so the CURRENT row set is replayed first and only live
231
+ * sessions/notes must find their immutable revisions. Anything deleted is
232
+ * not required to have one.
301
233
  */
302
- declare function readLegacyRows(db: Database, table: string): Promise<unknown[]>;
234
+ declare function assertLogPinClosure(ops: readonly StoredSyncOp[]): void;
303
235
 
304
- export { type ApplySyncBatchOptions, BrowserTursoStorage, Database, MAX_BODY, type RawSyncOp, SCHEMA_TABLE, SYNC_ARCHIVE_TABLE, SYNC_OPS_TABLE, SYNC_STATE_TABLE, SqlConnection, SqlStorage, type SqlStorageOptions, StorageError, type StoredSyncOp, type SyncEntry, type SyncMutation, type SyncTargetKind, type TursoSettings, applySyncBatch, clearRows, countRows, decodeRowJson, deleteRows, encodeRowJson, fetchSyncOps, getRow, getRows, handleDomainQuery, initSyncTarget, initializeDomain, initializeStore, listRows, listRowsFromIndexMany, probeSyncTarget, putRow, putRows, readLegacyRows, readRawSyncOps, rebuildProjections, refreshDomain, replaceSyncLog, storeDefinition, validateTursoUrl, verifyReadiness, wipeDomain, wipeRows };
236
+ export { BrowserTursoStorage, Database, type DatabaseFingerprint, MAX_BODY, SCHEMA_TABLE, SqlConnection, SqlStorage, type SqlStorageOptions, StorageError, StoredSyncOp, SyncMutation, type TursoSettings, assertDatabaseCopyMatches, assertLogPinClosure, assertPolicySnapshot, clearRows, countRows, databaseFingerprint, decodeRowJson, deleteRows, encodeRowJson, finalizeMigrationTarget, getRow, getRows, handleDomainQuery, initializeDomain, initializeStore, listRows, listRowsFromIndexMany, putRow, putRows, rebuildProjections, refreshDomain, storeDefinition, validateTursoUrl, verifyReadiness, wipeDomain, wipeRows };
@@ -1,6 +1,8 @@
1
1
  import { S as SqlConnection, D as Database } from '../connection-DxuCLpRc.js';
2
2
  export { L as LibsqlClient, a as SqlRow, b as SqlStatement, c as SqlValue, l as libsqlDatabase } from '../connection-DxuCLpRc.js';
3
- import { a as StoreDef, R as RangeDTO, K as KeyDTO, e as DomainQueryResult, T as TxOp, I as IStorage, S as StoreName, b as IReadOnlyStore, c as StoreType, d as IReadWriteStore, D as DomainQuery, f as IStorageTransaction } from '../wire-CKJDK_Nq.js';
3
+ import { e as StoreDef, R as RangeDTO, K as KeyDTO, f as DomainQueryResult, T as TxOp, I as IStorage, S as StoreName, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, D as DomainQuery, d as IStorageTransaction } from '../wire-DGYxXt3I.js';
4
+ import { b as StoredSyncOp, S as SyncMutation } from '../sync-Bjj_c1ea.js';
5
+ export { A as ApplySyncBatchOptions, R as RawSyncOp, c as SYNC_OPS_TABLE, d as SYNC_STATE_TABLE, a as SyncEntry, e as SyncTargetKind, f as applySyncBatch, g as fetchSyncOps, i as initSyncTarget, p as probeSyncTarget, r as readLegacyRows, h as readRawSyncOps } from '../sync-Bjj_c1ea.js';
4
6
  import '@libsql/client';
5
7
  import '@bitcobblers/wod-wiki-core';
6
8
  import '@bitcobblers/wod-wiki-lang';
@@ -157,148 +159,78 @@ declare class BrowserTursoStorage extends SqlStorage {
157
159
  }
158
160
 
159
161
  /**
160
- * Turso backup/sync protocol — the local-first sync tables layered on top of
161
- * any libsql database. The browser data plane is always IndexedDB; a saved
162
- * Turso target is a backup/sync destination, never the read/write provider.
162
+ * Native-copy verification + migration finalize — the pure-SQL seam the CLI
163
+ * `wod db backup/restore/migrate` commands run over any libsql Database.
163
164
  *
164
- * Design: the remote holds ONE op log (`_wodwiki_sync_ops`) plus a state row
165
- * (`_wodwiki_sync_state`) carrying {schema, rev}. Every committed local
166
- * transaction group appends one idempotent op-group (uuid op_id,
167
- * INSERT-or-ignore per op); `rev` is assigned monotonically per NEWLY applied
168
- * group — a retried group is recognized by its first op id, consumes no rev
169
- * and appends nothing. Pulls replay ops with rev > cursor in deterministic
170
- * (rev, idx) order. Tombstones (delete/clear/wipe) are ordinary ops —
171
- * deletions propagate.
165
+ * - databaseFingerprint: canonical sha-256 over EVERY user table (schema
166
+ * SQL + sorted typed-cell rows + the sync state row), so a Turso native
167
+ * seed-copy can be proven row- and binary-identical (BLOB bytes are hex
168
+ * encoded — JSON.stringify(ArrayBuffer) would collapse to {}) before any
169
+ * credential is minted or paired. Rows are encoded as typed cells inside
170
+ * JSON arrays and SORTED, so framing is unambiguous and rowid/physical
171
+ * order differences after a native copy cannot produce a false mismatch.
172
+ * - assertDatabaseCopyMatches: interrupted-copy AND concurrent-source
173
+ * refusal — the source must be unchanged since the copy started and the
174
+ * copy must match that exact snapshot.
175
+ * - finalizeMigrationTarget: CAS that flips a migration target from the
176
+ * non-legitimate "constructing" marker (state schema 0, unpairable by
177
+ * engine refusal and by `wod db pair`) to the live schema in ONE
178
+ * transaction, only after full replay/pin/binary validation passed.
172
179
  *
173
- * Fetch atomicity: a pull window is a set of WHOLE revision groups. The rev
174
- * boundary is chosen with a bounded subquery, then every row of those revs is
175
- * returned — a group is never split, so the cursor never skips the rest of
176
- * a group (one pathologically large group can exceed the nominal row bound;
177
- * that is the unavoidable atomicity ceiling).
178
- *
179
- * ponytail: op-log replay pulls — a fresh browser replays the whole log
180
- * (O(history)) instead of reading per-store current-state tables. Upgrade
181
- * path: add per-store state tables rev-indexed by primary key and collapse
182
- * the log into them periodically; the wire format (op_id, rev, mutation)
183
- * stays unchanged.
184
- *
185
- * Never-downgrade rule: the state row's `schema` is the managing client's
186
- * DB_VERSION at init time. Uploads verify `schema` INSIDE the write
187
- * transaction (a stale probe must not append current payloads to an old or
188
- * newer remote); an older remote log is migrated forward by the caller
189
- * (transform payloads, never just rewrite the marker), a newer remote is
190
- * never rewritten — the client refuses and keeps working locally.
180
+ * No source is ever mutated by these helpers (reads + one guarded state-row
181
+ * UPDATE on the target). Fingerprint reads run inside one transaction so the
182
+ * enumeration and the rows come from one consistent snapshot.
191
183
  */
192
184
 
193
- declare const SYNC_OPS_TABLE = "_wodwiki_sync_ops";
194
- declare const SYNC_STATE_TABLE = "_wodwiki_sync_state";
195
- /** Pre-migration op log copies live here — the recoverable old source,
196
- * retained after a schema migration replaces the active log. */
197
- declare const SYNC_ARCHIVE_TABLE = "_wodwiki_sync_ops_archive";
198
- /** One committed local transaction group, as journaled in IndexedDB. */
199
- type SyncMutation = {
200
- op: 'put';
201
- store: StoreName;
202
- key: KeyDTO;
203
- value: unknown;
204
- } | {
205
- op: 'delete';
206
- store: StoreName;
207
- key: KeyDTO;
208
- } | {
209
- op: 'clear';
210
- store: StoreName;
211
- } | {
212
- op: 'wipe';
213
- };
214
- interface SyncEntry {
215
- /** Idempotency key — one uuid per committed local transaction group. */
216
- id: string;
217
- at: number;
218
- ops: SyncMutation[];
219
- }
220
- interface StoredSyncOp {
221
- opId: string;
222
- rev: number;
223
- idx: number;
224
- at: number;
225
- mutation: SyncMutation;
226
- }
227
- type SyncTargetKind =
228
- /** No tables at all (beyond libsql internals): fresh database. */
229
- 'empty'
230
- /** Tables exist but neither sync state nor a legacy schema guard — not
231
- * ours; initializing over unknown data is refused by the caller. */
232
- | {
233
- kind: 'unknown';
234
- }
235
- /** Legacy direct-Turso SqlStorage layout (`_wodwiki_schema` guard). */
236
- | {
237
- kind: 'legacy';
238
- schemaVersion: number;
239
- }
240
- /** This sync protocol's layout; payload = state row. */
241
- | {
242
- kind: 'current';
243
- schemaVersion: number;
244
- rev: number;
245
- };
246
- /** Classify the sync target WITHOUT mutating it. */
247
- declare function probeSyncTarget(db: Database): Promise<SyncTargetKind>;
248
- /** Create the sync tables + state row. Only for empty targets; existing
249
- * state (current, legacy or unknown) is never overwritten by init. */
250
- declare function initSyncTarget(db: Database, schemaVersion: number): Promise<void>;
251
- interface ApplySyncBatchOptions {
252
- /** Schema the caller verified at probe time — rechecked INSIDE the write
253
- * transaction so a schema change between probe and append aborts. */
254
- expectSchema?: number;
255
- /** Bump the state schema version in the SAME transaction (remote-schema
256
- * migrations append converted payloads + move the marker atomically). */
257
- toSchemaVersion?: number;
185
+ interface DatabaseFingerprint {
186
+ tables: number;
187
+ rows: number;
188
+ /** sha-256 hex over the canonical whole-database rendering. */
189
+ digest: string;
190
+ /** The sync protocol state row when this database carries that layout. */
191
+ sync: {
192
+ schema: number;
193
+ rev: number;
194
+ } | undefined;
258
195
  }
259
196
  /**
260
- * Append op-groups to the log in ONE SQL transaction, idempotently by
261
- * group: a group whose first op id already exists is skipped entirely and
262
- * consumes no rev (retries never duplicate, never allocate revisions).
263
- * Returns the number of NEW groups committed — ack semantics for the
264
- * uploader's watermark.
197
+ * Canonical fingerprint of every user table (sqlite_% / libsql_% internal
198
+ * tables excluded): per table the CREATE statement text plus its rows as
199
+ * typed-cell JSON arrays, SORTED so physical row order cannot influence the
200
+ * digest. Reads run inside one transaction for a consistent snapshot.
265
201
  */
266
- declare function applySyncBatch(db: Database, entries: SyncEntry[], opts?: ApplySyncBatchOptions): Promise<number>;
202
+ declare function databaseFingerprint(db: Database): Promise<DatabaseFingerprint>;
267
203
  /**
268
- * Replay window: ops with rev > afterRev, in deterministic commit order.
269
- * The window covers WHOLE revision groups: up to `limit` distinct revs are
270
- * chosen (bounded subquery), then every row of those revs is returned, so a
271
- * cursor advanced to the last returned rev never splits a group. A single
272
- * group larger than `limit` is returned whole — the atomicity ceiling.
204
+ * Prove a native copy: the copy must equal the exact source snapshot the
205
+ * caller fingerprinted BEFORE starting the copy (`before`), and the source
206
+ * must not have advanced meanwhile (writers were supposed to be retired).
207
+ * Returns the re-read source fingerprint.
273
208
  */
274
- declare function fetchSyncOps(db: Database, afterRev: number, limit: number): Promise<StoredSyncOp[]>;
209
+ declare function assertDatabaseCopyMatches(source: Database, copy: Database, before: DatabaseFingerprint): Promise<DatabaseFingerprint>;
275
210
  /**
276
- * Schema-migration replacement for the active op log, in ONE transaction:
277
- * every previous op is copied into the archive table (the recoverable old
278
- * source — nothing is destroyed), the active log is cleared, and the
279
- * converted snapshot groups become the entire replayable history at
280
- * rev 1..n with the schema marker moved forward atomically. Fresh browsers
281
- * replay converted payloads only, so pulls never touch legacy row shapes.
211
+ * CAS-finalize a migration target constructed at the non-legitimate marker
212
+ * schema 0: requires the state row to be exactly {schema: 0, rev:
213
+ * expectRev}, then publishes the live schema in the same transaction. A
214
+ * target that never reaches this call stays schema 0 — probes report
215
+ * current/0, which no client accepts and `wod db pair` refuses.
282
216
  */
283
- declare function replaceSyncLog(db: Database, entries: SyncEntry[], toSchemaVersion: number): Promise<void>;
284
- /** Migration-only raw replay: parsed op payloads WITHOUT store/key validation
285
- * (old logs reference era store names like `segments`). Only
286
- * migrateRemoteToCurrent consumes this — pulls use fetchSyncOps, which
287
- * validates every row against the current schema. Binary cells revive. */
288
- interface RawSyncOp {
289
- op: string;
290
- store: string;
291
- key: unknown;
292
- value?: unknown;
293
- }
294
- declare function readRawSyncOps(db: Database): Promise<RawSyncOp[]>;
217
+ declare function finalizeMigrationTarget(db: Database, toSchema: number, expectRev: number): Promise<void>;
218
+ /**
219
+ * Policy snapshot validation shared by `wod db migrate` (pre-write) and
220
+ * `wod db pair` (over the full replayed remote log): a user-scope snapshot
221
+ * is policy-compliant PUTS ONLY — any delete/clear/wipe means non-user
222
+ * policy operations leaked into the copy — and every session pin and note
223
+ * block pin must have its immutable block_revisions row present (missing
224
+ * pins fail the copy, they are never silently dropped).
225
+ */
226
+ declare function assertPolicySnapshot(ops: readonly SyncMutation[]): void;
295
227
  /**
296
- * Import rows from a legacy direct-Turso (SqlStorage) database into op-
297
- * groups. Reads the per-store JSON `data` columns directly — the legacy
298
- * schema guard may hold an older version SqlStorage refuses to open, so the
299
- * rows are lifted raw, never through SqlStorage. Missing tables contribute
300
- * no rows; nothing is written or deleted remotely.
228
+ * Pin closure over a LIVE op log (pair verification): unlike a conversion
229
+ * snapshot, an operational log legitimately contains delete/clear/wipe
230
+ * tombstones, so the CURRENT row set is replayed first and only live
231
+ * sessions/notes must find their immutable revisions. Anything deleted is
232
+ * not required to have one.
301
233
  */
302
- declare function readLegacyRows(db: Database, table: string): Promise<unknown[]>;
234
+ declare function assertLogPinClosure(ops: readonly StoredSyncOp[]): void;
303
235
 
304
- export { type ApplySyncBatchOptions, BrowserTursoStorage, Database, MAX_BODY, type RawSyncOp, SCHEMA_TABLE, SYNC_ARCHIVE_TABLE, SYNC_OPS_TABLE, SYNC_STATE_TABLE, SqlConnection, SqlStorage, type SqlStorageOptions, StorageError, type StoredSyncOp, type SyncEntry, type SyncMutation, type SyncTargetKind, type TursoSettings, applySyncBatch, clearRows, countRows, decodeRowJson, deleteRows, encodeRowJson, fetchSyncOps, getRow, getRows, handleDomainQuery, initSyncTarget, initializeDomain, initializeStore, listRows, listRowsFromIndexMany, probeSyncTarget, putRow, putRows, readLegacyRows, readRawSyncOps, rebuildProjections, refreshDomain, replaceSyncLog, storeDefinition, validateTursoUrl, verifyReadiness, wipeDomain, wipeRows };
236
+ export { BrowserTursoStorage, Database, type DatabaseFingerprint, MAX_BODY, SCHEMA_TABLE, SqlConnection, SqlStorage, type SqlStorageOptions, StorageError, StoredSyncOp, SyncMutation, type TursoSettings, assertDatabaseCopyMatches, assertLogPinClosure, assertPolicySnapshot, clearRows, countRows, databaseFingerprint, decodeRowJson, deleteRows, encodeRowJson, finalizeMigrationTarget, getRow, getRows, handleDomainQuery, initializeDomain, initializeStore, listRows, listRowsFromIndexMany, putRow, putRows, rebuildProjections, refreshDomain, storeDefinition, validateTursoUrl, verifyReadiness, wipeDomain, wipeRows };