cojson-storage-sqlite 0.15.7 → 0.15.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.js","sourceRoot":"","sources":["../../src/tests/testUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,MAAM,QAAQ,GAGR,EAAE,CAAC;IAET,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,SAAS,CAAC,iBAAiB,CAAC;IACjD,MAAM,uBAAuB,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC;IAEnE,kBAAkB,CAAC,SAAS,CAAC,iBAAiB,GAAG,KAAK,WAAW,GAAG;QAClE,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ;YACd,GAAG;SACJ,CAAC,CAAC;QACH,OAAO,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC,CAAC;IAEF,IAAI,CAAC,WAAW,CAAC,iBAAiB,GAAG,KAAK,WAAW,GAAG,EAAE,IAAI;QAC5D,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,SAAS;YACf,GAAG;SACJ,CAAC,CAAC;QACH,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,kBAAkB,CAAC,SAAS,CAAC,iBAAiB,GAAG,yBAAyB,CAAC;QAC3E,IAAI,CAAC,WAAW,CAAC,iBAAiB,GAAG,uBAAuB,CAAC;IAC/D,CAAC,CAAC;IAEF,cAAc,CAAC,GAAG,EAAE;QAClB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,QAAQ;QACR,OAAO;KACR,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,OAAO,CACrB,QAAkE;IAElE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;YAC7B,IAAI,CAAC;gBACH,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC/C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YACtC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,MAAM,WAAW,EAAE,CAAC;YAE1C,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;gBACjB,aAAa,CAAC,QAAQ,CAAC,CAAC;gBACxB,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,IAAI,EAAE,OAAO,GAAG,EAAE,EAAE,CAAC;gBACnB,aAAa,CAAC,QAAQ,CAAC,CAAC;gBACxB,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"testUtils.js","sourceRoot":"","sources":["../../src/tests/testUtils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "cojson-storage-sqlite",
3
3
  "type": "module",
4
- "version": "0.15.7",
4
+ "version": "0.15.9",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
9
  "better-sqlite3": "^11.7.0",
10
- "cojson": "0.15.7",
11
- "cojson-storage": "0.15.7"
10
+ "cojson": "0.15.9"
12
11
  },
13
12
  "devDependencies": {
14
13
  "@types/better-sqlite3": "^7.6.12",
package/src/index.ts CHANGED
@@ -1 +1,39 @@
1
- export { SQLiteNode, SQLiteNode as SQLiteStorage } from "./sqliteNode.js";
1
+ import Database, { type Database as DatabaseT } from "better-sqlite3";
2
+ import type { SQLiteDatabaseDriver } from "cojson";
3
+ import { getSqliteStorage } from "cojson";
4
+
5
+ export class BetterSqliteDriver implements SQLiteDatabaseDriver {
6
+ private readonly db: DatabaseT;
7
+
8
+ constructor(filename: string) {
9
+ const db = new Database(filename);
10
+ this.db = db;
11
+ db.pragma("journal_mode = WAL");
12
+ }
13
+
14
+ run(sql: string, params: unknown[]) {
15
+ this.db.prepare(sql).run(params);
16
+ }
17
+
18
+ query<T>(sql: string, params: unknown[]): T[] {
19
+ return this.db.prepare(sql).all(params) as T[];
20
+ }
21
+
22
+ get<T>(sql: string, params: unknown[]): T | undefined {
23
+ return this.db.prepare(sql).get(params) as T | undefined;
24
+ }
25
+
26
+ transaction(callback: () => unknown) {
27
+ return this.db.transaction(callback)();
28
+ }
29
+
30
+ closeDb() {
31
+ this.db.close();
32
+ }
33
+ }
34
+
35
+ export function getBetterSqliteStorage(filename: string) {
36
+ const db = new BetterSqliteDriver(filename);
37
+
38
+ return getSqliteStorage(db);
39
+ }
@@ -2,18 +2,16 @@ import { randomUUID } from "node:crypto";
2
2
  import { unlinkSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
- import { LocalNode, cojsonInternals } from "cojson";
6
- import { SQLiteNodeBase, StorageManagerSync } from "cojson-storage";
5
+ import { LocalNode, StorageApiSync, cojsonInternals } from "cojson";
7
6
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
8
7
  import { expect, onTestFinished, test, vi } from "vitest";
9
- import { BetterSqliteDriver } from "../betterSqliteDriver.js";
10
- import { SQLiteNode } from "../index.js";
8
+ import { getBetterSqliteStorage } from "../index.js";
11
9
  import { toSimplifiedMessages } from "./messagesTestUtils.js";
12
10
  import { trackMessages, waitFor } from "./testUtils.js";
13
11
 
14
12
  const Crypto = await WasmCrypto.create();
15
13
 
16
- async function createSQLiteStorage(defaultDbPath?: string) {
14
+ function createSQLiteStorage(defaultDbPath?: string) {
17
15
  const dbPath = defaultDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
18
16
 
19
17
  if (!defaultDbPath) {
@@ -23,29 +21,11 @@ async function createSQLiteStorage(defaultDbPath?: string) {
23
21
  }
24
22
 
25
23
  return {
26
- peer: await SQLiteNode.asPeer({
27
- filename: dbPath,
28
- }),
24
+ storage: getBetterSqliteStorage(dbPath),
29
25
  dbPath,
30
26
  };
31
27
  }
32
28
 
33
- test("Should be able to initialize and load from empty DB", async () => {
34
- const agentSecret = Crypto.newRandomAgentSecret();
35
-
36
- const node = new LocalNode(
37
- agentSecret,
38
- Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
39
- Crypto,
40
- );
41
-
42
- node.syncManager.addPeer((await createSQLiteStorage()).peer);
43
-
44
- await new Promise((resolve) => setTimeout(resolve, 200));
45
-
46
- expect(node.syncManager.peers.storage).toBeDefined();
47
- });
48
-
49
29
  test("should sync and load data from storage", async () => {
50
30
  const agentSecret = Crypto.newRandomAgentSecret();
51
31
 
@@ -55,11 +35,11 @@ test("should sync and load data from storage", async () => {
55
35
  Crypto,
56
36
  );
57
37
 
58
- const node1Sync = trackMessages(node1);
38
+ const node1Sync = trackMessages();
59
39
 
60
- const { peer, dbPath } = await createSQLiteStorage();
40
+ const { storage, dbPath } = createSQLiteStorage();
61
41
 
62
- node1.syncManager.addPeer(peer);
42
+ node1.setStorage(storage);
63
43
 
64
44
  const group = node1.createGroup();
65
45
 
@@ -80,9 +60,7 @@ test("should sync and load data from storage", async () => {
80
60
  ).toMatchInlineSnapshot(`
81
61
  [
82
62
  "client -> CONTENT Group header: true new: After: 0 New: 3",
83
- "storage -> KNOWN Group sessions: header/3",
84
63
  "client -> CONTENT Map header: true new: After: 0 New: 1",
85
- "storage -> KNOWN Map sessions: header/1",
86
64
  ]
87
65
  `);
88
66
 
@@ -94,11 +72,9 @@ test("should sync and load data from storage", async () => {
94
72
  Crypto,
95
73
  );
96
74
 
97
- const node2Sync = trackMessages(node2);
98
-
99
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
75
+ const node2Sync = trackMessages();
100
76
 
101
- node2.syncManager.addPeer(peer2);
77
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
102
78
 
103
79
  const map2 = await node2.load(map.id);
104
80
  if (map2 === "unavailable") {
@@ -119,9 +95,7 @@ test("should sync and load data from storage", async () => {
119
95
  [
120
96
  "client -> LOAD Map sessions: empty",
121
97
  "storage -> CONTENT Group header: true new: After: 0 New: 3",
122
- "client -> KNOWN Group sessions: header/3",
123
98
  "storage -> CONTENT Map header: true new: After: 0 New: 1",
124
- "client -> KNOWN Map sessions: header/1",
125
99
  ]
126
100
  `);
127
101
 
@@ -137,11 +111,11 @@ test("should send an empty content message if there is no content", async () =>
137
111
  Crypto,
138
112
  );
139
113
 
140
- const node1Sync = trackMessages(node1);
114
+ const node1Sync = trackMessages();
141
115
 
142
- const { peer, dbPath } = await createSQLiteStorage();
116
+ const { storage, dbPath } = createSQLiteStorage();
143
117
 
144
- node1.syncManager.addPeer(peer);
118
+ node1.setStorage(storage);
145
119
 
146
120
  const group = node1.createGroup();
147
121
 
@@ -160,9 +134,7 @@ test("should send an empty content message if there is no content", async () =>
160
134
  ).toMatchInlineSnapshot(`
161
135
  [
162
136
  "client -> CONTENT Group header: true new: After: 0 New: 3",
163
- "storage -> KNOWN Group sessions: header/3",
164
137
  "client -> CONTENT Map header: true new: ",
165
- "storage -> KNOWN Map sessions: header/0",
166
138
  ]
167
139
  `);
168
140
 
@@ -174,11 +146,9 @@ test("should send an empty content message if there is no content", async () =>
174
146
  Crypto,
175
147
  );
176
148
 
177
- const node2Sync = trackMessages(node2);
178
-
179
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
149
+ const node2Sync = trackMessages();
180
150
 
181
- node2.syncManager.addPeer(peer2);
151
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
182
152
 
183
153
  const map2 = await node2.load(map.id);
184
154
  if (map2 === "unavailable") {
@@ -197,9 +167,7 @@ test("should send an empty content message if there is no content", async () =>
197
167
  [
198
168
  "client -> LOAD Map sessions: empty",
199
169
  "storage -> CONTENT Group header: true new: After: 0 New: 3",
200
- "client -> KNOWN Group sessions: header/3",
201
170
  "storage -> CONTENT Map header: true new: ",
202
- "client -> KNOWN Map sessions: header/0",
203
171
  ]
204
172
  `);
205
173
 
@@ -215,11 +183,11 @@ test("should load dependencies correctly (group inheritance)", async () => {
215
183
  Crypto,
216
184
  );
217
185
 
218
- const node1Sync = trackMessages(node1);
186
+ const node1Sync = trackMessages();
219
187
 
220
- const { peer, dbPath } = await createSQLiteStorage();
188
+ const { storage, dbPath } = createSQLiteStorage();
221
189
 
222
- node1.syncManager.addPeer(peer);
190
+ node1.setStorage(storage);
223
191
 
224
192
  const group = node1.createGroup();
225
193
  const parentGroup = node1.createGroup();
@@ -243,12 +211,9 @@ test("should load dependencies correctly (group inheritance)", async () => {
243
211
  ),
244
212
  ).toMatchInlineSnapshot(`
245
213
  [
246
- "client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
247
- "storage -> KNOWN ParentGroup sessions: header/4",
248
214
  "client -> CONTENT Group header: true new: After: 0 New: 5",
249
- "storage -> KNOWN Group sessions: header/5",
215
+ "client -> CONTENT ParentGroup header: true new: After: 0 New: 4",
250
216
  "client -> CONTENT Map header: true new: After: 0 New: 1",
251
- "storage -> KNOWN Map sessions: header/1",
252
217
  ]
253
218
  `);
254
219
 
@@ -260,11 +225,9 @@ test("should load dependencies correctly (group inheritance)", async () => {
260
225
  Crypto,
261
226
  );
262
227
 
263
- const node2Sync = trackMessages(node2);
264
-
265
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
228
+ const node2Sync = trackMessages();
266
229
 
267
- node2.syncManager.addPeer(peer2);
230
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
268
231
 
269
232
  await node2.load(map.id);
270
233
 
@@ -285,11 +248,8 @@ test("should load dependencies correctly (group inheritance)", async () => {
285
248
  [
286
249
  "client -> LOAD Map sessions: empty",
287
250
  "storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
288
- "client -> KNOWN ParentGroup sessions: header/4",
289
251
  "storage -> CONTENT Group header: true new: After: 0 New: 5",
290
- "client -> KNOWN Group sessions: header/5",
291
252
  "storage -> CONTENT Map header: true new: After: 0 New: 1",
292
- "client -> KNOWN Map sessions: header/1",
293
253
  ]
294
254
  `);
295
255
  });
@@ -303,11 +263,11 @@ test("should not send the same dependency value twice", async () => {
303
263
  Crypto,
304
264
  );
305
265
 
306
- const node1Sync = trackMessages(node1);
266
+ const node1Sync = trackMessages();
307
267
 
308
- const { peer, dbPath } = await createSQLiteStorage();
268
+ const { storage, dbPath } = createSQLiteStorage();
309
269
 
310
- node1.syncManager.addPeer(peer);
270
+ node1.setStorage(storage);
311
271
 
312
272
  const group = node1.createGroup();
313
273
  const parentGroup = node1.createGroup();
@@ -330,11 +290,9 @@ test("should not send the same dependency value twice", async () => {
330
290
  Crypto,
331
291
  );
332
292
 
333
- const node2Sync = trackMessages(node2);
293
+ const node2Sync = trackMessages();
334
294
 
335
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
336
-
337
- node2.syncManager.addPeer(peer2);
295
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
338
296
 
339
297
  await node2.load(map.id);
340
298
  await node2.load(mapFromParent.id);
@@ -358,14 +316,10 @@ test("should not send the same dependency value twice", async () => {
358
316
  [
359
317
  "client -> LOAD Map sessions: empty",
360
318
  "storage -> CONTENT ParentGroup header: true new: After: 0 New: 4",
361
- "client -> KNOWN ParentGroup sessions: header/4",
362
319
  "storage -> CONTENT Group header: true new: After: 0 New: 5",
363
- "client -> KNOWN Group sessions: header/5",
364
320
  "storage -> CONTENT Map header: true new: After: 0 New: 1",
365
- "client -> KNOWN Map sessions: header/1",
366
321
  "client -> LOAD MapFromParent sessions: empty",
367
322
  "storage -> CONTENT MapFromParent header: true new: After: 0 New: 1",
368
- "client -> KNOWN MapFromParent sessions: header/1",
369
323
  ]
370
324
  `);
371
325
  });
@@ -379,11 +333,11 @@ test("should recover from data loss", async () => {
379
333
  Crypto,
380
334
  );
381
335
 
382
- const node1Sync = trackMessages(node1);
336
+ const node1Sync = trackMessages();
383
337
 
384
- const { peer, dbPath } = await createSQLiteStorage();
338
+ const { storage, dbPath } = createSQLiteStorage();
385
339
 
386
- node1.syncManager.addPeer(peer);
340
+ node1.setStorage(storage);
387
341
 
388
342
  const group = node1.createGroup();
389
343
 
@@ -394,8 +348,8 @@ test("should recover from data loss", async () => {
394
348
  await new Promise((resolve) => setTimeout(resolve, 200));
395
349
 
396
350
  const mock = vi
397
- .spyOn(StorageManagerSync.prototype, "handleSyncMessage")
398
- .mockImplementation(() => Promise.resolve());
351
+ .spyOn(StorageApiSync.prototype, "store")
352
+ .mockImplementation(() => false);
399
353
 
400
354
  map.set("1", 1);
401
355
  map.set("2", 2);
@@ -419,13 +373,8 @@ test("should recover from data loss", async () => {
419
373
  ).toMatchInlineSnapshot(`
420
374
  [
421
375
  "client -> CONTENT Group header: true new: After: 0 New: 3",
422
- "storage -> KNOWN Group sessions: header/3",
423
376
  "client -> CONTENT Map header: true new: After: 0 New: 1",
424
- "storage -> KNOWN Map sessions: header/1",
425
- "client -> CONTENT Map header: false new: After: 3 New: 1",
426
- "storage -> KNOWN CORRECTION Map sessions: header/1",
427
377
  "client -> CONTENT Map header: false new: After: 1 New: 3",
428
- "storage -> KNOWN Map sessions: header/4",
429
378
  ]
430
379
  `);
431
380
 
@@ -437,11 +386,9 @@ test("should recover from data loss", async () => {
437
386
  Crypto,
438
387
  );
439
388
 
440
- const node2Sync = trackMessages(node2);
441
-
442
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
389
+ const node2Sync = trackMessages();
443
390
 
444
- node2.syncManager.addPeer(peer2);
391
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
445
392
 
446
393
  const map2 = await node2.load(map.id);
447
394
 
@@ -468,9 +415,7 @@ test("should recover from data loss", async () => {
468
415
  [
469
416
  "client -> LOAD Map sessions: empty",
470
417
  "storage -> CONTENT Group header: true new: After: 0 New: 3",
471
- "client -> KNOWN Group sessions: header/3",
472
418
  "storage -> CONTENT Map header: true new: After: 0 New: 4",
473
- "client -> KNOWN Map sessions: header/4",
474
419
  ]
475
420
  `);
476
421
  });
@@ -501,24 +446,28 @@ test("should recover missing dependencies from storage", async () => {
501
446
  node1.syncManager.addPeer(serverPeer);
502
447
  serverNode.syncManager.addPeer(clientPeer);
503
448
 
504
- const handleSyncMessage = StorageManagerSync.prototype.handleSyncMessage;
449
+ const store = StorageApiSync.prototype.store;
505
450
 
506
451
  const mock = vi
507
- .spyOn(StorageManagerSync.prototype, "handleSyncMessage")
508
- .mockImplementation(function (this: StorageManagerSync, msg) {
452
+ .spyOn(StorageApiSync.prototype, "store")
453
+ .mockImplementation(function (
454
+ this: StorageApiSync,
455
+ data,
456
+ correctionCallback,
457
+ ) {
509
458
  if (
510
- msg.action === "content" &&
511
- [group.core.id, account.core.id].includes(msg.id)
459
+ data[0]?.id &&
460
+ [group.core.id, account.core.id as string].includes(data[0].id)
512
461
  ) {
513
- return Promise.resolve();
462
+ return false;
514
463
  }
515
464
 
516
- return handleSyncMessage.call(this, msg);
465
+ return store.call(this, data, correctionCallback);
517
466
  });
518
467
 
519
- const { peer, dbPath } = await createSQLiteStorage();
468
+ const { storage, dbPath } = createSQLiteStorage();
520
469
 
521
- node1.syncManager.addPeer(peer);
470
+ node1.setStorage(storage);
522
471
 
523
472
  const group = node1.createGroup();
524
473
  group.addMember("everyone", "writer");
@@ -549,9 +498,7 @@ test("should recover missing dependencies from storage", async () => {
549
498
  node2.syncManager.addPeer(serverPeer2);
550
499
  serverNode.syncManager.addPeer(clientPeer2);
551
500
 
552
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
553
-
554
- node2.syncManager.addPeer(peer2);
501
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
555
502
 
556
503
  const map2 = await node2.load(map.id);
557
504
 
@@ -573,9 +520,9 @@ test("should sync multiple sessions in a single content message", async () => {
573
520
  Crypto,
574
521
  );
575
522
 
576
- const { peer, dbPath } = await createSQLiteStorage();
523
+ const { storage, dbPath } = createSQLiteStorage();
577
524
 
578
- node1.syncManager.addPeer(peer);
525
+ node1.setStorage(storage);
579
526
 
580
527
  const group = node1.createGroup();
581
528
 
@@ -593,7 +540,7 @@ test("should sync multiple sessions in a single content message", async () => {
593
540
  Crypto,
594
541
  );
595
542
 
596
- node2.syncManager.addPeer((await createSQLiteStorage(dbPath)).peer);
543
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
597
544
 
598
545
  const map2 = await node2.load(map.id);
599
546
  if (map2 === "unavailable") {
@@ -614,9 +561,9 @@ test("should sync multiple sessions in a single content message", async () => {
614
561
  Crypto,
615
562
  );
616
563
 
617
- const node3Sync = trackMessages(node3);
564
+ const node3Sync = trackMessages();
618
565
 
619
- node3.syncManager.addPeer((await createSQLiteStorage(dbPath)).peer);
566
+ node3.setStorage(createSQLiteStorage(dbPath).storage);
620
567
 
621
568
  const map3 = await node3.load(map.id);
622
569
  if (map3 === "unavailable") {
@@ -637,9 +584,7 @@ test("should sync multiple sessions in a single content message", async () => {
637
584
  [
638
585
  "client -> LOAD Map sessions: empty",
639
586
  "storage -> CONTENT Group header: true new: After: 0 New: 3",
640
- "client -> KNOWN Group sessions: header/3",
641
587
  "storage -> CONTENT Map header: true new: After: 0 New: 1 | After: 0 New: 1",
642
- "client -> KNOWN Map sessions: header/2",
643
588
  ]
644
589
  `);
645
590
 
@@ -655,9 +600,9 @@ test("large coValue upload streaming", async () => {
655
600
  Crypto,
656
601
  );
657
602
 
658
- const { peer, dbPath } = await createSQLiteStorage();
603
+ const { storage, dbPath } = createSQLiteStorage();
659
604
 
660
- node1.syncManager.addPeer(peer);
605
+ node1.setStorage(storage);
661
606
 
662
607
  const group = node1.createGroup();
663
608
  const largeMap = group.createMap();
@@ -683,11 +628,9 @@ test("large coValue upload streaming", async () => {
683
628
  Crypto,
684
629
  );
685
630
 
686
- const node2Sync = trackMessages(node2);
631
+ const node2Sync = trackMessages();
687
632
 
688
- const { peer: peer2 } = await createSQLiteStorage(dbPath);
689
-
690
- node2.syncManager.addPeer(peer2);
633
+ node2.setStorage(createSQLiteStorage(dbPath).storage);
691
634
 
692
635
  const largeMapOnNode2 = await node2.load(largeMap.id);
693
636
 
@@ -714,51 +657,10 @@ test("large coValue upload streaming", async () => {
714
657
  ).toMatchInlineSnapshot(`
715
658
  [
716
659
  "client -> LOAD Map sessions: empty",
717
- "storage -> KNOWN Map sessions: header/200",
718
660
  "storage -> CONTENT Group header: true new: After: 0 New: 3",
719
- "client -> KNOWN Group sessions: header/3",
720
661
  "storage -> CONTENT Map header: true new: After: 0 New: 97",
721
- "client -> KNOWN Map sessions: header/97",
722
662
  "storage -> CONTENT Map header: true new: After: 97 New: 97",
723
- "client -> KNOWN Map sessions: header/194",
724
663
  "storage -> CONTENT Map header: true new: After: 194 New: 6",
725
- "client -> KNOWN Map sessions: header/200",
726
664
  ]
727
665
  `);
728
666
  });
729
-
730
- test("should close the db when the node is closed", async () => {
731
- const agentSecret = Crypto.newRandomAgentSecret();
732
-
733
- const node1 = new LocalNode(
734
- agentSecret,
735
- Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
736
- Crypto,
737
- );
738
-
739
- const dbPath = join(tmpdir(), `test-${randomUUID()}.db`);
740
-
741
- const db = new BetterSqliteDriver(dbPath);
742
-
743
- const peer = SQLiteNodeBase.create({
744
- db,
745
- localNodeName: "test",
746
- maxBlockingTime: 500,
747
- });
748
-
749
- const spy = vi.spyOn(db, "closeDb");
750
-
751
- node1.syncManager.addPeer(peer);
752
-
753
- await new Promise((resolve) => setTimeout(resolve, 10));
754
-
755
- expect(spy).not.toHaveBeenCalled();
756
-
757
- node1.gracefulShutdown();
758
-
759
- await new Promise((resolve) => setTimeout(resolve, 10));
760
-
761
- expect(spy).toHaveBeenCalled();
762
-
763
- unlinkSync(dbPath);
764
- });
@@ -1,36 +1,64 @@
1
- import type { LocalNode, SyncMessage } from "cojson";
2
- import { StorageManagerSync } from "cojson-storage";
1
+ import type { LocalNode, RawCoID, SyncMessage } from "cojson";
2
+ import { StorageApiSync } from "cojson";
3
3
  import { onTestFinished } from "vitest";
4
4
 
5
- export function trackMessages(node: LocalNode) {
5
+ export function trackMessages() {
6
6
  const messages: {
7
7
  from: "client" | "server" | "storage";
8
8
  msg: SyncMessage;
9
9
  }[] = [];
10
10
 
11
- const originalHandleSyncMessage =
12
- StorageManagerSync.prototype.handleSyncMessage;
13
- const originalNodeSyncMessage = node.syncManager.handleSyncMessage;
11
+ const originalLoad = StorageApiSync.prototype.load;
12
+ const originalStore = StorageApiSync.prototype.store;
14
13
 
15
- StorageManagerSync.prototype.handleSyncMessage = async function (msg) {
14
+ StorageApiSync.prototype.load = async function (id, callback, done) {
16
15
  messages.push({
17
16
  from: "client",
18
- msg,
17
+ msg: {
18
+ action: "load",
19
+ id: id as RawCoID,
20
+ header: false,
21
+ sessions: {},
22
+ },
19
23
  });
20
- return originalHandleSyncMessage.call(this, msg);
24
+ return originalLoad.call(
25
+ this,
26
+ id,
27
+ (msg) => {
28
+ messages.push({
29
+ from: "storage",
30
+ msg,
31
+ });
32
+ callback(msg);
33
+ },
34
+ done,
35
+ );
21
36
  };
22
37
 
23
- node.syncManager.handleSyncMessage = async function (msg, peer) {
24
- messages.push({
25
- from: "storage",
26
- msg,
38
+ StorageApiSync.prototype.store = function (data, correctionCallback) {
39
+ for (const msg of data) {
40
+ messages.push({
41
+ from: "client",
42
+ msg,
43
+ });
44
+ }
45
+ return originalStore.call(this, data, (msg) => {
46
+ messages.push({
47
+ from: "storage",
48
+ msg: {
49
+ action: "known",
50
+ isCorrection: true,
51
+ ...msg,
52
+ },
53
+ });
54
+ correctionCallback(msg);
27
55
  });
28
- return originalNodeSyncMessage.call(this, msg, peer);
29
56
  };
30
57
 
31
58
  const restore = () => {
32
- StorageManagerSync.prototype.handleSyncMessage = originalHandleSyncMessage;
33
- node.syncManager.handleSyncMessage = originalNodeSyncMessage;
59
+ StorageApiSync.prototype.load = originalLoad;
60
+ StorageApiSync.prototype.store = originalStore;
61
+ messages.length = 0;
34
62
  };
35
63
 
36
64
  onTestFinished(() => {
@@ -1,11 +0,0 @@
1
- import type { SQLiteDatabaseDriver } from "cojson-storage";
2
- export declare class BetterSqliteDriver implements SQLiteDatabaseDriver {
3
- private readonly db;
4
- constructor(filename: string);
5
- run(sql: string, params: unknown[]): void;
6
- query<T>(sql: string, params: unknown[]): T[];
7
- get<T>(sql: string, params: unknown[]): T | undefined;
8
- transaction(callback: () => unknown): unknown;
9
- closeDb(): void;
10
- }
11
- //# sourceMappingURL=betterSqliteDriver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"betterSqliteDriver.d.ts","sourceRoot":"","sources":["../src/betterSqliteDriver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,qBAAa,kBAAmB,YAAW,oBAAoB;IAC7D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;gBAEnB,QAAQ,EAAE,MAAM;IAM5B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;IAIlC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE;IAI7C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,SAAS;IAIrD,WAAW,CAAC,QAAQ,EAAE,MAAM,OAAO;IAInC,OAAO;CAGR"}
@@ -1,24 +0,0 @@
1
- import Database from "better-sqlite3";
2
- export class BetterSqliteDriver {
3
- constructor(filename) {
4
- const db = new Database(filename);
5
- this.db = db;
6
- db.pragma("journal_mode = WAL");
7
- }
8
- run(sql, params) {
9
- this.db.prepare(sql).run(params);
10
- }
11
- query(sql, params) {
12
- return this.db.prepare(sql).all(params);
13
- }
14
- get(sql, params) {
15
- return this.db.prepare(sql).get(params);
16
- }
17
- transaction(callback) {
18
- return this.db.transaction(callback)();
19
- }
20
- closeDb() {
21
- this.db.close();
22
- }
23
- }
24
- //# sourceMappingURL=betterSqliteDriver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"betterSqliteDriver.js","sourceRoot":"","sources":["../src/betterSqliteDriver.ts"],"names":[],"mappings":"AAAA,OAAO,QAAwC,MAAM,gBAAgB,CAAC;AAGtE,MAAM,OAAO,kBAAkB;IAG7B,YAAY,QAAgB;QAC1B,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,MAAiB;QAChC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAI,GAAW,EAAE,MAAiB;QACrC,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAQ,CAAC;IACjD,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,MAAiB;QACnC,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAkB,CAAC;IAC3D,CAAC;IAED,WAAW,CAAC,QAAuB;QACjC,OAAO,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,OAAO;QACL,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF"}