@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/chunk.ts
CHANGED
|
@@ -88,8 +88,8 @@ export class JsonChunkAssembler {
|
|
|
88
88
|
*
|
|
89
89
|
* @param msg - The message to process, either JSON or chunk format
|
|
90
90
|
* @returns Result object with data/stringified on success, error object on failure, or null for incomplete chunks
|
|
91
|
-
*
|
|
92
|
-
*
|
|
91
|
+
* - `\{ data: object, stringified: string \}` - Successfully parsed complete message
|
|
92
|
+
* - `\{ error: Error \}` - Parse error or invalid chunk sequence
|
|
93
93
|
* - `null` - Chunk received but more chunks expected
|
|
94
94
|
*
|
|
95
95
|
* @example
|
package/src/test/FuzzEditor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Editor,
|
|
3
3
|
PageRecordType,
|
|
4
|
+
TLArrowBinding,
|
|
4
5
|
TLPage,
|
|
5
6
|
TLPageId,
|
|
6
7
|
TLShape,
|
|
@@ -40,8 +41,8 @@ export type Op =
|
|
|
40
41
|
}
|
|
41
42
|
| {
|
|
42
43
|
type: 'create-arrow'
|
|
43
|
-
start: VecModel
|
|
44
|
-
end: VecModel
|
|
44
|
+
start: TLArrowBinding | VecModel
|
|
45
|
+
end: TLArrowBinding | VecModel
|
|
45
46
|
}
|
|
46
47
|
| {
|
|
47
48
|
type: 'delete-shape'
|
|
@@ -314,8 +315,8 @@ export class FuzzEditor extends RandomSource {
|
|
|
314
315
|
x: 0,
|
|
315
316
|
y: 0,
|
|
316
317
|
props: {
|
|
317
|
-
start: op.start
|
|
318
|
-
end: op.end
|
|
318
|
+
start: op.start,
|
|
319
|
+
end: op.end,
|
|
319
320
|
},
|
|
320
321
|
})
|
|
321
322
|
break
|
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
InstancePresenceRecordType,
|
|
4
|
-
PageRecordType,
|
|
5
|
-
TLDocument,
|
|
6
|
-
TLPage,
|
|
7
|
-
TLRecord,
|
|
8
|
-
} from '@tldraw/tlschema'
|
|
9
|
-
import {
|
|
10
|
-
createTLSchema,
|
|
11
|
-
createTLStore,
|
|
12
|
-
IndexKey,
|
|
13
|
-
promiseWithResolve,
|
|
14
|
-
sortById,
|
|
15
|
-
ZERO_INDEX_KEY,
|
|
16
|
-
} from 'tldraw'
|
|
1
|
+
import { InstancePresenceRecordType, PageRecordType } from '@tldraw/tlschema'
|
|
2
|
+
import { createTLSchema, createTLStore, ZERO_INDEX_KEY } from 'tldraw'
|
|
17
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
18
4
|
import { RecordOpType } from '../lib/diff'
|
|
19
|
-
import { DEFAULT_INITIAL_SNAPSHOT, InMemorySyncStorage } from '../lib/InMemorySyncStorage'
|
|
20
5
|
import { getTlsyncProtocolVersion } from '../lib/protocol'
|
|
21
6
|
import { WebSocketMinimal } from '../lib/ServerSocketAdapter'
|
|
22
7
|
import { TLSocketRoom, TLSyncLog } from '../lib/TLSocketRoom'
|
|
23
8
|
import { TLSyncErrorCloseEventReason } from '../lib/TLSyncClient'
|
|
24
|
-
import { RoomSnapshot } from '../lib/TLSyncRoom'
|
|
25
9
|
|
|
26
10
|
function getStore() {
|
|
27
11
|
const schema = createTLSchema()
|
|
@@ -57,7 +41,7 @@ describe(TLSocketRoom, () => {
|
|
|
57
41
|
initialSnapshot: snapshot,
|
|
58
42
|
})
|
|
59
43
|
expect(room.getCurrentSnapshot()).not.toMatchObject({ clock: 0, documents: [] })
|
|
60
|
-
expect(room.getCurrentSnapshot().
|
|
44
|
+
expect(room.getCurrentSnapshot().clock).toBe(0)
|
|
61
45
|
expect(room.getCurrentSnapshot().documents.sort((a, b) => a.state.id.localeCompare(b.state.id)))
|
|
62
46
|
.toMatchInlineSnapshot(`
|
|
63
47
|
[
|
|
@@ -91,7 +75,7 @@ describe(TLSocketRoom, () => {
|
|
|
91
75
|
initialSnapshot: store.getStoreSnapshot(),
|
|
92
76
|
})
|
|
93
77
|
|
|
94
|
-
expect(room.getCurrentSnapshot()).toMatchObject({
|
|
78
|
+
expect(room.getCurrentSnapshot()).toMatchObject({ clock: 0, documents: [] })
|
|
95
79
|
|
|
96
80
|
// populate with an empty document (document:document and page:page records)
|
|
97
81
|
store.ensureStoreIsUsable()
|
|
@@ -99,7 +83,7 @@ describe(TLSocketRoom, () => {
|
|
|
99
83
|
const snapshot = store.getStoreSnapshot()
|
|
100
84
|
room.loadSnapshot(snapshot)
|
|
101
85
|
|
|
102
|
-
expect(room.getCurrentSnapshot().
|
|
86
|
+
expect(room.getCurrentSnapshot().clock).toBe(1)
|
|
103
87
|
expect(room.getCurrentSnapshot().documents.sort((a, b) => a.state.id.localeCompare(b.state.id)))
|
|
104
88
|
.toMatchInlineSnapshot(`
|
|
105
89
|
[
|
|
@@ -303,38 +287,50 @@ describe(TLSocketRoom, () => {
|
|
|
303
287
|
})
|
|
304
288
|
|
|
305
289
|
describe('Room state resetting behavior', () => {
|
|
306
|
-
it('
|
|
290
|
+
it('sets documentClock to oldRoom.clock + 1 when resetting room state', () => {
|
|
307
291
|
const store = getStore()
|
|
308
292
|
store.ensureStoreIsUsable()
|
|
309
293
|
const room = new TLSocketRoom({
|
|
310
294
|
initialSnapshot: store.getStoreSnapshot(),
|
|
311
295
|
})
|
|
312
296
|
|
|
313
|
-
|
|
314
|
-
|
|
297
|
+
// Load a snapshot to increment the clock
|
|
298
|
+
const snapshot = store.getStoreSnapshot()
|
|
299
|
+
room.loadSnapshot(snapshot)
|
|
315
300
|
|
|
316
|
-
|
|
317
|
-
|
|
301
|
+
const oldClock = room.getCurrentSnapshot().clock
|
|
302
|
+
expect(oldClock).toBe(1)
|
|
303
|
+
|
|
304
|
+
// Reset with a new snapshot
|
|
318
305
|
const newSnapshot = store.getStoreSnapshot()
|
|
319
306
|
room.loadSnapshot(newSnapshot)
|
|
320
307
|
|
|
321
|
-
|
|
308
|
+
const newSnapshotResult = room.getCurrentSnapshot()
|
|
309
|
+
expect(newSnapshotResult.documentClock).toBe(oldClock + 1)
|
|
310
|
+
expect(newSnapshotResult.clock).toBe(oldClock + 1)
|
|
322
311
|
})
|
|
323
312
|
|
|
324
|
-
it('
|
|
313
|
+
it('updates all documents lastChangedClock when resetting', () => {
|
|
325
314
|
const store = getStore()
|
|
326
315
|
store.ensureStoreIsUsable()
|
|
327
316
|
const room = new TLSocketRoom({
|
|
328
317
|
initialSnapshot: store.getStoreSnapshot(),
|
|
329
318
|
})
|
|
330
319
|
|
|
331
|
-
|
|
320
|
+
// Get initial clock
|
|
321
|
+
const initialClock = room.getCurrentSnapshot().clock
|
|
322
|
+
|
|
323
|
+
// Reset with a new snapshot
|
|
324
|
+
const newSnapshot = store.getStoreSnapshot()
|
|
325
|
+
room.loadSnapshot(newSnapshot)
|
|
332
326
|
|
|
333
|
-
|
|
334
|
-
|
|
327
|
+
const result = room.getCurrentSnapshot()
|
|
328
|
+
expect(result.clock).toBe(initialClock + 1)
|
|
335
329
|
|
|
336
|
-
//
|
|
337
|
-
|
|
330
|
+
// All documents should have updated lastChangedClock
|
|
331
|
+
for (const doc of result.documents) {
|
|
332
|
+
expect(doc.lastChangedClock).toBe(initialClock + 1)
|
|
333
|
+
}
|
|
338
334
|
})
|
|
339
335
|
|
|
340
336
|
it('preserves existing tombstones with original clock values', async () => {
|
|
@@ -360,13 +356,30 @@ describe(TLSocketRoom, () => {
|
|
|
360
356
|
|
|
361
357
|
room.loadSnapshot(room.getCurrentSnapshot())
|
|
362
358
|
|
|
363
|
-
// Tombstones should be preserved
|
|
364
359
|
expect(room.getCurrentSnapshot().tombstones).toEqual({
|
|
365
360
|
[testPageId]: deletionClock,
|
|
366
361
|
})
|
|
367
362
|
|
|
368
|
-
|
|
369
|
-
|
|
363
|
+
expect(room.getCurrentSnapshot().documentClock).toBe(deletionClock + 1)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('handles empty snapshot reset correctly', () => {
|
|
367
|
+
const store = getStore()
|
|
368
|
+
// Don't call ensureStoreIsUsable to get an empty snapshot
|
|
369
|
+
const room = new TLSocketRoom({
|
|
370
|
+
initialSnapshot: store.getStoreSnapshot(),
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
const oldClock = room.getCurrentSnapshot().clock
|
|
374
|
+
|
|
375
|
+
// Reset with empty snapshot
|
|
376
|
+
const emptySnapshot = store.getStoreSnapshot()
|
|
377
|
+
room.loadSnapshot(emptySnapshot)
|
|
378
|
+
|
|
379
|
+
const result = room.getCurrentSnapshot()
|
|
380
|
+
expect(result.documentClock).toBe(oldClock + 1)
|
|
381
|
+
expect(result.clock).toBe(oldClock + 1)
|
|
382
|
+
expect(result.documents).toHaveLength(0)
|
|
370
383
|
})
|
|
371
384
|
|
|
372
385
|
it('preserves schema when resetting room state', () => {
|
|
@@ -804,222 +817,3 @@ describe(TLSocketRoom, () => {
|
|
|
804
817
|
})
|
|
805
818
|
})
|
|
806
819
|
})
|
|
807
|
-
|
|
808
|
-
describe('TLSocketRoom.updateStore', () => {
|
|
809
|
-
let storage = new InMemorySyncStorage<TLRecord>({ snapshot: DEFAULT_INITIAL_SNAPSHOT })
|
|
810
|
-
|
|
811
|
-
let room = new TLSocketRoom<TLRecord, undefined>({ storage })
|
|
812
|
-
function init(snapshot?: RoomSnapshot) {
|
|
813
|
-
storage = new InMemorySyncStorage<TLRecord>({ snapshot: snapshot ?? DEFAULT_INITIAL_SNAPSHOT })
|
|
814
|
-
room = new TLSocketRoom<TLRecord, undefined>({
|
|
815
|
-
storage,
|
|
816
|
-
})
|
|
817
|
-
}
|
|
818
|
-
beforeEach(() => {
|
|
819
|
-
init()
|
|
820
|
-
})
|
|
821
|
-
|
|
822
|
-
test('it allows updating records', async () => {
|
|
823
|
-
const clock = storage.getClock()
|
|
824
|
-
await room.updateStore((store) => {
|
|
825
|
-
const document = store.get('document:document') as TLDocument
|
|
826
|
-
document.name = 'My lovely document'
|
|
827
|
-
store.put(document)
|
|
828
|
-
})
|
|
829
|
-
expect(
|
|
830
|
-
(
|
|
831
|
-
storage.getSnapshot().documents.find((r) => r.state.id === 'document:document')
|
|
832
|
-
?.state as any
|
|
833
|
-
).name
|
|
834
|
-
).toBe('My lovely document')
|
|
835
|
-
expect(clock).toBeLessThan(storage.getClock())
|
|
836
|
-
})
|
|
837
|
-
|
|
838
|
-
test('it does not update unless you call .set', () => {
|
|
839
|
-
const clock = storage.getClock()
|
|
840
|
-
room.updateStore((store) => {
|
|
841
|
-
const document = store.get('document:document') as TLDocument
|
|
842
|
-
document.name = 'My lovely document'
|
|
843
|
-
})
|
|
844
|
-
expect(
|
|
845
|
-
(
|
|
846
|
-
storage.getSnapshot().documents.find((r) => r.state.id === 'document:document')
|
|
847
|
-
?.state as any
|
|
848
|
-
).name
|
|
849
|
-
).toBe('')
|
|
850
|
-
expect(clock).toBe(storage.getClock())
|
|
851
|
-
})
|
|
852
|
-
|
|
853
|
-
test('triggers onChange events on the storage if something changed', async () => {
|
|
854
|
-
const onChange = vi.fn()
|
|
855
|
-
storage.onChange(onChange)
|
|
856
|
-
await room.updateStore((store) => {
|
|
857
|
-
const document = store.get('document:document') as TLDocument
|
|
858
|
-
document.name = 'My lovely document'
|
|
859
|
-
store.put(document)
|
|
860
|
-
})
|
|
861
|
-
expect(onChange).toHaveBeenCalled()
|
|
862
|
-
})
|
|
863
|
-
|
|
864
|
-
test('does not trigger onChange events if the change is not committed', async () => {
|
|
865
|
-
const onChange = vi.fn()
|
|
866
|
-
storage.onChange(onChange)
|
|
867
|
-
room.updateStore((store) => {
|
|
868
|
-
const document = store.get('document:document') as TLDocument
|
|
869
|
-
document.name = 'My lovely document'
|
|
870
|
-
})
|
|
871
|
-
expect(onChange).not.toHaveBeenCalled()
|
|
872
|
-
})
|
|
873
|
-
|
|
874
|
-
test('it allows adding new records', async () => {
|
|
875
|
-
const id = PageRecordType.createId('page_3')
|
|
876
|
-
await room.updateStore((store) => {
|
|
877
|
-
const page = PageRecordType.create({ id, name: 'page 3', index: 'a0' as IndexKey })
|
|
878
|
-
store.put(page)
|
|
879
|
-
})
|
|
880
|
-
|
|
881
|
-
expect(storage.getSnapshot().documents.find((r) => r.state.id === id)?.state).toBeTruthy()
|
|
882
|
-
})
|
|
883
|
-
|
|
884
|
-
test('it allows deleting records', async () => {
|
|
885
|
-
await room.updateStore((store) => {
|
|
886
|
-
store.delete('page:page_2')
|
|
887
|
-
})
|
|
888
|
-
|
|
889
|
-
expect(storage.getSnapshot().documents.find((r) => r.state.id === 'page:page_2')).toBeFalsy()
|
|
890
|
-
})
|
|
891
|
-
|
|
892
|
-
test('it returns all records if you ask for them', async () => {
|
|
893
|
-
let allRecords
|
|
894
|
-
await room.updateStore((store) => {
|
|
895
|
-
allRecords = store.getAll()
|
|
896
|
-
})
|
|
897
|
-
expect(allRecords!.sort(sortById)).toEqual(
|
|
898
|
-
storage
|
|
899
|
-
.getSnapshot()
|
|
900
|
-
.documents.map((r) => r.state)
|
|
901
|
-
.sort(sortById)
|
|
902
|
-
)
|
|
903
|
-
await room.updateStore((store) => {
|
|
904
|
-
const page3 = PageRecordType.create({ name: 'page 3', index: 'a0' as IndexKey })
|
|
905
|
-
store.put(page3)
|
|
906
|
-
allRecords = store.getAll()
|
|
907
|
-
expect(allRecords.sort(sortById)).toEqual(
|
|
908
|
-
[...storage.getSnapshot().documents.map((r) => r.state), page3].sort(sortById)
|
|
909
|
-
)
|
|
910
|
-
store.delete(page3)
|
|
911
|
-
allRecords = store.getAll()
|
|
912
|
-
})
|
|
913
|
-
expect(allRecords!.sort(sortById)).toEqual(
|
|
914
|
-
storage
|
|
915
|
-
.getSnapshot()
|
|
916
|
-
.documents.map((r) => r.state)
|
|
917
|
-
.sort(sortById)
|
|
918
|
-
)
|
|
919
|
-
})
|
|
920
|
-
|
|
921
|
-
test('all operations fail after the store is closed', async () => {
|
|
922
|
-
let store
|
|
923
|
-
await room.updateStore((s) => {
|
|
924
|
-
store = s
|
|
925
|
-
})
|
|
926
|
-
expect(() => {
|
|
927
|
-
store!.put(PageRecordType.create({ name: 'page 3', index: 'a0' as IndexKey }))
|
|
928
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: StoreUpdateContext is closed]`)
|
|
929
|
-
expect(() => {
|
|
930
|
-
store!.delete('page:page_2')
|
|
931
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: StoreUpdateContext is closed]`)
|
|
932
|
-
expect(() => {
|
|
933
|
-
store!.getAll()
|
|
934
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: StoreUpdateContext is closed]`)
|
|
935
|
-
expect(() => {
|
|
936
|
-
store!.get('page:page_2')
|
|
937
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: StoreUpdateContext is closed]`)
|
|
938
|
-
})
|
|
939
|
-
|
|
940
|
-
test('it fails if the room is closed', async () => {
|
|
941
|
-
room.close()
|
|
942
|
-
await expect(
|
|
943
|
-
room.updateStore(() => {
|
|
944
|
-
// noop
|
|
945
|
-
})
|
|
946
|
-
).rejects.toMatchInlineSnapshot(`[Error: Cannot update store on a closed room]`)
|
|
947
|
-
})
|
|
948
|
-
|
|
949
|
-
test('it fails if you try to add bad data', async () => {
|
|
950
|
-
await expect(
|
|
951
|
-
room.updateStore((store) => {
|
|
952
|
-
const page = store.get('page:page') as TLPage
|
|
953
|
-
page.index = 34 as any
|
|
954
|
-
store.put(page)
|
|
955
|
-
})
|
|
956
|
-
).rejects.toMatchInlineSnapshot(
|
|
957
|
-
`[ValidationError: At page.index: Expected string, got a number]`
|
|
958
|
-
)
|
|
959
|
-
})
|
|
960
|
-
|
|
961
|
-
test('changes in multiple transaction are isolated from one another', async () => {
|
|
962
|
-
const page3 = PageRecordType.create({ name: 'page 3', index: 'a0' as IndexKey })
|
|
963
|
-
const didDelete = promiseWithResolve()
|
|
964
|
-
const didPut = promiseWithResolve()
|
|
965
|
-
const doneA = room.updateStore(async (store) => {
|
|
966
|
-
store.put(page3)
|
|
967
|
-
didPut.resolve(null)
|
|
968
|
-
await didDelete
|
|
969
|
-
expect(store.get(page3.id)).toBeTruthy()
|
|
970
|
-
})
|
|
971
|
-
const doneB = room.updateStore(async (store) => {
|
|
972
|
-
await didPut
|
|
973
|
-
expect(store.get(page3.id)).toBeFalsy()
|
|
974
|
-
store.delete(page3.id)
|
|
975
|
-
didDelete.resolve(null)
|
|
976
|
-
})
|
|
977
|
-
await Promise.all([doneA, doneB])
|
|
978
|
-
})
|
|
979
|
-
|
|
980
|
-
test('getting something that was deleted in the same transaction returns null', async () => {
|
|
981
|
-
await room.updateStore((store) => {
|
|
982
|
-
expect(store.get('page:page')).toBeTruthy()
|
|
983
|
-
store.delete('page:page')
|
|
984
|
-
expect(store.get('page:page')).toBe(null)
|
|
985
|
-
})
|
|
986
|
-
})
|
|
987
|
-
|
|
988
|
-
test('getting something that never existed in the first place returns null', async () => {
|
|
989
|
-
await room.updateStore((store) => {
|
|
990
|
-
expect(store.get('page:page_3')).toBe(null)
|
|
991
|
-
})
|
|
992
|
-
})
|
|
993
|
-
|
|
994
|
-
test('mutations to shapes gotten via .get are not committed unless you .put', async () => {
|
|
995
|
-
const page3 = PageRecordType.create({ name: 'page 3', index: 'a0' as IndexKey })
|
|
996
|
-
let page4 = PageRecordType.create({ name: 'page 4', index: 'a1' as IndexKey })
|
|
997
|
-
let page1
|
|
998
|
-
await room.updateStore((store) => {
|
|
999
|
-
page1 = store.get('page:page') as TLPage
|
|
1000
|
-
page1.name = 'my lovely page 1'
|
|
1001
|
-
store.put(page3)
|
|
1002
|
-
page3.name = 'my lovely page 3'
|
|
1003
|
-
store.put(page4)
|
|
1004
|
-
page4 = store.get(page4.id) as TLPage
|
|
1005
|
-
page4.name = 'my lovely page 4'
|
|
1006
|
-
})
|
|
1007
|
-
|
|
1008
|
-
const getPageNames = () =>
|
|
1009
|
-
room
|
|
1010
|
-
.getCurrentSnapshot()
|
|
1011
|
-
.documents.filter((r) => r.state.typeName === 'page')
|
|
1012
|
-
.map((r) => (r.state as any).name)
|
|
1013
|
-
.sort()
|
|
1014
|
-
|
|
1015
|
-
expect(getPageNames()).toEqual(['Page 1', 'page 3', 'page 4'])
|
|
1016
|
-
|
|
1017
|
-
await room.updateStore((store) => {
|
|
1018
|
-
store.put(page1!)
|
|
1019
|
-
store.put(page3)
|
|
1020
|
-
store.put(page4)
|
|
1021
|
-
})
|
|
1022
|
-
|
|
1023
|
-
expect(getPageNames()).toEqual(['my lovely page 1', 'my lovely page 3', 'my lovely page 4'])
|
|
1024
|
-
})
|
|
1025
|
-
})
|