cojson 0.19.2 → 0.19.4

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 (42) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/coValues/coList.d.ts +10 -4
  4. package/dist/coValues/coList.d.ts.map +1 -1
  5. package/dist/coValues/coList.js +30 -4
  6. package/dist/coValues/coList.js.map +1 -1
  7. package/dist/coValues/coPlainText.d.ts +2 -1
  8. package/dist/coValues/coPlainText.d.ts.map +1 -1
  9. package/dist/coValues/coPlainText.js +5 -2
  10. package/dist/coValues/coPlainText.js.map +1 -1
  11. package/dist/storage/sqlite/client.d.ts +3 -3
  12. package/dist/storage/sqlite/client.d.ts.map +1 -1
  13. package/dist/storage/sqlite/client.js +1 -1
  14. package/dist/storage/sqlite/client.js.map +1 -1
  15. package/dist/storage/sqliteAsync/client.d.ts +3 -3
  16. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  17. package/dist/storage/sqliteAsync/client.js +1 -1
  18. package/dist/storage/sqliteAsync/client.js.map +1 -1
  19. package/dist/storage/storageAsync.d.ts.map +1 -1
  20. package/dist/storage/storageAsync.js +7 -7
  21. package/dist/storage/storageAsync.js.map +1 -1
  22. package/dist/storage/storageSync.d.ts.map +1 -1
  23. package/dist/storage/storageSync.js +7 -7
  24. package/dist/storage/storageSync.js.map +1 -1
  25. package/dist/storage/types.d.ts +18 -14
  26. package/dist/storage/types.d.ts.map +1 -1
  27. package/dist/tests/coList.test.js +28 -1
  28. package/dist/tests/coList.test.js.map +1 -1
  29. package/dist/tests/sync.multipleServers.test.d.ts +2 -0
  30. package/dist/tests/sync.multipleServers.test.d.ts.map +1 -0
  31. package/dist/tests/sync.multipleServers.test.js +399 -0
  32. package/dist/tests/sync.multipleServers.test.js.map +1 -0
  33. package/package.json +3 -3
  34. package/src/coValues/coList.ts +41 -8
  35. package/src/coValues/coPlainText.ts +6 -2
  36. package/src/storage/sqlite/client.ts +6 -3
  37. package/src/storage/sqliteAsync/client.ts +8 -3
  38. package/src/storage/storageAsync.ts +8 -5
  39. package/src/storage/storageSync.ts +8 -5
  40. package/src/storage/types.ts +43 -37
  41. package/src/tests/coList.test.ts +36 -3
  42. package/src/tests/sync.multipleServers.test.ts +491 -0
@@ -0,0 +1,399 @@
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+ import { WasmCrypto } from "../crypto/WasmCrypto";
3
+ import { SyncMessagesLog, TEST_NODE_CONFIG, fillCoMapWithLargeData, loadCoValueOrFail, setupTestNode, waitFor, } from "./testUtils";
4
+ // We want to simulate a real world communication that happens asynchronously
5
+ TEST_NODE_CONFIG.withAsyncPeers = true;
6
+ const Crypto = await WasmCrypto.create();
7
+ let server1;
8
+ let server2;
9
+ beforeEach(async () => {
10
+ SyncMessagesLog.clear();
11
+ server1 = setupTestNode();
12
+ server2 = setupTestNode();
13
+ });
14
+ function connectServers(client) {
15
+ client.connectToSyncServer({
16
+ ourName: "client",
17
+ syncServerName: "server1",
18
+ syncServer: server1.node,
19
+ });
20
+ client.connectToSyncServer({
21
+ ourName: "client",
22
+ syncServerName: "server2",
23
+ syncServer: server2.node,
24
+ });
25
+ }
26
+ describe("multiple servers peers", () => {
27
+ test("coValue uploading", async () => {
28
+ const client = setupTestNode();
29
+ connectServers(client);
30
+ const group = client.node.createGroup();
31
+ const map = group.createMap();
32
+ map.set("hello", "world", "trusting");
33
+ await map.core.waitForSync();
34
+ expect(SyncMessagesLog.getMessages({
35
+ Group: group.core,
36
+ Map: map.core,
37
+ })).toMatchInlineSnapshot(`
38
+ [
39
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 3",
40
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 3",
41
+ "client -> server1 | CONTENT Map header: true new: After: 0 New: 1",
42
+ "client -> server2 | CONTENT Map header: true new: After: 0 New: 1",
43
+ "server1 -> client | KNOWN Group sessions: header/3",
44
+ "server2 -> client | KNOWN Group sessions: header/3",
45
+ "server1 -> client | KNOWN Map sessions: header/1",
46
+ "server2 -> client | KNOWN Map sessions: header/1",
47
+ ]
48
+ `);
49
+ const mapOnServer1 = server1.node.getCoValue(map.id);
50
+ const mapOnServer2 = server2.node.getCoValue(map.id);
51
+ expect(mapOnServer1.knownState()).toEqual(map.core.knownState());
52
+ expect(mapOnServer2.knownState()).toEqual(map.core.knownState());
53
+ });
54
+ test("coValue sync across clients", async () => {
55
+ const client = setupTestNode();
56
+ connectServers(client);
57
+ const group = client.node.createGroup();
58
+ const map = group.createMap();
59
+ map.set("count", 1, "trusting");
60
+ const session2 = client.spawnNewSession();
61
+ connectServers(session2);
62
+ const mapOnSession2 = await loadCoValueOrFail(session2.node, map.id);
63
+ mapOnSession2.set("count", 2, "trusting");
64
+ map.set("count", 3, "trusting");
65
+ await new Promise((resolve) => setTimeout(resolve, 10));
66
+ mapOnSession2.set("count", 4, "trusting");
67
+ await waitFor(() => {
68
+ expect(map.get("count")).toEqual(4);
69
+ expect(mapOnSession2.get("count")).toEqual(4);
70
+ });
71
+ expect(SyncMessagesLog.getMessages({
72
+ Group: group.core,
73
+ Map: map.core,
74
+ })).toMatchInlineSnapshot(`
75
+ [
76
+ "client -> server1 | LOAD Map sessions: empty",
77
+ "client -> server2 | LOAD Map sessions: empty",
78
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 3",
79
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 3",
80
+ "client -> server1 | CONTENT Map header: true new: After: 0 New: 1",
81
+ "client -> server2 | CONTENT Map header: true new: After: 0 New: 1",
82
+ "server1 -> client | KNOWN Map sessions: empty",
83
+ "server2 -> client | KNOWN Map sessions: empty",
84
+ "server1 -> client | KNOWN Group sessions: header/3",
85
+ "server2 -> client | KNOWN Group sessions: header/3",
86
+ "server1 -> client | KNOWN Map sessions: header/1",
87
+ "server1 -> client | CONTENT Group header: true new: After: 0 New: 3",
88
+ "server1 -> client | CONTENT Map header: true new: After: 0 New: 1",
89
+ "server2 -> client | KNOWN Map sessions: header/1",
90
+ "server2 -> client | CONTENT Group header: true new: After: 0 New: 3",
91
+ "server2 -> client | CONTENT Map header: true new: After: 0 New: 1",
92
+ "client -> server1 | KNOWN Group sessions: header/3",
93
+ "client -> server2 | LOAD Group sessions: header/3",
94
+ "client -> server1 | KNOWN Map sessions: header/1",
95
+ "client -> server2 | CONTENT Map header: true new: After: 0 New: 1",
96
+ "client -> server1 | CONTENT Map header: false new: After: 0 New: 1",
97
+ "client -> server2 | CONTENT Map header: false new: After: 0 New: 1",
98
+ "client -> server1 | CONTENT Map header: false new: After: 1 New: 1",
99
+ "client -> server2 | CONTENT Map header: false new: After: 1 New: 1",
100
+ "client -> server2 | KNOWN Group sessions: header/3",
101
+ "client -> server2 | CONTENT Group header: false new: After: 0 New: 3",
102
+ "client -> server2 | KNOWN Map sessions: header/2",
103
+ "server2 -> client | KNOWN Map sessions: header/1",
104
+ "server1 -> client | KNOWN Map sessions: header/2",
105
+ "server1 -> client | CONTENT Map header: false new: After: 0 New: 1",
106
+ "server2 -> client | KNOWN Map sessions: header/2",
107
+ "server2 -> client | CONTENT Map header: false new: After: 0 New: 1",
108
+ "server1 -> client | KNOWN Map sessions: header/3",
109
+ "server1 -> client | CONTENT Map header: false new: After: 1 New: 1",
110
+ "server2 -> client | KNOWN Map sessions: header/3",
111
+ "server2 -> client | CONTENT Map header: false new: After: 1 New: 1",
112
+ "server2 -> client | KNOWN Group sessions: header/3",
113
+ "client -> server1 | KNOWN Map sessions: header/3",
114
+ "client -> server2 | CONTENT Map header: false new: After: 0 New: 1",
115
+ "client -> server2 | KNOWN Map sessions: header/3",
116
+ "client -> server1 | KNOWN Map sessions: header/3",
117
+ "client -> server2 | CONTENT Map header: false new: After: 1 New: 1",
118
+ "client -> server2 | KNOWN Map sessions: header/3",
119
+ "server2 -> client | KNOWN Map sessions: header/3",
120
+ "server2 -> client | KNOWN Map sessions: header/3",
121
+ "client -> server1 | CONTENT Map header: false new: After: 1 New: 1",
122
+ "client -> server2 | CONTENT Map header: false new: After: 1 New: 1",
123
+ "server1 -> client | KNOWN Map sessions: header/4",
124
+ "server1 -> client | CONTENT Map header: false new: After: 1 New: 1",
125
+ "server2 -> client | KNOWN Map sessions: header/4",
126
+ "server2 -> client | CONTENT Map header: false new: After: 1 New: 1",
127
+ "client -> server1 | KNOWN Map sessions: header/4",
128
+ "client -> server2 | CONTENT Map header: false new: After: 1 New: 1",
129
+ "client -> server2 | KNOWN Map sessions: header/4",
130
+ "server2 -> client | KNOWN Map sessions: header/4",
131
+ ]
132
+ `);
133
+ const mapOnServer1 = server1.node.getCoValue(map.id);
134
+ const mapOnServer2 = server2.node.getCoValue(map.id);
135
+ expect(mapOnServer1.knownState()).toEqual(map.core.knownState());
136
+ expect(mapOnServer2.knownState()).toEqual(map.core.knownState());
137
+ });
138
+ test("coValue with parent groups uploading", async () => {
139
+ const client = setupTestNode();
140
+ connectServers(client);
141
+ const group = client.node.createGroup();
142
+ const parentGroup = client.node.createGroup();
143
+ parentGroup.addMember("everyone", "reader");
144
+ group.extend(parentGroup);
145
+ const map = group.createMap();
146
+ map.set("hello", "world", "trusting");
147
+ await map.core.waitForSync();
148
+ expect(SyncMessagesLog.getMessages({
149
+ ParentGroup: parentGroup.core,
150
+ Group: group.core,
151
+ Map: map.core,
152
+ })).toMatchInlineSnapshot(`
153
+ [
154
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 3",
155
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 3",
156
+ "client -> server1 | CONTENT ParentGroup header: true new: After: 0 New: 5",
157
+ "client -> server2 | CONTENT ParentGroup header: true new: After: 0 New: 5",
158
+ "client -> server1 | CONTENT Group header: false new: After: 3 New: 2",
159
+ "client -> server2 | CONTENT Group header: false new: After: 3 New: 2",
160
+ "client -> server1 | CONTENT Map header: true new: After: 0 New: 1",
161
+ "client -> server2 | CONTENT Map header: true new: After: 0 New: 1",
162
+ "server1 -> client | KNOWN Group sessions: header/3",
163
+ "server2 -> client | KNOWN Group sessions: header/3",
164
+ "server1 -> client | KNOWN ParentGroup sessions: header/5",
165
+ "server2 -> client | KNOWN ParentGroup sessions: header/5",
166
+ "server1 -> client | KNOWN Group sessions: header/5",
167
+ "server2 -> client | KNOWN Group sessions: header/5",
168
+ "server1 -> client | KNOWN Map sessions: header/1",
169
+ "server2 -> client | KNOWN Map sessions: header/1",
170
+ ]
171
+ `);
172
+ const mapOnServer1 = server1.node.getCoValue(map.id);
173
+ const mapOnServer2 = server2.node.getCoValue(map.id);
174
+ expect(mapOnServer1.knownState()).toEqual(map.core.knownState());
175
+ expect(mapOnServer2.knownState()).toEqual(map.core.knownState());
176
+ });
177
+ test("wrong optimistic known state should be corrected", async () => {
178
+ const client = setupTestNode();
179
+ connectServers(client);
180
+ const group = client.node.createGroup();
181
+ group.addMember("everyone", "writer");
182
+ const map = group.createMap({
183
+ fromServer: "initial",
184
+ fromClient: "initial",
185
+ });
186
+ // Load the coValue on the client
187
+ await map.core.waitForSync();
188
+ // Forcefully delete the coValue from server1 (simulating some data loss)
189
+ server1.node.internalDeleteCoValue(map.id);
190
+ map.set("fromClient", "updated", "trusting");
191
+ await waitFor(() => {
192
+ const mapOnServer1 = server1.node.getCoValue(map.id);
193
+ const mapOnServer2 = server2.node.getCoValue(map.id);
194
+ expect(mapOnServer1.knownState()).toEqual(map.core.knownState());
195
+ expect(mapOnServer2.knownState()).toEqual(map.core.knownState());
196
+ });
197
+ expect(SyncMessagesLog.getMessages({
198
+ Group: group.core,
199
+ Map: map.core,
200
+ })).toMatchInlineSnapshot(`
201
+ [
202
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 5",
203
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 5",
204
+ "client -> server1 | CONTENT Map header: true new: After: 0 New: 1",
205
+ "client -> server2 | CONTENT Map header: true new: After: 0 New: 1",
206
+ "server1 -> client | KNOWN Group sessions: header/5",
207
+ "server2 -> client | KNOWN Group sessions: header/5",
208
+ "server1 -> client | KNOWN Map sessions: header/1",
209
+ "server2 -> client | KNOWN Map sessions: header/1",
210
+ "client -> server1 | CONTENT Map header: false new: After: 1 New: 1",
211
+ "client -> server2 | CONTENT Map header: false new: After: 1 New: 1",
212
+ "server1 -> client | KNOWN CORRECTION Map sessions: empty",
213
+ "server2 -> client | KNOWN Map sessions: header/2",
214
+ "client -> server1 | CONTENT Map header: true new: After: 0 New: 2",
215
+ "server1 -> client | KNOWN Map sessions: header/2",
216
+ ]
217
+ `);
218
+ });
219
+ test("local updates batching", async () => {
220
+ const client = setupTestNode();
221
+ connectServers(client);
222
+ const group = client.node.createGroup();
223
+ const initialMap = group.createMap();
224
+ const child = group.createMap();
225
+ child.set("parent", initialMap.id);
226
+ initialMap.set("child", child.id);
227
+ await initialMap.core.waitForSync();
228
+ expect(SyncMessagesLog.getMessages({
229
+ Group: group.core,
230
+ InitialMap: initialMap.core,
231
+ ChildMap: child.core,
232
+ })).toMatchInlineSnapshot(`
233
+ [
234
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 3",
235
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 3",
236
+ "client -> server1 | CONTENT InitialMap header: true new: ",
237
+ "client -> server2 | CONTENT InitialMap header: true new: ",
238
+ "client -> server1 | CONTENT ChildMap header: true new: After: 0 New: 1",
239
+ "client -> server2 | CONTENT ChildMap header: true new: After: 0 New: 1",
240
+ "client -> server1 | CONTENT InitialMap header: false new: After: 0 New: 1",
241
+ "client -> server2 | CONTENT InitialMap header: false new: After: 0 New: 1",
242
+ "server1 -> client | KNOWN Group sessions: header/3",
243
+ "server2 -> client | KNOWN Group sessions: header/3",
244
+ "server1 -> client | KNOWN InitialMap sessions: header/0",
245
+ "server2 -> client | KNOWN InitialMap sessions: header/0",
246
+ "server1 -> client | KNOWN ChildMap sessions: header/1",
247
+ "server2 -> client | KNOWN ChildMap sessions: header/1",
248
+ "server1 -> client | KNOWN InitialMap sessions: header/1",
249
+ "server2 -> client | KNOWN InitialMap sessions: header/1",
250
+ ]
251
+ `);
252
+ });
253
+ test("large coValue upload streaming", async () => {
254
+ const client = setupTestNode();
255
+ connectServers(client);
256
+ const group = client.node.createGroup();
257
+ group.addMember("everyone", "writer");
258
+ const largeMap = group.createMap();
259
+ fillCoMapWithLargeData(largeMap);
260
+ await largeMap.core.waitForSync();
261
+ expect(SyncMessagesLog.getMessages({
262
+ Group: group.core,
263
+ Map: largeMap.core,
264
+ })).toMatchInlineSnapshot(`
265
+ [
266
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 5",
267
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 5",
268
+ "client -> server1 | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
269
+ "client -> server2 | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
270
+ "client -> server1 | CONTENT Map header: false new: After: 73 New: 73",
271
+ "client -> server2 | CONTENT Map header: false new: After: 73 New: 73",
272
+ "client -> server1 | CONTENT Map header: false new: After: 146 New: 54",
273
+ "client -> server2 | CONTENT Map header: false new: After: 146 New: 54",
274
+ "server1 -> client | KNOWN Group sessions: header/5",
275
+ "server2 -> client | KNOWN Group sessions: header/5",
276
+ "server1 -> client | KNOWN Map sessions: header/73",
277
+ "server2 -> client | KNOWN Map sessions: header/73",
278
+ "server1 -> client | KNOWN Map sessions: header/146",
279
+ "server2 -> client | KNOWN Map sessions: header/146",
280
+ "server1 -> client | KNOWN Map sessions: header/200",
281
+ "server2 -> client | KNOWN Map sessions: header/200",
282
+ ]
283
+ `);
284
+ });
285
+ test("uploading a large update", async () => {
286
+ const client = setupTestNode();
287
+ connectServers(client);
288
+ const group = client.node.createGroup();
289
+ group.addMember("everyone", "writer");
290
+ const largeMap = group.createMap();
291
+ await largeMap.core.waitForSync();
292
+ fillCoMapWithLargeData(largeMap);
293
+ await largeMap.core.waitForSync();
294
+ expect(SyncMessagesLog.getMessages({
295
+ Group: group.core,
296
+ Map: largeMap.core,
297
+ })).toMatchInlineSnapshot(`
298
+ [
299
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 5",
300
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 5",
301
+ "client -> server1 | CONTENT Map header: true new: ",
302
+ "client -> server2 | CONTENT Map header: true new: ",
303
+ "client -> server1 | CONTENT Map header: false new: After: 0 New: 73 expectContentUntil: header/200",
304
+ "client -> server2 | CONTENT Map header: false new: After: 0 New: 73 expectContentUntil: header/200",
305
+ "client -> server1 | CONTENT Map header: false new: After: 73 New: 73",
306
+ "client -> server2 | CONTENT Map header: false new: After: 73 New: 73",
307
+ "client -> server1 | CONTENT Map header: false new: After: 146 New: 54",
308
+ "client -> server2 | CONTENT Map header: false new: After: 146 New: 54",
309
+ "server1 -> client | KNOWN Group sessions: header/5",
310
+ "server2 -> client | KNOWN Group sessions: header/5",
311
+ "server1 -> client | KNOWN Map sessions: header/0",
312
+ "server2 -> client | KNOWN Map sessions: header/0",
313
+ "server1 -> client | KNOWN Map sessions: header/73",
314
+ "server2 -> client | KNOWN Map sessions: header/73",
315
+ "server1 -> client | KNOWN Map sessions: header/146",
316
+ "server2 -> client | KNOWN Map sessions: header/146",
317
+ "server1 -> client | KNOWN Map sessions: header/200",
318
+ "server2 -> client | KNOWN Map sessions: header/200",
319
+ ]
320
+ `);
321
+ });
322
+ test("uploading a large update between two clients", async () => {
323
+ const client = setupTestNode();
324
+ connectServers(client);
325
+ const client2 = setupTestNode();
326
+ connectServers(client2);
327
+ const group = client.node.createGroup();
328
+ group.addMember("everyone", "writer");
329
+ const largeMap = group.createMap();
330
+ const largeMapOnClient2 = await loadCoValueOrFail(client2.node, largeMap.id);
331
+ fillCoMapWithLargeData(largeMap);
332
+ await waitFor(() => {
333
+ expect(largeMapOnClient2.core.knownState()).toEqual(largeMap.core.knownState());
334
+ });
335
+ expect(SyncMessagesLog.getMessages({
336
+ Group: group.core,
337
+ Map: largeMap.core,
338
+ })).toMatchInlineSnapshot(`
339
+ [
340
+ "client -> server1 | LOAD Map sessions: empty",
341
+ "client -> server2 | LOAD Map sessions: empty",
342
+ "client -> server1 | CONTENT Group header: true new: After: 0 New: 5",
343
+ "client -> server2 | CONTENT Group header: true new: After: 0 New: 5",
344
+ "client -> server1 | CONTENT Map header: true new: ",
345
+ "client -> server2 | CONTENT Map header: true new: ",
346
+ "server1 -> client | KNOWN Map sessions: empty",
347
+ "server2 -> client | KNOWN Map sessions: empty",
348
+ "server1 -> client | KNOWN Group sessions: header/5",
349
+ "server2 -> client | KNOWN Group sessions: header/5",
350
+ "server1 -> client | KNOWN Map sessions: header/0",
351
+ "server1 -> client | CONTENT Group header: true new: After: 0 New: 5",
352
+ "server1 -> client | CONTENT Map header: true new: ",
353
+ "server2 -> client | KNOWN Map sessions: header/0",
354
+ "server2 -> client | CONTENT Group header: true new: After: 0 New: 5",
355
+ "server2 -> client | CONTENT Map header: true new: ",
356
+ "client -> server1 | KNOWN Group sessions: header/5",
357
+ "client -> server2 | LOAD Group sessions: header/5",
358
+ "client -> server1 | KNOWN Map sessions: header/0",
359
+ "client -> server2 | CONTENT Map header: true new: ",
360
+ "client -> server1 | CONTENT Map header: false new: After: 0 New: 73 expectContentUntil: header/200",
361
+ "client -> server2 | CONTENT Map header: false new: After: 0 New: 73 expectContentUntil: header/200",
362
+ "client -> server1 | CONTENT Map header: false new: After: 73 New: 73",
363
+ "client -> server2 | CONTENT Map header: false new: After: 73 New: 73",
364
+ "client -> server1 | CONTENT Map header: false new: After: 146 New: 54",
365
+ "client -> server2 | CONTENT Map header: false new: After: 146 New: 54",
366
+ "client -> server2 | KNOWN Group sessions: header/5",
367
+ "client -> server2 | CONTENT Group header: false new: After: 0 New: 5",
368
+ "client -> server2 | KNOWN Map sessions: header/0",
369
+ "server2 -> client | KNOWN Map sessions: header/0",
370
+ "server1 -> client | KNOWN Map sessions: header/73",
371
+ "server1 -> client | CONTENT Map header: false new: After: 0 New: 73",
372
+ "server2 -> client | KNOWN Map sessions: header/73",
373
+ "server2 -> client | CONTENT Map header: false new: After: 0 New: 73",
374
+ "server1 -> client | KNOWN Map sessions: header/146",
375
+ "server1 -> client | CONTENT Map header: false new: After: 73 New: 73",
376
+ "server2 -> client | KNOWN Map sessions: header/146",
377
+ "server2 -> client | CONTENT Map header: false new: After: 73 New: 73",
378
+ "server1 -> client | KNOWN Map sessions: header/200",
379
+ "server1 -> client | CONTENT Map header: false new: After: 146 New: 54",
380
+ "server2 -> client | KNOWN Map sessions: header/200",
381
+ "server2 -> client | CONTENT Map header: false new: After: 146 New: 54",
382
+ "server2 -> client | KNOWN Group sessions: header/5",
383
+ "client -> server1 | KNOWN Map sessions: header/73",
384
+ "client -> server2 | CONTENT Map header: false new: After: 0 New: 73",
385
+ "client -> server2 | KNOWN Map sessions: header/73",
386
+ "client -> server1 | KNOWN Map sessions: header/146",
387
+ "client -> server2 | CONTENT Map header: false new: After: 73 New: 73",
388
+ "client -> server2 | KNOWN Map sessions: header/146",
389
+ "client -> server1 | KNOWN Map sessions: header/200",
390
+ "client -> server2 | CONTENT Map header: false new: After: 146 New: 54",
391
+ "client -> server2 | KNOWN Map sessions: header/200",
392
+ "server2 -> client | KNOWN Map sessions: header/200",
393
+ "server2 -> client | KNOWN Map sessions: header/200",
394
+ "server2 -> client | KNOWN Map sessions: header/200",
395
+ ]
396
+ `);
397
+ });
398
+ });
399
+ //# sourceMappingURL=sync.multipleServers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.multipleServers.test.js","sourceRoot":"","sources":["../../src/tests/sync.multipleServers.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "type": "module",
35
35
  "license": "MIT",
36
- "version": "0.19.2",
36
+ "version": "0.19.4",
37
37
  "devDependencies": {
38
38
  "@opentelemetry/sdk-metrics": "^2.0.0",
39
39
  "libsql": "^0.5.13",
@@ -46,8 +46,8 @@
46
46
  "@opentelemetry/api": "^1.9.0",
47
47
  "@scure/base": "1.2.1",
48
48
  "unicode-segmenter": "^0.12.0",
49
- "cojson-core-wasm": "0.19.2",
50
- "cojson-core-napi": "0.19.2"
49
+ "cojson-core-wasm": "0.19.4",
50
+ "cojson-core-napi": "0.19.4"
51
51
  },
52
52
  "gitHead": "33c27053293b4801b968c61d5c4c989f93a67d13",
53
53
  "scripts": {
@@ -6,7 +6,6 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
6
6
  import { isCoValue } from "../typeUtils/isCoValue.js";
7
7
  import { RawAccountID } from "./account.js";
8
8
  import { RawGroup } from "./group.js";
9
- import { Transaction } from "../coValueCore/verifiedState.js";
10
9
 
11
10
  export type OpID = TransactionID & { changeIdx: number };
12
11
 
@@ -73,6 +72,14 @@ export class RawCoList<
73
72
  };
74
73
  };
75
74
  } = {};
75
+
76
+ /** @internal */
77
+ atTimeFilter?: number = undefined;
78
+
79
+ /** @category 6. Meta */
80
+ readonly _item!: Item;
81
+
82
+ /** @internal */
76
83
  _cachedEntries?: {
77
84
  value: Item;
78
85
  madeAt: number;
@@ -94,14 +101,19 @@ export class RawCoList<
94
101
  this.totalValidTransactions = 0;
95
102
  }
96
103
 
97
- /** @category 6. Meta */
98
- readonly _item!: Item;
99
-
100
104
  /** @internal */
101
- constructor(core: AvailableCoValueCore) {
105
+ constructor(core: AvailableCoValueCore, atTimeFilter?: number) {
102
106
  this.id = core.id as CoID<this>;
103
107
  this.core = core;
104
- this.processNewTransactions();
108
+
109
+ this.insertions = {};
110
+ this.deletionsByInsertion = {};
111
+ this.afterStart = [];
112
+ this.beforeEnd = [];
113
+ this.knownTransactions = { [core.id]: 0 };
114
+ this.atTimeFilter = atTimeFilter;
115
+
116
+ this._processNewTransactions();
105
117
  }
106
118
 
107
119
  private getInsertionsEntry(opID: OpID) {
@@ -188,6 +200,13 @@ export class RawCoList<
188
200
  }
189
201
 
190
202
  processNewTransactions() {
203
+ if (this.isTimeTravelEntity()) {
204
+ throw new Error("Cannot process transactions on a time travel entity");
205
+ }
206
+ this._processNewTransactions();
207
+ }
208
+
209
+ private _processNewTransactions() {
191
210
  const transactions = this.core.getValidSortedTransactions({
192
211
  ignorePrivateTransactions: false,
193
212
  knownTransactions: this.knownTransactions,
@@ -202,6 +221,9 @@ export class RawCoList<
202
221
  this._cachedEntries = undefined;
203
222
 
204
223
  for (const { txID, changes, madeAt } of transactions) {
224
+ if (this.isFilteredOut(madeAt)) {
225
+ continue;
226
+ }
205
227
  lastValidTransaction = Math.max(lastValidTransaction ?? 0, madeAt);
206
228
  oldestValidTransaction = Math.min(
207
229
  oldestValidTransaction ?? Infinity,
@@ -290,6 +312,17 @@ export class RawCoList<
290
312
  this.processNewTransactions();
291
313
  }
292
314
 
315
+ isTimeTravelEntity() {
316
+ return Boolean(this.atTimeFilter);
317
+ }
318
+
319
+ private isFilteredOut(time: number): boolean {
320
+ if (this.atTimeFilter === undefined) {
321
+ return false;
322
+ }
323
+ return time > this.atTimeFilter;
324
+ }
325
+
293
326
  /** @category 6. Meta */
294
327
  get headerMeta(): Meta {
295
328
  return this.core.verified.header.meta as Meta;
@@ -305,8 +338,8 @@ export class RawCoList<
305
338
  *
306
339
  * @category 4. Time travel
307
340
  */
308
- atTime(_time: number): this {
309
- throw new Error("Not yet implemented");
341
+ atTime(time: number): this {
342
+ return new RawCoList(this.core, time) as this;
310
343
  }
311
344
 
312
345
  /**
@@ -55,8 +55,8 @@ export class RawCoPlainText<
55
55
  PlaintextIdxMapping
56
56
  >;
57
57
 
58
- constructor(core: AvailableCoValueCore) {
59
- super(core);
58
+ constructor(core: AvailableCoValueCore, atTimeFilter?: number) {
59
+ super(core, atTimeFilter);
60
60
  this._cachedMapping = new WeakMap();
61
61
  }
62
62
 
@@ -97,6 +97,10 @@ export class RawCoPlainText<
97
97
  .join("");
98
98
  }
99
99
 
100
+ atTime(time: number): this {
101
+ return new RawCoPlainText(this.core, time) as this;
102
+ }
103
+
100
104
  /**
101
105
  * Inserts `text` before the character at index `idx`.
102
106
  * If idx is 0, inserts at the start of the text.
@@ -8,6 +8,7 @@ import { logger } from "../../logger.js";
8
8
  import type { NewContentMessage } from "../../sync.js";
9
9
  import type {
10
10
  DBClientInterfaceSync,
11
+ DBTransactionInterfaceSync,
11
12
  SessionRow,
12
13
  SignatureAfterRow,
13
14
  StoredCoValueRow,
@@ -31,7 +32,9 @@ export function getErrorMessage(error: unknown) {
31
32
  return error instanceof Error ? error.message : "Unknown error";
32
33
  }
33
34
 
34
- export class SQLiteClient implements DBClientInterfaceSync {
35
+ export class SQLiteClient
36
+ implements DBClientInterfaceSync, DBTransactionInterfaceSync
37
+ {
35
38
  private readonly db: SQLiteDatabaseDriver;
36
39
 
37
40
  constructor(db: SQLiteDatabaseDriver) {
@@ -187,8 +190,8 @@ export class SQLiteClient implements DBClientInterfaceSync {
187
190
  );
188
191
  }
189
192
 
190
- transaction(operationsCallback: () => unknown) {
191
- this.db.transaction(operationsCallback);
193
+ transaction(operationsCallback: (tx: DBTransactionInterfaceSync) => unknown) {
194
+ this.db.transaction(() => operationsCallback(this));
192
195
  return undefined;
193
196
  }
194
197
  }
@@ -8,6 +8,7 @@ import { logger } from "../../logger.js";
8
8
  import type { NewContentMessage } from "../../sync.js";
9
9
  import type {
10
10
  DBClientInterfaceAsync,
11
+ DBTransactionInterfaceAsync,
11
12
  SessionRow,
12
13
  SignatureAfterRow,
13
14
  StoredCoValueRow,
@@ -31,7 +32,9 @@ export function getErrorMessage(error: unknown) {
31
32
  return error instanceof Error ? error.message : "Unknown error";
32
33
  }
33
34
 
34
- export class SQLiteClientAsync implements DBClientInterfaceAsync {
35
+ export class SQLiteClientAsync
36
+ implements DBClientInterfaceAsync, DBTransactionInterfaceAsync
37
+ {
35
38
  private readonly db: SQLiteDatabaseDriverAsync;
36
39
 
37
40
  constructor(db: SQLiteDatabaseDriverAsync) {
@@ -194,7 +197,9 @@ export class SQLiteClientAsync implements DBClientInterfaceAsync {
194
197
  );
195
198
  }
196
199
 
197
- async transaction(operationsCallback: () => unknown) {
198
- return this.db.transaction(operationsCallback);
200
+ async transaction(
201
+ operationsCallback: (tx: DBTransactionInterfaceAsync) => Promise<unknown>,
202
+ ) {
203
+ return this.db.transaction(() => operationsCallback(this));
199
204
  }
200
205
  }