@tldraw/sync-core 4.2.2 → 4.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +58 -483
- package/dist-cjs/index.js +3 -13
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/RoomSession.js.map +1 -1
- package/dist-cjs/lib/TLSocketRoom.js +69 -117
- package/dist-cjs/lib/TLSocketRoom.js.map +2 -2
- package/dist-cjs/lib/TLSyncClient.js +0 -7
- package/dist-cjs/lib/TLSyncClient.js.map +2 -2
- package/dist-cjs/lib/TLSyncRoom.js +688 -357
- package/dist-cjs/lib/TLSyncRoom.js.map +3 -3
- package/dist-cjs/lib/chunk.js +2 -2
- package/dist-cjs/lib/chunk.js.map +1 -1
- package/dist-esm/index.d.mts +58 -483
- package/dist-esm/index.mjs +5 -20
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/RoomSession.mjs.map +1 -1
- package/dist-esm/lib/TLSocketRoom.mjs +70 -121
- package/dist-esm/lib/TLSocketRoom.mjs.map +2 -2
- package/dist-esm/lib/TLSyncClient.mjs +0 -7
- package/dist-esm/lib/TLSyncClient.mjs.map +2 -2
- package/dist-esm/lib/TLSyncRoom.mjs +702 -370
- package/dist-esm/lib/TLSyncRoom.mjs.map +3 -3
- package/dist-esm/lib/chunk.mjs +2 -2
- package/dist-esm/lib/chunk.mjs.map +1 -1
- package/package.json +11 -12
- package/src/index.ts +3 -32
- package/src/lib/ClientWebSocketAdapter.test.ts +0 -3
- package/src/lib/RoomSession.test.ts +0 -1
- package/src/lib/RoomSession.ts +0 -2
- package/src/lib/TLSocketRoom.ts +114 -228
- package/src/lib/TLSyncClient.ts +0 -12
- package/src/lib/TLSyncRoom.ts +913 -473
- package/src/lib/chunk.ts +2 -2
- package/src/test/FuzzEditor.ts +5 -4
- package/src/test/TLSocketRoom.test.ts +49 -255
- package/src/test/TLSyncRoom.test.ts +534 -1024
- package/src/test/TestServer.ts +1 -12
- package/src/test/customMessages.test.ts +1 -1
- package/src/test/presenceMode.test.ts +6 -6
- package/src/test/pruneTombstones.test.ts +178 -0
- package/src/test/syncFuzz.test.ts +4 -2
- package/src/test/upgradeDowngrade.test.ts +8 -290
- package/src/test/validation.test.ts +10 -15
- package/dist-cjs/lib/DurableObjectSqliteSyncWrapper.js +0 -55
- package/dist-cjs/lib/DurableObjectSqliteSyncWrapper.js.map +0 -7
- package/dist-cjs/lib/InMemorySyncStorage.js +0 -287
- package/dist-cjs/lib/InMemorySyncStorage.js.map +0 -7
- package/dist-cjs/lib/MicrotaskNotifier.js +0 -50
- package/dist-cjs/lib/MicrotaskNotifier.js.map +0 -7
- package/dist-cjs/lib/NodeSqliteWrapper.js +0 -48
- package/dist-cjs/lib/NodeSqliteWrapper.js.map +0 -7
- package/dist-cjs/lib/SQLiteSyncStorage.js +0 -428
- package/dist-cjs/lib/SQLiteSyncStorage.js.map +0 -7
- package/dist-cjs/lib/TLSyncStorage.js +0 -76
- package/dist-cjs/lib/TLSyncStorage.js.map +0 -7
- package/dist-cjs/lib/recordDiff.js +0 -52
- package/dist-cjs/lib/recordDiff.js.map +0 -7
- package/dist-esm/lib/DurableObjectSqliteSyncWrapper.mjs +0 -35
- package/dist-esm/lib/DurableObjectSqliteSyncWrapper.mjs.map +0 -7
- package/dist-esm/lib/InMemorySyncStorage.mjs +0 -272
- package/dist-esm/lib/InMemorySyncStorage.mjs.map +0 -7
- package/dist-esm/lib/MicrotaskNotifier.mjs +0 -30
- package/dist-esm/lib/MicrotaskNotifier.mjs.map +0 -7
- package/dist-esm/lib/NodeSqliteWrapper.mjs +0 -28
- package/dist-esm/lib/NodeSqliteWrapper.mjs.map +0 -7
- package/dist-esm/lib/SQLiteSyncStorage.mjs +0 -414
- package/dist-esm/lib/SQLiteSyncStorage.mjs.map +0 -7
- package/dist-esm/lib/TLSyncStorage.mjs +0 -56
- package/dist-esm/lib/TLSyncStorage.mjs.map +0 -7
- package/dist-esm/lib/recordDiff.mjs +0 -32
- package/dist-esm/lib/recordDiff.mjs.map +0 -7
- package/src/lib/DurableObjectSqliteSyncWrapper.ts +0 -95
- package/src/lib/InMemorySyncStorage.ts +0 -387
- package/src/lib/MicrotaskNotifier.test.ts +0 -429
- package/src/lib/MicrotaskNotifier.ts +0 -38
- package/src/lib/NodeSqliteSyncWrapper.integration.test.ts +0 -270
- package/src/lib/NodeSqliteSyncWrapper.test.ts +0 -272
- package/src/lib/NodeSqliteWrapper.ts +0 -99
- package/src/lib/SQLiteSyncStorage.ts +0 -627
- package/src/lib/TLSyncStorage.ts +0 -216
- package/src/lib/computeTombstonePruning.test.ts +0 -352
- package/src/lib/recordDiff.ts +0 -73
- package/src/test/InMemorySyncStorage.test.ts +0 -1684
- package/src/test/SQLiteSyncStorage.test.ts +0 -1378
package/src/lib/TLSocketRoom.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import type { StoreSchema, UnknownRecord } from '@tldraw/store'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { DEFAULT_INITIAL_SNAPSHOT, InMemorySyncStorage } from './InMemorySyncStorage'
|
|
2
|
+
import { TLStoreSnapshot, createTLSchema } from '@tldraw/tlschema'
|
|
3
|
+
import { objectMapValues, structuredClone } from '@tldraw/utils'
|
|
5
4
|
import { RoomSessionState } from './RoomSession'
|
|
6
5
|
import { ServerSocketAdapter, WebSocketMinimal } from './ServerSocketAdapter'
|
|
7
6
|
import { TLSyncErrorCloseEventReason } from './TLSyncClient'
|
|
8
|
-
import { RoomSnapshot, TLSyncRoom } from './TLSyncRoom'
|
|
9
|
-
import {
|
|
10
|
-
convertStoreSnapshotToRoomSnapshot,
|
|
11
|
-
loadSnapshotIntoStorage,
|
|
12
|
-
TLSyncStorage,
|
|
13
|
-
} from './TLSyncStorage'
|
|
7
|
+
import { RoomSnapshot, RoomStoreMethods, TLSyncRoom } from './TLSyncRoom'
|
|
14
8
|
import { JsonChunkAssembler } from './chunk'
|
|
15
9
|
import { TLSocketServerSentEvent } from './protocol'
|
|
16
10
|
|
|
@@ -43,51 +37,6 @@ export interface TLSyncLog {
|
|
|
43
37
|
error?(...args: any[]): void
|
|
44
38
|
}
|
|
45
39
|
|
|
46
|
-
/**
|
|
47
|
-
* Base options for TLSocketRoom.
|
|
48
|
-
* @public
|
|
49
|
-
*/
|
|
50
|
-
export interface TLSocketRoomOptions<R extends UnknownRecord, SessionMeta> {
|
|
51
|
-
storage?: TLSyncStorage<R>
|
|
52
|
-
/**
|
|
53
|
-
* @deprecated use the storage option instead
|
|
54
|
-
*/
|
|
55
|
-
initialSnapshot?: RoomSnapshot | TLStoreSnapshot
|
|
56
|
-
/**
|
|
57
|
-
* @deprecated use the storage option with an onChange callback instead
|
|
58
|
-
*/
|
|
59
|
-
onDataChange?(): void
|
|
60
|
-
schema?: StoreSchema<R, any>
|
|
61
|
-
// how long to wait for a client to communicate before disconnecting them
|
|
62
|
-
clientTimeout?: number
|
|
63
|
-
log?: TLSyncLog
|
|
64
|
-
// a callback that is called when a client is disconnected
|
|
65
|
-
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
66
|
-
onSessionRemoved?: (
|
|
67
|
-
room: TLSocketRoom<R, SessionMeta>,
|
|
68
|
-
args: { sessionId: string; numSessionsRemaining: number; meta: SessionMeta }
|
|
69
|
-
) => void
|
|
70
|
-
// a callback that is called whenever a message is sent
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
72
|
-
onBeforeSendMessage?: (args: {
|
|
73
|
-
sessionId: string
|
|
74
|
-
/** @internal keep the protocol private for now */
|
|
75
|
-
message: TLSocketServerSentEvent<R>
|
|
76
|
-
stringified: string
|
|
77
|
-
meta: SessionMeta
|
|
78
|
-
}) => void
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
80
|
-
onAfterReceiveMessage?: (args: {
|
|
81
|
-
sessionId: string
|
|
82
|
-
/** @internal keep the protocol private for now */
|
|
83
|
-
message: TLSocketServerSentEvent<R>
|
|
84
|
-
stringified: string
|
|
85
|
-
meta: SessionMeta
|
|
86
|
-
}) => void
|
|
87
|
-
/** @internal */
|
|
88
|
-
onPresenceChange?(): void
|
|
89
|
-
}
|
|
90
|
-
|
|
91
40
|
/**
|
|
92
41
|
* A server-side room that manages WebSocket connections and synchronizes tldraw document state
|
|
93
42
|
* between multiple clients in real-time. Each room represents a collaborative document space
|
|
@@ -156,10 +105,10 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
156
105
|
{ assembler: JsonChunkAssembler; socket: WebSocketMinimal; unlisten: () => void }
|
|
157
106
|
>()
|
|
158
107
|
readonly log?: TLSyncLog
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
108
|
+
private readonly syncCallbacks: {
|
|
109
|
+
onDataChange?(): void
|
|
110
|
+
onPresenceChange?(): void
|
|
111
|
+
}
|
|
163
112
|
|
|
164
113
|
/**
|
|
165
114
|
* Creates a new TLSocketRoom instance for managing collaborative document synchronization.
|
|
@@ -175,36 +124,56 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
175
124
|
* - onDataChange - Called when document data changes
|
|
176
125
|
* - onPresenceChange - Called when presence data changes
|
|
177
126
|
*/
|
|
178
|
-
constructor(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
127
|
+
constructor(
|
|
128
|
+
public readonly opts: {
|
|
129
|
+
initialSnapshot?: RoomSnapshot | TLStoreSnapshot
|
|
130
|
+
schema?: StoreSchema<R, any>
|
|
131
|
+
// how long to wait for a client to communicate before disconnecting them
|
|
132
|
+
clientTimeout?: number
|
|
133
|
+
log?: TLSyncLog
|
|
134
|
+
// a callback that is called when a client is disconnected
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
136
|
+
onSessionRemoved?: (
|
|
137
|
+
room: TLSocketRoom<R, SessionMeta>,
|
|
138
|
+
args: { sessionId: string; numSessionsRemaining: number; meta: SessionMeta }
|
|
139
|
+
) => void
|
|
140
|
+
// a callback that is called whenever a message is sent
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
142
|
+
onBeforeSendMessage?: (args: {
|
|
143
|
+
sessionId: string
|
|
144
|
+
/** @internal keep the protocol private for now */
|
|
145
|
+
message: TLSocketServerSentEvent<R>
|
|
146
|
+
stringified: string
|
|
147
|
+
meta: SessionMeta
|
|
148
|
+
}) => void
|
|
149
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
150
|
+
onAfterReceiveMessage?: (args: {
|
|
151
|
+
sessionId: string
|
|
152
|
+
/** @internal keep the protocol private for now */
|
|
153
|
+
message: TLSocketServerSentEvent<R>
|
|
154
|
+
stringified: string
|
|
155
|
+
meta: SessionMeta
|
|
156
|
+
}) => void
|
|
157
|
+
onDataChange?(): void
|
|
158
|
+
/** @internal */
|
|
159
|
+
onPresenceChange?(): void
|
|
182
160
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
opts.initialSnapshot ?? DEFAULT_INITIAL_SNAPSHOT
|
|
189
|
-
),
|
|
190
|
-
})
|
|
161
|
+
) {
|
|
162
|
+
const initialSnapshot =
|
|
163
|
+
opts.initialSnapshot && 'store' in opts.initialSnapshot
|
|
164
|
+
? convertStoreSnapshotToRoomSnapshot(opts.initialSnapshot!)
|
|
165
|
+
: opts.initialSnapshot
|
|
191
166
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
storage.onChange(() => {
|
|
196
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
197
|
-
opts.onDataChange?.()
|
|
198
|
-
})
|
|
199
|
-
)
|
|
167
|
+
this.syncCallbacks = {
|
|
168
|
+
onDataChange: opts.onDataChange,
|
|
169
|
+
onPresenceChange: opts.onPresenceChange,
|
|
200
170
|
}
|
|
201
171
|
this.room = new TLSyncRoom<R, SessionMeta>({
|
|
202
|
-
|
|
172
|
+
...this.syncCallbacks,
|
|
203
173
|
schema: opts.schema ?? (createTLSchema() as any),
|
|
174
|
+
snapshot: initialSnapshot,
|
|
204
175
|
log: opts.log,
|
|
205
|
-
storage,
|
|
206
176
|
})
|
|
207
|
-
this.storage = storage
|
|
208
177
|
this.room.events.on('session_removed', (args) => {
|
|
209
178
|
this.sessions.delete(args.sessionId)
|
|
210
179
|
if (this.opts.onSessionRemoved) {
|
|
@@ -427,7 +396,7 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
427
396
|
* ```
|
|
428
397
|
*/
|
|
429
398
|
getCurrentDocumentClock() {
|
|
430
|
-
return this.
|
|
399
|
+
return this.room.documentClock
|
|
431
400
|
}
|
|
432
401
|
|
|
433
402
|
/**
|
|
@@ -449,9 +418,7 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
449
418
|
* ```
|
|
450
419
|
*/
|
|
451
420
|
getRecord(id: string) {
|
|
452
|
-
return this.
|
|
453
|
-
return structuredClone(txn.get(id)) as any
|
|
454
|
-
}).result as R
|
|
421
|
+
return structuredClone(this.room.documents.get(id)?.state)
|
|
455
422
|
}
|
|
456
423
|
|
|
457
424
|
/**
|
|
@@ -499,7 +466,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
499
466
|
* to restore the room state later or revert to a previous version.
|
|
500
467
|
*
|
|
501
468
|
* @returns Complete room snapshot including documents, clock values, and tombstones
|
|
502
|
-
* @deprecated if you need to do this use
|
|
503
469
|
*
|
|
504
470
|
* @example
|
|
505
471
|
* ```ts
|
|
@@ -513,10 +479,7 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
513
479
|
* ```
|
|
514
480
|
*/
|
|
515
481
|
getCurrentSnapshot() {
|
|
516
|
-
|
|
517
|
-
return this.storage.getSnapshot()
|
|
518
|
-
}
|
|
519
|
-
throw new Error('getCurrentSnapshot is not supported for this storage type')
|
|
482
|
+
return this.room.getSnapshot()
|
|
520
483
|
}
|
|
521
484
|
|
|
522
485
|
/**
|
|
@@ -528,12 +491,25 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
528
491
|
*/
|
|
529
492
|
getPresenceRecords() {
|
|
530
493
|
const result = {} as Record<string, UnknownRecord>
|
|
531
|
-
for (const
|
|
532
|
-
|
|
494
|
+
for (const document of this.room.documents.values()) {
|
|
495
|
+
if (document.state.typeName === this.room.presenceType?.typeName) {
|
|
496
|
+
result[document.state.id] = document.state
|
|
497
|
+
}
|
|
533
498
|
}
|
|
534
499
|
return result
|
|
535
500
|
}
|
|
536
501
|
|
|
502
|
+
/**
|
|
503
|
+
* Returns a JSON-serialized snapshot of the current document state. This is
|
|
504
|
+
* equivalent to JSON.stringify(getCurrentSnapshot()) but provided as a convenience.
|
|
505
|
+
*
|
|
506
|
+
* @returns JSON string representation of the room snapshot
|
|
507
|
+
* @internal
|
|
508
|
+
*/
|
|
509
|
+
getCurrentSerializedSnapshot() {
|
|
510
|
+
return JSON.stringify(this.room.getSnapshot())
|
|
511
|
+
}
|
|
512
|
+
|
|
537
513
|
/**
|
|
538
514
|
* Loads a document snapshot, completely replacing the current room state.
|
|
539
515
|
* This will disconnect all current clients and update the document to match
|
|
@@ -553,9 +529,42 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
553
529
|
* ```
|
|
554
530
|
*/
|
|
555
531
|
loadSnapshot(snapshot: RoomSnapshot | TLStoreSnapshot) {
|
|
556
|
-
|
|
557
|
-
|
|
532
|
+
if ('store' in snapshot) {
|
|
533
|
+
snapshot = convertStoreSnapshotToRoomSnapshot(snapshot)
|
|
534
|
+
}
|
|
535
|
+
const oldRoom = this.room
|
|
536
|
+
const oldRoomSnapshot = oldRoom.getSnapshot()
|
|
537
|
+
const oldIds = oldRoomSnapshot.documents.map((d) => d.state.id)
|
|
538
|
+
const newIds = new Set(snapshot.documents.map((d) => d.state.id))
|
|
539
|
+
const removedIds = oldIds.filter((id) => !newIds.has(id))
|
|
540
|
+
|
|
541
|
+
const tombstones: RoomSnapshot['tombstones'] = { ...oldRoomSnapshot.tombstones }
|
|
542
|
+
removedIds.forEach((id) => {
|
|
543
|
+
tombstones[id] = oldRoom.clock + 1
|
|
558
544
|
})
|
|
545
|
+
newIds.forEach((id) => {
|
|
546
|
+
delete tombstones[id]
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
const newRoom = new TLSyncRoom<R, SessionMeta>({
|
|
550
|
+
...this.syncCallbacks,
|
|
551
|
+
schema: oldRoom.schema,
|
|
552
|
+
snapshot: {
|
|
553
|
+
clock: oldRoom.clock + 1,
|
|
554
|
+
documentClock: oldRoom.clock + 1,
|
|
555
|
+
documents: snapshot.documents.map((d) => ({
|
|
556
|
+
lastChangedClock: oldRoom.clock + 1,
|
|
557
|
+
state: d.state,
|
|
558
|
+
})),
|
|
559
|
+
schema: snapshot.schema,
|
|
560
|
+
tombstones,
|
|
561
|
+
tombstoneHistoryStartsAtClock: oldRoomSnapshot.tombstoneHistoryStartsAtClock,
|
|
562
|
+
},
|
|
563
|
+
log: this.log,
|
|
564
|
+
})
|
|
565
|
+
// replace room with new one and kick out all the clients
|
|
566
|
+
this.room = newRoom
|
|
567
|
+
oldRoom.close()
|
|
559
568
|
}
|
|
560
569
|
|
|
561
570
|
/**
|
|
@@ -600,32 +609,9 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
600
609
|
* }
|
|
601
610
|
* })
|
|
602
611
|
* ```
|
|
603
|
-
* @deprecated use the storage.transaction method instead
|
|
604
612
|
*/
|
|
605
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
606
613
|
async updateStore(updater: (store: RoomStoreMethods<R>) => void | Promise<void>) {
|
|
607
|
-
|
|
608
|
-
throw new Error('Cannot update store on a closed room')
|
|
609
|
-
}
|
|
610
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
611
|
-
const ctx = new StoreUpdateContext<R>(
|
|
612
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
613
|
-
Object.fromEntries(this.getCurrentSnapshot().documents.map((d) => [d.state.id, d.state])),
|
|
614
|
-
this.room.schema
|
|
615
|
-
)
|
|
616
|
-
try {
|
|
617
|
-
await updater(ctx)
|
|
618
|
-
} finally {
|
|
619
|
-
ctx.close()
|
|
620
|
-
}
|
|
621
|
-
this.storage.transaction((txn) => {
|
|
622
|
-
for (const [id, record] of Object.entries(ctx.updates.puts)) {
|
|
623
|
-
txn.set(id, record as R)
|
|
624
|
-
}
|
|
625
|
-
for (const id of ctx.updates.deletes) {
|
|
626
|
-
txn.delete(id)
|
|
627
|
-
}
|
|
628
|
-
})
|
|
614
|
+
return this.room.updateStore(updater)
|
|
629
615
|
}
|
|
630
616
|
|
|
631
617
|
/**
|
|
@@ -702,8 +688,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
702
688
|
*/
|
|
703
689
|
close() {
|
|
704
690
|
this.room.close()
|
|
705
|
-
this.disposables.forEach((d) => d())
|
|
706
|
-
this.disposables.clear()
|
|
707
691
|
}
|
|
708
692
|
|
|
709
693
|
/**
|
|
@@ -745,113 +729,15 @@ export type OmitVoid<T, KS extends keyof T = keyof T> = {
|
|
|
745
729
|
[K in KS extends any ? (void extends T[KS] ? never : KS) : never]: T[K]
|
|
746
730
|
}
|
|
747
731
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
* }
|
|
759
|
-
* store.delete('shape:456')
|
|
760
|
-
* })
|
|
761
|
-
* ```
|
|
762
|
-
*
|
|
763
|
-
* @public
|
|
764
|
-
* @deprecated use the storage.transaction method instead
|
|
765
|
-
*/
|
|
766
|
-
export interface RoomStoreMethods<R extends UnknownRecord = UnknownRecord> {
|
|
767
|
-
/**
|
|
768
|
-
* Add or update a record in the store.
|
|
769
|
-
*
|
|
770
|
-
* @param record - The record to store
|
|
771
|
-
*/
|
|
772
|
-
put(record: R): void
|
|
773
|
-
/**
|
|
774
|
-
* Delete a record from the store.
|
|
775
|
-
*
|
|
776
|
-
* @param recordOrId - The record or record ID to delete
|
|
777
|
-
*/
|
|
778
|
-
delete(recordOrId: R | string): void
|
|
779
|
-
/**
|
|
780
|
-
* Get a record by its ID.
|
|
781
|
-
*
|
|
782
|
-
* @param id - The record ID
|
|
783
|
-
* @returns The record or null if not found
|
|
784
|
-
*/
|
|
785
|
-
get(id: string): R | null
|
|
786
|
-
/**
|
|
787
|
-
* Get all records in the store.
|
|
788
|
-
*
|
|
789
|
-
* @returns Array of all records
|
|
790
|
-
*/
|
|
791
|
-
getAll(): R[]
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
/**
|
|
795
|
-
* @deprecated use the storage.transaction method instead
|
|
796
|
-
*/
|
|
797
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
798
|
-
class StoreUpdateContext<R extends UnknownRecord> implements RoomStoreMethods<R> {
|
|
799
|
-
constructor(
|
|
800
|
-
private readonly snapshot: Record<string, UnknownRecord>,
|
|
801
|
-
private readonly schema: StoreSchema<R, any>
|
|
802
|
-
) {}
|
|
803
|
-
readonly updates = {
|
|
804
|
-
puts: {} as Record<string, UnknownRecord>,
|
|
805
|
-
deletes: new Set<string>(),
|
|
806
|
-
}
|
|
807
|
-
put(record: R): void {
|
|
808
|
-
if (this._isClosed) throw new Error('StoreUpdateContext is closed')
|
|
809
|
-
const recordType = getOwnProperty(this.schema.types, record.typeName)
|
|
810
|
-
if (!recordType) {
|
|
811
|
-
throw new Error(`Missing definition for record type ${record.typeName}`)
|
|
812
|
-
}
|
|
813
|
-
const recordBefore = this.snapshot[record.id] ?? undefined
|
|
814
|
-
recordType.validate(record, recordBefore as R)
|
|
815
|
-
|
|
816
|
-
if (record.id in this.snapshot && isEqual(this.snapshot[record.id], record)) {
|
|
817
|
-
delete this.updates.puts[record.id]
|
|
818
|
-
} else {
|
|
819
|
-
this.updates.puts[record.id] = structuredClone(record)
|
|
820
|
-
}
|
|
821
|
-
this.updates.deletes.delete(record.id)
|
|
822
|
-
}
|
|
823
|
-
delete(recordOrId: R | string): void {
|
|
824
|
-
if (this._isClosed) throw new Error('StoreUpdateContext is closed')
|
|
825
|
-
const id = typeof recordOrId === 'string' ? recordOrId : recordOrId.id
|
|
826
|
-
delete this.updates.puts[id]
|
|
827
|
-
if (this.snapshot[id]) {
|
|
828
|
-
this.updates.deletes.add(id)
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
get(id: string): R | null {
|
|
832
|
-
if (this._isClosed) throw new Error('StoreUpdateContext is closed')
|
|
833
|
-
if (hasOwnProperty(this.updates.puts, id)) {
|
|
834
|
-
return structuredClone(this.updates.puts[id]) as R
|
|
835
|
-
}
|
|
836
|
-
if (this.updates.deletes.has(id)) {
|
|
837
|
-
return null
|
|
838
|
-
}
|
|
839
|
-
return structuredClone(this.snapshot[id] ?? null) as R
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
getAll(): R[] {
|
|
843
|
-
if (this._isClosed) throw new Error('StoreUpdateContext is closed')
|
|
844
|
-
const result = Object.values(this.updates.puts)
|
|
845
|
-
for (const [id, record] of Object.entries(this.snapshot)) {
|
|
846
|
-
if (!this.updates.deletes.has(id) && !hasOwnProperty(this.updates.puts, id)) {
|
|
847
|
-
result.push(record)
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
return structuredClone(result) as R[]
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
private _isClosed = false
|
|
854
|
-
close() {
|
|
855
|
-
this._isClosed = true
|
|
732
|
+
function convertStoreSnapshotToRoomSnapshot(snapshot: TLStoreSnapshot): RoomSnapshot {
|
|
733
|
+
return {
|
|
734
|
+
clock: 0,
|
|
735
|
+
documentClock: 0,
|
|
736
|
+
documents: objectMapValues(snapshot.store).map((state) => ({
|
|
737
|
+
state,
|
|
738
|
+
lastChangedClock: 0,
|
|
739
|
+
})),
|
|
740
|
+
schema: snapshot.schema,
|
|
741
|
+
tombstones: {},
|
|
856
742
|
}
|
|
857
743
|
}
|
package/src/lib/TLSyncClient.ts
CHANGED
|
@@ -109,18 +109,6 @@ export const TLSyncErrorCloseEventReason = {
|
|
|
109
109
|
/** Room has reached maximum capacity */
|
|
110
110
|
ROOM_FULL: 'ROOM_FULL',
|
|
111
111
|
} as const
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* @internal
|
|
115
|
-
*/
|
|
116
|
-
export class TLSyncError extends Error {
|
|
117
|
-
constructor(
|
|
118
|
-
message: string,
|
|
119
|
-
public reason: TLSyncErrorCloseEventReason
|
|
120
|
-
) {
|
|
121
|
-
super(message)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
112
|
/**
|
|
125
113
|
* Union type of all possible server connection close reasons.
|
|
126
114
|
* Represents the string values that can be passed when a server closes
|