@tldraw/sync-core 4.2.1 → 4.2.2

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 (84) hide show
  1. package/dist-cjs/index.d.ts +483 -58
  2. package/dist-cjs/index.js +13 -3
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/DurableObjectSqliteSyncWrapper.js +55 -0
  5. package/dist-cjs/lib/DurableObjectSqliteSyncWrapper.js.map +7 -0
  6. package/dist-cjs/lib/InMemorySyncStorage.js +287 -0
  7. package/dist-cjs/lib/InMemorySyncStorage.js.map +7 -0
  8. package/dist-cjs/lib/MicrotaskNotifier.js +50 -0
  9. package/dist-cjs/lib/MicrotaskNotifier.js.map +7 -0
  10. package/dist-cjs/lib/NodeSqliteWrapper.js +48 -0
  11. package/dist-cjs/lib/NodeSqliteWrapper.js.map +7 -0
  12. package/dist-cjs/lib/RoomSession.js.map +1 -1
  13. package/dist-cjs/lib/SQLiteSyncStorage.js +428 -0
  14. package/dist-cjs/lib/SQLiteSyncStorage.js.map +7 -0
  15. package/dist-cjs/lib/TLSocketRoom.js +117 -69
  16. package/dist-cjs/lib/TLSocketRoom.js.map +2 -2
  17. package/dist-cjs/lib/TLSyncClient.js +7 -0
  18. package/dist-cjs/lib/TLSyncClient.js.map +2 -2
  19. package/dist-cjs/lib/TLSyncRoom.js +357 -688
  20. package/dist-cjs/lib/TLSyncRoom.js.map +3 -3
  21. package/dist-cjs/lib/TLSyncStorage.js +76 -0
  22. package/dist-cjs/lib/TLSyncStorage.js.map +7 -0
  23. package/dist-cjs/lib/chunk.js +2 -2
  24. package/dist-cjs/lib/chunk.js.map +1 -1
  25. package/dist-cjs/lib/recordDiff.js +52 -0
  26. package/dist-cjs/lib/recordDiff.js.map +7 -0
  27. package/dist-esm/index.d.mts +483 -58
  28. package/dist-esm/index.mjs +20 -5
  29. package/dist-esm/index.mjs.map +2 -2
  30. package/dist-esm/lib/DurableObjectSqliteSyncWrapper.mjs +35 -0
  31. package/dist-esm/lib/DurableObjectSqliteSyncWrapper.mjs.map +7 -0
  32. package/dist-esm/lib/InMemorySyncStorage.mjs +272 -0
  33. package/dist-esm/lib/InMemorySyncStorage.mjs.map +7 -0
  34. package/dist-esm/lib/MicrotaskNotifier.mjs +30 -0
  35. package/dist-esm/lib/MicrotaskNotifier.mjs.map +7 -0
  36. package/dist-esm/lib/NodeSqliteWrapper.mjs +28 -0
  37. package/dist-esm/lib/NodeSqliteWrapper.mjs.map +7 -0
  38. package/dist-esm/lib/RoomSession.mjs.map +1 -1
  39. package/dist-esm/lib/SQLiteSyncStorage.mjs +414 -0
  40. package/dist-esm/lib/SQLiteSyncStorage.mjs.map +7 -0
  41. package/dist-esm/lib/TLSocketRoom.mjs +121 -70
  42. package/dist-esm/lib/TLSocketRoom.mjs.map +2 -2
  43. package/dist-esm/lib/TLSyncClient.mjs +7 -0
  44. package/dist-esm/lib/TLSyncClient.mjs.map +2 -2
  45. package/dist-esm/lib/TLSyncRoom.mjs +370 -702
  46. package/dist-esm/lib/TLSyncRoom.mjs.map +3 -3
  47. package/dist-esm/lib/TLSyncStorage.mjs +56 -0
  48. package/dist-esm/lib/TLSyncStorage.mjs.map +7 -0
  49. package/dist-esm/lib/chunk.mjs +2 -2
  50. package/dist-esm/lib/chunk.mjs.map +1 -1
  51. package/dist-esm/lib/recordDiff.mjs +32 -0
  52. package/dist-esm/lib/recordDiff.mjs.map +7 -0
  53. package/package.json +12 -11
  54. package/src/index.ts +32 -3
  55. package/src/lib/ClientWebSocketAdapter.test.ts +3 -0
  56. package/src/lib/DurableObjectSqliteSyncWrapper.ts +95 -0
  57. package/src/lib/InMemorySyncStorage.ts +387 -0
  58. package/src/lib/MicrotaskNotifier.test.ts +429 -0
  59. package/src/lib/MicrotaskNotifier.ts +38 -0
  60. package/src/lib/NodeSqliteSyncWrapper.integration.test.ts +270 -0
  61. package/src/lib/NodeSqliteSyncWrapper.test.ts +272 -0
  62. package/src/lib/NodeSqliteWrapper.ts +99 -0
  63. package/src/lib/RoomSession.test.ts +1 -0
  64. package/src/lib/RoomSession.ts +2 -0
  65. package/src/lib/SQLiteSyncStorage.ts +627 -0
  66. package/src/lib/TLSocketRoom.ts +228 -114
  67. package/src/lib/TLSyncClient.ts +12 -0
  68. package/src/lib/TLSyncRoom.ts +473 -913
  69. package/src/lib/TLSyncStorage.ts +216 -0
  70. package/src/lib/chunk.ts +2 -2
  71. package/src/lib/computeTombstonePruning.test.ts +352 -0
  72. package/src/lib/recordDiff.ts +73 -0
  73. package/src/test/FuzzEditor.ts +4 -5
  74. package/src/test/InMemorySyncStorage.test.ts +1684 -0
  75. package/src/test/SQLiteSyncStorage.test.ts +1378 -0
  76. package/src/test/TLSocketRoom.test.ts +255 -49
  77. package/src/test/TLSyncRoom.test.ts +1024 -534
  78. package/src/test/TestServer.ts +12 -1
  79. package/src/test/customMessages.test.ts +1 -1
  80. package/src/test/presenceMode.test.ts +6 -6
  81. package/src/test/syncFuzz.test.ts +2 -4
  82. package/src/test/upgradeDowngrade.test.ts +290 -8
  83. package/src/test/validation.test.ts +15 -10
  84. package/src/test/pruneTombstones.test.ts +0 -178
@@ -1,15 +1,21 @@
1
1
  import { Atom } from '@tldraw/state';
2
2
  import { AtomMap } from '@tldraw/store';
3
+ import { DebouncedFunc } from 'lodash';
3
4
  import { Emitter } from 'nanoevents';
4
5
  import { RecordsDiff } from '@tldraw/store';
5
6
  import { RecordType } from '@tldraw/store';
6
- import { Result } from '@tldraw/utils';
7
7
  import { SerializedSchema } from '@tldraw/store';
8
+ import { SerializedSchemaV2 } from '@tldraw/store';
8
9
  import { Signal } from '@tldraw/state';
9
10
  import { Store } from '@tldraw/store';
10
11
  import { StoreSchema } from '@tldraw/store';
12
+ import { StoreSnapshot } from '@tldraw/store';
13
+ import { SynchronousStorage } from '@tldraw/store';
14
+ import { TLDocument } from '@tldraw/tlschema';
15
+ import { TLPage } from '@tldraw/tlschema';
11
16
  import { TLRecord } from '@tldraw/tlschema';
12
17
  import { TLStoreSnapshot } from '@tldraw/tlschema';
18
+ import { TLStoreSnapshot as TLStoreSnapshot_2 } from 'tldraw';
13
19
  import { UnknownRecord } from '@tldraw/store';
14
20
 
15
21
  /* Excluded from this release type: AppendOp */
@@ -20,16 +26,103 @@ import { UnknownRecord } from '@tldraw/store';
20
26
 
21
27
  /* Excluded from this release type: ClientWebSocketAdapter */
22
28
 
29
+ /**
30
+ * Default initial snapshot for a new room.
31
+ * @public
32
+ */
33
+ export declare const DEFAULT_INITIAL_SNAPSHOT: {
34
+ documentClock: number;
35
+ documents: ({
36
+ lastChangedClock: number;
37
+ state: TLDocument;
38
+ } | {
39
+ lastChangedClock: number;
40
+ state: TLPage;
41
+ })[];
42
+ schema: SerializedSchemaV2;
43
+ tombstoneHistoryStartsAtClock: number;
44
+ };
45
+
23
46
  /* Excluded from this release type: DeleteOp */
24
47
 
25
48
  /* Excluded from this release type: diffRecord */
26
49
 
27
- /* Excluded from this release type: DocumentState */
50
+ /**
51
+ * A wrapper around Cloudflare Durable Object's SqlStorage that implements TLSyncSqliteWrapper.
52
+ *
53
+ * Use this wrapper with SQLiteSyncStorage to persist tldraw sync state using
54
+ * Cloudflare Durable Object's built-in SQLite storage. This provides automatic
55
+ * persistence that survives Durable Object hibernation and restarts.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { SQLiteSyncStorage, DurableObjectSqliteSyncWrapper } from '@tldraw/sync-core'
60
+ *
61
+ * // In your Durable Object class:
62
+ * class MyDurableObject extends DurableObject {
63
+ * private storage: SQLiteSyncStorage
64
+ *
65
+ * constructor(ctx: DurableObjectState, env: Env) {
66
+ * super(ctx, env)
67
+ * const sql = new DurableObjectSqliteSyncWrapper(ctx.storage)
68
+ * this.storage = new SQLiteSyncStorage({ sql })
69
+ * }
70
+ * }
71
+ * ```
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * // With table prefix to avoid conflicts with other tables
76
+ * const sql = new DurableObjectSqliteSyncWrapper(this.ctx.storage, { tablePrefix: 'tldraw_' })
77
+ * // Creates tables: tldraw_documents, tldraw_tombstones, tldraw_metadata
78
+ * ```
79
+ *
80
+ * @public
81
+ */
82
+ export declare class DurableObjectSqliteSyncWrapper implements TLSyncSqliteWrapper {
83
+ private storage;
84
+ config?: TLSyncSqliteWrapperConfig | undefined;
85
+ constructor(storage: {
86
+ sql: {
87
+ exec(sql: string, ...bindings: unknown[]): Iterable<any> & {
88
+ toArray(): any[];
89
+ };
90
+ };
91
+ transactionSync(callback: () => any): any;
92
+ }, config?: TLSyncSqliteWrapperConfig | undefined);
93
+ exec(sql: string): void;
94
+ prepare<TResult extends TLSqliteRow | void = void, TParams extends TLSqliteInputValue[] = []>(sql: string): TLSyncSqliteStatement<TResult, TParams>;
95
+ transaction<T>(callback: () => T): T;
96
+ }
28
97
 
29
98
  /* Excluded from this release type: getNetworkDiff */
30
99
 
31
100
  /* Excluded from this release type: getTlsyncProtocolVersion */
32
101
 
102
+ /**
103
+ * In-memory implementation of TLSyncStorage using AtomMap for documents and tombstones,
104
+ * and atoms for clock values. This is the default storage implementation used by TLSyncRoom.
105
+ *
106
+ * @public
107
+ */
108
+ export declare class InMemorySyncStorage<R extends UnknownRecord> implements TLSyncStorage<R> {
109
+ /* Excluded from this release type: documents */
110
+ /* Excluded from this release type: tombstones */
111
+ /* Excluded from this release type: schema */
112
+ /* Excluded from this release type: documentClock */
113
+ /* Excluded from this release type: tombstoneHistoryStartsAtClock */
114
+ private notifier;
115
+ onChange(callback: (arg: TLSyncStorageOnChangeCallbackProps) => unknown): () => void;
116
+ constructor({ snapshot, onChange, }?: {
117
+ onChange?(arg: TLSyncStorageOnChangeCallbackProps): unknown;
118
+ snapshot?: RoomSnapshot;
119
+ });
120
+ transaction<T>(callback: TLSyncStorageTransactionCallback<R, T>, opts?: TLSyncStorageTransactionOptions): TLSyncStorageTransactionResult<T, R>;
121
+ getClock(): number;
122
+ /* Excluded from this release type: pruneTombstones */
123
+ getSnapshot(): RoomSnapshot;
124
+ }
125
+
33
126
  /**
34
127
  * Assembles chunked JSON messages back into complete objects.
35
128
  * Handles both regular JSON messages and chunked messages created by the chunk() function.
@@ -66,8 +159,8 @@ export declare class JsonChunkAssembler {
66
159
  *
67
160
  * @param msg - The message to process, either JSON or chunk format
68
161
  * @returns Result object with data/stringified on success, error object on failure, or null for incomplete chunks
69
- * - `\{ data: object, stringified: string \}` - Successfully parsed complete message
70
- * - `\{ error: Error \}` - Parse error or invalid chunk sequence
162
+ * - `\{ data: object, stringified: string \}` - Successfully parsed complete message
163
+ * - `\{ error: Error \}` - Parse error or invalid chunk sequence
71
164
  * - `null` - Chunk received but more chunks expected
72
165
  *
73
166
  * @example
@@ -94,8 +187,68 @@ export declare class JsonChunkAssembler {
94
187
  } | null;
95
188
  }
96
189
 
190
+ /**
191
+ * Loads a snapshot into storage during a transaction.
192
+ * Migrates the snapshot to the current schema and loads it into storage.
193
+ *
194
+ * @public
195
+ * @param txn - The transaction to load the snapshot into
196
+ * @param schema - The current schema
197
+ * @param snapshot - The snapshot to load
198
+ */
199
+ export declare function loadSnapshotIntoStorage<R extends UnknownRecord>(txn: TLSyncStorageTransaction<R>, schema: StoreSchema<R, any>, snapshot: RoomSnapshot | TLStoreSnapshot_2): void;
200
+
201
+ /* Excluded from this release type: MinimalDocStore */
202
+
97
203
  /* Excluded from this release type: NetworkDiff */
98
204
 
205
+ /**
206
+ * A wrapper around synchronous SQLite databases that implements TLSyncSqliteWrapper.
207
+ * Works with both `node:sqlite` DatabaseSync (Node.js 22.5+) and `better-sqlite3` Database.
208
+ *
209
+ * Use this wrapper with SQLiteSyncStorage to persist tldraw sync state to a SQLite database
210
+ * in Node.js environments.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * // With node:sqlite (Node.js 22.5+)
215
+ * import { DatabaseSync } from 'node:sqlite'
216
+ * import { SQLiteSyncStorage, NodeSqliteWrapper } from '@tldraw/sync-core'
217
+ *
218
+ * const db = new DatabaseSync(':memory:')
219
+ * const sql = new NodeSqliteWrapper(db)
220
+ * const storage = new SQLiteSyncStorage({ sql })
221
+ * ```
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * // With better-sqlite3
226
+ * import Database from 'better-sqlite3'
227
+ * import { SQLiteSyncStorage, NodeSqliteWrapper } from '@tldraw/sync-core'
228
+ *
229
+ * const db = new Database(':memory:')
230
+ * const sql = new NodeSqliteWrapper(db)
231
+ * const storage = new SQLiteSyncStorage({ sql })
232
+ * ```
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * // With table prefix to avoid conflicts with other tables
237
+ * const sql = new NodeSqliteWrapper(db, { tablePrefix: 'tldraw_' })
238
+ * // Creates tables: tldraw_documents, tldraw_tombstones, tldraw_metadata
239
+ * ```
240
+ *
241
+ * @public
242
+ */
243
+ export declare class NodeSqliteWrapper implements TLSyncSqliteWrapper {
244
+ private db;
245
+ config?: TLSyncSqliteWrapperConfig | undefined;
246
+ constructor(db: SyncSqliteDatabase, config?: TLSyncSqliteWrapperConfig | undefined);
247
+ exec(sql: string): void;
248
+ prepare<TResult extends TLSqliteRow | void = void, TParams extends TLSqliteInputValue[] = TLSqliteInputValue[]>(sql: string): TLSyncSqliteStatement<TResult, TParams>;
249
+ transaction<T>(callback: () => T): T;
250
+ }
251
+
99
252
  /* Excluded from this release type: ObjectDiff */
100
253
 
101
254
  /**
@@ -119,6 +272,8 @@ export declare type OmitVoid<T, KS extends keyof T = keyof T> = {
119
272
 
120
273
  /* Excluded from this release type: PersistedRoomSnapshotForSupabase */
121
274
 
275
+ /* Excluded from this release type: PresenceStore */
276
+
122
277
  /* Excluded from this release type: PutOp */
123
278
 
124
279
  /* Excluded from this release type: ReconnectManager */
@@ -143,7 +298,7 @@ export declare interface RoomSnapshot {
143
298
  /**
144
299
  * The current logical clock value for the room
145
300
  */
146
- clock: number;
301
+ clock?: number;
147
302
  /**
148
303
  * Clock value when document data was last changed (optional for backwards compatibility)
149
304
  */
@@ -185,6 +340,7 @@ export declare interface RoomSnapshot {
185
340
  * ```
186
341
  *
187
342
  * @public
343
+ * @deprecated use the storage.transaction method instead
188
344
  */
189
345
  export declare interface RoomStoreMethods<R extends UnknownRecord = UnknownRecord> {
190
346
  /**
@@ -214,6 +370,73 @@ export declare interface RoomStoreMethods<R extends UnknownRecord = UnknownRecor
214
370
  getAll(): R[];
215
371
  }
216
372
 
373
+ /**
374
+ * SQLite-based implementation of TLSyncStorage.
375
+ * Stores documents, tombstones, metadata, and clock values in SQLite tables.
376
+ *
377
+ * This storage backend provides persistent synchronization state that survives
378
+ * process restarts, unlike InMemorySyncStorage which loses data when the process ends.
379
+ *
380
+ * @example
381
+ * ```ts
382
+ * // With Cloudflare Durable Objects
383
+ * import { SQLiteSyncStorage, DurableObjectSqliteSyncWrapper } from '@tldraw/sync-core'
384
+ *
385
+ * const sql = new DurableObjectSqliteSyncWrapper(this.ctx.storage)
386
+ * const storage = new SQLiteSyncStorage({ sql })
387
+ * ```
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * // With Node.js sqlite (Node 22.5+)
392
+ * import { DatabaseSync } from 'node:sqlite'
393
+ * import { SQLiteSyncStorage, NodeSqliteWrapper } from '@tldraw/sync-core'
394
+ *
395
+ * const db = new DatabaseSync('sync-state.db')
396
+ * const sql = new NodeSqliteWrapper(db)
397
+ * const storage = new SQLiteSyncStorage({ sql })
398
+ * ```
399
+ *
400
+ * @example
401
+ * ```ts
402
+ * // Initialize with an existing snapshot
403
+ * const storage = new SQLiteSyncStorage({ sql, snapshot: existingSnapshot })
404
+ * ```
405
+ *
406
+ * @public
407
+ */
408
+ export declare class SQLiteSyncStorage<R extends UnknownRecord> implements TLSyncStorage<R> {
409
+ /**
410
+ * Check if the storage has been initialized (has data in the clock table).
411
+ * Useful for determining whether to load from an external source on first access.
412
+ */
413
+ static hasBeenInitialized(storage: TLSyncSqliteWrapper): boolean;
414
+ /**
415
+ * Get the current document clock value from storage without fully initializing.
416
+ * Returns null if storage has not been initialized.
417
+ * Useful for comparing storage freshness against external sources.
418
+ */
419
+ static getDocumentClock(storage: TLSyncSqliteWrapper): null | number;
420
+ private readonly stmts;
421
+ private readonly sql;
422
+ constructor({ sql, snapshot, onChange, }: {
423
+ onChange?(arg: TLSyncStorageOnChangeCallbackProps): unknown;
424
+ snapshot?: RoomSnapshot | StoreSnapshot<R>;
425
+ sql: TLSyncSqliteWrapper;
426
+ });
427
+ private notifier;
428
+ onChange(callback: (arg: TLSyncStorageOnChangeCallbackProps) => void): () => void;
429
+ transaction<T>(callback: TLSyncStorageTransactionCallback<R, T>, opts?: TLSyncStorageTransactionOptions): TLSyncStorageTransactionResult<T, R>;
430
+ getClock(): number;
431
+ /* Excluded from this release type: _getTombstoneHistoryStartsAtClock */
432
+ /* Excluded from this release type: _getSchema */
433
+ /* Excluded from this release type: _setSchema */
434
+ /* Excluded from this release type: pruneTombstones */
435
+ getSnapshot(): RoomSnapshot;
436
+ private _iterateDocuments;
437
+ private _iterateTombstones;
438
+ }
439
+
217
440
  /**
218
441
  * Function type for subscribing to events with a callback.
219
442
  * Returns an unsubscribe function to clean up the listener.
@@ -225,6 +448,29 @@ export declare interface RoomStoreMethods<R extends UnknownRecord = UnknownRecor
225
448
  */
226
449
  export declare type SubscribingFn<T> = (cb: (val: T) => void) => () => void;
227
450
 
451
+ /**
452
+ * Minimal interface for a synchronous SQLite database.
453
+ *
454
+ * This interface is compatible with:
455
+ * - `node:sqlite` DatabaseSync (Node.js 22.5+)
456
+ * - `better-sqlite3` Database
457
+ *
458
+ * Any SQLite library that provides synchronous `exec` and `prepare` methods
459
+ * with the signatures below can be used with {@link NodeSqliteWrapper}.
460
+ *
461
+ * @public
462
+ */
463
+ export declare interface SyncSqliteDatabase {
464
+ /** Execute raw SQL without returning results */
465
+ exec(sql: string): void;
466
+ /** Prepare a statement for execution */
467
+ prepare(sql: string): {
468
+ all(...params: unknown[]): unknown[];
469
+ iterate(...params: unknown[]): IterableIterator<unknown>;
470
+ run(...params: unknown[]): unknown;
471
+ };
472
+ }
473
+
228
474
  /* Excluded from this release type: TLConnectRequest */
229
475
 
230
476
  /**
@@ -453,35 +699,12 @@ export declare class TLRemoteSyncError extends Error {
453
699
  * @public
454
700
  */
455
701
  export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta = void> {
456
- readonly opts: {
457
- /* Excluded from this release type: onPresenceChange */
458
- clientTimeout?: number;
459
- initialSnapshot?: RoomSnapshot | TLStoreSnapshot;
460
- log?: TLSyncLog;
461
- onAfterReceiveMessage?: (args: {
462
- /* Excluded from this release type: message */
463
- meta: SessionMeta;
464
- sessionId: string;
465
- stringified: string;
466
- }) => void;
467
- onBeforeSendMessage?: (args: {
468
- /* Excluded from this release type: message */
469
- meta: SessionMeta;
470
- sessionId: string;
471
- stringified: string;
472
- }) => void;
473
- onDataChange?(): void;
474
- onSessionRemoved?: (room: TLSocketRoom<R, SessionMeta>, args: {
475
- meta: SessionMeta;
476
- numSessionsRemaining: number;
477
- sessionId: string;
478
- }) => void;
479
- schema?: StoreSchema<R, any>;
480
- };
702
+ readonly opts: TLSocketRoomOptions<R, SessionMeta>;
481
703
  private room;
482
704
  private readonly sessions;
483
705
  readonly log?: TLSyncLog;
484
- private readonly syncCallbacks;
706
+ storage: TLSyncStorage<R>;
707
+ private disposables;
485
708
  /**
486
709
  * Creates a new TLSocketRoom instance for managing collaborative document synchronization.
487
710
  *
@@ -496,31 +719,7 @@ export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, Sessi
496
719
  * - onDataChange - Called when document data changes
497
720
  * - onPresenceChange - Called when presence data changes
498
721
  */
499
- constructor(opts: {
500
- /* Excluded from this release type: onPresenceChange */
501
- clientTimeout?: number;
502
- initialSnapshot?: RoomSnapshot | TLStoreSnapshot;
503
- log?: TLSyncLog;
504
- onAfterReceiveMessage?: (args: {
505
- /* Excluded from this release type: message */
506
- meta: SessionMeta;
507
- sessionId: string;
508
- stringified: string;
509
- }) => void;
510
- onBeforeSendMessage?: (args: {
511
- /* Excluded from this release type: message */
512
- meta: SessionMeta;
513
- sessionId: string;
514
- stringified: string;
515
- }) => void;
516
- onDataChange?(): void;
517
- onSessionRemoved?: (room: TLSocketRoom<R, SessionMeta>, args: {
518
- meta: SessionMeta;
519
- numSessionsRemaining: number;
520
- sessionId: string;
521
- }) => void;
522
- schema?: StoreSchema<R, any>;
523
- });
722
+ constructor(opts: TLSocketRoomOptions<R, SessionMeta>);
524
723
  /**
525
724
  * Returns the number of active sessions.
526
725
  * Note that this is not the same as the number of connected sockets!
@@ -658,7 +857,7 @@ export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, Sessi
658
857
  * }
659
858
  * ```
660
859
  */
661
- getRecord(id: string): R | undefined;
860
+ getRecord(id: string): R;
662
861
  /**
663
862
  * Returns information about all active sessions in the room. Each session
664
863
  * represents a connected client with their current connection status and metadata.
@@ -694,6 +893,7 @@ export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, Sessi
694
893
  * to restore the room state later or revert to a previous version.
695
894
  *
696
895
  * @returns Complete room snapshot including documents, clock values, and tombstones
896
+ * @deprecated if you need to do this use
697
897
  *
698
898
  * @example
699
899
  * ```ts
@@ -708,7 +908,6 @@ export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, Sessi
708
908
  */
709
909
  getCurrentSnapshot(): RoomSnapshot;
710
910
  /* Excluded from this release type: getPresenceRecords */
711
- /* Excluded from this release type: getCurrentSerializedSnapshot */
712
911
  /**
713
912
  * Loads a document snapshot, completely replacing the current room state.
714
913
  * This will disconnect all current clients and update the document to match
@@ -770,6 +969,7 @@ export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, Sessi
770
969
  * }
771
970
  * })
772
971
  * ```
972
+ * @deprecated use the storage.transaction method instead
773
973
  */
774
974
  updateStore(updater: (store: RoomStoreMethods<R>) => Promise<void> | void): Promise<void>;
775
975
  /**
@@ -859,6 +1059,43 @@ export declare class TLSocketRoom<R extends UnknownRecord = UnknownRecord, Sessi
859
1059
  isClosed(): boolean;
860
1060
  }
861
1061
 
1062
+ /**
1063
+ * Base options for TLSocketRoom.
1064
+ * @public
1065
+ */
1066
+ export declare interface TLSocketRoomOptions<R extends UnknownRecord, SessionMeta> {
1067
+ storage?: TLSyncStorage<R>;
1068
+ /**
1069
+ * @deprecated use the storage option instead
1070
+ */
1071
+ initialSnapshot?: RoomSnapshot | TLStoreSnapshot;
1072
+ /**
1073
+ * @deprecated use the storage option with an onChange callback instead
1074
+ */
1075
+ onDataChange?(): void;
1076
+ schema?: StoreSchema<R, any>;
1077
+ clientTimeout?: number;
1078
+ log?: TLSyncLog;
1079
+ onSessionRemoved?: (room: TLSocketRoom<R, SessionMeta>, args: {
1080
+ meta: SessionMeta;
1081
+ numSessionsRemaining: number;
1082
+ sessionId: string;
1083
+ }) => void;
1084
+ onBeforeSendMessage?: (args: {
1085
+ /* Excluded from this release type: message */
1086
+ meta: SessionMeta;
1087
+ sessionId: string;
1088
+ stringified: string;
1089
+ }) => void;
1090
+ onAfterReceiveMessage?: (args: {
1091
+ /* Excluded from this release type: message */
1092
+ meta: SessionMeta;
1093
+ sessionId: string;
1094
+ stringified: string;
1095
+ }) => void;
1096
+ /* Excluded from this release type: onPresenceChange */
1097
+ }
1098
+
862
1099
  /* Excluded from this release type: TLSocketServerSentDataEvent */
863
1100
 
864
1101
  /* Excluded from this release type: TLSocketServerSentEvent */
@@ -881,6 +1118,26 @@ export declare type TLSocketStatusChangeEvent = {
881
1118
 
882
1119
  /* Excluded from this release type: TLSocketStatusListener */
883
1120
 
1121
+ /**
1122
+ * Valid input value types for SQLite query parameters.
1123
+ * These are the types that can be passed as bindings to prepared statements.
1124
+ * @public
1125
+ */
1126
+ export declare type TLSqliteInputValue = bigint | null | number | string | Uint8Array;
1127
+
1128
+ /**
1129
+ * Possible output value types returned from SQLite queries.
1130
+ * Includes all input types plus Uint8Array for BLOB columns.
1131
+ * @public
1132
+ */
1133
+ export declare type TLSqliteOutputValue = bigint | null | number | string | Uint8Array;
1134
+
1135
+ /**
1136
+ * A row returned from a SQLite query, mapping column names to their values.
1137
+ * @public
1138
+ */
1139
+ export declare type TLSqliteRow = Record<string, TLSqliteOutputValue>;
1140
+
884
1141
  /**
885
1142
  * Main client-side synchronization engine for collaborative tldraw applications.
886
1143
  *
@@ -1144,6 +1401,15 @@ export declare const TLSyncErrorCloseEventReason: {
1144
1401
  */
1145
1402
  export declare type TLSyncErrorCloseEventReason = (typeof TLSyncErrorCloseEventReason)[keyof typeof TLSyncErrorCloseEventReason];
1146
1403
 
1404
+ /**
1405
+ * Respresents a diff of puts and deletes.
1406
+ * @public
1407
+ */
1408
+ export declare interface TLSyncForwardDiff<R extends UnknownRecord> {
1409
+ puts: Record<string, [before: R, after: R] | R>;
1410
+ deletes: string[];
1411
+ }
1412
+
1147
1413
  /**
1148
1414
  * Logging interface for TLSocketRoom operations. Provides optional methods
1149
1415
  * for warning and error logging during synchronization operations.
@@ -1175,6 +1441,165 @@ export declare interface TLSyncLog {
1175
1441
 
1176
1442
  /* Excluded from this release type: TLSyncRoom */
1177
1443
 
1444
+ /**
1445
+ * A prepared statement that can be executed multiple times with different bindings.
1446
+ * @public
1447
+ */
1448
+ export declare interface TLSyncSqliteStatement<TResult extends TLSqliteRow | void, TParams extends TLSqliteInputValue[] = []> {
1449
+ /** Execute the statement and iterate over results one at a time */
1450
+ iterate(...bindings: TParams): IterableIterator<TResult>;
1451
+ /** Execute the statement and return all results as an array */
1452
+ all(...bindings: TParams): TResult[];
1453
+ /** Execute the statement without returning results (for DML) */
1454
+ run(...bindings: TParams): void;
1455
+ }
1456
+
1457
+ /**
1458
+ * Interface for SQLite storage with prepare, exec and transaction capabilities.
1459
+ * @public
1460
+ */
1461
+ export declare interface TLSyncSqliteWrapper {
1462
+ /** Optional configuration for table names. If not provided, defaults are used. */
1463
+ readonly config?: TLSyncSqliteWrapperConfig;
1464
+ /** Prepare a SQL statement for execution */
1465
+ prepare<TResult extends TLSqliteRow | void, TParams extends TLSqliteInputValue[] = []>(sql: string): TLSyncSqliteStatement<TResult, TParams>;
1466
+ /** Execute raw SQL (for DDL, multi-statement scripts) */
1467
+ exec(sql: string): void;
1468
+ /** Execute a callback within a transaction */
1469
+ transaction<T>(callback: () => T): T;
1470
+ }
1471
+
1472
+ /**
1473
+ * Configuration for SQLiteSyncStorage.
1474
+ * @public
1475
+ */
1476
+ export declare interface TLSyncSqliteWrapperConfig {
1477
+ /** Prefix for all table names (default: ''). E.g. 'sync_' creates tables 'sync_documents', 'sync_tombstones', 'sync_metadata' */
1478
+ tablePrefix?: string;
1479
+ }
1480
+
1481
+ /**
1482
+ * Pluggable synchronous transactional storage layer for TLSyncRoom.
1483
+ * Provides methods for managing documents, tombstones, and clocks within transactions.
1484
+ *
1485
+ * @public
1486
+ */
1487
+ export declare interface TLSyncStorage<R extends UnknownRecord> {
1488
+ transaction<T>(callback: TLSyncStorageTransactionCallback<R, T>, opts?: TLSyncStorageTransactionOptions): TLSyncStorageTransactionResult<T, R>;
1489
+ getClock(): number;
1490
+ onChange(callback: (arg: TLSyncStorageOnChangeCallbackProps) => unknown): () => void;
1491
+ getSnapshot?(): RoomSnapshot;
1492
+ }
1493
+
1494
+ /**
1495
+ * Result returned from getChangesSince, containing all changes since a given clock time.
1496
+ * @public
1497
+ */
1498
+ export declare interface TLSyncStorageGetChangesSinceResult<R extends UnknownRecord> {
1499
+ /**
1500
+ * The changes as a TLSyncForwardDiff.
1501
+ */
1502
+ diff: TLSyncForwardDiff<R>;
1503
+ /**
1504
+ * If true, the client should wipe all local data and replace with the server's state.
1505
+ * This happens when the client's clock is too old and we've lost tombstone history.
1506
+ */
1507
+ wipeAll: boolean;
1508
+ }
1509
+
1510
+ /**
1511
+ * Properties passed to the onChange callback.
1512
+ * @public
1513
+ */
1514
+ export declare interface TLSyncStorageOnChangeCallbackProps {
1515
+ /**
1516
+ * The ID of the transaction that caused the change.
1517
+ * This is useful for ignoring certain changes in onChange callbacks.
1518
+ */
1519
+ id?: string;
1520
+ documentClock: number;
1521
+ }
1522
+
1523
+ /**
1524
+ * Transaction interface for storage operations. Provides methods to read and modify
1525
+ * documents, tombstones, and metadata within a transaction.
1526
+ *
1527
+ * @public
1528
+ */
1529
+ export declare interface TLSyncStorageTransaction<R extends UnknownRecord> extends SynchronousStorage<R> {
1530
+ /**
1531
+ * Get the current clock value.
1532
+ * If the clock has incremented during the transaction,
1533
+ * the incremented value will be returned.
1534
+ *
1535
+ * @returns The current clock value
1536
+ */
1537
+ getClock(): number;
1538
+ /**
1539
+ * Get all changes (document updates and deletions) since a given clock time.
1540
+ * This is the main method for calculating diffs for client sync.
1541
+ *
1542
+ * @param sinceClock - The clock time to get changes since
1543
+ * @returns Changes since the specified clock time
1544
+ */
1545
+ getChangesSince(sinceClock: number): TLSyncStorageGetChangesSinceResult<R> | undefined;
1546
+ }
1547
+
1548
+ /**
1549
+ * Callback type for a transaction.
1550
+ * The conditional return type ensures that the callback is synchronous.
1551
+ * @public
1552
+ */
1553
+ export declare type TLSyncStorageTransactionCallback<R extends UnknownRecord, T> = (txn: TLSyncStorageTransaction<R>) => T extends Promise<any> ? {
1554
+ __error: 'Transaction callbacks cannot be async. Use synchronous operations only.';
1555
+ } : T;
1556
+
1557
+ /**
1558
+ * Options for a transaction.
1559
+ * @public
1560
+ */
1561
+ export declare interface TLSyncStorageTransactionOptions {
1562
+ /**
1563
+ * Use this if you need to identify the transaction for logging or debugging purposes
1564
+ * or for ignoring certain changes in onChange callbacks
1565
+ */
1566
+ id?: string;
1567
+ /**
1568
+ * Controls when the storage layer should emit the actual changes that occurred during the transaction.
1569
+ *
1570
+ * - `'always'` - Always emit the changes, regardless of whether they were applied verbatim
1571
+ * - `'when-different'` - Only emit changes if the storage layer modified/embellished the records
1572
+ * (e.g., added server timestamps, normalized data, etc.)
1573
+ *
1574
+ * When changes are emitted, they will be available in the `changes` field of the transaction result.
1575
+ * This is useful when the storage layer may transform records and the caller needs to know
1576
+ * what actually changed rather than what was requested.
1577
+ */
1578
+ emitChanges?: 'always' | 'when-different';
1579
+ }
1580
+
1581
+ /**
1582
+ * Result returned from a storage transaction.
1583
+ * @public
1584
+ */
1585
+ export declare interface TLSyncStorageTransactionResult<T, R extends UnknownRecord = UnknownRecord> {
1586
+ documentClock: number;
1587
+ didChange: boolean;
1588
+ result: T;
1589
+ /**
1590
+ * The actual changes that occurred during the transaction, if requested via `emitChanges` option.
1591
+ * This is a RecordsDiff where:
1592
+ * - `added` contains records that were put (we don't have "from" state for emitted changes)
1593
+ * - `removed` contains records that were deleted (with placeholder values since we only have IDs)
1594
+ * - `updated` is empty (emitted changes don't track before/after pairs)
1595
+ *
1596
+ * Only populated when:
1597
+ * - `emitChanges: 'always'` was specified, or
1598
+ * - `emitChanges: 'when-different'` was specified and the storage layer modified records
1599
+ */
1600
+ changes?: TLSyncForwardDiff<R>;
1601
+ }
1602
+
1178
1603
  /* Excluded from this release type: ValueOp */
1179
1604
 
1180
1605
  /* Excluded from this release type: ValueOpType */