cojson 0.19.22 → 0.20.1

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 (223) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +66 -0
  3. package/dist/PeerState.d.ts +6 -1
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +18 -3
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/coValueContentMessage.d.ts +0 -2
  8. package/dist/coValueContentMessage.d.ts.map +1 -1
  9. package/dist/coValueContentMessage.js +0 -8
  10. package/dist/coValueContentMessage.js.map +1 -1
  11. package/dist/coValueCore/SessionMap.d.ts +4 -2
  12. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  13. package/dist/coValueCore/SessionMap.js +30 -0
  14. package/dist/coValueCore/SessionMap.js.map +1 -1
  15. package/dist/coValueCore/coValueCore.d.ts +70 -5
  16. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  17. package/dist/coValueCore/coValueCore.js +302 -31
  18. package/dist/coValueCore/coValueCore.js.map +1 -1
  19. package/dist/coValueCore/verifiedState.d.ts +6 -1
  20. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  21. package/dist/coValueCore/verifiedState.js +9 -0
  22. package/dist/coValueCore/verifiedState.js.map +1 -1
  23. package/dist/coValues/coList.d.ts +4 -2
  24. package/dist/coValues/coList.d.ts.map +1 -1
  25. package/dist/coValues/coList.js +3 -0
  26. package/dist/coValues/coList.js.map +1 -1
  27. package/dist/coValues/group.d.ts.map +1 -1
  28. package/dist/coValues/group.js +3 -6
  29. package/dist/coValues/group.js.map +1 -1
  30. package/dist/config.d.ts +2 -8
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js +4 -12
  33. package/dist/config.js.map +1 -1
  34. package/dist/crypto/NapiCrypto.d.ts +1 -2
  35. package/dist/crypto/NapiCrypto.d.ts.map +1 -1
  36. package/dist/crypto/NapiCrypto.js +19 -4
  37. package/dist/crypto/NapiCrypto.js.map +1 -1
  38. package/dist/crypto/RNCrypto.d.ts.map +1 -1
  39. package/dist/crypto/RNCrypto.js +19 -4
  40. package/dist/crypto/RNCrypto.js.map +1 -1
  41. package/dist/crypto/WasmCrypto.d.ts +11 -4
  42. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  43. package/dist/crypto/WasmCrypto.js +52 -10
  44. package/dist/crypto/WasmCrypto.js.map +1 -1
  45. package/dist/crypto/WasmCryptoEdge.d.ts +1 -0
  46. package/dist/crypto/WasmCryptoEdge.d.ts.map +1 -1
  47. package/dist/crypto/WasmCryptoEdge.js +4 -1
  48. package/dist/crypto/WasmCryptoEdge.js.map +1 -1
  49. package/dist/crypto/crypto.d.ts +3 -3
  50. package/dist/crypto/crypto.d.ts.map +1 -1
  51. package/dist/crypto/crypto.js +6 -1
  52. package/dist/crypto/crypto.js.map +1 -1
  53. package/dist/exports.d.ts +5 -5
  54. package/dist/exports.d.ts.map +1 -1
  55. package/dist/exports.js +4 -3
  56. package/dist/exports.js.map +1 -1
  57. package/dist/ids.d.ts +4 -1
  58. package/dist/ids.d.ts.map +1 -1
  59. package/dist/ids.js +4 -0
  60. package/dist/ids.js.map +1 -1
  61. package/dist/knownState.d.ts +2 -0
  62. package/dist/knownState.d.ts.map +1 -1
  63. package/dist/localNode.d.ts +12 -0
  64. package/dist/localNode.d.ts.map +1 -1
  65. package/dist/localNode.js +14 -0
  66. package/dist/localNode.js.map +1 -1
  67. package/dist/platformUtils.d.ts +3 -0
  68. package/dist/platformUtils.d.ts.map +1 -0
  69. package/dist/platformUtils.js +24 -0
  70. package/dist/platformUtils.js.map +1 -0
  71. package/dist/queue/LinkedList.d.ts +9 -3
  72. package/dist/queue/LinkedList.d.ts.map +1 -1
  73. package/dist/queue/LinkedList.js +30 -1
  74. package/dist/queue/LinkedList.js.map +1 -1
  75. package/dist/queue/OutgoingLoadQueue.d.ts +95 -0
  76. package/dist/queue/OutgoingLoadQueue.d.ts.map +1 -0
  77. package/dist/queue/OutgoingLoadQueue.js +240 -0
  78. package/dist/queue/OutgoingLoadQueue.js.map +1 -0
  79. package/dist/storage/DeletedCoValuesEraserScheduler.d.ts +30 -0
  80. package/dist/storage/DeletedCoValuesEraserScheduler.d.ts.map +1 -0
  81. package/dist/storage/DeletedCoValuesEraserScheduler.js +84 -0
  82. package/dist/storage/DeletedCoValuesEraserScheduler.js.map +1 -0
  83. package/dist/storage/sqlite/client.d.ts +3 -0
  84. package/dist/storage/sqlite/client.d.ts.map +1 -1
  85. package/dist/storage/sqlite/client.js +44 -0
  86. package/dist/storage/sqlite/client.js.map +1 -1
  87. package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
  88. package/dist/storage/sqlite/sqliteMigrations.js +7 -0
  89. package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
  90. package/dist/storage/sqliteAsync/client.d.ts +3 -0
  91. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  92. package/dist/storage/sqliteAsync/client.js +42 -0
  93. package/dist/storage/sqliteAsync/client.js.map +1 -1
  94. package/dist/storage/storageAsync.d.ts +7 -0
  95. package/dist/storage/storageAsync.d.ts.map +1 -1
  96. package/dist/storage/storageAsync.js +48 -0
  97. package/dist/storage/storageAsync.js.map +1 -1
  98. package/dist/storage/storageSync.d.ts +6 -0
  99. package/dist/storage/storageSync.d.ts.map +1 -1
  100. package/dist/storage/storageSync.js +42 -0
  101. package/dist/storage/storageSync.js.map +1 -1
  102. package/dist/storage/types.d.ts +59 -0
  103. package/dist/storage/types.d.ts.map +1 -1
  104. package/dist/storage/types.js +12 -1
  105. package/dist/storage/types.js.map +1 -1
  106. package/dist/sync.d.ts.map +1 -1
  107. package/dist/sync.js +66 -43
  108. package/dist/sync.js.map +1 -1
  109. package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts +2 -0
  110. package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts.map +1 -0
  111. package/dist/tests/DeletedCoValuesEraserScheduler.test.js +149 -0
  112. package/dist/tests/DeletedCoValuesEraserScheduler.test.js.map +1 -0
  113. package/dist/tests/GarbageCollector.test.js +5 -6
  114. package/dist/tests/GarbageCollector.test.js.map +1 -1
  115. package/dist/tests/LinkedList.test.js +90 -0
  116. package/dist/tests/LinkedList.test.js.map +1 -1
  117. package/dist/tests/OutgoingLoadQueue.test.d.ts +2 -0
  118. package/dist/tests/OutgoingLoadQueue.test.d.ts.map +1 -0
  119. package/dist/tests/OutgoingLoadQueue.test.js +814 -0
  120. package/dist/tests/OutgoingLoadQueue.test.js.map +1 -0
  121. package/dist/tests/StorageApiAsync.test.js +484 -152
  122. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  123. package/dist/tests/StorageApiSync.test.js +505 -136
  124. package/dist/tests/StorageApiSync.test.js.map +1 -1
  125. package/dist/tests/WasmCrypto.test.js +6 -3
  126. package/dist/tests/WasmCrypto.test.js.map +1 -1
  127. package/dist/tests/coValueCore.loadFromStorage.test.js +3 -0
  128. package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
  129. package/dist/tests/coValueCore.test.js +34 -13
  130. package/dist/tests/coValueCore.test.js.map +1 -1
  131. package/dist/tests/coreWasm.test.js +127 -4
  132. package/dist/tests/coreWasm.test.js.map +1 -1
  133. package/dist/tests/crypto.test.js +89 -93
  134. package/dist/tests/crypto.test.js.map +1 -1
  135. package/dist/tests/deleteCoValue.test.d.ts +2 -0
  136. package/dist/tests/deleteCoValue.test.d.ts.map +1 -0
  137. package/dist/tests/deleteCoValue.test.js +313 -0
  138. package/dist/tests/deleteCoValue.test.js.map +1 -0
  139. package/dist/tests/group.removeMember.test.js +18 -30
  140. package/dist/tests/group.removeMember.test.js.map +1 -1
  141. package/dist/tests/knownState.lazyLoading.test.js +3 -0
  142. package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
  143. package/dist/tests/sync.concurrentLoad.test.d.ts +2 -0
  144. package/dist/tests/sync.concurrentLoad.test.d.ts.map +1 -0
  145. package/dist/tests/sync.concurrentLoad.test.js +481 -0
  146. package/dist/tests/sync.concurrentLoad.test.js.map +1 -0
  147. package/dist/tests/sync.deleted.test.d.ts +2 -0
  148. package/dist/tests/sync.deleted.test.d.ts.map +1 -0
  149. package/dist/tests/sync.deleted.test.js +214 -0
  150. package/dist/tests/sync.deleted.test.js.map +1 -0
  151. package/dist/tests/sync.mesh.test.js +3 -2
  152. package/dist/tests/sync.mesh.test.js.map +1 -1
  153. package/dist/tests/sync.storage.test.js +4 -3
  154. package/dist/tests/sync.storage.test.js.map +1 -1
  155. package/dist/tests/sync.test.js +3 -2
  156. package/dist/tests/sync.test.js.map +1 -1
  157. package/dist/tests/testStorage.d.ts +3 -0
  158. package/dist/tests/testStorage.d.ts.map +1 -1
  159. package/dist/tests/testStorage.js +17 -1
  160. package/dist/tests/testStorage.js.map +1 -1
  161. package/dist/tests/testUtils.d.ts +7 -3
  162. package/dist/tests/testUtils.d.ts.map +1 -1
  163. package/dist/tests/testUtils.js +19 -4
  164. package/dist/tests/testUtils.js.map +1 -1
  165. package/package.json +6 -16
  166. package/src/PeerState.ts +26 -3
  167. package/src/coValueContentMessage.ts +0 -14
  168. package/src/coValueCore/SessionMap.ts +43 -1
  169. package/src/coValueCore/coValueCore.ts +415 -27
  170. package/src/coValueCore/verifiedState.ts +26 -3
  171. package/src/coValues/coList.ts +9 -3
  172. package/src/coValues/group.ts +5 -6
  173. package/src/config.ts +4 -13
  174. package/src/crypto/NapiCrypto.ts +29 -13
  175. package/src/crypto/RNCrypto.ts +29 -11
  176. package/src/crypto/WasmCrypto.ts +67 -20
  177. package/src/crypto/WasmCryptoEdge.ts +5 -1
  178. package/src/crypto/crypto.ts +16 -4
  179. package/src/exports.ts +4 -2
  180. package/src/ids.ts +11 -1
  181. package/src/localNode.ts +15 -0
  182. package/src/platformUtils.ts +26 -0
  183. package/src/queue/LinkedList.ts +34 -4
  184. package/src/queue/OutgoingLoadQueue.ts +307 -0
  185. package/src/storage/DeletedCoValuesEraserScheduler.ts +124 -0
  186. package/src/storage/sqlite/client.ts +77 -0
  187. package/src/storage/sqlite/sqliteMigrations.ts +7 -0
  188. package/src/storage/sqliteAsync/client.ts +75 -0
  189. package/src/storage/storageAsync.ts +62 -0
  190. package/src/storage/storageSync.ts +58 -0
  191. package/src/storage/types.ts +69 -0
  192. package/src/sync.ts +78 -46
  193. package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
  194. package/src/tests/GarbageCollector.test.ts +6 -10
  195. package/src/tests/LinkedList.test.ts +111 -0
  196. package/src/tests/OutgoingLoadQueue.test.ts +1129 -0
  197. package/src/tests/StorageApiAsync.test.ts +572 -162
  198. package/src/tests/StorageApiSync.test.ts +580 -143
  199. package/src/tests/WasmCrypto.test.ts +8 -3
  200. package/src/tests/coValueCore.loadFromStorage.test.ts +6 -0
  201. package/src/tests/coValueCore.test.ts +49 -14
  202. package/src/tests/coreWasm.test.ts +319 -10
  203. package/src/tests/crypto.test.ts +141 -150
  204. package/src/tests/deleteCoValue.test.ts +528 -0
  205. package/src/tests/group.removeMember.test.ts +35 -35
  206. package/src/tests/knownState.lazyLoading.test.ts +6 -0
  207. package/src/tests/sync.concurrentLoad.test.ts +650 -0
  208. package/src/tests/sync.deleted.test.ts +294 -0
  209. package/src/tests/sync.mesh.test.ts +5 -2
  210. package/src/tests/sync.storage.test.ts +6 -3
  211. package/src/tests/sync.test.ts +5 -2
  212. package/src/tests/testStorage.ts +31 -2
  213. package/src/tests/testUtils.ts +31 -10
  214. package/dist/crypto/PureJSCrypto.d.ts +0 -77
  215. package/dist/crypto/PureJSCrypto.d.ts.map +0 -1
  216. package/dist/crypto/PureJSCrypto.js +0 -236
  217. package/dist/crypto/PureJSCrypto.js.map +0 -1
  218. package/dist/tests/PureJSCrypto.test.d.ts +0 -2
  219. package/dist/tests/PureJSCrypto.test.d.ts.map +0 -1
  220. package/dist/tests/PureJSCrypto.test.js +0 -145
  221. package/dist/tests/PureJSCrypto.test.js.map +0 -1
  222. package/src/crypto/PureJSCrypto.ts +0 -429
  223. package/src/tests/PureJSCrypto.test.ts +0 -217
@@ -1,14 +1,8 @@
1
- import { randomUUID } from "node:crypto";
2
- import { unlinkSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { afterEach, describe, expect, onTestFinished, test, vi } from "vitest";
6
- import { WasmCrypto } from "../crypto/WasmCrypto.js";
7
- import { LocalNode, logger } from "../exports.js";
8
- import { createAsyncStorage } from "./testStorage.js";
9
- import { SyncMessagesLog, loadCoValueOrFail, randomAgentAndSessionID, waitFor, } from "./testUtils.js";
1
+ import { afterEach, describe, expect, test, vi } from "vitest";
2
+ import { logger } from "../exports.js";
3
+ import { createAsyncStorage, getAllCoValuesWaitingForDelete, getCoValueStoredSessions, getDbPath, } from "./testStorage.js";
4
+ import { SyncMessagesLog, fillCoMapWithLargeData, loadCoValueOrFail, setupTestNode, waitFor, } from "./testUtils.js";
10
5
  import { emptyKnownState } from "../knownState.js";
11
- const crypto = await WasmCrypto.create();
12
6
  /**
13
7
  * Helper function that gets new content since a known state, throwing if:
14
8
  * - The coValue is not verified
@@ -24,63 +18,34 @@ function getNewContentSince(coValue, knownState) {
24
18
  }
25
19
  return contentMessage;
26
20
  }
27
- async function createFixturesNode(customDbPath) {
28
- const [admin, session] = randomAgentAndSessionID();
29
- const node = new LocalNode(admin.agentSecret, session, crypto);
30
- // Create a unique database file for each test
31
- const dbPath = customDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
32
- const storage = await createAsyncStorage({
33
- filename: dbPath,
34
- nodeName: "test",
35
- storageName: "test-storage",
36
- });
37
- onTestFinished(() => {
38
- try {
39
- unlinkSync(dbPath);
40
- }
41
- catch { }
42
- });
43
- onTestFinished(async () => {
44
- await node.gracefulShutdown();
45
- });
46
- node.setStorage(storage);
47
- return {
48
- fixturesNode: node,
49
- dbPath,
50
- };
51
- }
52
- async function createTestNode(dbPath) {
53
- const [admin, session] = randomAgentAndSessionID();
54
- const node = new LocalNode(admin.agentSecret, session, crypto);
55
- const storage = await createAsyncStorage({
56
- filename: dbPath,
57
- nodeName: "test",
58
- storageName: "test-storage",
59
- });
60
- onTestFinished(async () => {
61
- await node.gracefulShutdown();
62
- await storage.close();
63
- });
64
- return {
65
- node,
66
- storage,
67
- };
68
- }
69
21
  afterEach(() => {
70
22
  SyncMessagesLog.clear();
23
+ vi.useRealTimers();
71
24
  });
72
25
  describe("StorageApiAsync", () => {
73
26
  describe("getKnownState", () => {
74
27
  test("should return known state for existing coValue ID", async () => {
75
- const { fixturesNode } = await createFixturesNode();
76
- const { storage } = await createTestNode();
77
- const id = fixturesNode.createGroup().id;
28
+ const fixtures = setupTestNode();
29
+ await fixtures.addAsyncStorage({
30
+ ourName: "test",
31
+ storageName: "test-storage",
32
+ });
33
+ const client = setupTestNode();
34
+ const { storage } = await client.addAsyncStorage({
35
+ ourName: "test",
36
+ storageName: "test-storage",
37
+ });
38
+ const id = fixtures.node.createGroup().id;
78
39
  const knownState = storage.getKnownState(id);
79
40
  expect(knownState).toEqual(emptyKnownState(id));
80
41
  expect(storage.getKnownState(id)).toBe(knownState); // Should return same instance
81
42
  });
82
43
  test("should return different known states for different coValue IDs", async () => {
83
- const { storage } = await createTestNode();
44
+ const client = setupTestNode();
45
+ const { storage } = await client.addAsyncStorage({
46
+ ourName: "test",
47
+ storageName: "test-storage",
48
+ });
84
49
  const id1 = "test-id-1";
85
50
  const id2 = "test-id-2";
86
51
  const knownState1 = storage.getKnownState(id1);
@@ -90,7 +55,11 @@ describe("StorageApiAsync", () => {
90
55
  });
91
56
  describe("load", () => {
92
57
  test("should handle non-existent coValue gracefully", async () => {
93
- const { storage } = await createTestNode();
58
+ const client = setupTestNode();
59
+ const { storage } = await client.addAsyncStorage({
60
+ ourName: "test",
61
+ storageName: "test-storage",
62
+ });
94
63
  const id = "non-existent-id";
95
64
  const callback = vi.fn();
96
65
  const done = vi.fn();
@@ -105,12 +74,23 @@ describe("StorageApiAsync", () => {
105
74
  expect(afterLoadKnownState).toEqual(initialKnownState);
106
75
  });
107
76
  test("should load coValue with header only successfully", async () => {
108
- const { fixturesNode, dbPath } = await createFixturesNode();
109
- const { node, storage } = await createTestNode(dbPath);
110
- const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
77
+ const dbPath = getDbPath();
78
+ const fixtures = setupTestNode();
79
+ await fixtures.addAsyncStorage({
80
+ ourName: "test",
81
+ storageName: "test-storage",
82
+ filename: dbPath,
83
+ });
84
+ const client = setupTestNode();
85
+ const { storage } = await client.addAsyncStorage({
86
+ ourName: "test",
87
+ storageName: "test-storage",
88
+ filename: dbPath,
89
+ });
90
+ const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
111
91
  const done = vi.fn();
112
92
  // Create a real group and get its content message
113
- const group = fixturesNode.createGroup();
93
+ const group = fixtures.node.createGroup();
114
94
  await group.core.waitForSync();
115
95
  // Get initial known state
116
96
  const initialKnownState = storage.getKnownState(group.id);
@@ -125,16 +105,27 @@ describe("StorageApiAsync", () => {
125
105
  // Verify that storage known state is updated after load
126
106
  const updatedKnownState = storage.getKnownState(group.id);
127
107
  expect(updatedKnownState).toEqual(group.core.knownState());
128
- const groupOnNode = await loadCoValueOrFail(node, group.id);
108
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
129
109
  expect(groupOnNode.core.verified.header).toEqual(group.core.verified.header);
130
110
  });
131
111
  test("should load coValue with sessions and transactions successfully", async () => {
132
- const { fixturesNode, dbPath } = await createFixturesNode();
133
- const { node, storage } = await createTestNode(dbPath);
134
- const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
112
+ const dbPath = getDbPath();
113
+ const fixtures = setupTestNode();
114
+ await fixtures.addAsyncStorage({
115
+ ourName: "test",
116
+ storageName: "test-storage",
117
+ filename: dbPath,
118
+ });
119
+ const client = setupTestNode();
120
+ const { storage } = await client.addAsyncStorage({
121
+ ourName: "test",
122
+ storageName: "test-storage",
123
+ filename: dbPath,
124
+ });
125
+ const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
135
126
  const done = vi.fn();
136
127
  // Create a real group and add a member to create transactions
137
- const group = fixturesNode.createGroup();
128
+ const group = fixtures.node.createGroup();
138
129
  group.addMember("everyone", "reader");
139
130
  await group.core.waitForSync();
140
131
  // Get initial known state
@@ -145,23 +136,31 @@ describe("StorageApiAsync", () => {
145
136
  id: group.id,
146
137
  header: group.core.verified.header,
147
138
  new: expect.objectContaining({
148
- [fixturesNode.currentSessionID]: expect.any(Object),
139
+ [fixtures.node.currentSessionID]: expect.any(Object),
149
140
  }),
150
141
  }));
151
142
  expect(done).toHaveBeenCalledWith(true);
152
143
  // Verify that storage known state is updated after load
153
144
  const updatedKnownState = storage.getKnownState(group.id);
154
145
  expect(updatedKnownState).toEqual(group.core.knownState());
155
- const groupOnNode = await loadCoValueOrFail(node, group.id);
146
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
156
147
  expect(groupOnNode.get("everyone")).toEqual("reader");
157
148
  });
158
149
  });
159
150
  describe("store", () => {
160
151
  test("should store new coValue with header successfully", async () => {
161
- const { fixturesNode } = await createFixturesNode();
162
- const { node, storage } = await createTestNode();
152
+ const fixtures = setupTestNode();
153
+ await fixtures.addAsyncStorage({
154
+ ourName: "test",
155
+ storageName: "test-storage",
156
+ });
157
+ const client = setupTestNode();
158
+ const storage = await createAsyncStorage({
159
+ nodeName: "test",
160
+ storageName: "test-storage",
161
+ });
163
162
  // Create a real group and get its content message
164
- const group = fixturesNode.createGroup();
163
+ const group = fixtures.node.createGroup();
165
164
  const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
166
165
  const correctionCallback = vi.fn();
167
166
  // Get initial known state
@@ -172,15 +171,23 @@ describe("StorageApiAsync", () => {
172
171
  // Verify that storage known state is updated after store
173
172
  const updatedKnownState = storage.getKnownState(group.id);
174
173
  expect(updatedKnownState).toEqual(group.core.knownState());
175
- node.setStorage(storage);
176
- const groupOnNode = await loadCoValueOrFail(node, group.id);
174
+ client.addStorage({ storage });
175
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
177
176
  expect(groupOnNode.core.verified.header).toEqual(group.core.verified.header);
178
177
  });
179
178
  test("should store coValue with transactions successfully", async () => {
180
- const { fixturesNode } = await createFixturesNode();
181
- const { node, storage } = await createTestNode();
179
+ const fixtures = setupTestNode();
180
+ await fixtures.addAsyncStorage({
181
+ ourName: "test",
182
+ storageName: "test-storage",
183
+ });
184
+ const client = setupTestNode();
185
+ const storage = await createAsyncStorage({
186
+ nodeName: "test",
187
+ storageName: "test-storage",
188
+ });
182
189
  // Create a real group and add a member to create transactions
183
- const group = fixturesNode.createGroup();
190
+ const group = fixtures.node.createGroup();
184
191
  const knownState = group.core.knownState();
185
192
  group.addMember("everyone", "reader");
186
193
  const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
@@ -193,14 +200,22 @@ describe("StorageApiAsync", () => {
193
200
  // Verify that storage known state is updated after store
194
201
  const updatedKnownState = storage.getKnownState(group.id);
195
202
  expect(updatedKnownState).toEqual(group.core.knownState());
196
- node.setStorage(storage);
197
- const groupOnNode = await loadCoValueOrFail(node, group.id);
203
+ client.addStorage({ storage });
204
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
198
205
  expect(groupOnNode.get("everyone")).toEqual("reader");
199
206
  });
200
207
  test("should handle invalid assumption on header presence with correction", async () => {
201
- const { fixturesNode } = await createFixturesNode();
202
- const { node, storage } = await createTestNode();
203
- const group = fixturesNode.createGroup();
208
+ const fixtures = setupTestNode();
209
+ await fixtures.addAsyncStorage({
210
+ ourName: "test",
211
+ storageName: "test-storage",
212
+ });
213
+ const client = setupTestNode();
214
+ const storage = await createAsyncStorage({
215
+ nodeName: "test",
216
+ storageName: "test-storage",
217
+ });
218
+ const group = fixtures.node.createGroup();
204
219
  const knownState = group.core.knownState();
205
220
  group.addMember("everyone", "reader");
206
221
  const contentMessage = getNewContentSince(group.core, knownState);
@@ -217,14 +232,22 @@ describe("StorageApiAsync", () => {
217
232
  // Verify that storage known state is updated after store with correction
218
233
  const updatedKnownState = storage.getKnownState(group.id);
219
234
  expect(updatedKnownState).toEqual(group.core.knownState());
220
- node.setStorage(storage);
221
- const groupOnNode = await loadCoValueOrFail(node, group.id);
235
+ client.addStorage({ storage });
236
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
222
237
  expect(groupOnNode.get("everyone")).toEqual("reader");
223
238
  });
224
239
  test("should handle invalid assumption on new content with correction", async () => {
225
- const { fixturesNode } = await createFixturesNode();
226
- const { node, storage } = await createTestNode();
227
- const group = fixturesNode.createGroup();
240
+ const fixtures = setupTestNode();
241
+ await fixtures.addAsyncStorage({
242
+ ourName: "test",
243
+ storageName: "test-storage",
244
+ });
245
+ const client = setupTestNode();
246
+ const storage = await createAsyncStorage({
247
+ nodeName: "test",
248
+ storageName: "test-storage",
249
+ });
250
+ const group = fixtures.node.createGroup();
228
251
  const initialContent = getNewContentSince(group.core, emptyKnownState(group.id));
229
252
  const initialKnownState = group.core.knownState();
230
253
  group.addMember("everyone", "reader");
@@ -245,14 +268,22 @@ describe("StorageApiAsync", () => {
245
268
  // Verify that storage known state is updated after store with correction
246
269
  const finalKnownState = storage.getKnownState(group.id);
247
270
  expect(finalKnownState).toEqual(group.core.knownState());
248
- node.setStorage(storage);
249
- const groupOnNode = await loadCoValueOrFail(node, group.id);
271
+ client.addStorage({ storage });
272
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
250
273
  expect(groupOnNode.get("everyone")).toEqual("writer");
251
274
  });
252
275
  test("should log an error when the correction callback returns undefined", async () => {
253
- const { fixturesNode } = await createFixturesNode();
254
- const { storage } = await createTestNode();
255
- const group = fixturesNode.createGroup();
276
+ const fixtures = setupTestNode();
277
+ await fixtures.addAsyncStorage({
278
+ ourName: "test",
279
+ storageName: "test-storage",
280
+ });
281
+ const client = setupTestNode();
282
+ const { storage } = await client.addAsyncStorage({
283
+ ourName: "test",
284
+ storageName: "test-storage",
285
+ });
286
+ const group = fixtures.node.createGroup();
256
287
  const knownState = group.core.knownState();
257
288
  group.addMember("everyone", "writer");
258
289
  const contentMessage = getNewContentSince(group.core, knownState);
@@ -277,9 +308,17 @@ describe("StorageApiAsync", () => {
277
308
  errorSpy.mockClear();
278
309
  });
279
310
  test("should log an error when the correction callback returns an invalid content message", async () => {
280
- const { fixturesNode } = await createFixturesNode();
281
- const { storage } = await createTestNode();
282
- const group = fixturesNode.createGroup();
311
+ const fixtures = setupTestNode();
312
+ await fixtures.addAsyncStorage({
313
+ ourName: "test",
314
+ storageName: "test-storage",
315
+ });
316
+ const client = setupTestNode();
317
+ const { storage } = await client.addAsyncStorage({
318
+ ourName: "test",
319
+ storageName: "test-storage",
320
+ });
321
+ const group = fixtures.node.createGroup();
283
322
  const knownState = group.core.knownState();
284
323
  group.addMember("everyone", "writer");
285
324
  const contentMessage = getNewContentSince(group.core, knownState);
@@ -308,17 +347,21 @@ describe("StorageApiAsync", () => {
308
347
  errorSpy.mockClear();
309
348
  });
310
349
  test("should handle invalid assumption when pushing multiple transactions with correction", async () => {
311
- const { node, storage } = await createTestNode();
312
- const core = node.createCoValue({
350
+ const client = setupTestNode();
351
+ const storage = await createAsyncStorage({
352
+ nodeName: "test",
353
+ storageName: "test-storage",
354
+ });
355
+ const core = client.node.createCoValue({
313
356
  type: "comap",
314
357
  ruleset: { type: "unsafeAllowAll" },
315
358
  meta: null,
316
- ...crypto.createdNowUnique(),
359
+ ...client.node.crypto.createdNowUnique(),
317
360
  });
318
361
  core.makeTransaction([{ count: 1 }], "trusting");
319
362
  await core.waitForSync();
320
363
  // Add storage later
321
- node.setStorage(storage);
364
+ client.addStorage({ storage });
322
365
  core.makeTransaction([{ count: 2 }], "trusting");
323
366
  core.makeTransaction([{ count: 3 }], "trusting");
324
367
  await new Promise((resolve) => setTimeout(resolve, 10));
@@ -338,24 +381,28 @@ describe("StorageApiAsync", () => {
338
381
  `);
339
382
  });
340
383
  test("should handle invalid assumption when pushing multiple transactions on different coValues with correction", async () => {
341
- const { node, storage } = await createTestNode();
342
- const core = node.createCoValue({
384
+ const client = setupTestNode();
385
+ const storage = await createAsyncStorage({
386
+ nodeName: "test",
387
+ storageName: "test-storage",
388
+ });
389
+ const core = client.node.createCoValue({
343
390
  type: "comap",
344
391
  ruleset: { type: "unsafeAllowAll" },
345
392
  meta: null,
346
- ...crypto.createdNowUnique(),
393
+ ...client.node.crypto.createdNowUnique(),
347
394
  });
348
- const core2 = node.createCoValue({
395
+ const core2 = client.node.createCoValue({
349
396
  type: "comap",
350
397
  ruleset: { type: "unsafeAllowAll" },
351
398
  meta: null,
352
- ...crypto.createdNowUnique(),
399
+ ...client.node.crypto.createdNowUnique(),
353
400
  });
354
401
  core.makeTransaction([{ count: 1 }], "trusting");
355
402
  core2.makeTransaction([{ count: 1 }], "trusting");
356
403
  await core.waitForSync();
357
404
  // Add storage later
358
- node.setStorage(storage);
405
+ client.addStorage({ storage });
359
406
  core.makeTransaction([{ count: 2 }], "trusting");
360
407
  core2.makeTransaction([{ count: 2 }], "trusting");
361
408
  core.makeTransaction([{ count: 3 }], "trusting");
@@ -388,24 +435,28 @@ describe("StorageApiAsync", () => {
388
435
  `);
389
436
  });
390
437
  test("should handle close while pushing multiple transactions on different coValues with an invalid assumption", async () => {
391
- const { node, storage } = await createTestNode();
392
- const core = node.createCoValue({
438
+ const client = setupTestNode();
439
+ const storage = await createAsyncStorage({
440
+ nodeName: "test",
441
+ storageName: "test-storage",
442
+ });
443
+ const core = client.node.createCoValue({
393
444
  type: "comap",
394
445
  ruleset: { type: "unsafeAllowAll" },
395
446
  meta: null,
396
- ...crypto.createdNowUnique(),
447
+ ...client.node.crypto.createdNowUnique(),
397
448
  });
398
- const core2 = node.createCoValue({
449
+ const core2 = client.node.createCoValue({
399
450
  type: "comap",
400
451
  ruleset: { type: "unsafeAllowAll" },
401
452
  meta: null,
402
- ...crypto.createdNowUnique(),
453
+ ...client.node.crypto.createdNowUnique(),
403
454
  });
404
455
  core.makeTransaction([{ count: 1 }], "trusting");
405
456
  core2.makeTransaction([{ count: 1 }], "trusting");
406
457
  await core.waitForSync();
407
458
  // Add storage later
408
- node.setStorage(storage);
459
+ client.addStorage({ storage });
409
460
  core.makeTransaction([{ count: 2 }], "trusting");
410
461
  core2.makeTransaction([{ count: 2 }], "trusting");
411
462
  core.makeTransaction([{ count: 3 }], "trusting");
@@ -438,14 +489,29 @@ describe("StorageApiAsync", () => {
438
489
  expect(storage.getKnownState(core.id)).toEqual(knownState);
439
490
  });
440
491
  test("should handle multiple sessions correctly", async () => {
441
- const { fixturesNode, dbPath } = await createFixturesNode();
442
- const { fixturesNode: fixtureNode2 } = await createFixturesNode(dbPath);
443
- const { node, storage } = await createTestNode();
444
- const coValue = fixturesNode.createCoValue({
492
+ const dbPath = getDbPath();
493
+ const fixtures = setupTestNode();
494
+ await fixtures.addAsyncStorage({
495
+ ourName: "test",
496
+ storageName: "test-storage",
497
+ filename: dbPath,
498
+ });
499
+ const fixture2 = setupTestNode();
500
+ await fixture2.addAsyncStorage({
501
+ ourName: "test",
502
+ storageName: "test-storage",
503
+ filename: dbPath,
504
+ });
505
+ const client = setupTestNode();
506
+ const storage = await createAsyncStorage({
507
+ nodeName: "test",
508
+ storageName: "test-storage",
509
+ });
510
+ const coValue = fixtures.node.createCoValue({
445
511
  type: "comap",
446
512
  ruleset: { type: "unsafeAllowAll" },
447
513
  meta: null,
448
- ...crypto.createdNowUnique(),
514
+ ...fixtures.node.crypto.createdNowUnique(),
449
515
  });
450
516
  coValue.makeTransaction([
451
517
  {
@@ -453,7 +519,7 @@ describe("StorageApiAsync", () => {
453
519
  },
454
520
  ], "trusting");
455
521
  await coValue.waitForSync();
456
- const mapOnNode2 = await loadCoValueOrFail(fixtureNode2, coValue.id);
522
+ const mapOnNode2 = await loadCoValueOrFail(fixture2.node, coValue.id);
457
523
  coValue.makeTransaction([
458
524
  {
459
525
  count: 2,
@@ -464,22 +530,215 @@ describe("StorageApiAsync", () => {
464
530
  const correctionCallback = vi.fn();
465
531
  await storage.store(contentMessage, correctionCallback);
466
532
  await storage.waitForSync(mapOnNode2.id, mapOnNode2.core);
467
- node.setStorage(storage);
468
- const finalMap = await loadCoValueOrFail(node, mapOnNode2.id);
533
+ client.addStorage({ storage });
534
+ const finalMap = await loadCoValueOrFail(client.node, mapOnNode2.id);
469
535
  expect(finalMap.core.knownState()).toEqual(knownState);
470
536
  });
471
537
  });
538
+ describe("delete flow", () => {
539
+ test("deleteCoValue enqueues the coValue for erasure", async () => {
540
+ const storage = await createAsyncStorage({
541
+ nodeName: "test",
542
+ storageName: "test-storage",
543
+ });
544
+ const client = setupTestNode();
545
+ client.addStorage({ storage });
546
+ const group = client.node.createGroup();
547
+ const map = group.createMap();
548
+ map.core.deleteCoValue();
549
+ await map.core.waitForSync();
550
+ expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
551
+ });
552
+ test("background erasure doesn't run if not enabled", async () => {
553
+ const dbPath = getDbPath();
554
+ const node = setupTestNode();
555
+ const { storage } = await node.addAsyncStorage({
556
+ ourName: "test",
557
+ storageName: "test-storage",
558
+ filename: dbPath,
559
+ });
560
+ const group = node.node.createGroup();
561
+ const map = group.createMap();
562
+ map.set("k", "v");
563
+ await map.core.waitForSync();
564
+ vi.useFakeTimers();
565
+ map.core.deleteCoValue();
566
+ await map.core.waitForSync();
567
+ await vi.runAllTimersAsync();
568
+ expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
569
+ });
570
+ test("background erasure runs if enabled", async () => {
571
+ const dbPath = getDbPath();
572
+ const node = setupTestNode();
573
+ const { storage } = await node.addAsyncStorage({
574
+ ourName: "test",
575
+ storageName: "test-storage",
576
+ filename: dbPath,
577
+ });
578
+ vi.useFakeTimers();
579
+ node.node.enableDeletedCoValuesErasure();
580
+ const group = node.node.createGroup();
581
+ const map = group.createMap();
582
+ map.set("k", "v");
583
+ await map.core.waitForSync();
584
+ map.core.deleteCoValue();
585
+ await map.core.waitForSync();
586
+ await vi.runAllTimersAsync();
587
+ expect(await getAllCoValuesWaitingForDelete(storage)).not.toContain(map.id);
588
+ const sessionIDs = await getCoValueStoredSessions(storage, map.id);
589
+ expect(sessionIDs).toHaveLength(1);
590
+ expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
591
+ });
592
+ test("eraseAllDeletedCoValues deletes history but preserves tombstone", async () => {
593
+ const dbPath = getDbPath();
594
+ const node = setupTestNode();
595
+ const { storage } = await node.addAsyncStorage({
596
+ ourName: "test",
597
+ storageName: "test-storage",
598
+ filename: dbPath,
599
+ });
600
+ const group = node.node.createGroup();
601
+ const map = group.createMap();
602
+ map.set("k", "v");
603
+ await map.core.waitForSync();
604
+ map.core.deleteCoValue();
605
+ await map.core.waitForSync();
606
+ await waitFor(async () => {
607
+ const queued = await getAllCoValuesWaitingForDelete(storage);
608
+ expect(queued).toContain(map.id);
609
+ return true;
610
+ });
611
+ await storage.eraseAllDeletedCoValues();
612
+ const queued = await getAllCoValuesWaitingForDelete(storage);
613
+ expect(queued).not.toContain(map.id);
614
+ // Tombstone preserved + history erased when loaded from storage
615
+ const client = setupTestNode();
616
+ const { storage: clientStorage } = await client.addAsyncStorage({
617
+ ourName: "test",
618
+ storageName: "test-storage",
619
+ filename: dbPath,
620
+ });
621
+ const loaded = await loadCoValueOrFail(client.node, map.id);
622
+ expect(loaded.core.isDeleted).toBe(true);
623
+ expect(loaded.get("k")).toBeUndefined();
624
+ const sessionIDs = await getCoValueStoredSessions(clientStorage, map.id);
625
+ expect(sessionIDs).toHaveLength(1);
626
+ expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
627
+ });
628
+ test("eraseAllDeletedCoValues does not break when called while a coValue is streaming from storage", async () => {
629
+ const dbPath = getDbPath();
630
+ const node = setupTestNode();
631
+ const { storage } = await node.addAsyncStorage({
632
+ ourName: "test",
633
+ storageName: "test-storage",
634
+ filename: dbPath,
635
+ });
636
+ const group = node.node.createGroup();
637
+ const map = group.createMap();
638
+ fillCoMapWithLargeData(map);
639
+ await map.core.waitForSync();
640
+ map.core.deleteCoValue();
641
+ await map.core.waitForSync();
642
+ storage.close();
643
+ const newSession = node.spawnNewSession();
644
+ const { storage: newStorage } = await newSession.addAsyncStorage({
645
+ ourName: "test",
646
+ storageName: "test-storage",
647
+ filename: dbPath,
648
+ });
649
+ const callback = vi.fn();
650
+ const loadPromise = new Promise((resolve) => {
651
+ newStorage.load(map.id, callback, resolve);
652
+ });
653
+ await newStorage.eraseAllDeletedCoValues();
654
+ expect(await loadPromise).toBe(true);
655
+ });
656
+ test("load interrupts eraseAllDeletedCoValues mid-run (resolves early, leaves some queued)", async () => {
657
+ const dbPath = getDbPath();
658
+ const node = setupTestNode();
659
+ const { storage } = await node.addAsyncStorage({
660
+ ourName: "test",
661
+ storageName: "test-storage",
662
+ filename: dbPath,
663
+ });
664
+ const group = node.node.createGroup();
665
+ const map1 = group.createMap();
666
+ map1.set("k", "v");
667
+ await map1.core.waitForSync();
668
+ const map2 = group.createMap();
669
+ map2.set("k", "v");
670
+ await map2.core.waitForSync();
671
+ map1.core.deleteCoValue();
672
+ map2.core.deleteCoValue();
673
+ await map1.core.waitForSync();
674
+ await map2.core.waitForSync();
675
+ await waitFor(async () => {
676
+ const queued = await getAllCoValuesWaitingForDelete(storage);
677
+ expect(queued).toEqual(expect.arrayContaining([map1.id, map2.id]));
678
+ return true;
679
+ });
680
+ let releaseBarrier;
681
+ const barrier = new Promise((resolve) => {
682
+ releaseBarrier = resolve;
683
+ });
684
+ let firstTxStartedResolve;
685
+ const firstTxStarted = new Promise((resolve) => {
686
+ firstTxStartedResolve = resolve;
687
+ });
688
+ // @ts-expect-error - dbClient is private
689
+ const dbClient = storage.dbClient;
690
+ const originalTransaction = dbClient.transaction.bind(dbClient);
691
+ let txCalls = 0;
692
+ const txSpy = vi
693
+ .spyOn(dbClient, "transaction")
694
+ .mockImplementation(async (callback) => {
695
+ txCalls += 1;
696
+ return originalTransaction(async (tx) => {
697
+ if (txCalls === 1) {
698
+ firstTxStartedResolve();
699
+ await barrier;
700
+ }
701
+ return callback(tx);
702
+ });
703
+ });
704
+ const erasePromise = storage.eraseAllDeletedCoValues();
705
+ // Ensure the eraser is in-flight and inside its first transaction.
706
+ await firstTxStarted;
707
+ // Trigger interruption. We don't await the load immediately to avoid doing
708
+ // DB reads while the transaction is being held open by the barrier.
709
+ const loadDone = new Promise((resolve) => {
710
+ void storage.load("non-existent-id", () => { }, resolve);
711
+ });
712
+ releaseBarrier();
713
+ await erasePromise;
714
+ await loadDone;
715
+ const queuedAfter = await getAllCoValuesWaitingForDelete(storage);
716
+ expect(queuedAfter).toHaveLength(1);
717
+ txSpy.mockRestore();
718
+ });
719
+ });
472
720
  describe("dependencies", () => {
473
721
  test("should push dependencies before the coValue", async () => {
474
- const { fixturesNode, dbPath } = await createFixturesNode();
475
- const { node, storage } = await createTestNode(dbPath);
722
+ const dbPath = getDbPath();
723
+ const fixtures = setupTestNode();
724
+ await fixtures.addAsyncStorage({
725
+ ourName: "test",
726
+ storageName: "test-storage",
727
+ filename: dbPath,
728
+ });
729
+ const client = setupTestNode();
730
+ const storage = await createAsyncStorage({
731
+ nodeName: "test",
732
+ storageName: "test-storage",
733
+ filename: dbPath,
734
+ });
476
735
  // Create a group and a map owned by that group to create dependencies
477
- const group = fixturesNode.createGroup();
736
+ const group = fixtures.node.createGroup();
478
737
  group.addMember("everyone", "reader");
479
738
  const map = group.createMap({ test: "value" });
480
739
  await group.core.waitForSync();
481
740
  await map.core.waitForSync();
482
- const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
741
+ const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
483
742
  const done = vi.fn();
484
743
  // Get initial known states
485
744
  const initialGroupKnownState = storage.getKnownState(group.id);
@@ -501,20 +760,31 @@ describe("StorageApiAsync", () => {
501
760
  const updatedMapKnownState = storage.getKnownState(map.id);
502
761
  expect(updatedGroupKnownState).toEqual(group.core.knownState());
503
762
  expect(updatedMapKnownState).toEqual(map.core.knownState());
504
- node.setStorage(storage);
505
- const mapOnNode = await loadCoValueOrFail(node, map.id);
763
+ client.addStorage({ storage });
764
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
506
765
  expect(mapOnNode.get("test")).toEqual("value");
507
766
  });
508
767
  test("should handle dependencies that are already loaded correctly", async () => {
509
- const { fixturesNode, dbPath } = await createFixturesNode();
510
- const { node, storage } = await createTestNode(dbPath);
768
+ const dbPath = getDbPath();
769
+ const fixtures = setupTestNode();
770
+ await fixtures.addAsyncStorage({
771
+ ourName: "test",
772
+ storageName: "test-storage",
773
+ filename: dbPath,
774
+ });
775
+ const client = setupTestNode();
776
+ const storage = await createAsyncStorage({
777
+ nodeName: "test",
778
+ storageName: "test-storage",
779
+ filename: dbPath,
780
+ });
511
781
  // Create a group and a map owned by that group
512
- const group = fixturesNode.createGroup();
782
+ const group = fixtures.node.createGroup();
513
783
  group.addMember("everyone", "reader");
514
784
  const map = group.createMap({ test: "value" });
515
785
  await group.core.waitForSync();
516
786
  await map.core.waitForSync();
517
- const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
787
+ const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
518
788
  const done = vi.fn();
519
789
  // Get initial known states
520
790
  const initialGroupKnownState = storage.getKnownState(group.id);
@@ -539,20 +809,31 @@ describe("StorageApiAsync", () => {
539
809
  // Verify map known state is updated after second load
540
810
  const finalMapKnownState = storage.getKnownState(map.id);
541
811
  expect(finalMapKnownState).toEqual(map.core.knownState());
542
- node.setStorage(storage);
543
- const mapOnNode = await loadCoValueOrFail(node, map.id);
812
+ client.addStorage({ storage });
813
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
544
814
  expect(mapOnNode.get("test")).toEqual("value");
545
815
  });
546
816
  test("should load dependencies again if they were unmounted", async () => {
547
- const { fixturesNode, dbPath } = await createFixturesNode();
548
- const { node, storage } = await createTestNode(dbPath);
817
+ const dbPath = getDbPath();
818
+ const fixtures = setupTestNode();
819
+ await fixtures.addAsyncStorage({
820
+ ourName: "test",
821
+ storageName: "test-storage",
822
+ filename: dbPath,
823
+ });
824
+ const client = setupTestNode();
825
+ const { storage } = await client.addAsyncStorage({
826
+ ourName: "test",
827
+ storageName: "test-storage",
828
+ filename: dbPath,
829
+ });
549
830
  // Create a group and a map owned by that group
550
- const group = fixturesNode.createGroup();
831
+ const group = fixtures.node.createGroup();
551
832
  group.addMember("everyone", "reader");
552
833
  const map = group.createMap({ test: "value" });
553
834
  await group.core.waitForSync();
554
835
  await map.core.waitForSync();
555
- const callback = vi.fn((content) => node.syncManager.handleNewContent(content, "storage"));
836
+ const callback = vi.fn((content) => client.node.syncManager.handleNewContent(content, "storage"));
556
837
  const done = vi.fn();
557
838
  // Load the map (and its group)
558
839
  await storage.load(map.id, callback, done);
@@ -571,26 +852,37 @@ describe("StorageApiAsync", () => {
571
852
  id: map.id,
572
853
  }));
573
854
  expect(done).toHaveBeenCalledWith(true);
574
- node.setStorage(storage);
575
- const mapOnNode = await loadCoValueOrFail(node, map.id);
855
+ client.addStorage({ storage });
856
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
576
857
  expect(mapOnNode.get("test")).toEqual("value");
577
858
  });
578
859
  });
579
860
  describe("waitForSync", () => {
580
861
  test("should resolve when the coValue is already synced", async () => {
581
- const { fixturesNode, dbPath } = await createFixturesNode();
582
- const { node, storage } = await createTestNode(dbPath);
862
+ const dbPath = getDbPath();
863
+ const fixtures = setupTestNode();
864
+ await fixtures.addAsyncStorage({
865
+ ourName: "test",
866
+ storageName: "test-storage",
867
+ filename: dbPath,
868
+ });
869
+ const client = setupTestNode();
870
+ const storage = await createAsyncStorage({
871
+ nodeName: "test",
872
+ storageName: "test-storage",
873
+ filename: dbPath,
874
+ });
583
875
  // Create a group and add a member
584
- const group = fixturesNode.createGroup();
876
+ const group = fixtures.node.createGroup();
585
877
  group.addMember("everyone", "reader");
586
878
  await group.core.waitForSync();
587
879
  // Store the group in storage
588
880
  const contentMessage = getNewContentSince(group.core, emptyKnownState(group.id));
589
881
  const correctionCallback = vi.fn();
590
882
  await storage.store(contentMessage, correctionCallback);
591
- node.setStorage(storage);
883
+ client.addStorage({ storage });
592
884
  // Load the group on the new node
593
- const groupOnNode = await loadCoValueOrFail(node, group.id);
885
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
594
886
  // Wait for sync should resolve immediately since the coValue is already synced
595
887
  await expect(storage.waitForSync(group.id, groupOnNode.core)).resolves.toBeUndefined();
596
888
  expect(groupOnNode.get("everyone")).toEqual("reader");
@@ -598,16 +890,30 @@ describe("StorageApiAsync", () => {
598
890
  });
599
891
  describe("close", () => {
600
892
  test("should close without throwing an error", async () => {
601
- const { storage } = await createTestNode();
893
+ const storage = await createAsyncStorage({
894
+ nodeName: "test",
895
+ storageName: "test-storage",
896
+ });
602
897
  expect(() => storage.close()).not.toThrow();
603
898
  });
604
899
  });
605
900
  describe("loadKnownState", () => {
606
901
  test("should return cached knownState if available", async () => {
607
- const { fixturesNode, dbPath } = await createFixturesNode();
608
- const { storage } = await createTestNode(dbPath);
902
+ const dbPath = getDbPath();
903
+ const fixtures = setupTestNode();
904
+ await fixtures.addAsyncStorage({
905
+ ourName: "test",
906
+ storageName: "test-storage",
907
+ filename: dbPath,
908
+ });
909
+ const client = setupTestNode();
910
+ const { storage } = await client.addAsyncStorage({
911
+ ourName: "test",
912
+ storageName: "test-storage",
913
+ filename: dbPath,
914
+ });
609
915
  // Create a group to have data in the database
610
- const group = fixturesNode.createGroup();
916
+ const group = fixtures.node.createGroup();
611
917
  group.addMember("everyone", "reader");
612
918
  await group.core.waitForSync();
613
919
  // First call should hit the database and cache the result
@@ -624,17 +930,32 @@ describe("StorageApiAsync", () => {
624
930
  expect(result2).toEqual(result1);
625
931
  });
626
932
  test("should return undefined for non-existent CoValue", async () => {
627
- const { storage } = await createTestNode();
933
+ const client = setupTestNode();
934
+ const { storage } = await client.addAsyncStorage({
935
+ ourName: "test",
936
+ storageName: "test-storage",
937
+ });
628
938
  const result = await new Promise((resolve) => {
629
939
  storage.loadKnownState("co_nonexistent", resolve);
630
940
  });
631
941
  expect(result).toBeUndefined();
632
942
  });
633
943
  test("should deduplicate concurrent requests for the same ID", async () => {
634
- const { fixturesNode, dbPath } = await createFixturesNode();
635
- const { storage } = await createTestNode(dbPath);
944
+ const dbPath = getDbPath();
945
+ const fixtures = setupTestNode();
946
+ await fixtures.addAsyncStorage({
947
+ ourName: "test",
948
+ storageName: "test-storage",
949
+ filename: dbPath,
950
+ });
951
+ const client = setupTestNode();
952
+ const { storage } = await client.addAsyncStorage({
953
+ ourName: "test",
954
+ storageName: "test-storage",
955
+ filename: dbPath,
956
+ });
636
957
  // Create a group to have data in the database
637
- const group = fixturesNode.createGroup();
958
+ const group = fixtures.node.createGroup();
638
959
  group.addMember("everyone", "reader");
639
960
  await group.core.waitForSync();
640
961
  // Clear the cache to force database access
@@ -662,10 +983,21 @@ describe("StorageApiAsync", () => {
662
983
  expect(dbClientSpy).toHaveBeenCalledTimes(1);
663
984
  });
664
985
  test("should use cache and not query database when cache is populated", async () => {
665
- const { fixturesNode, dbPath } = await createFixturesNode();
666
- const { storage } = await createTestNode(dbPath);
986
+ const dbPath = getDbPath();
987
+ const fixtures = setupTestNode();
988
+ await fixtures.addAsyncStorage({
989
+ ourName: "test",
990
+ storageName: "test-storage",
991
+ filename: dbPath,
992
+ });
993
+ const client = setupTestNode();
994
+ const { storage } = await client.addAsyncStorage({
995
+ ourName: "test",
996
+ storageName: "test-storage",
997
+ filename: dbPath,
998
+ });
667
999
  // Create a group to have data in the database
668
- const group = fixturesNode.createGroup();
1000
+ const group = fixtures.node.createGroup();
669
1001
  group.addMember("everyone", "reader");
670
1002
  await group.core.waitForSync();
671
1003
  // Spy on the database client to track calls