@tldraw/sync-core 5.3.0-internal.fba91ed55f6c → 5.3.0-next.4123e97459a9
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 +3 -154
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/InMemorySyncStorage.js +20 -55
- package/dist-cjs/lib/InMemorySyncStorage.js.map +2 -2
- package/dist-cjs/lib/RoomSession.js.map +1 -1
- package/dist-cjs/lib/SQLiteSyncStorage.js +52 -115
- package/dist-cjs/lib/SQLiteSyncStorage.js.map +2 -2
- package/dist-cjs/lib/TLSocketRoom.js +2 -27
- package/dist-cjs/lib/TLSocketRoom.js.map +2 -2
- package/dist-cjs/lib/TLSyncClient.js +1 -6
- package/dist-cjs/lib/TLSyncClient.js.map +2 -2
- package/dist-cjs/lib/TLSyncRoom.js +10 -160
- package/dist-cjs/lib/TLSyncRoom.js.map +3 -3
- package/dist-cjs/lib/TLSyncStorage.js.map +2 -2
- package/dist-cjs/lib/protocol.js.map +2 -2
- package/dist-esm/index.d.mts +3 -154
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/InMemorySyncStorage.mjs +20 -55
- package/dist-esm/lib/InMemorySyncStorage.mjs.map +2 -2
- package/dist-esm/lib/RoomSession.mjs.map +1 -1
- package/dist-esm/lib/SQLiteSyncStorage.mjs +52 -115
- package/dist-esm/lib/SQLiteSyncStorage.mjs.map +2 -2
- package/dist-esm/lib/TLSocketRoom.mjs +2 -27
- package/dist-esm/lib/TLSocketRoom.mjs.map +2 -2
- package/dist-esm/lib/TLSyncClient.mjs +1 -6
- package/dist-esm/lib/TLSyncClient.mjs.map +2 -2
- package/dist-esm/lib/TLSyncRoom.mjs +10 -160
- package/dist-esm/lib/TLSyncRoom.mjs.map +3 -3
- package/dist-esm/lib/TLSyncStorage.mjs.map +2 -2
- package/dist-esm/lib/protocol.mjs.map +2 -2
- package/package.json +6 -6
- package/src/index.ts +0 -3
- package/src/lib/InMemorySyncStorage.ts +21 -74
- package/src/lib/RoomSession.ts +2 -7
- package/src/lib/SQLiteSyncStorage.test.ts +2 -29
- package/src/lib/SQLiteSyncStorage.ts +59 -158
- package/src/lib/TLSocketRoom.ts +3 -61
- package/src/lib/TLSyncClient.test.ts +2 -9
- package/src/lib/TLSyncClient.ts +3 -15
- package/src/lib/TLSyncRoom.ts +10 -294
- package/src/lib/TLSyncStorage.ts +0 -8
- package/src/lib/protocol.ts +0 -17
- package/src/test/TLSocketRoom.test.ts +2 -37
- package/src/test/TLSyncRoom.test.ts +0 -25
- package/src/test/TestServer.ts +4 -14
- package/src/test/storageContractSuite.ts +0 -79
- package/src/test/upgradeDowngrade.test.ts +2 -144
- package/src/test/objectStore.test.ts +0 -630
package/src/lib/TLSocketRoom.ts
CHANGED
|
@@ -3,15 +3,14 @@ import { createTLSchema, TLInstancePresence, TLStoreSnapshot } from '@tldraw/tls
|
|
|
3
3
|
import { getOwnProperty, hasOwnProperty, isEqual, structuredClone } from '@tldraw/utils'
|
|
4
4
|
import { JsonChunkAssembler } from './chunk'
|
|
5
5
|
import { DEFAULT_INITIAL_SNAPSHOT, InMemorySyncStorage } from './InMemorySyncStorage'
|
|
6
|
-
import {
|
|
6
|
+
import { TLSocketServerSentEvent } from './protocol'
|
|
7
7
|
import { RoomSessionState } from './RoomSession'
|
|
8
8
|
import { ServerSocketAdapter, WebSocketMinimal } from './ServerSocketAdapter'
|
|
9
9
|
import { TLSyncErrorCloseEventReason } from './TLSyncClient'
|
|
10
|
-
import { RoomSnapshot,
|
|
10
|
+
import { RoomSnapshot, TLSyncRoom } from './TLSyncRoom'
|
|
11
11
|
import {
|
|
12
12
|
convertStoreSnapshotToRoomSnapshot,
|
|
13
13
|
loadSnapshotIntoStorage,
|
|
14
|
-
TLSyncForwardDiff,
|
|
15
14
|
TLSyncStorage,
|
|
16
15
|
} from './TLSyncStorage'
|
|
17
16
|
|
|
@@ -72,11 +71,6 @@ export interface TLSyncLog {
|
|
|
72
71
|
export interface SessionStateSnapshot {
|
|
73
72
|
serializedSchema: SerializedSchema
|
|
74
73
|
isReadonly: boolean
|
|
75
|
-
/**
|
|
76
|
-
* Write access for the record types listed in `objectTypes`. Optional so snapshots persisted
|
|
77
|
-
* before this field existed resume cleanly (they fail closed to 'read').
|
|
78
|
-
*/
|
|
79
|
-
objectAccess?: TLObjectStoreAccess
|
|
80
74
|
presenceId: string | null
|
|
81
75
|
presenceRecord: UnknownRecord | null
|
|
82
76
|
requiresLegacyRejection: boolean
|
|
@@ -126,30 +120,6 @@ export interface TLSocketRoomOptions<R extends UnknownRecord, SessionMeta> {
|
|
|
126
120
|
}) => void
|
|
127
121
|
/** @internal */
|
|
128
122
|
onPresenceChange?(): void
|
|
129
|
-
/**
|
|
130
|
-
* Called once after a client push commits, with the committed document diff. Use to react to
|
|
131
|
-
* document changes as soon as they commit — e.g. project certain record types to an external
|
|
132
|
-
* store. Best-effort: do not throw and do not block.
|
|
133
|
-
*
|
|
134
|
-
* Only fires for client pushes. Server-initiated changes — `updateStore`, `loadSnapshot`, or
|
|
135
|
-
* writing to the storage directly — do not trigger it, so anything mirroring room state through
|
|
136
|
-
* this callback must handle those paths itself.
|
|
137
|
-
*/
|
|
138
|
-
// eslint-disable-next-line tldraw/method-signature-style
|
|
139
|
-
onCommittedChanges?: (args: { diff: TLSyncForwardDiff<R>; documentClock: number }) => void
|
|
140
|
-
/**
|
|
141
|
-
* Record type names to serve through the object-store lane instead of the document lane.
|
|
142
|
-
* Each must be a document-scoped type registered in the schema. Object-lane writes are gated
|
|
143
|
-
* per session by `objectAccess` (see {@link TLSocketRoom.handleSocketConnect}) rather than
|
|
144
|
-
* `isReadonly`, so e.g. a session can be allowed to comment without being allowed to edit.
|
|
145
|
-
*/
|
|
146
|
-
objectTypes?: readonly string[]
|
|
147
|
-
/**
|
|
148
|
-
* Per-type authorizers for client record writes (create, update, delete): veto writes the
|
|
149
|
-
* session isn't allowed to make, or rewrite the record on create — e.g. force a comment's
|
|
150
|
-
* `authorId` to the signed-in user. See {@link TLRecordAuthorizers}.
|
|
151
|
-
*/
|
|
152
|
-
authorizeRecord?: TLRecordAuthorizers<R, SessionMeta>
|
|
153
123
|
/**
|
|
154
124
|
* When set, the room will call {@link TLSocketRoom.getSessionSnapshot} after
|
|
155
125
|
* no message activity for a session for 5s and pass the result to this callback.
|
|
@@ -260,8 +230,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
260
230
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
261
231
|
opts.initialSnapshot ?? DEFAULT_INITIAL_SNAPSHOT
|
|
262
232
|
),
|
|
263
|
-
// keep the storage partition in step with the room's object lane
|
|
264
|
-
objectTypes: opts.objectTypes,
|
|
265
233
|
})
|
|
266
234
|
|
|
267
235
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
@@ -275,9 +243,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
275
243
|
}
|
|
276
244
|
this.room = new TLSyncRoom<R, SessionMeta>({
|
|
277
245
|
onPresenceChange: opts.onPresenceChange,
|
|
278
|
-
onCommittedChanges: opts.onCommittedChanges,
|
|
279
|
-
objectTypes: opts.objectTypes,
|
|
280
|
-
authorizeRecord: opts.authorizeRecord,
|
|
281
246
|
schema: opts.schema ?? (createTLSchema() as any),
|
|
282
247
|
log: opts.log,
|
|
283
248
|
storage,
|
|
@@ -318,9 +283,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
318
283
|
* - sessionId - Unique identifier for the client session (typically from browser tab)
|
|
319
284
|
* - socket - WebSocket-like object for client communication
|
|
320
285
|
* - isReadonly - Whether the client can modify the document (defaults to false)
|
|
321
|
-
* - objectAccess - Write access for object-store lane record types (defaults to 'write').
|
|
322
|
-
* Independent of isReadonly, so a session can be allowed to write objects (e.g. comments)
|
|
323
|
-
* without being allowed to edit the document.
|
|
324
286
|
* - meta - Additional session metadata (required if SessionMeta is not void)
|
|
325
287
|
*
|
|
326
288
|
* @example
|
|
@@ -348,10 +310,9 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
348
310
|
sessionId: string
|
|
349
311
|
socket: WebSocketMinimal
|
|
350
312
|
isReadonly?: boolean
|
|
351
|
-
objectAccess?: TLObjectStoreAccess
|
|
352
313
|
} & (SessionMeta extends void ? object : { meta: SessionMeta })
|
|
353
314
|
) {
|
|
354
|
-
const { sessionId, socket, isReadonly = false
|
|
315
|
+
const { sessionId, socket, isReadonly = false } = opts
|
|
355
316
|
const handleSocketMessage = (event: MessageEvent) =>
|
|
356
317
|
this.handleSocketMessage(sessionId, event.data)
|
|
357
318
|
const handleSocketError = this.handleSocketError.bind(this, sessionId)
|
|
@@ -370,7 +331,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
370
331
|
this.room.handleNewSession({
|
|
371
332
|
sessionId,
|
|
372
333
|
isReadonly,
|
|
373
|
-
objectAccess,
|
|
374
334
|
socket: new ServerSocketAdapter({
|
|
375
335
|
ws: socket,
|
|
376
336
|
onBeforeSendMessage: this.opts.onBeforeSendMessage
|
|
@@ -580,9 +540,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
580
540
|
this.room.handleResumedSession({
|
|
581
541
|
sessionId,
|
|
582
542
|
isReadonly: snapshot.isReadonly,
|
|
583
|
-
// snapshots persisted before this field existed fail closed — those sessions predate
|
|
584
|
-
// the record types gated by objectAccess, so they have nothing to write anyway
|
|
585
|
-
objectAccess: snapshot.objectAccess ?? 'read',
|
|
586
543
|
serializedSchema: snapshot.serializedSchema,
|
|
587
544
|
presenceId: snapshot.presenceId,
|
|
588
545
|
presenceRecord: snapshot.presenceRecord,
|
|
@@ -638,7 +595,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
638
595
|
return {
|
|
639
596
|
serializedSchema: session.serializedSchema,
|
|
640
597
|
isReadonly: session.isReadonly,
|
|
641
|
-
objectAccess: session.objectAccess,
|
|
642
598
|
presenceId: session.presenceId,
|
|
643
599
|
presenceRecord,
|
|
644
600
|
requiresLegacyRejection: session.requiresLegacyRejection,
|
|
@@ -714,7 +670,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
714
670
|
sessionId: string
|
|
715
671
|
isConnected: boolean
|
|
716
672
|
isReadonly: boolean
|
|
717
|
-
objectAccess: TLObjectStoreAccess
|
|
718
673
|
meta: SessionMeta
|
|
719
674
|
}> {
|
|
720
675
|
return [...this.room.sessions.values()].map((session) => {
|
|
@@ -722,7 +677,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
722
677
|
sessionId: session.sessionId,
|
|
723
678
|
isConnected: session.state === RoomSessionState.Connected,
|
|
724
679
|
isReadonly: session.isReadonly,
|
|
725
|
-
objectAccess: session.objectAccess,
|
|
726
680
|
meta: session.meta,
|
|
727
681
|
}
|
|
728
682
|
})
|
|
@@ -754,18 +708,6 @@ export class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta =
|
|
|
754
708
|
throw new Error('getCurrentSnapshot is not supported for this storage type')
|
|
755
709
|
}
|
|
756
710
|
|
|
757
|
-
/**
|
|
758
|
-
* Returns a snapshot of the object-store lane (see `objectTypes`), for persisting
|
|
759
|
-
* object-lane records separately from the document. Same entry shape as
|
|
760
|
-
* {@link RoomSnapshot.documents}.
|
|
761
|
-
*/
|
|
762
|
-
getCurrentObjectsSnapshot(): RoomSnapshot['documents'] {
|
|
763
|
-
if (this.storage.getObjectsSnapshot) {
|
|
764
|
-
return this.storage.getObjectsSnapshot()
|
|
765
|
-
}
|
|
766
|
-
throw new Error('getCurrentObjectsSnapshot is not supported for this storage type')
|
|
767
|
-
}
|
|
768
|
-
|
|
769
711
|
/**
|
|
770
712
|
* Retrieves all presence records from the document store. Presence records
|
|
771
713
|
* contain ephemeral user state like cursor positions and selections.
|
|
@@ -476,20 +476,13 @@ describe('TLSyncClient', () => {
|
|
|
476
476
|
client = createClient()
|
|
477
477
|
socket.mockServerMessage(createConnectMessage({ isReadonly: false }))
|
|
478
478
|
expect(client.isConnectedToRoom).toBe(true)
|
|
479
|
-
|
|
480
|
-
expect(onAfterConnect).toHaveBeenCalledWith(client, {
|
|
481
|
-
isReadonly: false,
|
|
482
|
-
objectAccess: 'write',
|
|
483
|
-
})
|
|
479
|
+
expect(onAfterConnect).toHaveBeenCalledWith(client, { isReadonly: false })
|
|
484
480
|
|
|
485
481
|
// reconnect as readonly
|
|
486
482
|
socket.mockConnectionStatus('offline')
|
|
487
483
|
socket.mockConnectionStatus('online')
|
|
488
484
|
socket.mockServerMessage(createConnectMessage({ isReadonly: true }))
|
|
489
|
-
expect(onAfterConnect).toHaveBeenLastCalledWith(client, {
|
|
490
|
-
isReadonly: true,
|
|
491
|
-
objectAccess: 'write',
|
|
492
|
-
})
|
|
485
|
+
expect(onAfterConnect).toHaveBeenLastCalledWith(client, { isReadonly: true })
|
|
493
486
|
})
|
|
494
487
|
|
|
495
488
|
it('[CL7] pushes the current presence state after connecting', () => {
|
package/src/lib/TLSyncClient.ts
CHANGED
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
} from './diff'
|
|
25
25
|
import { interval } from './interval'
|
|
26
26
|
import {
|
|
27
|
-
TLObjectStoreAccess,
|
|
28
27
|
TLPushRequest,
|
|
29
28
|
TLSocketClientSentEvent,
|
|
30
29
|
TLSocketServerSentDataEvent,
|
|
@@ -438,13 +437,8 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
|
|
|
438
437
|
* @param self - The TLSyncClient instance that connected
|
|
439
438
|
* @param details - Connection details
|
|
440
439
|
* - isReadonly - Whether the connection is in read-only mode
|
|
441
|
-
* - objectAccess - Write access for object-store lane record types (defaults to 'write'
|
|
442
|
-
* when the server doesn't send it, e.g. older servers or rooms with no object lane)
|
|
443
440
|
*/
|
|
444
|
-
private readonly onAfterConnect?: (
|
|
445
|
-
self: this,
|
|
446
|
-
details: { isReadonly: boolean; objectAccess: TLObjectStoreAccess }
|
|
447
|
-
) => void
|
|
441
|
+
private readonly onAfterConnect?: (self: this, details: { isReadonly: boolean }) => void
|
|
448
442
|
|
|
449
443
|
private readonly onCustomMessageReceived?: TLCustomMessageHandler
|
|
450
444
|
|
|
@@ -484,10 +478,7 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
|
|
|
484
478
|
onLoad(self: TLSyncClient<R, S>): void
|
|
485
479
|
onSyncError(reason: string): void
|
|
486
480
|
onCustomMessageReceived?: TLCustomMessageHandler
|
|
487
|
-
onAfterConnect?(
|
|
488
|
-
self: TLSyncClient<R, S>,
|
|
489
|
-
details: { isReadonly: boolean; objectAccess: TLObjectStoreAccess }
|
|
490
|
-
): void
|
|
481
|
+
onAfterConnect?(self: TLSyncClient<R, S>, details: { isReadonly: boolean }): void
|
|
491
482
|
didCancel?(): boolean
|
|
492
483
|
}) {
|
|
493
484
|
this.didCancel = config.didCancel
|
|
@@ -766,10 +757,7 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
|
|
|
766
757
|
// this.isConnectedToRoom = true
|
|
767
758
|
// this.store.applyDiff(stashedChanges, false)
|
|
768
759
|
|
|
769
|
-
this.onAfterConnect?.(this, {
|
|
770
|
-
isReadonly: event.isReadonly,
|
|
771
|
-
objectAccess: event.objectAccess ?? 'write',
|
|
772
|
-
})
|
|
760
|
+
this.onAfterConnect?.(this, { isReadonly: event.isReadonly })
|
|
773
761
|
const presence = this.presenceState?.get()
|
|
774
762
|
if (presence) {
|
|
775
763
|
this.pushPresence(presence)
|