cojson-storage-sqlite 0.15.8 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +8 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/tests/storage.sqlite.test.js +51 -120
- package/dist/tests/storage.sqlite.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +2 -2
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +38 -14
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -3
- package/src/index.ts +39 -1
- package/src/tests/storage.sqlite.test.ts +55 -153
- package/src/tests/testUtils.ts +44 -16
- package/dist/betterSqliteDriver.d.ts +0 -11
- package/dist/betterSqliteDriver.d.ts.map +0 -1
- package/dist/betterSqliteDriver.js +0 -24
- package/dist/betterSqliteDriver.js.map +0 -1
- package/dist/sqliteNode.d.ts +0 -9
- package/dist/sqliteNode.d.ts.map +0 -1
- package/dist/sqliteNode.js +0 -13
- package/dist/sqliteNode.js.map +0 -1
- package/src/betterSqliteDriver.ts +0 -32
- package/src/sqliteNode.ts +0 -21
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cojson-storage-sqlite",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.15.
|
|
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.
|
|
11
|
-
"cojson-storage": "0.15.8"
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
38
|
+
const node1Sync = trackMessages();
|
|
59
39
|
|
|
60
|
-
const {
|
|
40
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
61
41
|
|
|
62
|
-
node1.
|
|
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(
|
|
98
|
-
|
|
99
|
-
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
|
75
|
+
const node2Sync = trackMessages();
|
|
100
76
|
|
|
101
|
-
node2.
|
|
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(
|
|
114
|
+
const node1Sync = trackMessages();
|
|
141
115
|
|
|
142
|
-
const {
|
|
116
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
143
117
|
|
|
144
|
-
node1.
|
|
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(
|
|
178
|
-
|
|
179
|
-
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
|
149
|
+
const node2Sync = trackMessages();
|
|
180
150
|
|
|
181
|
-
node2.
|
|
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(
|
|
186
|
+
const node1Sync = trackMessages();
|
|
219
187
|
|
|
220
|
-
const {
|
|
188
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
221
189
|
|
|
222
|
-
node1.
|
|
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
|
-
"
|
|
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(
|
|
264
|
-
|
|
265
|
-
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
|
228
|
+
const node2Sync = trackMessages();
|
|
266
229
|
|
|
267
|
-
node2.
|
|
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(
|
|
266
|
+
const node1Sync = trackMessages();
|
|
307
267
|
|
|
308
|
-
const {
|
|
268
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
309
269
|
|
|
310
|
-
node1.
|
|
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(
|
|
293
|
+
const node2Sync = trackMessages();
|
|
334
294
|
|
|
335
|
-
|
|
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(
|
|
336
|
+
const node1Sync = trackMessages();
|
|
383
337
|
|
|
384
|
-
const {
|
|
338
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
385
339
|
|
|
386
|
-
node1.
|
|
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(
|
|
398
|
-
.mockImplementation(() =>
|
|
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(
|
|
441
|
-
|
|
442
|
-
const { peer: peer2 } = await createSQLiteStorage(dbPath);
|
|
389
|
+
const node2Sync = trackMessages();
|
|
443
390
|
|
|
444
|
-
node2.
|
|
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
|
|
449
|
+
const store = StorageApiSync.prototype.store;
|
|
505
450
|
|
|
506
451
|
const mock = vi
|
|
507
|
-
.spyOn(
|
|
508
|
-
.mockImplementation(function (
|
|
452
|
+
.spyOn(StorageApiSync.prototype, "store")
|
|
453
|
+
.mockImplementation(function (
|
|
454
|
+
this: StorageApiSync,
|
|
455
|
+
data,
|
|
456
|
+
correctionCallback,
|
|
457
|
+
) {
|
|
509
458
|
if (
|
|
510
|
-
|
|
511
|
-
[group.core.id, account.core.id].includes(
|
|
459
|
+
data[0]?.id &&
|
|
460
|
+
[group.core.id, account.core.id as string].includes(data[0].id)
|
|
512
461
|
) {
|
|
513
|
-
return
|
|
462
|
+
return false;
|
|
514
463
|
}
|
|
515
464
|
|
|
516
|
-
return
|
|
465
|
+
return store.call(this, data, correctionCallback);
|
|
517
466
|
});
|
|
518
467
|
|
|
519
|
-
const {
|
|
468
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
520
469
|
|
|
521
|
-
node1.
|
|
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
|
-
|
|
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 {
|
|
523
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
577
524
|
|
|
578
|
-
node1.
|
|
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.
|
|
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(
|
|
564
|
+
const node3Sync = trackMessages();
|
|
618
565
|
|
|
619
|
-
node3.
|
|
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 {
|
|
603
|
+
const { storage, dbPath } = createSQLiteStorage();
|
|
659
604
|
|
|
660
|
-
node1.
|
|
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(
|
|
631
|
+
const node2Sync = trackMessages();
|
|
687
632
|
|
|
688
|
-
|
|
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
|
-
});
|
package/src/tests/testUtils.ts
CHANGED
|
@@ -1,36 +1,64 @@
|
|
|
1
|
-
import type { LocalNode, SyncMessage } from "cojson";
|
|
2
|
-
import {
|
|
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(
|
|
5
|
+
export function trackMessages() {
|
|
6
6
|
const messages: {
|
|
7
7
|
from: "client" | "server" | "storage";
|
|
8
8
|
msg: SyncMessage;
|
|
9
9
|
}[] = [];
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const originalNodeSyncMessage = node.syncManager.handleSyncMessage;
|
|
11
|
+
const originalLoad = StorageApiSync.prototype.load;
|
|
12
|
+
const originalStore = StorageApiSync.prototype.store;
|
|
14
13
|
|
|
15
|
-
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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"}
|
package/dist/sqliteNode.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Peer } from "cojson";
|
|
2
|
-
import { SQLiteNodeBase } from "cojson-storage";
|
|
3
|
-
export declare class SQLiteNode extends SQLiteNodeBase {
|
|
4
|
-
static asPeer({ filename, localNodeName, }: {
|
|
5
|
-
filename: string;
|
|
6
|
-
localNodeName?: string;
|
|
7
|
-
}): Promise<Peer>;
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=sqliteNode.d.ts.map
|
package/dist/sqliteNode.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sqliteNode.d.ts","sourceRoot":"","sources":["../src/sqliteNode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,qBAAa,UAAW,SAAQ,cAAc;WAC/B,MAAM,CAAC,EAClB,QAAQ,EACR,aAAuB,GACxB,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;CASlB"}
|