@tldraw/sync-core 5.3.0-internal.d205573d66cb → 5.3.0-next.299378752aaf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist-cjs/index.d.ts +3 -76
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/InMemorySyncStorage.js +20 -55
  5. package/dist-cjs/lib/InMemorySyncStorage.js.map +2 -2
  6. package/dist-cjs/lib/RoomSession.js.map +1 -1
  7. package/dist-cjs/lib/SQLiteSyncStorage.js +52 -115
  8. package/dist-cjs/lib/SQLiteSyncStorage.js.map +2 -2
  9. package/dist-cjs/lib/TLSocketRoom.js +2 -26
  10. package/dist-cjs/lib/TLSocketRoom.js.map +2 -2
  11. package/dist-cjs/lib/TLSyncClient.js +1 -6
  12. package/dist-cjs/lib/TLSyncClient.js.map +2 -2
  13. package/dist-cjs/lib/TLSyncRoom.js +5 -43
  14. package/dist-cjs/lib/TLSyncRoom.js.map +2 -2
  15. package/dist-cjs/lib/TLSyncStorage.js.map +2 -2
  16. package/dist-cjs/lib/protocol.js.map +2 -2
  17. package/dist-esm/index.d.mts +3 -76
  18. package/dist-esm/index.mjs +1 -1
  19. package/dist-esm/index.mjs.map +2 -2
  20. package/dist-esm/lib/InMemorySyncStorage.mjs +20 -55
  21. package/dist-esm/lib/InMemorySyncStorage.mjs.map +2 -2
  22. package/dist-esm/lib/RoomSession.mjs.map +1 -1
  23. package/dist-esm/lib/SQLiteSyncStorage.mjs +52 -115
  24. package/dist-esm/lib/SQLiteSyncStorage.mjs.map +2 -2
  25. package/dist-esm/lib/TLSocketRoom.mjs +2 -26
  26. package/dist-esm/lib/TLSocketRoom.mjs.map +2 -2
  27. package/dist-esm/lib/TLSyncClient.mjs +1 -6
  28. package/dist-esm/lib/TLSyncClient.mjs.map +2 -2
  29. package/dist-esm/lib/TLSyncRoom.mjs +5 -43
  30. package/dist-esm/lib/TLSyncRoom.mjs.map +2 -2
  31. package/dist-esm/lib/TLSyncStorage.mjs.map +2 -2
  32. package/dist-esm/lib/protocol.mjs.map +2 -2
  33. package/package.json +6 -6
  34. package/src/index.ts +0 -1
  35. package/src/lib/InMemorySyncStorage.ts +21 -74
  36. package/src/lib/RoomSession.ts +2 -7
  37. package/src/lib/SQLiteSyncStorage.test.ts +2 -29
  38. package/src/lib/SQLiteSyncStorage.ts +59 -158
  39. package/src/lib/TLSocketRoom.ts +2 -53
  40. package/src/lib/TLSyncClient.test.ts +2 -9
  41. package/src/lib/TLSyncClient.ts +3 -15
  42. package/src/lib/TLSyncRoom.ts +4 -76
  43. package/src/lib/TLSyncStorage.ts +0 -8
  44. package/src/lib/protocol.ts +0 -17
  45. package/src/test/TLSocketRoom.test.ts +2 -37
  46. package/src/test/TLSyncRoom.test.ts +0 -25
  47. package/src/test/TestServer.ts +4 -14
  48. package/src/test/storageContractSuite.ts +0 -79
  49. package/src/test/upgradeDowngrade.test.ts +0 -47
  50. package/src/test/objectStore.test.ts +0 -387
@@ -29,7 +29,6 @@ var import_MicrotaskNotifier = require("./MicrotaskNotifier");
29
29
  var import_TLSyncStorage = require("./TLSyncStorage");
30
30
  function migrateSqliteSyncStorage(storage, {
31
31
  documentsTable = "documents",
32
- objectsTable = "objects",
33
32
  tombstonesTable = "tombstones",
34
33
  metadataTable = "metadata"
35
34
  } = {}) {
@@ -77,29 +76,17 @@ function migrateSqliteSyncStorage(storage, {
77
76
  state BLOB NOT NULL,
78
77
  lastChangedClock INTEGER NOT NULL
79
78
  );
80
-
79
+
81
80
  INSERT INTO ${documentsTable}_new (id, state, lastChangedClock)
82
81
  SELECT id, CAST(state AS BLOB), lastChangedClock FROM ${documentsTable};
83
-
82
+
84
83
  DROP TABLE ${documentsTable};
85
-
84
+
86
85
  ALTER TABLE ${documentsTable}_new RENAME TO ${documentsTable};
87
-
86
+
88
87
  CREATE INDEX idx_${documentsTable}_lastChangedClock ON ${documentsTable}(lastChangedClock);
89
88
  `);
90
89
  }
91
- if (migrationVersion === 2) {
92
- migrationVersion++;
93
- storage.exec(`
94
- CREATE TABLE ${objectsTable} (
95
- id TEXT PRIMARY KEY,
96
- state BLOB NOT NULL,
97
- lastChangedClock INTEGER NOT NULL
98
- );
99
-
100
- CREATE INDEX idx_${objectsTable}_lastChangedClock ON ${objectsTable}(lastChangedClock);
101
- `);
102
- }
103
90
  storage.exec(`UPDATE ${metadataTable} SET migrationVersion = ${migrationVersion}`);
104
91
  }
105
92
  const textEncoder = new TextEncoder();
@@ -144,50 +131,17 @@ class SQLiteSyncStorage {
144
131
  // Prepared statements - created once, reused many times
145
132
  stmts;
146
133
  sql;
147
- /** @internal */
148
- objectTypes;
149
134
  constructor({
150
135
  sql,
151
136
  snapshot,
152
- objectTypes,
153
137
  onChange
154
138
  }) {
155
139
  this.sql = sql;
156
- this.objectTypes = new Set(objectTypes ?? []);
157
140
  const prefix = sql.config?.tablePrefix ?? "";
158
141
  const documentsTable = `${prefix}documents`;
159
- const objectsTable = `${prefix}objects`;
160
142
  const tombstonesTable = `${prefix}tombstones`;
161
143
  const metadataTable = `${prefix}metadata`;
162
- migrateSqliteSyncStorage(this.sql, {
163
- documentsTable,
164
- objectsTable,
165
- tombstonesTable,
166
- metadataTable
167
- });
168
- const makeRecordTableStmts = (table) => ({
169
- get: this.sql.prepare(
170
- `SELECT state FROM ${table} WHERE id = ?`
171
- ),
172
- insert: this.sql.prepare(
173
- `INSERT OR REPLACE INTO ${table} (id, state, lastChangedClock) VALUES (?, ?, ?)`
174
- ),
175
- delete: this.sql.prepare(`DELETE FROM ${table} WHERE id = ?`),
176
- exists: this.sql.prepare(
177
- `SELECT id FROM ${table} WHERE id = ?`
178
- ),
179
- iterate: this.sql.prepare(
180
- `SELECT state, lastChangedClock FROM ${table}`
181
- ),
182
- iterateEntries: this.sql.prepare(
183
- `SELECT id, state FROM ${table}`
184
- ),
185
- iterateKeys: this.sql.prepare(`SELECT id FROM ${table}`),
186
- iterateValues: this.sql.prepare(`SELECT state FROM ${table}`),
187
- changedSince: this.sql.prepare(
188
- `SELECT state FROM ${table} WHERE lastChangedClock > ?`
189
- )
190
- });
144
+ migrateSqliteSyncStorage(this.sql, { documentsTable, tombstonesTable, metadataTable });
191
145
  this.stmts = {
192
146
  // Metadata
193
147
  getDocumentClock: this.sql.prepare(
@@ -204,9 +158,30 @@ class SQLiteSyncStorage {
204
158
  incrementDocumentClock: this.sql.prepare(
205
159
  `UPDATE ${metadataTable} SET documentClock = documentClock + 1`
206
160
  ),
207
- // Record tables (document lane + object-store lane)
208
- documents: makeRecordTableStmts(documentsTable),
209
- objects: makeRecordTableStmts(objectsTable),
161
+ // Documents
162
+ getDocument: this.sql.prepare(
163
+ `SELECT state FROM ${documentsTable} WHERE id = ?`
164
+ ),
165
+ insertDocument: this.sql.prepare(`INSERT OR REPLACE INTO ${documentsTable} (id, state, lastChangedClock) VALUES (?, ?, ?)`),
166
+ deleteDocument: this.sql.prepare(
167
+ `DELETE FROM ${documentsTable} WHERE id = ?`
168
+ ),
169
+ documentExists: this.sql.prepare(
170
+ `SELECT id FROM ${documentsTable} WHERE id = ?`
171
+ ),
172
+ iterateDocuments: this.sql.prepare(
173
+ `SELECT state, lastChangedClock FROM ${documentsTable}`
174
+ ),
175
+ iterateDocumentEntries: this.sql.prepare(
176
+ `SELECT id, state FROM ${documentsTable}`
177
+ ),
178
+ iterateDocumentKeys: this.sql.prepare(`SELECT id FROM ${documentsTable}`),
179
+ iterateDocumentValues: this.sql.prepare(
180
+ `SELECT state FROM ${documentsTable}`
181
+ ),
182
+ getDocumentsChangedSince: this.sql.prepare(
183
+ `SELECT state FROM ${documentsTable} WHERE lastChangedClock > ?`
184
+ ),
210
185
  // Tombstones
211
186
  insertTombstone: this.sql.prepare(
212
187
  `INSERT OR REPLACE INTO ${tombstonesTable} (id, clock) VALUES (?, ?)`
@@ -238,12 +213,10 @@ class SQLiteSyncStorage {
238
213
  const tombstoneHistoryStartsAtClock = snapshot.tombstoneHistoryStartsAtClock ?? documentClock;
239
214
  this.sql.exec(`
240
215
  DELETE FROM ${documentsTable};
241
- DELETE FROM ${objectsTable};
242
216
  DELETE FROM ${tombstonesTable};
243
217
  `);
244
218
  for (const doc of snapshot.documents) {
245
- const table = this.objectTypes.has(doc.state.typeName) ? this.stmts.objects : this.stmts.documents;
246
- table.insert.run(doc.state.id, encodeState(doc.state), doc.lastChangedClock);
219
+ this.stmts.insertDocument.run(doc.state.id, encodeState(doc.state), doc.lastChangedClock);
247
220
  }
248
221
  if (snapshot.tombstones) {
249
222
  for (const [id, clock] of (0, import_utils.objectMapEntries)(snapshot.tombstones)) {
@@ -255,17 +228,6 @@ class SQLiteSyncStorage {
255
228
  tombstoneHistoryStartsAtClock,
256
229
  JSON.stringify(snapshot.schema)
257
230
  );
258
- } else {
259
- for (const typeName of this.objectTypes) {
260
- const lo = `${typeName}:`;
261
- const hi = `${typeName};`;
262
- this.sql.exec(`
263
- INSERT OR REPLACE INTO ${objectsTable} (id, state, lastChangedClock)
264
- SELECT id, state, lastChangedClock FROM ${documentsTable}
265
- WHERE id >= '${lo}' AND id < '${hi}';
266
- DELETE FROM ${documentsTable} WHERE id >= '${lo}' AND id < '${hi}';
267
- `);
268
- }
269
231
  }
270
232
  if (onChange) {
271
233
  this.onChange(onChange);
@@ -343,16 +305,13 @@ class SQLiteSyncStorage {
343
305
  return {
344
306
  tombstoneHistoryStartsAtClock: this._getTombstoneHistoryStartsAtClock(),
345
307
  documentClock: this.getClock(),
346
- documents: Array.from(this._iterateRecords(this.stmts.documents)),
308
+ documents: Array.from(this._iterateDocuments()),
347
309
  tombstones: Object.fromEntries(this._iterateTombstones()),
348
310
  schema: this._getSchema()
349
311
  };
350
312
  }
351
- getObjectsSnapshot() {
352
- return Array.from(this._iterateRecords(this.stmts.objects));
353
- }
354
- *_iterateRecords(table) {
355
- for (const row of table.iterate.iterate()) {
313
+ *_iterateDocuments() {
314
+ for (const row of this.stmts.iterateDocuments.iterate()) {
356
315
  yield { state: decodeState(row.state), lastChangedClock: row.lastChangedClock };
357
316
  }
358
317
  }
@@ -391,18 +350,9 @@ class SQLiteSyncStorageTransaction {
391
350
  }
392
351
  return this._clock;
393
352
  }
394
- /**
395
- * Which record table an id belongs to. Record ids are typeName-prefixed (`comment:abc`),
396
- * so the partition is derivable from the id alone.
397
- */
398
- tableFor(id) {
399
- const sep = id.indexOf(":");
400
- if (sep === -1) return this.stmts.documents;
401
- return this.storage.objectTypes.has(id.slice(0, sep)) ? this.stmts.objects : this.stmts.documents;
402
- }
403
353
  get(id) {
404
354
  this.assertNotClosed();
405
- const row = this.tableFor(id).get.all(id)[0];
355
+ const row = this.stmts.getDocument.all(id)[0];
406
356
  if (!row) return void 0;
407
357
  return decodeState(row.state);
408
358
  }
@@ -411,45 +361,36 @@ class SQLiteSyncStorageTransaction {
411
361
  (0, import_utils.assert)(id === record.id, `Record id mismatch: key does not match record.id`);
412
362
  const clock = this.getNextClock();
413
363
  this.stmts.deleteTombstone.run(id);
414
- const table = this.storage.objectTypes.has(record.typeName) ? this.stmts.objects : this.stmts.documents;
415
- table.insert.run(id, encodeState(record), clock);
364
+ this.stmts.insertDocument.run(id, encodeState(record), clock);
416
365
  }
417
366
  delete(id) {
418
367
  this.assertNotClosed();
419
- const table = this.tableFor(id);
420
- const exists = table.exists.all(id)[0];
368
+ const exists = this.stmts.documentExists.all(id)[0];
421
369
  if (!exists) return;
422
370
  const clock = this.getNextClock();
423
- table.delete.run(id);
371
+ this.stmts.deleteDocument.run(id);
424
372
  this.stmts.insertTombstone.run(id, clock);
425
373
  this.storage.pruneTombstones();
426
374
  }
427
- // iteration spans both record tables so schema migrations cover object-lane records too
428
375
  *entries() {
429
376
  this.assertNotClosed();
430
- for (const table of [this.stmts.documents, this.stmts.objects]) {
431
- for (const row of table.iterateEntries.iterate()) {
432
- this.assertNotClosed();
433
- yield [row.id, decodeState(row.state)];
434
- }
377
+ for (const row of this.stmts.iterateDocumentEntries.iterate()) {
378
+ this.assertNotClosed();
379
+ yield [row.id, decodeState(row.state)];
435
380
  }
436
381
  }
437
382
  *keys() {
438
383
  this.assertNotClosed();
439
- for (const table of [this.stmts.documents, this.stmts.objects]) {
440
- for (const row of table.iterateKeys.iterate()) {
441
- this.assertNotClosed();
442
- yield row.id;
443
- }
384
+ for (const row of this.stmts.iterateDocumentKeys.iterate()) {
385
+ this.assertNotClosed();
386
+ yield row.id;
444
387
  }
445
388
  }
446
389
  *values() {
447
390
  this.assertNotClosed();
448
- for (const table of [this.stmts.documents, this.stmts.objects]) {
449
- for (const row of table.iterateValues.iterate()) {
450
- this.assertNotClosed();
451
- yield decodeState(row.state);
452
- }
391
+ for (const row of this.stmts.iterateDocumentValues.iterate()) {
392
+ this.assertNotClosed();
393
+ yield decodeState(row.state);
453
394
  }
454
395
  }
455
396
  getSchema() {
@@ -470,18 +411,14 @@ class SQLiteSyncStorageTransaction {
470
411
  const diff = { puts: {}, deletes: [] };
471
412
  const wipeAll = sinceClock < this.storage._getTombstoneHistoryStartsAtClock();
472
413
  if (wipeAll) {
473
- for (const table of [this.stmts.documents, this.stmts.objects]) {
474
- for (const row of table.iterateValues.iterate()) {
475
- const state = decodeState(row.state);
476
- diff.puts[state.id] = state;
477
- }
414
+ for (const row of this.stmts.iterateDocumentValues.iterate()) {
415
+ const state = decodeState(row.state);
416
+ diff.puts[state.id] = state;
478
417
  }
479
418
  } else {
480
- for (const table of [this.stmts.documents, this.stmts.objects]) {
481
- for (const row of table.changedSince.iterate(sinceClock)) {
482
- const state = decodeState(row.state);
483
- diff.puts[state.id] = state;
484
- }
419
+ for (const row of this.stmts.getDocumentsChangedSince.iterate(sinceClock)) {
420
+ const state = decodeState(row.state);
421
+ diff.puts[state.id] = state;
485
422
  }
486
423
  for (const row of this.stmts.getTombstonesChangedSince.iterate(sinceClock)) {
487
424
  diff.deletes.push(row.id);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/SQLiteSyncStorage.ts"],
4
- "sourcesContent": ["import { transaction } from '@tldraw/state'\nimport { SerializedSchema, StoreSnapshot, UnknownRecord } from '@tldraw/store'\nimport { assert, objectMapEntries, throttle } from '@tldraw/utils'\nimport {\n\tcomputeTombstonePruning,\n\tDEFAULT_INITIAL_SNAPSHOT,\n\tMAX_TOMBSTONES,\n} from './InMemorySyncStorage'\nimport { MicrotaskNotifier } from './MicrotaskNotifier'\nimport { RoomSnapshot } from './TLSyncRoom'\nimport {\n\tconvertStoreSnapshotToRoomSnapshot,\n\tTLSyncForwardDiff,\n\tTLSyncStorage,\n\tTLSyncStorageGetChangesSinceResult,\n\tTLSyncStorageOnChangeCallbackProps,\n\tTLSyncStorageTransaction,\n\tTLSyncStorageTransactionCallback,\n\tTLSyncStorageTransactionOptions,\n\tTLSyncStorageTransactionResult,\n} from './TLSyncStorage'\n\n/**\n * Valid input value types for SQLite query parameters.\n * These are the types that can be passed as bindings to prepared statements.\n * @public\n */\nexport type TLSqliteInputValue = null | number | bigint | string | Uint8Array\n\n/**\n * Possible output value types returned from SQLite queries.\n * Includes all input types plus Uint8Array for BLOB columns.\n * @public\n */\nexport type TLSqliteOutputValue = null | number | bigint | string | Uint8Array\n\n/**\n * A row returned from a SQLite query, mapping column names to their values.\n * @public\n */\nexport type TLSqliteRow = Record<string, TLSqliteOutputValue>\n\n/**\n * A prepared statement that can be executed multiple times with different bindings.\n * @public\n */\nexport interface TLSyncSqliteStatement<\n\tTResult extends TLSqliteRow | void,\n\tTParams extends TLSqliteInputValue[] = [],\n> {\n\t/** Execute the statement and iterate over results one at a time */\n\titerate(...bindings: TParams): IterableIterator<TResult>\n\t/** Execute the statement and return all results as an array */\n\tall(...bindings: TParams): TResult[]\n\t/** Execute the statement without returning results (for DML) */\n\trun(...bindings: TParams): void\n}\n\n/**\n * Configuration for SQLiteSyncStorage.\n * @public\n */\nexport interface TLSyncSqliteWrapperConfig {\n\t/** Prefix for all table names (default: ''). E.g. 'sync_' creates tables 'sync_documents', 'sync_tombstones', 'sync_metadata' */\n\ttablePrefix?: string\n}\n\n/**\n * Interface for SQLite storage with prepare, exec and transaction capabilities.\n * @public\n */\nexport interface TLSyncSqliteWrapper {\n\t/** Optional configuration for table names. If not provided, defaults are used. */\n\treadonly config?: TLSyncSqliteWrapperConfig\n\t/** Prepare a SQL statement for execution */\n\tprepare<TResult extends TLSqliteRow | void, TParams extends TLSqliteInputValue[] = []>(\n\t\tsql: string\n\t): TLSyncSqliteStatement<TResult, TParams>\n\t/** Execute raw SQL (for DDL, multi-statement scripts) */\n\texec(sql: string): void\n\t/** Execute a callback within a transaction */\n\ttransaction<T>(callback: () => T): T\n}\n\nexport function migrateSqliteSyncStorage(\n\tstorage: TLSyncSqliteWrapper,\n\t{\n\t\tdocumentsTable = 'documents',\n\t\tobjectsTable = 'objects',\n\t\ttombstonesTable = 'tombstones',\n\t\tmetadataTable = 'metadata',\n\t}: {\n\t\tdocumentsTable?: string\n\t\tobjectsTable?: string\n\t\ttombstonesTable?: string\n\t\tmetadataTable?: string\n\t} = {}\n): void {\n\tlet migrationVersion = 0\n\ttry {\n\t\tconst row = storage\n\t\t\t.prepare<{\n\t\t\t\tmigrationVersion: number\n\t\t\t}>(`SELECT migrationVersion FROM ${metadataTable} LIMIT 1`)\n\t\t\t.all()[0]\n\t\tmigrationVersion = row?.migrationVersion ?? 0\n\t} catch (_e) {\n\t\t// noop\n\t}\n\n\tif (migrationVersion === 0) {\n\t\tmigrationVersion++\n\t\tstorage.exec(`\n\t\t\tCREATE TABLE ${documentsTable} (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tstate BLOB NOT NULL,\n\t\t\t\tlastChangedClock INTEGER NOT NULL\n\t\t\t);\n\n\t\t\tCREATE INDEX idx_${documentsTable}_lastChangedClock ON ${documentsTable}(lastChangedClock);\n\n\t\t\tCREATE TABLE ${tombstonesTable} (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tclock INTEGER NOT NULL\n\t\t\t);\n\t\t\tCREATE INDEX idx_${tombstonesTable}_clock ON ${tombstonesTable}(clock);\n\n\t\t\t-- This table is used to store the metadata for the sync storage.\n\t\t\t-- There should only be one row in this table.\n\t\t\tCREATE TABLE ${metadataTable} (\n\t\t\t migrationVersion INTEGER NOT NULL,\n\t\t\t\tdocumentClock INTEGER NOT NULL,\n\t\t\t\ttombstoneHistoryStartsAtClock INTEGER NOT NULL,\n\t\t\t\tschema TEXT NOT NULL\n\t\t\t);\n\t\t\t\n\t\t\tINSERT INTO ${metadataTable} (migrationVersion, documentClock, tombstoneHistoryStartsAtClock, schema) VALUES (2, 0, 0, '')\n\t\t`)\n\t\t// Skip migration 2 since we created the table with BLOB already\n\t\tmigrationVersion++\n\t}\n\n\tif (migrationVersion === 1) {\n\t\t// Migration 2: Convert state column from TEXT to BLOB\n\t\t// SQLite doesn't support ALTER COLUMN, so we need to recreate the table\n\t\tmigrationVersion++\n\t\tstorage.exec(`\n\t\t\tCREATE TABLE ${documentsTable}_new (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tstate BLOB NOT NULL,\n\t\t\t\tlastChangedClock INTEGER NOT NULL\n\t\t\t);\n\n\t\t\tINSERT INTO ${documentsTable}_new (id, state, lastChangedClock)\n\t\t\tSELECT id, CAST(state AS BLOB), lastChangedClock FROM ${documentsTable};\n\n\t\t\tDROP TABLE ${documentsTable};\n\n\t\t\tALTER TABLE ${documentsTable}_new RENAME TO ${documentsTable};\n\n\t\t\tCREATE INDEX idx_${documentsTable}_lastChangedClock ON ${documentsTable}(lastChangedClock);\n\t\t`)\n\t}\n\n\tif (migrationVersion === 2) {\n\t\t// Migration 3: Add the object-store lane table. Object-lane records (e.g. comments) are\n\t\t// partitioned out of the documents table so the document snapshot never contains them.\n\t\t// They share the documents' clock and tombstones.\n\t\tmigrationVersion++\n\t\tstorage.exec(`\n\t\t\tCREATE TABLE ${objectsTable} (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tstate BLOB NOT NULL,\n\t\t\t\tlastChangedClock INTEGER NOT NULL\n\t\t\t);\n\n\t\t\tCREATE INDEX idx_${objectsTable}_lastChangedClock ON ${objectsTable}(lastChangedClock);\n\t\t`)\n\t}\n\n\t// add more migrations here if and when needed\n\n\tstorage.exec(`UPDATE ${metadataTable} SET migrationVersion = ${migrationVersion}`)\n}\n\nconst textEncoder = new TextEncoder()\nconst textDecoder = new TextDecoder()\n\nfunction encodeState(state: unknown): Uint8Array {\n\treturn textEncoder.encode(JSON.stringify(state))\n}\n\nfunction decodeState<T>(state: Uint8Array): T {\n\treturn JSON.parse(textDecoder.decode(state))\n}\n\n/**\n * SQLite-based implementation of TLSyncStorage.\n * Stores documents, tombstones, metadata, and clock values in SQLite tables.\n *\n * This storage backend provides persistent synchronization state that survives\n * process restarts, unlike InMemorySyncStorage which loses data when the process ends.\n *\n * @example\n * ```ts\n * // With Cloudflare Durable Objects\n * import { SQLiteSyncStorage, DurableObjectSqliteSyncWrapper } from '@tldraw/sync-core'\n *\n * const sql = new DurableObjectSqliteSyncWrapper(this.ctx.storage)\n * const storage = new SQLiteSyncStorage({ sql })\n * ```\n *\n * @example\n * ```ts\n * // With Node.js sqlite (Node 22.5+)\n * import { DatabaseSync } from 'node:sqlite'\n * import { SQLiteSyncStorage, NodeSqliteWrapper } from '@tldraw/sync-core'\n *\n * const db = new DatabaseSync('sync-state.db')\n * const sql = new NodeSqliteWrapper(db)\n * const storage = new SQLiteSyncStorage({ sql })\n * ```\n *\n * @example\n * ```ts\n * // Initialize with an existing snapshot\n * const storage = new SQLiteSyncStorage({ sql, snapshot: existingSnapshot })\n * ```\n *\n * @public\n */\nexport class SQLiteSyncStorage<R extends UnknownRecord> implements TLSyncStorage<R> {\n\t/**\n\t * Check if the storage has been initialized (has data in the clock table).\n\t * Useful for determining whether to load from an external source on first access.\n\t */\n\tstatic hasBeenInitialized(storage: TLSyncSqliteWrapper): boolean {\n\t\tconst prefix = storage.config?.tablePrefix ?? ''\n\t\ttry {\n\t\t\tconst schema = storage\n\t\t\t\t.prepare<{ schema: string }>(`SELECT schema FROM ${prefix}metadata LIMIT 1`)\n\t\t\t\t.all()[0]?.schema\n\t\t\treturn !!schema\n\t\t} catch (_e) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Get the current document clock value from storage without fully initializing.\n\t * Returns null if storage has not been initialized.\n\t * Useful for comparing storage freshness against external sources.\n\t */\n\tstatic getDocumentClock(storage: TLSyncSqliteWrapper): number | null {\n\t\tconst prefix = storage.config?.tablePrefix ?? ''\n\t\ttry {\n\t\t\tconst row = storage\n\t\t\t\t.prepare<{ documentClock: number }>(`SELECT documentClock FROM ${prefix}metadata LIMIT 1`)\n\t\t\t\t.all()[0]\n\t\t\t// documentClock exists but could be 0, so we check if the storage is initialized\n\t\t\tif (row && SQLiteSyncStorage.hasBeenInitialized(storage)) {\n\t\t\t\treturn row.documentClock\n\t\t\t}\n\t\t\treturn null\n\t\t} catch (_e) {\n\t\t\treturn null\n\t\t}\n\t}\n\n\t// Prepared statements - created once, reused many times\n\tprivate readonly stmts\n\n\tprivate readonly sql: TLSyncSqliteWrapper\n\n\t/** @internal */\n\treadonly objectTypes: ReadonlySet<string>\n\n\tconstructor({\n\t\tsql,\n\t\tsnapshot,\n\t\tobjectTypes,\n\t\tonChange,\n\t}: {\n\t\tsql: TLSyncSqliteWrapper\n\t\tsnapshot?: RoomSnapshot | StoreSnapshot<R>\n\t\t/**\n\t\t * Record type names stored in the object-store lane. Records of these types are routed to\n\t\t * a separate table: excluded from `getSnapshot()`, returned by `getObjectsSnapshot()`.\n\t\t * They share the documents' clock, tombstones, and transactions.\n\t\t */\n\t\tobjectTypes?: readonly string[]\n\t\tonChange?(arg: TLSyncStorageOnChangeCallbackProps): unknown\n\t}) {\n\t\tthis.sql = sql\n\t\tthis.objectTypes = new Set(objectTypes ?? [])\n\t\tconst prefix = sql.config?.tablePrefix ?? ''\n\t\tconst documentsTable = `${prefix}documents`\n\t\tconst objectsTable = `${prefix}objects`\n\t\tconst tombstonesTable = `${prefix}tombstones`\n\t\tconst metadataTable = `${prefix}metadata`\n\n\t\tmigrateSqliteSyncStorage(this.sql, {\n\t\t\tdocumentsTable,\n\t\t\tobjectsTable,\n\t\t\ttombstonesTable,\n\t\t\tmetadataTable,\n\t\t})\n\n\t\t// Both record tables have identical shape; prepare the same statements for each.\n\t\tconst makeRecordTableStmts = (table: string) => ({\n\t\t\tget: this.sql.prepare<{ state: Uint8Array }, [id: string]>(\n\t\t\t\t`SELECT state FROM ${table} WHERE id = ?`\n\t\t\t),\n\t\t\tinsert: this.sql.prepare<void, [id: string, state: Uint8Array, lastChangedClock: number]>(\n\t\t\t\t`INSERT OR REPLACE INTO ${table} (id, state, lastChangedClock) VALUES (?, ?, ?)`\n\t\t\t),\n\t\t\tdelete: this.sql.prepare<void, [id: string]>(`DELETE FROM ${table} WHERE id = ?`),\n\t\t\texists: this.sql.prepare<{ id: string }, [id: string]>(\n\t\t\t\t`SELECT id FROM ${table} WHERE id = ?`\n\t\t\t),\n\t\t\titerate: this.sql.prepare<{ state: Uint8Array; lastChangedClock: number }>(\n\t\t\t\t`SELECT state, lastChangedClock FROM ${table}`\n\t\t\t),\n\t\t\titerateEntries: this.sql.prepare<{ id: string; state: Uint8Array }>(\n\t\t\t\t`SELECT id, state FROM ${table}`\n\t\t\t),\n\t\t\titerateKeys: this.sql.prepare<{ id: string }>(`SELECT id FROM ${table}`),\n\t\t\titerateValues: this.sql.prepare<{ state: Uint8Array }>(`SELECT state FROM ${table}`),\n\t\t\tchangedSince: this.sql.prepare<{ state: Uint8Array }, [sinceClock: number]>(\n\t\t\t\t`SELECT state FROM ${table} WHERE lastChangedClock > ?`\n\t\t\t),\n\t\t})\n\n\t\t// Prepare all statements once\n\t\tthis.stmts = {\n\t\t\t// Metadata\n\t\t\tgetDocumentClock: this.sql.prepare<{ documentClock: number }>(\n\t\t\t\t`SELECT documentClock FROM ${metadataTable} LIMIT 1`\n\t\t\t),\n\t\t\tgetTombstoneHistoryStartsAtClock: this.sql.prepare<{ tombstoneHistoryStartsAtClock: number }>(\n\t\t\t\t`SELECT tombstoneHistoryStartsAtClock FROM ${metadataTable}`\n\t\t\t),\n\t\t\tgetSchema: this.sql.prepare<{ schema: string }>(`SELECT schema FROM ${metadataTable}`),\n\t\t\tsetSchema: this.sql.prepare<void, [schema: string]>(`UPDATE ${metadataTable} SET schema = ?`),\n\t\t\tsetTombstoneHistoryStartsAtClock: this.sql.prepare<void, [clock: number]>(\n\t\t\t\t`UPDATE ${metadataTable} SET tombstoneHistoryStartsAtClock = ?`\n\t\t\t),\n\t\t\tincrementDocumentClock: this.sql.prepare<void>(\n\t\t\t\t`UPDATE ${metadataTable} SET documentClock = documentClock + 1`\n\t\t\t),\n\n\t\t\t// Record tables (document lane + object-store lane)\n\t\t\tdocuments: makeRecordTableStmts(documentsTable),\n\t\t\tobjects: makeRecordTableStmts(objectsTable),\n\n\t\t\t// Tombstones\n\t\t\tinsertTombstone: this.sql.prepare<void, [id: string, clock: number]>(\n\t\t\t\t`INSERT OR REPLACE INTO ${tombstonesTable} (id, clock) VALUES (?, ?)`\n\t\t\t),\n\t\t\tdeleteTombstone: this.sql.prepare<void, [id: string]>(\n\t\t\t\t`DELETE FROM ${tombstonesTable} WHERE id = ?`\n\t\t\t),\n\t\t\tdeleteTombstonesBefore: this.sql.prepare<void, [clock: number]>(\n\t\t\t\t`DELETE FROM ${tombstonesTable} WHERE clock < ?`\n\t\t\t),\n\t\t\tcountTombstones: this.sql.prepare<{ count: number }>(\n\t\t\t\t`SELECT count(*) as count FROM ${tombstonesTable}`\n\t\t\t),\n\t\t\titerateTombstones: this.sql.prepare<{ id: string; clock: number }>(\n\t\t\t\t`SELECT id, clock FROM ${tombstonesTable} ORDER BY clock ASC`\n\t\t\t),\n\t\t\tgetTombstonesChangedSince: this.sql.prepare<{ id: string }, [sinceClock: number]>(\n\t\t\t\t`SELECT id FROM ${tombstonesTable} WHERE clock > ?`\n\t\t\t),\n\n\t\t\t// Initial setup (only used when loading a snapshot)\n\t\t\tupdateMetadata: this.sql.prepare<\n\t\t\t\tvoid,\n\t\t\t\t[documentClock: number, tombstoneHistoryStartsAtClock: number, schema: string]\n\t\t\t>(\n\t\t\t\t`UPDATE ${metadataTable} SET documentClock = ?, tombstoneHistoryStartsAtClock = ?, schema = ?`\n\t\t\t),\n\t\t}\n\n\t\t// Check if we already have data\n\t\tconst hasData = SQLiteSyncStorage.hasBeenInitialized(sql)\n\n\t\tif (snapshot || !hasData) {\n\t\t\tsnapshot = convertStoreSnapshotToRoomSnapshot(snapshot ?? DEFAULT_INITIAL_SNAPSHOT)\n\n\t\t\tconst documentClock = snapshot.documentClock ?? snapshot.clock ?? 0\n\t\t\tconst tombstoneHistoryStartsAtClock = snapshot.tombstoneHistoryStartsAtClock ?? documentClock\n\n\t\t\t// Clear existing data\n\t\t\tthis.sql.exec(`\n\t\t\t\tDELETE FROM ${documentsTable};\n\t\t\t\tDELETE FROM ${objectsTable};\n\t\t\t\tDELETE FROM ${tombstonesTable};\n\t\t\t`)\n\n\t\t\t// Insert records, routing object-lane types into their partition (a seed snapshot may\n\t\t\t// carry object-lane records merged in, e.g. loaded from a separate persistence lane)\n\t\t\tfor (const doc of snapshot.documents) {\n\t\t\t\tconst table = this.objectTypes.has(doc.state.typeName)\n\t\t\t\t\t? this.stmts.objects\n\t\t\t\t\t: this.stmts.documents\n\t\t\t\ttable.insert.run(doc.state.id, encodeState(doc.state), doc.lastChangedClock)\n\t\t\t}\n\n\t\t\t// Insert tombstones\n\t\t\tif (snapshot.tombstones) {\n\t\t\t\tfor (const [id, clock] of objectMapEntries(snapshot.tombstones)) {\n\t\t\t\t\tthis.stmts.insertTombstone.run(id, clock)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Insert metadata row\n\t\t\tthis.stmts.updateMetadata.run(\n\t\t\t\tdocumentClock,\n\t\t\t\ttombstoneHistoryStartsAtClock,\n\t\t\t\tJSON.stringify(snapshot.schema)\n\t\t\t)\n\t\t} else {\n\t\t\t// One-time sweep: rooms written before the objects table existed may have object-lane\n\t\t\t// records sitting in the documents table. Record ids are typeName-prefixed, so a PK\n\t\t\t// range scan per object type moves them cheaply (no-op once clean).\n\t\t\tfor (const typeName of this.objectTypes) {\n\t\t\t\tconst lo = `${typeName}:`\n\t\t\t\t// ';' is the next code point after ':' \u2014 [lo, hi) covers exactly the `type:` prefix\n\t\t\t\tconst hi = `${typeName};`\n\t\t\t\tthis.sql.exec(`\n\t\t\t\t\tINSERT OR REPLACE INTO ${objectsTable} (id, state, lastChangedClock)\n\t\t\t\t\tSELECT id, state, lastChangedClock FROM ${documentsTable}\n\t\t\t\t\tWHERE id >= '${lo}' AND id < '${hi}';\n\t\t\t\t\tDELETE FROM ${documentsTable} WHERE id >= '${lo}' AND id < '${hi}';\n\t\t\t\t`)\n\t\t\t}\n\t\t}\n\t\tif (onChange) {\n\t\t\tthis.onChange(onChange)\n\t\t}\n\t}\n\n\tprivate notifier = new MicrotaskNotifier<[TLSyncStorageOnChangeCallbackProps]>()\n\tonChange(callback: (arg: TLSyncStorageOnChangeCallbackProps) => void): () => void {\n\t\treturn this.notifier.register(callback)\n\t}\n\n\ttransaction<T>(\n\t\tcallback: TLSyncStorageTransactionCallback<R, T>,\n\t\topts?: TLSyncStorageTransactionOptions\n\t): TLSyncStorageTransactionResult<T, R> {\n\t\tconst clockBefore = this.getClock()\n\t\tconst trackChanges = opts?.emitChanges === 'always'\n\t\treturn this.sql.transaction(() => {\n\t\t\tconst txn = new SQLiteSyncStorageTransaction<R>(this, this.stmts)\n\t\t\tlet result: T\n\t\t\tlet changes: TLSyncForwardDiff<R> | undefined\n\t\t\ttry {\n\t\t\t\tresult = transaction(() => {\n\t\t\t\t\treturn callback(txn)\n\t\t\t\t}) as T\n\t\t\t\tif (trackChanges) {\n\t\t\t\t\tchanges = txn.getChangesSince(clockBefore)?.diff\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\ttxn.close()\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttypeof result === 'object' &&\n\t\t\t\tresult &&\n\t\t\t\t'then' in result &&\n\t\t\t\ttypeof result.then === 'function'\n\t\t\t) {\n\t\t\t\tthrow new Error('Transaction must return a value, not a promise')\n\t\t\t}\n\n\t\t\tconst clockAfter = this.getClock()\n\t\t\tconst didChange = clockAfter > clockBefore\n\t\t\tif (didChange) {\n\t\t\t\tthis.notifier.notify({ id: opts?.id, documentClock: clockAfter })\n\t\t\t}\n\t\t\treturn { documentClock: clockAfter, didChange: clockAfter > clockBefore, result, changes }\n\t\t})\n\t}\n\n\tgetClock(): number {\n\t\tconst clockRow = this.stmts.getDocumentClock.all()[0]\n\t\treturn clockRow?.documentClock ?? 0\n\t}\n\n\t/** @internal */\n\t_getTombstoneHistoryStartsAtClock(): number {\n\t\tconst clockRow = this.stmts.getTombstoneHistoryStartsAtClock.all()[0]\n\t\treturn clockRow?.tombstoneHistoryStartsAtClock ?? 0\n\t}\n\n\t/** @internal */\n\t_getSchema(): SerializedSchema {\n\t\tconst clockRow = this.stmts.getSchema.all()[0]\n\t\tassert(clockRow, 'Storage not initialized - clock row missing')\n\t\treturn JSON.parse(clockRow.schema)\n\t}\n\n\t/** @internal */\n\t_setSchema(schema: SerializedSchema): void {\n\t\tthis.stmts.setSchema.run(JSON.stringify(schema))\n\t}\n\n\t/** @internal */\n\tpruneTombstones = throttle(\n\t\t() => {\n\t\t\tconst tombstoneCount = this.stmts.countTombstones.all()[0].count as number\n\t\t\tif (tombstoneCount > MAX_TOMBSTONES) {\n\t\t\t\t// Get all tombstones sorted by clock ascending (oldest first)\n\t\t\t\tconst tombstones = this.stmts.iterateTombstones.all()\n\n\t\t\t\tconst result = computeTombstonePruning({ tombstones, documentClock: this.getClock() })\n\t\t\t\tif (result) {\n\t\t\t\t\tthis.stmts.setTombstoneHistoryStartsAtClock.run(result.newTombstoneHistoryStartsAtClock)\n\t\t\t\t\t// Delete all tombstones with clock < newTombstoneHistoryStartsAtClock in one operation.\n\t\t\t\t\t// This works because computeTombstonePruning ensures we never split a clock value.\n\t\t\t\t\tthis.stmts.deleteTombstonesBefore.run(result.newTombstoneHistoryStartsAtClock)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t1000,\n\t\t// prevent this from running synchronously to avoid blocking requests\n\t\t{ leading: false }\n\t)\n\n\tgetSnapshot(): RoomSnapshot {\n\t\t// object-lane records live in their own table, so the document snapshot is\n\t\t// pure-document by construction\n\t\treturn {\n\t\t\ttombstoneHistoryStartsAtClock: this._getTombstoneHistoryStartsAtClock(),\n\t\t\tdocumentClock: this.getClock(),\n\t\t\tdocuments: Array.from(this._iterateRecords(this.stmts.documents)),\n\t\t\ttombstones: Object.fromEntries(this._iterateTombstones()),\n\t\t\tschema: this._getSchema(),\n\t\t}\n\t}\n\n\tgetObjectsSnapshot(): RoomSnapshot['documents'] {\n\t\treturn Array.from(this._iterateRecords(this.stmts.objects))\n\t}\n\n\tprivate *_iterateRecords(\n\t\ttable: SQLiteSyncStorage<R>['stmts']['documents']\n\t): IterableIterator<{ state: R; lastChangedClock: number }> {\n\t\tfor (const row of table.iterate.iterate()) {\n\t\t\tyield { state: decodeState<R>(row.state), lastChangedClock: row.lastChangedClock }\n\t\t}\n\t}\n\n\tprivate *_iterateTombstones(): IterableIterator<[string, number]> {\n\t\tfor (const row of this.stmts.iterateTombstones.iterate()) {\n\t\t\tyield [row.id, row.clock]\n\t\t}\n\t}\n}\n\n/**\n * Transaction implementation for SQLiteSyncStorage.\n * Provides access to documents, tombstones, and metadata within a transaction.\n *\n * @internal\n */\nclass SQLiteSyncStorageTransaction<R extends UnknownRecord> implements TLSyncStorageTransaction<R> {\n\tprivate _clock: number\n\tprivate _closed = false\n\tprivate _didIncrementClock: boolean = false\n\n\tconstructor(\n\t\tprivate storage: SQLiteSyncStorage<R>,\n\t\tprivate stmts: SQLiteSyncStorage<R>['stmts']\n\t) {\n\t\tthis._clock = this.storage.getClock()\n\t}\n\n\t/** @internal */\n\tclose() {\n\t\tthis._closed = true\n\t}\n\n\tprivate assertNotClosed() {\n\t\tassert(!this._closed, 'Transaction has ended, iterator cannot be consumed')\n\t}\n\n\tgetClock(): number {\n\t\treturn this._clock\n\t}\n\n\tprivate getNextClock(): number {\n\t\tif (!this._didIncrementClock) {\n\t\t\tthis._didIncrementClock = true\n\t\t\tthis.stmts.incrementDocumentClock.run()\n\t\t\tthis._clock = this.storage.getClock()\n\t\t}\n\t\treturn this._clock\n\t}\n\n\t/**\n\t * Which record table an id belongs to. Record ids are typeName-prefixed (`comment:abc`),\n\t * so the partition is derivable from the id alone.\n\t */\n\tprivate tableFor(id: string) {\n\t\tconst sep = id.indexOf(':')\n\t\tif (sep === -1) return this.stmts.documents\n\t\treturn this.storage.objectTypes.has(id.slice(0, sep))\n\t\t\t? this.stmts.objects\n\t\t\t: this.stmts.documents\n\t}\n\n\tget(id: string): R | undefined {\n\t\tthis.assertNotClosed()\n\t\tconst row = this.tableFor(id).get.all(id)[0]\n\t\tif (!row) return undefined\n\t\treturn decodeState<R>(row.state)\n\t}\n\n\tset(id: string, record: R): void {\n\t\tthis.assertNotClosed()\n\t\tassert(id === record.id, `Record id mismatch: key does not match record.id`)\n\t\tconst clock = this.getNextClock()\n\t\t// Automatically clear tombstone if it exists\n\t\tthis.stmts.deleteTombstone.run(id)\n\t\t// route by type: object-lane records live in their own table\n\t\tconst table = this.storage.objectTypes.has(record.typeName)\n\t\t\t? this.stmts.objects\n\t\t\t: this.stmts.documents\n\t\ttable.insert.run(id, encodeState(record), clock)\n\t}\n\n\tdelete(id: string): void {\n\t\tthis.assertNotClosed()\n\t\tconst table = this.tableFor(id)\n\t\t// Only create a tombstone if the record actually exists\n\t\tconst exists = table.exists.all(id)[0]\n\t\tif (!exists) return\n\t\tconst clock = this.getNextClock()\n\t\ttable.delete.run(id)\n\t\tthis.stmts.insertTombstone.run(id, clock)\n\t\tthis.storage.pruneTombstones()\n\t}\n\n\t// iteration spans both record tables so schema migrations cover object-lane records too\n\n\t*entries(): IterableIterator<[string, R]> {\n\t\tthis.assertNotClosed()\n\t\tfor (const table of [this.stmts.documents, this.stmts.objects]) {\n\t\t\tfor (const row of table.iterateEntries.iterate()) {\n\t\t\t\tthis.assertNotClosed()\n\t\t\t\tyield [row.id, decodeState<R>(row.state)]\n\t\t\t}\n\t\t}\n\t}\n\n\t*keys(): IterableIterator<string> {\n\t\tthis.assertNotClosed()\n\t\tfor (const table of [this.stmts.documents, this.stmts.objects]) {\n\t\t\tfor (const row of table.iterateKeys.iterate()) {\n\t\t\t\tthis.assertNotClosed()\n\t\t\t\tyield row.id\n\t\t\t}\n\t\t}\n\t}\n\n\t*values(): IterableIterator<R> {\n\t\tthis.assertNotClosed()\n\t\tfor (const table of [this.stmts.documents, this.stmts.objects]) {\n\t\t\tfor (const row of table.iterateValues.iterate()) {\n\t\t\t\tthis.assertNotClosed()\n\t\t\t\tyield decodeState<R>(row.state)\n\t\t\t}\n\t\t}\n\t}\n\n\tgetSchema(): SerializedSchema {\n\t\tthis.assertNotClosed()\n\t\treturn this.storage._getSchema()\n\t}\n\n\tsetSchema(schema: SerializedSchema): void {\n\t\tthis.assertNotClosed()\n\t\tthis.storage._setSchema(schema)\n\t}\n\n\tgetChangesSince(sinceClock: number): TLSyncStorageGetChangesSinceResult<R> | undefined {\n\t\tthis.assertNotClosed()\n\t\tconst clock = this.storage.getClock()\n\t\tif (sinceClock === clock) return undefined\n\t\tif (sinceClock > clock) {\n\t\t\t// something went wrong, wipe the slate clean\n\t\t\tsinceClock = -1\n\t\t}\n\t\tconst diff: TLSyncForwardDiff<R> = { puts: {}, deletes: [] }\n\t\tconst wipeAll = sinceClock < this.storage._getTombstoneHistoryStartsAtClock()\n\n\t\t// both record tables share the clock and tombstones, so the change feed spans both\n\t\tif (wipeAll) {\n\t\t\t// If wipeAll, include all records\n\t\t\tfor (const table of [this.stmts.documents, this.stmts.objects]) {\n\t\t\t\tfor (const row of table.iterateValues.iterate()) {\n\t\t\t\t\tconst state = decodeState<R>(row.state)\n\t\t\t\t\tdiff.puts[state.id] = state\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Get records changed since clock\n\t\t\tfor (const table of [this.stmts.documents, this.stmts.objects]) {\n\t\t\t\tfor (const row of table.changedSince.iterate(sinceClock)) {\n\t\t\t\t\tconst state = decodeState<R>(row.state)\n\t\t\t\t\tdiff.puts[state.id] = state\n\t\t\t\t}\n\t\t\t}\n\t\t\t// When wipeAll, deletes are redundant (full state is in puts). Only include tombstones otherwise.\n\t\t\tfor (const row of this.stmts.getTombstonesChangedSince.iterate(sinceClock)) {\n\t\t\t\tdiff.deletes.push(row.id)\n\t\t\t}\n\t\t}\n\n\t\treturn { diff, wipeAll }\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { transaction } from '@tldraw/state'\nimport { SerializedSchema, StoreSnapshot, UnknownRecord } from '@tldraw/store'\nimport { assert, objectMapEntries, throttle } from '@tldraw/utils'\nimport {\n\tcomputeTombstonePruning,\n\tDEFAULT_INITIAL_SNAPSHOT,\n\tMAX_TOMBSTONES,\n} from './InMemorySyncStorage'\nimport { MicrotaskNotifier } from './MicrotaskNotifier'\nimport { RoomSnapshot } from './TLSyncRoom'\nimport {\n\tconvertStoreSnapshotToRoomSnapshot,\n\tTLSyncForwardDiff,\n\tTLSyncStorage,\n\tTLSyncStorageGetChangesSinceResult,\n\tTLSyncStorageOnChangeCallbackProps,\n\tTLSyncStorageTransaction,\n\tTLSyncStorageTransactionCallback,\n\tTLSyncStorageTransactionOptions,\n\tTLSyncStorageTransactionResult,\n} from './TLSyncStorage'\n\n/**\n * Valid input value types for SQLite query parameters.\n * These are the types that can be passed as bindings to prepared statements.\n * @public\n */\nexport type TLSqliteInputValue = null | number | bigint | string | Uint8Array\n\n/**\n * Possible output value types returned from SQLite queries.\n * Includes all input types plus Uint8Array for BLOB columns.\n * @public\n */\nexport type TLSqliteOutputValue = null | number | bigint | string | Uint8Array\n\n/**\n * A row returned from a SQLite query, mapping column names to their values.\n * @public\n */\nexport type TLSqliteRow = Record<string, TLSqliteOutputValue>\n\n/**\n * A prepared statement that can be executed multiple times with different bindings.\n * @public\n */\nexport interface TLSyncSqliteStatement<\n\tTResult extends TLSqliteRow | void,\n\tTParams extends TLSqliteInputValue[] = [],\n> {\n\t/** Execute the statement and iterate over results one at a time */\n\titerate(...bindings: TParams): IterableIterator<TResult>\n\t/** Execute the statement and return all results as an array */\n\tall(...bindings: TParams): TResult[]\n\t/** Execute the statement without returning results (for DML) */\n\trun(...bindings: TParams): void\n}\n\n/**\n * Configuration for SQLiteSyncStorage.\n * @public\n */\nexport interface TLSyncSqliteWrapperConfig {\n\t/** Prefix for all table names (default: ''). E.g. 'sync_' creates tables 'sync_documents', 'sync_tombstones', 'sync_metadata' */\n\ttablePrefix?: string\n}\n\n/**\n * Interface for SQLite storage with prepare, exec and transaction capabilities.\n * @public\n */\nexport interface TLSyncSqliteWrapper {\n\t/** Optional configuration for table names. If not provided, defaults are used. */\n\treadonly config?: TLSyncSqliteWrapperConfig\n\t/** Prepare a SQL statement for execution */\n\tprepare<TResult extends TLSqliteRow | void, TParams extends TLSqliteInputValue[] = []>(\n\t\tsql: string\n\t): TLSyncSqliteStatement<TResult, TParams>\n\t/** Execute raw SQL (for DDL, multi-statement scripts) */\n\texec(sql: string): void\n\t/** Execute a callback within a transaction */\n\ttransaction<T>(callback: () => T): T\n}\n\nexport function migrateSqliteSyncStorage(\n\tstorage: TLSyncSqliteWrapper,\n\t{\n\t\tdocumentsTable = 'documents',\n\t\ttombstonesTable = 'tombstones',\n\t\tmetadataTable = 'metadata',\n\t}: { documentsTable?: string; tombstonesTable?: string; metadataTable?: string } = {}\n): void {\n\tlet migrationVersion = 0\n\ttry {\n\t\tconst row = storage\n\t\t\t.prepare<{\n\t\t\t\tmigrationVersion: number\n\t\t\t}>(`SELECT migrationVersion FROM ${metadataTable} LIMIT 1`)\n\t\t\t.all()[0]\n\t\tmigrationVersion = row?.migrationVersion ?? 0\n\t} catch (_e) {\n\t\t// noop\n\t}\n\n\tif (migrationVersion === 0) {\n\t\tmigrationVersion++\n\t\tstorage.exec(`\n\t\t\tCREATE TABLE ${documentsTable} (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tstate BLOB NOT NULL,\n\t\t\t\tlastChangedClock INTEGER NOT NULL\n\t\t\t);\n\n\t\t\tCREATE INDEX idx_${documentsTable}_lastChangedClock ON ${documentsTable}(lastChangedClock);\n\n\t\t\tCREATE TABLE ${tombstonesTable} (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tclock INTEGER NOT NULL\n\t\t\t);\n\t\t\tCREATE INDEX idx_${tombstonesTable}_clock ON ${tombstonesTable}(clock);\n\n\t\t\t-- This table is used to store the metadata for the sync storage.\n\t\t\t-- There should only be one row in this table.\n\t\t\tCREATE TABLE ${metadataTable} (\n\t\t\t migrationVersion INTEGER NOT NULL,\n\t\t\t\tdocumentClock INTEGER NOT NULL,\n\t\t\t\ttombstoneHistoryStartsAtClock INTEGER NOT NULL,\n\t\t\t\tschema TEXT NOT NULL\n\t\t\t);\n\t\t\t\n\t\t\tINSERT INTO ${metadataTable} (migrationVersion, documentClock, tombstoneHistoryStartsAtClock, schema) VALUES (2, 0, 0, '')\n\t\t`)\n\t\t// Skip migration 2 since we created the table with BLOB already\n\t\tmigrationVersion++\n\t}\n\n\tif (migrationVersion === 1) {\n\t\t// Migration 2: Convert state column from TEXT to BLOB\n\t\t// SQLite doesn't support ALTER COLUMN, so we need to recreate the table\n\t\tmigrationVersion++\n\t\tstorage.exec(`\n\t\t\tCREATE TABLE ${documentsTable}_new (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tstate BLOB NOT NULL,\n\t\t\t\tlastChangedClock INTEGER NOT NULL\n\t\t\t);\n\t\t\t\n\t\t\tINSERT INTO ${documentsTable}_new (id, state, lastChangedClock)\n\t\t\tSELECT id, CAST(state AS BLOB), lastChangedClock FROM ${documentsTable};\n\t\t\t\n\t\t\tDROP TABLE ${documentsTable};\n\t\t\t\n\t\t\tALTER TABLE ${documentsTable}_new RENAME TO ${documentsTable};\n\t\t\t\n\t\t\tCREATE INDEX idx_${documentsTable}_lastChangedClock ON ${documentsTable}(lastChangedClock);\n\t\t`)\n\t}\n\n\t// add more migrations here if and when needed\n\n\tstorage.exec(`UPDATE ${metadataTable} SET migrationVersion = ${migrationVersion}`)\n}\n\nconst textEncoder = new TextEncoder()\nconst textDecoder = new TextDecoder()\n\nfunction encodeState(state: unknown): Uint8Array {\n\treturn textEncoder.encode(JSON.stringify(state))\n}\n\nfunction decodeState<T>(state: Uint8Array): T {\n\treturn JSON.parse(textDecoder.decode(state))\n}\n\n/**\n * SQLite-based implementation of TLSyncStorage.\n * Stores documents, tombstones, metadata, and clock values in SQLite tables.\n *\n * This storage backend provides persistent synchronization state that survives\n * process restarts, unlike InMemorySyncStorage which loses data when the process ends.\n *\n * @example\n * ```ts\n * // With Cloudflare Durable Objects\n * import { SQLiteSyncStorage, DurableObjectSqliteSyncWrapper } from '@tldraw/sync-core'\n *\n * const sql = new DurableObjectSqliteSyncWrapper(this.ctx.storage)\n * const storage = new SQLiteSyncStorage({ sql })\n * ```\n *\n * @example\n * ```ts\n * // With Node.js sqlite (Node 22.5+)\n * import { DatabaseSync } from 'node:sqlite'\n * import { SQLiteSyncStorage, NodeSqliteWrapper } from '@tldraw/sync-core'\n *\n * const db = new DatabaseSync('sync-state.db')\n * const sql = new NodeSqliteWrapper(db)\n * const storage = new SQLiteSyncStorage({ sql })\n * ```\n *\n * @example\n * ```ts\n * // Initialize with an existing snapshot\n * const storage = new SQLiteSyncStorage({ sql, snapshot: existingSnapshot })\n * ```\n *\n * @public\n */\nexport class SQLiteSyncStorage<R extends UnknownRecord> implements TLSyncStorage<R> {\n\t/**\n\t * Check if the storage has been initialized (has data in the clock table).\n\t * Useful for determining whether to load from an external source on first access.\n\t */\n\tstatic hasBeenInitialized(storage: TLSyncSqliteWrapper): boolean {\n\t\tconst prefix = storage.config?.tablePrefix ?? ''\n\t\ttry {\n\t\t\tconst schema = storage\n\t\t\t\t.prepare<{ schema: string }>(`SELECT schema FROM ${prefix}metadata LIMIT 1`)\n\t\t\t\t.all()[0]?.schema\n\t\t\treturn !!schema\n\t\t} catch (_e) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Get the current document clock value from storage without fully initializing.\n\t * Returns null if storage has not been initialized.\n\t * Useful for comparing storage freshness against external sources.\n\t */\n\tstatic getDocumentClock(storage: TLSyncSqliteWrapper): number | null {\n\t\tconst prefix = storage.config?.tablePrefix ?? ''\n\t\ttry {\n\t\t\tconst row = storage\n\t\t\t\t.prepare<{ documentClock: number }>(`SELECT documentClock FROM ${prefix}metadata LIMIT 1`)\n\t\t\t\t.all()[0]\n\t\t\t// documentClock exists but could be 0, so we check if the storage is initialized\n\t\t\tif (row && SQLiteSyncStorage.hasBeenInitialized(storage)) {\n\t\t\t\treturn row.documentClock\n\t\t\t}\n\t\t\treturn null\n\t\t} catch (_e) {\n\t\t\treturn null\n\t\t}\n\t}\n\n\t// Prepared statements - created once, reused many times\n\tprivate readonly stmts\n\n\tprivate readonly sql: TLSyncSqliteWrapper\n\n\tconstructor({\n\t\tsql,\n\t\tsnapshot,\n\t\tonChange,\n\t}: {\n\t\tsql: TLSyncSqliteWrapper\n\t\tsnapshot?: RoomSnapshot | StoreSnapshot<R>\n\t\tonChange?(arg: TLSyncStorageOnChangeCallbackProps): unknown\n\t}) {\n\t\tthis.sql = sql\n\t\tconst prefix = sql.config?.tablePrefix ?? ''\n\t\tconst documentsTable = `${prefix}documents`\n\t\tconst tombstonesTable = `${prefix}tombstones`\n\t\tconst metadataTable = `${prefix}metadata`\n\n\t\tmigrateSqliteSyncStorage(this.sql, { documentsTable, tombstonesTable, metadataTable })\n\n\t\t// Prepare all statements once\n\t\tthis.stmts = {\n\t\t\t// Metadata\n\t\t\tgetDocumentClock: this.sql.prepare<{ documentClock: number }>(\n\t\t\t\t`SELECT documentClock FROM ${metadataTable} LIMIT 1`\n\t\t\t),\n\t\t\tgetTombstoneHistoryStartsAtClock: this.sql.prepare<{ tombstoneHistoryStartsAtClock: number }>(\n\t\t\t\t`SELECT tombstoneHistoryStartsAtClock FROM ${metadataTable}`\n\t\t\t),\n\t\t\tgetSchema: this.sql.prepare<{ schema: string }>(`SELECT schema FROM ${metadataTable}`),\n\t\t\tsetSchema: this.sql.prepare<void, [schema: string]>(`UPDATE ${metadataTable} SET schema = ?`),\n\t\t\tsetTombstoneHistoryStartsAtClock: this.sql.prepare<void, [clock: number]>(\n\t\t\t\t`UPDATE ${metadataTable} SET tombstoneHistoryStartsAtClock = ?`\n\t\t\t),\n\t\t\tincrementDocumentClock: this.sql.prepare<void>(\n\t\t\t\t`UPDATE ${metadataTable} SET documentClock = documentClock + 1`\n\t\t\t),\n\n\t\t\t// Documents\n\t\t\tgetDocument: this.sql.prepare<{ state: Uint8Array }, [id: string]>(\n\t\t\t\t`SELECT state FROM ${documentsTable} WHERE id = ?`\n\t\t\t),\n\t\t\tinsertDocument: this.sql.prepare<\n\t\t\t\tvoid,\n\t\t\t\t[id: string, state: Uint8Array, lastChangedClock: number]\n\t\t\t>(`INSERT OR REPLACE INTO ${documentsTable} (id, state, lastChangedClock) VALUES (?, ?, ?)`),\n\t\t\tdeleteDocument: this.sql.prepare<void, [id: string]>(\n\t\t\t\t`DELETE FROM ${documentsTable} WHERE id = ?`\n\t\t\t),\n\t\t\tdocumentExists: this.sql.prepare<{ id: string }, [id: string]>(\n\t\t\t\t`SELECT id FROM ${documentsTable} WHERE id = ?`\n\t\t\t),\n\t\t\titerateDocuments: this.sql.prepare<{ state: Uint8Array; lastChangedClock: number }>(\n\t\t\t\t`SELECT state, lastChangedClock FROM ${documentsTable}`\n\t\t\t),\n\t\t\titerateDocumentEntries: this.sql.prepare<{ id: string; state: Uint8Array }>(\n\t\t\t\t`SELECT id, state FROM ${documentsTable}`\n\t\t\t),\n\t\t\titerateDocumentKeys: this.sql.prepare<{ id: string }>(`SELECT id FROM ${documentsTable}`),\n\t\t\titerateDocumentValues: this.sql.prepare<{ state: Uint8Array }>(\n\t\t\t\t`SELECT state FROM ${documentsTable}`\n\t\t\t),\n\t\t\tgetDocumentsChangedSince: this.sql.prepare<{ state: Uint8Array }, [sinceClock: number]>(\n\t\t\t\t`SELECT state FROM ${documentsTable} WHERE lastChangedClock > ?`\n\t\t\t),\n\n\t\t\t// Tombstones\n\t\t\tinsertTombstone: this.sql.prepare<void, [id: string, clock: number]>(\n\t\t\t\t`INSERT OR REPLACE INTO ${tombstonesTable} (id, clock) VALUES (?, ?)`\n\t\t\t),\n\t\t\tdeleteTombstone: this.sql.prepare<void, [id: string]>(\n\t\t\t\t`DELETE FROM ${tombstonesTable} WHERE id = ?`\n\t\t\t),\n\t\t\tdeleteTombstonesBefore: this.sql.prepare<void, [clock: number]>(\n\t\t\t\t`DELETE FROM ${tombstonesTable} WHERE clock < ?`\n\t\t\t),\n\t\t\tcountTombstones: this.sql.prepare<{ count: number }>(\n\t\t\t\t`SELECT count(*) as count FROM ${tombstonesTable}`\n\t\t\t),\n\t\t\titerateTombstones: this.sql.prepare<{ id: string; clock: number }>(\n\t\t\t\t`SELECT id, clock FROM ${tombstonesTable} ORDER BY clock ASC`\n\t\t\t),\n\t\t\tgetTombstonesChangedSince: this.sql.prepare<{ id: string }, [sinceClock: number]>(\n\t\t\t\t`SELECT id FROM ${tombstonesTable} WHERE clock > ?`\n\t\t\t),\n\n\t\t\t// Initial setup (only used when loading a snapshot)\n\t\t\tupdateMetadata: this.sql.prepare<\n\t\t\t\tvoid,\n\t\t\t\t[documentClock: number, tombstoneHistoryStartsAtClock: number, schema: string]\n\t\t\t>(\n\t\t\t\t`UPDATE ${metadataTable} SET documentClock = ?, tombstoneHistoryStartsAtClock = ?, schema = ?`\n\t\t\t),\n\t\t}\n\n\t\t// Check if we already have data\n\t\tconst hasData = SQLiteSyncStorage.hasBeenInitialized(sql)\n\n\t\tif (snapshot || !hasData) {\n\t\t\tsnapshot = convertStoreSnapshotToRoomSnapshot(snapshot ?? DEFAULT_INITIAL_SNAPSHOT)\n\n\t\t\tconst documentClock = snapshot.documentClock ?? snapshot.clock ?? 0\n\t\t\tconst tombstoneHistoryStartsAtClock = snapshot.tombstoneHistoryStartsAtClock ?? documentClock\n\n\t\t\t// Clear existing data\n\t\t\tthis.sql.exec(`\n\t\t\t\tDELETE FROM ${documentsTable};\n\t\t\t\tDELETE FROM ${tombstonesTable};\n\t\t\t`)\n\n\t\t\t// Insert documents\n\t\t\tfor (const doc of snapshot.documents) {\n\t\t\t\tthis.stmts.insertDocument.run(doc.state.id, encodeState(doc.state), doc.lastChangedClock)\n\t\t\t}\n\n\t\t\t// Insert tombstones\n\t\t\tif (snapshot.tombstones) {\n\t\t\t\tfor (const [id, clock] of objectMapEntries(snapshot.tombstones)) {\n\t\t\t\t\tthis.stmts.insertTombstone.run(id, clock)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Insert metadata row\n\t\t\tthis.stmts.updateMetadata.run(\n\t\t\t\tdocumentClock,\n\t\t\t\ttombstoneHistoryStartsAtClock,\n\t\t\t\tJSON.stringify(snapshot.schema)\n\t\t\t)\n\t\t}\n\t\tif (onChange) {\n\t\t\tthis.onChange(onChange)\n\t\t}\n\t}\n\n\tprivate notifier = new MicrotaskNotifier<[TLSyncStorageOnChangeCallbackProps]>()\n\tonChange(callback: (arg: TLSyncStorageOnChangeCallbackProps) => void): () => void {\n\t\treturn this.notifier.register(callback)\n\t}\n\n\ttransaction<T>(\n\t\tcallback: TLSyncStorageTransactionCallback<R, T>,\n\t\topts?: TLSyncStorageTransactionOptions\n\t): TLSyncStorageTransactionResult<T, R> {\n\t\tconst clockBefore = this.getClock()\n\t\tconst trackChanges = opts?.emitChanges === 'always'\n\t\treturn this.sql.transaction(() => {\n\t\t\tconst txn = new SQLiteSyncStorageTransaction<R>(this, this.stmts)\n\t\t\tlet result: T\n\t\t\tlet changes: TLSyncForwardDiff<R> | undefined\n\t\t\ttry {\n\t\t\t\tresult = transaction(() => {\n\t\t\t\t\treturn callback(txn)\n\t\t\t\t}) as T\n\t\t\t\tif (trackChanges) {\n\t\t\t\t\tchanges = txn.getChangesSince(clockBefore)?.diff\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\ttxn.close()\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttypeof result === 'object' &&\n\t\t\t\tresult &&\n\t\t\t\t'then' in result &&\n\t\t\t\ttypeof result.then === 'function'\n\t\t\t) {\n\t\t\t\tthrow new Error('Transaction must return a value, not a promise')\n\t\t\t}\n\n\t\t\tconst clockAfter = this.getClock()\n\t\t\tconst didChange = clockAfter > clockBefore\n\t\t\tif (didChange) {\n\t\t\t\tthis.notifier.notify({ id: opts?.id, documentClock: clockAfter })\n\t\t\t}\n\t\t\treturn { documentClock: clockAfter, didChange: clockAfter > clockBefore, result, changes }\n\t\t})\n\t}\n\n\tgetClock(): number {\n\t\tconst clockRow = this.stmts.getDocumentClock.all()[0]\n\t\treturn clockRow?.documentClock ?? 0\n\t}\n\n\t/** @internal */\n\t_getTombstoneHistoryStartsAtClock(): number {\n\t\tconst clockRow = this.stmts.getTombstoneHistoryStartsAtClock.all()[0]\n\t\treturn clockRow?.tombstoneHistoryStartsAtClock ?? 0\n\t}\n\n\t/** @internal */\n\t_getSchema(): SerializedSchema {\n\t\tconst clockRow = this.stmts.getSchema.all()[0]\n\t\tassert(clockRow, 'Storage not initialized - clock row missing')\n\t\treturn JSON.parse(clockRow.schema)\n\t}\n\n\t/** @internal */\n\t_setSchema(schema: SerializedSchema): void {\n\t\tthis.stmts.setSchema.run(JSON.stringify(schema))\n\t}\n\n\t/** @internal */\n\tpruneTombstones = throttle(\n\t\t() => {\n\t\t\tconst tombstoneCount = this.stmts.countTombstones.all()[0].count as number\n\t\t\tif (tombstoneCount > MAX_TOMBSTONES) {\n\t\t\t\t// Get all tombstones sorted by clock ascending (oldest first)\n\t\t\t\tconst tombstones = this.stmts.iterateTombstones.all()\n\n\t\t\t\tconst result = computeTombstonePruning({ tombstones, documentClock: this.getClock() })\n\t\t\t\tif (result) {\n\t\t\t\t\tthis.stmts.setTombstoneHistoryStartsAtClock.run(result.newTombstoneHistoryStartsAtClock)\n\t\t\t\t\t// Delete all tombstones with clock < newTombstoneHistoryStartsAtClock in one operation.\n\t\t\t\t\t// This works because computeTombstonePruning ensures we never split a clock value.\n\t\t\t\t\tthis.stmts.deleteTombstonesBefore.run(result.newTombstoneHistoryStartsAtClock)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t1000,\n\t\t// prevent this from running synchronously to avoid blocking requests\n\t\t{ leading: false }\n\t)\n\n\tgetSnapshot(): RoomSnapshot {\n\t\treturn {\n\t\t\ttombstoneHistoryStartsAtClock: this._getTombstoneHistoryStartsAtClock(),\n\t\t\tdocumentClock: this.getClock(),\n\t\t\tdocuments: Array.from(this._iterateDocuments()),\n\t\t\ttombstones: Object.fromEntries(this._iterateTombstones()),\n\t\t\tschema: this._getSchema(),\n\t\t}\n\t}\n\tprivate *_iterateDocuments(): IterableIterator<{ state: R; lastChangedClock: number }> {\n\t\tfor (const row of this.stmts.iterateDocuments.iterate()) {\n\t\t\tyield { state: decodeState<R>(row.state), lastChangedClock: row.lastChangedClock }\n\t\t}\n\t}\n\n\tprivate *_iterateTombstones(): IterableIterator<[string, number]> {\n\t\tfor (const row of this.stmts.iterateTombstones.iterate()) {\n\t\t\tyield [row.id, row.clock]\n\t\t}\n\t}\n}\n\n/**\n * Transaction implementation for SQLiteSyncStorage.\n * Provides access to documents, tombstones, and metadata within a transaction.\n *\n * @internal\n */\nclass SQLiteSyncStorageTransaction<R extends UnknownRecord> implements TLSyncStorageTransaction<R> {\n\tprivate _clock: number\n\tprivate _closed = false\n\tprivate _didIncrementClock: boolean = false\n\n\tconstructor(\n\t\tprivate storage: SQLiteSyncStorage<R>,\n\t\tprivate stmts: SQLiteSyncStorage<R>['stmts']\n\t) {\n\t\tthis._clock = this.storage.getClock()\n\t}\n\n\t/** @internal */\n\tclose() {\n\t\tthis._closed = true\n\t}\n\n\tprivate assertNotClosed() {\n\t\tassert(!this._closed, 'Transaction has ended, iterator cannot be consumed')\n\t}\n\n\tgetClock(): number {\n\t\treturn this._clock\n\t}\n\n\tprivate getNextClock(): number {\n\t\tif (!this._didIncrementClock) {\n\t\t\tthis._didIncrementClock = true\n\t\t\tthis.stmts.incrementDocumentClock.run()\n\t\t\tthis._clock = this.storage.getClock()\n\t\t}\n\t\treturn this._clock\n\t}\n\n\tget(id: string): R | undefined {\n\t\tthis.assertNotClosed()\n\t\tconst row = this.stmts.getDocument.all(id)[0]\n\t\tif (!row) return undefined\n\t\treturn decodeState<R>(row.state)\n\t}\n\n\tset(id: string, record: R): void {\n\t\tthis.assertNotClosed()\n\t\tassert(id === record.id, `Record id mismatch: key does not match record.id`)\n\t\tconst clock = this.getNextClock()\n\t\t// Automatically clear tombstone if it exists\n\t\tthis.stmts.deleteTombstone.run(id)\n\t\tthis.stmts.insertDocument.run(id, encodeState(record), clock)\n\t}\n\n\tdelete(id: string): void {\n\t\tthis.assertNotClosed()\n\t\t// Only create a tombstone if the record actually exists\n\t\tconst exists = this.stmts.documentExists.all(id)[0]\n\t\tif (!exists) return\n\t\tconst clock = this.getNextClock()\n\t\tthis.stmts.deleteDocument.run(id)\n\t\tthis.stmts.insertTombstone.run(id, clock)\n\t\tthis.storage.pruneTombstones()\n\t}\n\n\t*entries(): IterableIterator<[string, R]> {\n\t\tthis.assertNotClosed()\n\t\tfor (const row of this.stmts.iterateDocumentEntries.iterate()) {\n\t\t\tthis.assertNotClosed()\n\t\t\tyield [row.id, decodeState<R>(row.state)]\n\t\t}\n\t}\n\n\t*keys(): IterableIterator<string> {\n\t\tthis.assertNotClosed()\n\t\tfor (const row of this.stmts.iterateDocumentKeys.iterate()) {\n\t\t\tthis.assertNotClosed()\n\t\t\tyield row.id\n\t\t}\n\t}\n\n\t*values(): IterableIterator<R> {\n\t\tthis.assertNotClosed()\n\t\tfor (const row of this.stmts.iterateDocumentValues.iterate()) {\n\t\t\tthis.assertNotClosed()\n\t\t\tyield decodeState<R>(row.state)\n\t\t}\n\t}\n\n\tgetSchema(): SerializedSchema {\n\t\tthis.assertNotClosed()\n\t\treturn this.storage._getSchema()\n\t}\n\n\tsetSchema(schema: SerializedSchema): void {\n\t\tthis.assertNotClosed()\n\t\tthis.storage._setSchema(schema)\n\t}\n\n\tgetChangesSince(sinceClock: number): TLSyncStorageGetChangesSinceResult<R> | undefined {\n\t\tthis.assertNotClosed()\n\t\tconst clock = this.storage.getClock()\n\t\tif (sinceClock === clock) return undefined\n\t\tif (sinceClock > clock) {\n\t\t\t// something went wrong, wipe the slate clean\n\t\t\tsinceClock = -1\n\t\t}\n\t\tconst diff: TLSyncForwardDiff<R> = { puts: {}, deletes: [] }\n\t\tconst wipeAll = sinceClock < this.storage._getTombstoneHistoryStartsAtClock()\n\n\t\tif (wipeAll) {\n\t\t\t// If wipeAll, include all documents\n\t\t\tfor (const row of this.stmts.iterateDocumentValues.iterate()) {\n\t\t\t\tconst state = decodeState<R>(row.state)\n\t\t\t\tdiff.puts[state.id] = state\n\t\t\t}\n\t\t} else {\n\t\t\t// Get documents changed since clock\n\t\t\tfor (const row of this.stmts.getDocumentsChangedSince.iterate(sinceClock)) {\n\t\t\t\tconst state = decodeState<R>(row.state)\n\t\t\t\tdiff.puts[state.id] = state\n\t\t\t}\n\t\t\t// When wipeAll, deletes are redundant (full state is in puts). Only include tombstones otherwise.\n\t\t\tfor (const row of this.stmts.getTombstonesChangedSince.iterate(sinceClock)) {\n\t\t\t\tdiff.deletes.push(row.id)\n\t\t\t}\n\t\t}\n\n\t\treturn { diff, wipeAll }\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -63,9 +63,7 @@ class TLSocketRoom {
63
63
  snapshot: (0, import_TLSyncStorage.convertStoreSnapshotToRoomSnapshot)(
64
64
  // eslint-disable-next-line @typescript-eslint/no-deprecated
65
65
  opts.initialSnapshot ?? import_InMemorySyncStorage.DEFAULT_INITIAL_SNAPSHOT
66
- ),
67
- // keep the storage partition in step with the room's object lane
68
- objectTypes: opts.objectTypes
66
+ )
69
67
  });
70
68
  if ("onDataChange" in opts && opts.onDataChange) {
71
69
  this.disposables.add(
@@ -76,8 +74,6 @@ class TLSocketRoom {
76
74
  }
77
75
  this.room = new import_TLSyncRoom.TLSyncRoom({
78
76
  onPresenceChange: opts.onPresenceChange,
79
- onCommittedChanges: opts.onCommittedChanges,
80
- objectTypes: opts.objectTypes,
81
77
  schema: opts.schema ?? (0, import_tlschema.createTLSchema)(),
82
78
  log: opts.log,
83
79
  storage,
@@ -123,9 +119,6 @@ class TLSocketRoom {
123
119
  * - sessionId - Unique identifier for the client session (typically from browser tab)
124
120
  * - socket - WebSocket-like object for client communication
125
121
  * - isReadonly - Whether the client can modify the document (defaults to false)
126
- * - objectAccess - Write access for object-store lane record types (defaults to 'write').
127
- * Independent of isReadonly, so a session can be allowed to write objects (e.g. comments)
128
- * without being allowed to edit the document.
129
122
  * - meta - Additional session metadata (required if SessionMeta is not void)
130
123
  *
131
124
  * @example
@@ -149,7 +142,7 @@ class TLSocketRoom {
149
142
  * ```
150
143
  */
151
144
  handleSocketConnect(opts) {
152
- const { sessionId, socket, isReadonly = false, objectAccess = "write" } = opts;
145
+ const { sessionId, socket, isReadonly = false } = opts;
153
146
  const handleSocketMessage = (event) => this.handleSocketMessage(sessionId, event.data);
154
147
  const handleSocketError = this.handleSocketError.bind(this, sessionId);
155
148
  const handleSocketClose = this.handleSocketClose.bind(this, sessionId);
@@ -165,7 +158,6 @@ class TLSocketRoom {
165
158
  this.room.handleNewSession({
166
159
  sessionId,
167
160
  isReadonly,
168
- objectAccess,
169
161
  socket: new import_ServerSocketAdapter.ServerSocketAdapter({
170
162
  ws: socket,
171
163
  onBeforeSendMessage: this.opts.onBeforeSendMessage ? (message, stringified) => this.opts.onBeforeSendMessage({
@@ -346,9 +338,6 @@ class TLSocketRoom {
346
338
  this.room.handleResumedSession({
347
339
  sessionId,
348
340
  isReadonly: snapshot.isReadonly,
349
- // snapshots persisted before this field existed fail closed — those sessions predate
350
- // the record types gated by objectAccess, so they have nothing to write anyway
351
- objectAccess: snapshot.objectAccess ?? "read",
352
341
  serializedSchema: snapshot.serializedSchema,
353
342
  presenceId: snapshot.presenceId,
354
343
  presenceRecord: snapshot.presenceRecord,
@@ -398,7 +387,6 @@ class TLSocketRoom {
398
387
  return {
399
388
  serializedSchema: session.serializedSchema,
400
389
  isReadonly: session.isReadonly,
401
- objectAccess: session.objectAccess,
402
390
  presenceId: session.presenceId,
403
391
  presenceRecord,
404
392
  requiresLegacyRejection: session.requiresLegacyRejection,
@@ -473,7 +461,6 @@ class TLSocketRoom {
473
461
  sessionId: session.sessionId,
474
462
  isConnected: session.state === import_RoomSession.RoomSessionState.Connected,
475
463
  isReadonly: session.isReadonly,
476
- objectAccess: session.objectAccess,
477
464
  meta: session.meta
478
465
  };
479
466
  });
@@ -503,17 +490,6 @@ class TLSocketRoom {
503
490
  }
504
491
  throw new Error("getCurrentSnapshot is not supported for this storage type");
505
492
  }
506
- /**
507
- * Returns a snapshot of the object-store lane (see `objectTypes`), for persisting
508
- * object-lane records separately from the document. Same entry shape as
509
- * {@link RoomSnapshot.documents}.
510
- */
511
- getCurrentObjectsSnapshot() {
512
- if (this.storage.getObjectsSnapshot) {
513
- return this.storage.getObjectsSnapshot();
514
- }
515
- throw new Error("getCurrentObjectsSnapshot is not supported for this storage type");
516
- }
517
493
  /**
518
494
  * Retrieves all presence records from the document store. Presence records
519
495
  * contain ephemeral user state like cursor positions and selections.