cojson-storage-do-sqlite 0.18.28

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 (39) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +9 -0
  3. package/LICENSE.txt +19 -0
  4. package/README.md +4 -0
  5. package/dist/index.d.ts +15 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +37 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/tests/dosqlite.d.ts +89 -0
  10. package/dist/tests/dosqlite.d.ts.map +1 -0
  11. package/dist/tests/dosqlite.js +489 -0
  12. package/dist/tests/dosqlite.js.map +1 -0
  13. package/dist/tests/dosqlite.test.d.ts +2 -0
  14. package/dist/tests/dosqlite.test.d.ts.map +1 -0
  15. package/dist/tests/dosqlite.test.js +284 -0
  16. package/dist/tests/dosqlite.test.js.map +1 -0
  17. package/dist/tests/messagesTestUtils.d.ts +10 -0
  18. package/dist/tests/messagesTestUtils.d.ts.map +1 -0
  19. package/dist/tests/messagesTestUtils.js +42 -0
  20. package/dist/tests/messagesTestUtils.js.map +1 -0
  21. package/dist/tests/testUtils.d.ts +10 -0
  22. package/dist/tests/testUtils.d.ts.map +1 -0
  23. package/dist/tests/testUtils.js +88 -0
  24. package/dist/tests/testUtils.js.map +1 -0
  25. package/dist/tests/vitest.config.d.ts +3 -0
  26. package/dist/tests/vitest.config.d.ts.map +1 -0
  27. package/dist/tests/vitest.config.js +8 -0
  28. package/dist/tests/vitest.config.js.map +1 -0
  29. package/package.json +28 -0
  30. package/src/index.ts +60 -0
  31. package/src/tests/dosqlite.test.ts +338 -0
  32. package/src/tests/dosqlite.ts +771 -0
  33. package/src/tests/messagesTestUtils.ts +72 -0
  34. package/src/tests/testUtils.ts +112 -0
  35. package/src/tests/tsconfig.json +43 -0
  36. package/src/tests/vitest.config.ts +8 -0
  37. package/src/tests/worker-configuration.d.ts +9681 -0
  38. package/src/tests/wrangler.jsonc +20 -0
  39. package/tsconfig.json +17 -0
@@ -0,0 +1,338 @@
1
+ import { dirname, join } from "path";
2
+ import { randomUUID } from "node:crypto";
3
+ import { fileURLToPath } from "url";
4
+ import { execa } from "execa";
5
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
6
+
7
+ // @ts-ignore
8
+ const currentDir = dirname(fileURLToPath(import.meta.url));
9
+ const packageRoot = join(currentDir, "../..");
10
+ const projectRoot = join(packageRoot, "../..");
11
+
12
+ describe("Cloudflare DurableObject SQL storage", () => {
13
+ let server: ReturnType<typeof execa>;
14
+ let url: URL;
15
+
16
+ beforeAll(async () => {
17
+ server = execa(join(projectRoot, "node_modules/.bin/wrangler"), ["dev"], {
18
+ cwd: currentDir,
19
+ });
20
+ // Wait for server to be ready
21
+ url = await new Promise<URL>((resolve, reject) => {
22
+ server.stdout?.on("data", (data) => {
23
+ console.log("stdout server:", data.toString());
24
+ if (data.toString().includes("Ready on http://localhost:")) {
25
+ resolve(new URL(data.toString().split("Ready on ")[1].trim()));
26
+ }
27
+ });
28
+
29
+ server.stderr?.on("data", (data) => {
30
+ console.log("stderr server:", data.toString());
31
+ });
32
+
33
+ // Reject if server fails to start within 10 seconds
34
+ setTimeout(() => {
35
+ reject(new Error("Server failed to start within timeout"));
36
+ }, 10000);
37
+ });
38
+ });
39
+
40
+ afterAll(async () => {
41
+ server.kill();
42
+ });
43
+
44
+ test("should sync and load data from storage", async () => {
45
+ const doId = randomUUID();
46
+ const res = await fetch(`${url}sync-and-load`, {
47
+ method: "POST",
48
+ headers: {
49
+ "Content-Type": "application/json",
50
+ },
51
+ body: JSON.stringify({ doId }),
52
+ });
53
+
54
+ const json = (await res.json()) as unknown;
55
+ if (
56
+ !(
57
+ typeof json === "object" &&
58
+ json !== null &&
59
+ "messageRes1" in json &&
60
+ "messageRes2" in json &&
61
+ "map2Hello" in json
62
+ )
63
+ ) {
64
+ throw new Error("Invalid response");
65
+ }
66
+ expect(json.messageRes1).toMatchInlineSnapshot(`
67
+ [
68
+ "client -> CONTENT Group header: true new: After: 0 New: 3",
69
+ "client -> CONTENT Map header: true new: After: 0 New: 1",
70
+ ]
71
+ `);
72
+ expect(json.map2Hello).toEqual("world");
73
+ expect(json.messageRes2).toMatchInlineSnapshot(`
74
+ [
75
+ "client -> LOAD Map sessions: empty",
76
+ "storage -> CONTENT Group header: true new: After: 0 New: 3",
77
+ "storage -> CONTENT Map header: true new: After: 0 New: 1",
78
+ ]
79
+ `);
80
+ });
81
+
82
+ test("should send an empty content message if there is no content", async () => {
83
+ const doId = randomUUID();
84
+ const res = await fetch(`${url}sync-and-load-empty`, {
85
+ method: "POST",
86
+ headers: {
87
+ "Content-Type": "application/json",
88
+ },
89
+ body: JSON.stringify({ doId }),
90
+ });
91
+
92
+ const json = (await res.json()) as unknown;
93
+ if (
94
+ !(
95
+ typeof json === "object" &&
96
+ json !== null &&
97
+ "messageRes1" in json &&
98
+ "messageRes2" in json
99
+ )
100
+ ) {
101
+ throw new Error("Invalid response");
102
+ }
103
+ expect(json.messageRes1).toMatchInlineSnapshot(`
104
+ [
105
+ "client -> CONTENT Group header: true new: After: 0 New: 3",
106
+ "client -> CONTENT Map header: true new: ",
107
+ ]
108
+ `);
109
+ expect(json.messageRes2).toMatchInlineSnapshot(`
110
+ [
111
+ "client -> LOAD Map sessions: empty",
112
+ "storage -> CONTENT Group header: true new: After: 0 New: 3",
113
+ "storage -> CONTENT Map header: true new: ",
114
+ ]
115
+ `);
116
+ });
117
+
118
+ test("should load dependencies correctly (group inheritance)", async () => {
119
+ const doId = randomUUID();
120
+ const res = await fetch(`${url}group-load`, {
121
+ method: "POST",
122
+ headers: {
123
+ "Content-Type": "application/json",
124
+ },
125
+ body: JSON.stringify({ doId }),
126
+ });
127
+
128
+ const json = (await res.json()) as unknown;
129
+ if (
130
+ !(
131
+ typeof json === "object" &&
132
+ json !== null &&
133
+ "messageRes1" in json &&
134
+ "messageRes2" in json &&
135
+ "mapLoaded" in json &&
136
+ "groupLoaded" in json &&
137
+ "parentGroupLoaded" in json
138
+ )
139
+ ) {
140
+ throw new Error("Invalid response");
141
+ }
142
+
143
+ expect(json.messageRes1).toMatchInlineSnapshot(`
144
+ [
145
+ "client -> CONTENT Group header: true new: After: 0 New: 3",
146
+ "client -> CONTENT ParentGroup header: true new: After: 0 New: 3",
147
+ "client -> CONTENT Group header: false new: After: 3 New: 2",
148
+ "client -> CONTENT Map header: true new: After: 0 New: 1",
149
+ ]
150
+ `);
151
+ expect(json.mapLoaded).toBeTruthy();
152
+ expect(json.groupLoaded).toBeTruthy();
153
+ expect(json.parentGroupLoaded).toBeTruthy();
154
+
155
+ expect(json.messageRes2).toMatchInlineSnapshot(`
156
+ [
157
+ "client -> LOAD Map sessions: empty",
158
+ "storage -> CONTENT ParentGroup header: true new: After: 0 New: 3",
159
+ "storage -> CONTENT Group header: true new: After: 0 New: 5",
160
+ "storage -> CONTENT Map header: true new: After: 0 New: 1",
161
+ ]
162
+ `);
163
+ });
164
+
165
+ test("should not send the same dependency value twice", async () => {
166
+ const doId = randomUUID();
167
+ const res = await fetch(`${url}group-load-duplicate`, {
168
+ method: "POST",
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ },
172
+ body: JSON.stringify({ doId }),
173
+ });
174
+
175
+ const json = (await res.json()) as unknown;
176
+ if (
177
+ !(
178
+ typeof json === "object" &&
179
+ json !== null &&
180
+ "messageRes1" in json &&
181
+ "mapLoaded" in json &&
182
+ "mapFromParentLoaded" in json &&
183
+ "groupLoaded" in json &&
184
+ "parentGroupLoaded" in json
185
+ )
186
+ ) {
187
+ console.log(json);
188
+ throw new Error("Invalid response");
189
+ }
190
+
191
+ expect(json.mapLoaded).toBeTruthy();
192
+ expect(json.mapFromParentLoaded).toBeTruthy();
193
+ expect(json.groupLoaded).toBeTruthy();
194
+ expect(json.parentGroupLoaded).toBeTruthy();
195
+ expect(json.messageRes1).toMatchInlineSnapshot(`
196
+ [
197
+ "client -> LOAD Map sessions: empty",
198
+ "storage -> CONTENT ParentGroup header: true new: After: 0 New: 3",
199
+ "storage -> CONTENT Group header: true new: After: 0 New: 5",
200
+ "storage -> CONTENT Map header: true new: After: 0 New: 1",
201
+ "client -> LOAD MapFromParent sessions: empty",
202
+ "storage -> CONTENT MapFromParent header: true new: After: 0 New: 1",
203
+ ]
204
+ `);
205
+ });
206
+
207
+ test("should recover from data loss", async () => {
208
+ const doId = randomUUID();
209
+ const res = await fetch(`${url}data-loss-recovery`, {
210
+ method: "POST",
211
+ headers: {
212
+ "Content-Type": "application/json",
213
+ },
214
+ body: JSON.stringify({ doId }),
215
+ });
216
+
217
+ const json = (await res.json()) as unknown;
218
+ if (
219
+ !(
220
+ typeof json === "object" &&
221
+ json !== null &&
222
+ "messageRes1" in json &&
223
+ "messageRes2" in json &&
224
+ "mapContent" in json
225
+ )
226
+ ) {
227
+ console.log(json);
228
+ throw new Error("Invalid response");
229
+ }
230
+
231
+ expect(json.messageRes1).toMatchInlineSnapshot(`
232
+ [
233
+ "client -> CONTENT Group header: true new: After: 0 New: 3",
234
+ "client -> CONTENT Map header: true new: After: 0 New: 1",
235
+ "client -> CONTENT Map header: false new: After: 3 New: 1",
236
+ "storage -> KNOWN CORRECTION Map sessions: header/4",
237
+ "client -> CONTENT Map header: false new: After: 1 New: 3",
238
+ ]
239
+ `);
240
+
241
+ expect(json.mapContent).toEqual({
242
+ "0": 0,
243
+ "1": 1,
244
+ "2": 2,
245
+ "3": 3,
246
+ });
247
+
248
+ expect(json.messageRes2).toMatchInlineSnapshot(`
249
+ [
250
+ "client -> LOAD Map sessions: empty",
251
+ "storage -> CONTENT Group header: true new: After: 0 New: 3",
252
+ "storage -> CONTENT Map header: true new: After: 0 New: 4",
253
+ ]
254
+ `);
255
+ });
256
+
257
+ test("should recover missing dependencies from storage", async () => {
258
+ const doId = randomUUID();
259
+ const res = await fetch(`${url}missing-dependency-recovery`, {
260
+ method: "POST",
261
+ headers: {
262
+ "Content-Type": "application/json",
263
+ },
264
+ body: JSON.stringify({ doId }),
265
+ });
266
+
267
+ const json = (await res.json()) as unknown;
268
+ if (!(typeof json === "object" && json !== null && "mapContent" in json)) {
269
+ console.log(json);
270
+ throw new Error("Invalid response");
271
+ }
272
+
273
+ expect(json.mapContent).toEqual({
274
+ "0": 0,
275
+ });
276
+ });
277
+
278
+ test("should sync multiple sessions in a single content message", async () => {
279
+ const doId = randomUUID();
280
+ const res = await fetch(`${url}multiple-sessions`, {
281
+ method: "POST",
282
+ headers: {
283
+ "Content-Type": "application/json",
284
+ },
285
+ body: JSON.stringify({ doId }),
286
+ });
287
+
288
+ const json = (await res.json()) as unknown;
289
+ if (
290
+ !(
291
+ typeof json === "object" &&
292
+ json !== null &&
293
+ "messageRes1" in json &&
294
+ "map2Hello" in json &&
295
+ "map3Hello" in json
296
+ )
297
+ ) {
298
+ console.log(json);
299
+ throw new Error("Invalid response");
300
+ }
301
+
302
+ expect(json.map2Hello).toBe("world");
303
+ expect(json.map3Hello).toBe("world2");
304
+ expect(json.messageRes1).toMatchInlineSnapshot(`
305
+ [
306
+ "client -> LOAD Map sessions: empty",
307
+ "storage -> CONTENT Group header: true new: After: 0 New: 3",
308
+ "storage -> CONTENT Map header: true new: After: 0 New: 1 | After: 0 New: 1",
309
+ ]
310
+ `);
311
+ });
312
+
313
+ test("large coValue upload streaming", async () => {
314
+ const doId = randomUUID();
315
+ const res = await fetch(`${url}large-covalue-upload`, {
316
+ method: "POST",
317
+ headers: {
318
+ "Content-Type": "application/json",
319
+ },
320
+ body: JSON.stringify({ doId }),
321
+ });
322
+
323
+ const json = (await res.json()) as unknown;
324
+ if (!(typeof json === "object" && json !== null && "messageRes1" in json)) {
325
+ console.log(json);
326
+ throw new Error("Invalid response");
327
+ }
328
+
329
+ expect(json.messageRes1).toMatchInlineSnapshot(`
330
+ [
331
+ "client -> LOAD Map sessions: empty",
332
+ "storage -> CONTENT Group header: true new: After: 0 New: 3",
333
+ "storage -> CONTENT Map header: true new: After: 0 New: 193",
334
+ "storage -> CONTENT Map header: true new: After: 193 New: 7",
335
+ ]
336
+ `);
337
+ });
338
+ });