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,23 +1,22 @@
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 { CoID, LocalNode, RawCoMap, logger } from "../exports.js";
1
+ import { afterEach, describe, expect, test, vi } from "vitest";
2
+ import { CoID, RawCoMap, logger } from "../exports.js";
8
3
  import { CoValueCore } from "../exports.js";
9
4
  import { NewContentMessage } from "../sync.js";
10
- import { createAsyncStorage } from "./testStorage.js";
5
+ import {
6
+ createAsyncStorage,
7
+ getAllCoValuesWaitingForDelete,
8
+ getCoValueStoredSessions,
9
+ getDbPath,
10
+ } from "./testStorage.js";
11
11
  import {
12
12
  SyncMessagesLog,
13
+ fillCoMapWithLargeData,
13
14
  loadCoValueOrFail,
14
- randomAgentAndSessionID,
15
+ setupTestNode,
15
16
  waitFor,
16
17
  } from "./testUtils.js";
17
18
  import { CoValueKnownState, emptyKnownState } from "../knownState.js";
18
19
 
19
- const crypto = await WasmCrypto.create();
20
-
21
20
  /**
22
21
  * Helper function that gets new content since a known state, throwing if:
23
22
  * - The coValue is not verified
@@ -40,68 +39,27 @@ function getNewContentSince(
40
39
  return contentMessage;
41
40
  }
42
41
 
43
- async function createFixturesNode(customDbPath?: string) {
44
- const [admin, session] = randomAgentAndSessionID();
45
- const node = new LocalNode(admin.agentSecret, session, crypto);
46
-
47
- // Create a unique database file for each test
48
- const dbPath = customDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
49
- const storage = await createAsyncStorage({
50
- filename: dbPath,
51
- nodeName: "test",
52
- storageName: "test-storage",
53
- });
54
-
55
- onTestFinished(() => {
56
- try {
57
- unlinkSync(dbPath);
58
- } catch {}
59
- });
60
-
61
- onTestFinished(async () => {
62
- await node.gracefulShutdown();
63
- });
64
-
65
- node.setStorage(storage);
66
-
67
- return {
68
- fixturesNode: node,
69
- dbPath,
70
- };
71
- }
72
-
73
- async function createTestNode(dbPath?: string) {
74
- const [admin, session] = randomAgentAndSessionID();
75
- const node = new LocalNode(admin.agentSecret, session, crypto);
76
-
77
- const storage = await createAsyncStorage({
78
- filename: dbPath,
79
- nodeName: "test",
80
- storageName: "test-storage",
81
- });
82
-
83
- onTestFinished(async () => {
84
- await node.gracefulShutdown();
85
- await storage.close();
86
- });
87
-
88
- return {
89
- node,
90
- storage,
91
- };
92
- }
93
-
94
42
  afterEach(() => {
95
43
  SyncMessagesLog.clear();
44
+ vi.useRealTimers();
96
45
  });
97
46
 
98
47
  describe("StorageApiAsync", () => {
99
48
  describe("getKnownState", () => {
100
49
  test("should return known state for existing coValue ID", async () => {
101
- const { fixturesNode } = await createFixturesNode();
102
- const { storage } = await createTestNode();
50
+ const fixtures = setupTestNode();
51
+ await fixtures.addAsyncStorage({
52
+ ourName: "test",
53
+ storageName: "test-storage",
54
+ });
55
+
56
+ const client = setupTestNode();
57
+ const { storage } = await client.addAsyncStorage({
58
+ ourName: "test",
59
+ storageName: "test-storage",
60
+ });
103
61
 
104
- const id = fixturesNode.createGroup().id;
62
+ const id = fixtures.node.createGroup().id;
105
63
  const knownState = storage.getKnownState(id);
106
64
 
107
65
  expect(knownState).toEqual(emptyKnownState(id));
@@ -109,7 +67,11 @@ describe("StorageApiAsync", () => {
109
67
  });
110
68
 
111
69
  test("should return different known states for different coValue IDs", async () => {
112
- const { storage } = await createTestNode();
70
+ const client = setupTestNode();
71
+ const { storage } = await client.addAsyncStorage({
72
+ ourName: "test",
73
+ storageName: "test-storage",
74
+ });
113
75
  const id1 = "test-id-1";
114
76
  const id2 = "test-id-2";
115
77
 
@@ -122,7 +84,11 @@ describe("StorageApiAsync", () => {
122
84
 
123
85
  describe("load", () => {
124
86
  test("should handle non-existent coValue gracefully", async () => {
125
- const { storage } = await createTestNode();
87
+ const client = setupTestNode();
88
+ const { storage } = await client.addAsyncStorage({
89
+ ourName: "test",
90
+ storageName: "test-storage",
91
+ });
126
92
  const id = "non-existent-id";
127
93
  const callback = vi.fn();
128
94
  const done = vi.fn();
@@ -142,15 +108,27 @@ describe("StorageApiAsync", () => {
142
108
  });
143
109
 
144
110
  test("should load coValue with header only successfully", async () => {
145
- const { fixturesNode, dbPath } = await createFixturesNode();
146
- const { node, storage } = await createTestNode(dbPath);
111
+ const dbPath = getDbPath();
112
+ const fixtures = setupTestNode();
113
+ await fixtures.addAsyncStorage({
114
+ ourName: "test",
115
+ storageName: "test-storage",
116
+ filename: dbPath,
117
+ });
118
+
119
+ const client = setupTestNode();
120
+ const { storage } = await client.addAsyncStorage({
121
+ ourName: "test",
122
+ storageName: "test-storage",
123
+ filename: dbPath,
124
+ });
147
125
  const callback = vi.fn((content) =>
148
- node.syncManager.handleNewContent(content, "storage"),
126
+ client.node.syncManager.handleNewContent(content, "storage"),
149
127
  );
150
128
  const done = vi.fn();
151
129
 
152
130
  // Create a real group and get its content message
153
- const group = fixturesNode.createGroup();
131
+ const group = fixtures.node.createGroup();
154
132
  await group.core.waitForSync();
155
133
 
156
134
  // Get initial known state
@@ -172,7 +150,7 @@ describe("StorageApiAsync", () => {
172
150
  const updatedKnownState = storage.getKnownState(group.id);
173
151
  expect(updatedKnownState).toEqual(group.core.knownState());
174
152
 
175
- const groupOnNode = await loadCoValueOrFail(node, group.id);
153
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
176
154
 
177
155
  expect(groupOnNode.core.verified.header).toEqual(
178
156
  group.core.verified.header,
@@ -180,15 +158,27 @@ describe("StorageApiAsync", () => {
180
158
  });
181
159
 
182
160
  test("should load coValue with sessions and transactions successfully", async () => {
183
- const { fixturesNode, dbPath } = await createFixturesNode();
184
- const { node, storage } = await createTestNode(dbPath);
161
+ const dbPath = getDbPath();
162
+ const fixtures = setupTestNode();
163
+ await fixtures.addAsyncStorage({
164
+ ourName: "test",
165
+ storageName: "test-storage",
166
+ filename: dbPath,
167
+ });
168
+
169
+ const client = setupTestNode();
170
+ const { storage } = await client.addAsyncStorage({
171
+ ourName: "test",
172
+ storageName: "test-storage",
173
+ filename: dbPath,
174
+ });
185
175
  const callback = vi.fn((content) =>
186
- node.syncManager.handleNewContent(content, "storage"),
176
+ client.node.syncManager.handleNewContent(content, "storage"),
187
177
  );
188
178
  const done = vi.fn();
189
179
 
190
180
  // Create a real group and add a member to create transactions
191
- const group = fixturesNode.createGroup();
181
+ const group = fixtures.node.createGroup();
192
182
  group.addMember("everyone", "reader");
193
183
  await group.core.waitForSync();
194
184
 
@@ -203,7 +193,7 @@ describe("StorageApiAsync", () => {
203
193
  id: group.id,
204
194
  header: group.core.verified.header,
205
195
  new: expect.objectContaining({
206
- [fixturesNode.currentSessionID]: expect.any(Object),
196
+ [fixtures.node.currentSessionID]: expect.any(Object),
207
197
  }),
208
198
  }),
209
199
  );
@@ -213,17 +203,26 @@ describe("StorageApiAsync", () => {
213
203
  const updatedKnownState = storage.getKnownState(group.id);
214
204
  expect(updatedKnownState).toEqual(group.core.knownState());
215
205
 
216
- const groupOnNode = await loadCoValueOrFail(node, group.id);
206
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
217
207
  expect(groupOnNode.get("everyone")).toEqual("reader");
218
208
  });
219
209
  });
220
210
 
221
211
  describe("store", () => {
222
212
  test("should store new coValue with header successfully", async () => {
223
- const { fixturesNode } = await createFixturesNode();
224
- const { node, storage } = await createTestNode();
213
+ const fixtures = setupTestNode();
214
+ await fixtures.addAsyncStorage({
215
+ ourName: "test",
216
+ storageName: "test-storage",
217
+ });
218
+
219
+ const client = setupTestNode();
220
+ const storage = await createAsyncStorage({
221
+ nodeName: "test",
222
+ storageName: "test-storage",
223
+ });
225
224
  // Create a real group and get its content message
226
- const group = fixturesNode.createGroup();
225
+ const group = fixtures.node.createGroup();
227
226
  const contentMessage = getNewContentSince(
228
227
  group.core,
229
228
  emptyKnownState(group.id),
@@ -241,9 +240,9 @@ describe("StorageApiAsync", () => {
241
240
  const updatedKnownState = storage.getKnownState(group.id);
242
241
  expect(updatedKnownState).toEqual(group.core.knownState());
243
242
 
244
- node.setStorage(storage);
243
+ client.addStorage({ storage });
245
244
 
246
- const groupOnNode = await loadCoValueOrFail(node, group.id);
245
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
247
246
 
248
247
  expect(groupOnNode.core.verified.header).toEqual(
249
248
  group.core.verified.header,
@@ -251,11 +250,20 @@ describe("StorageApiAsync", () => {
251
250
  });
252
251
 
253
252
  test("should store coValue with transactions successfully", async () => {
254
- const { fixturesNode } = await createFixturesNode();
255
- const { node, storage } = await createTestNode();
253
+ const fixtures = setupTestNode();
254
+ await fixtures.addAsyncStorage({
255
+ ourName: "test",
256
+ storageName: "test-storage",
257
+ });
258
+
259
+ const client = setupTestNode();
260
+ const storage = await createAsyncStorage({
261
+ nodeName: "test",
262
+ storageName: "test-storage",
263
+ });
256
264
 
257
265
  // Create a real group and add a member to create transactions
258
- const group = fixturesNode.createGroup();
266
+ const group = fixtures.node.createGroup();
259
267
  const knownState = group.core.knownState();
260
268
 
261
269
  group.addMember("everyone", "reader");
@@ -277,17 +285,26 @@ describe("StorageApiAsync", () => {
277
285
  const updatedKnownState = storage.getKnownState(group.id);
278
286
  expect(updatedKnownState).toEqual(group.core.knownState());
279
287
 
280
- node.setStorage(storage);
288
+ client.addStorage({ storage });
281
289
 
282
- const groupOnNode = await loadCoValueOrFail(node, group.id);
290
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
283
291
  expect(groupOnNode.get("everyone")).toEqual("reader");
284
292
  });
285
293
 
286
294
  test("should handle invalid assumption on header presence with correction", async () => {
287
- const { fixturesNode } = await createFixturesNode();
288
- const { node, storage } = await createTestNode();
295
+ const fixtures = setupTestNode();
296
+ await fixtures.addAsyncStorage({
297
+ ourName: "test",
298
+ storageName: "test-storage",
299
+ });
289
300
 
290
- const group = fixturesNode.createGroup();
301
+ const client = setupTestNode();
302
+ const storage = await createAsyncStorage({
303
+ nodeName: "test",
304
+ storageName: "test-storage",
305
+ });
306
+
307
+ const group = fixtures.node.createGroup();
291
308
  const knownState = group.core.knownState();
292
309
 
293
310
  group.addMember("everyone", "reader");
@@ -311,17 +328,26 @@ describe("StorageApiAsync", () => {
311
328
  const updatedKnownState = storage.getKnownState(group.id);
312
329
  expect(updatedKnownState).toEqual(group.core.knownState());
313
330
 
314
- node.setStorage(storage);
315
- const groupOnNode = await loadCoValueOrFail(node, group.id);
331
+ client.addStorage({ storage });
332
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
316
333
 
317
334
  expect(groupOnNode.get("everyone")).toEqual("reader");
318
335
  });
319
336
 
320
337
  test("should handle invalid assumption on new content with correction", async () => {
321
- const { fixturesNode } = await createFixturesNode();
322
- const { node, storage } = await createTestNode();
338
+ const fixtures = setupTestNode();
339
+ await fixtures.addAsyncStorage({
340
+ ourName: "test",
341
+ storageName: "test-storage",
342
+ });
343
+
344
+ const client = setupTestNode();
345
+ const storage = await createAsyncStorage({
346
+ nodeName: "test",
347
+ storageName: "test-storage",
348
+ });
323
349
 
324
- const group = fixturesNode.createGroup();
350
+ const group = fixtures.node.createGroup();
325
351
 
326
352
  const initialContent = getNewContentSince(
327
353
  group.core,
@@ -357,17 +383,26 @@ describe("StorageApiAsync", () => {
357
383
  const finalKnownState = storage.getKnownState(group.id);
358
384
  expect(finalKnownState).toEqual(group.core.knownState());
359
385
 
360
- node.setStorage(storage);
361
- const groupOnNode = await loadCoValueOrFail(node, group.id);
386
+ client.addStorage({ storage });
387
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
362
388
 
363
389
  expect(groupOnNode.get("everyone")).toEqual("writer");
364
390
  });
365
391
 
366
392
  test("should log an error when the correction callback returns undefined", async () => {
367
- const { fixturesNode } = await createFixturesNode();
368
- const { storage } = await createTestNode();
393
+ const fixtures = setupTestNode();
394
+ await fixtures.addAsyncStorage({
395
+ ourName: "test",
396
+ storageName: "test-storage",
397
+ });
398
+
399
+ const client = setupTestNode();
400
+ const { storage } = await client.addAsyncStorage({
401
+ ourName: "test",
402
+ storageName: "test-storage",
403
+ });
369
404
 
370
- const group = fixturesNode.createGroup();
405
+ const group = fixtures.node.createGroup();
371
406
 
372
407
  const knownState = group.core.knownState();
373
408
  group.addMember("everyone", "writer");
@@ -404,10 +439,19 @@ describe("StorageApiAsync", () => {
404
439
  });
405
440
 
406
441
  test("should log an error when the correction callback returns an invalid content message", async () => {
407
- const { fixturesNode } = await createFixturesNode();
408
- const { storage } = await createTestNode();
442
+ const fixtures = setupTestNode();
443
+ await fixtures.addAsyncStorage({
444
+ ourName: "test",
445
+ storageName: "test-storage",
446
+ });
447
+
448
+ const client = setupTestNode();
449
+ const { storage } = await client.addAsyncStorage({
450
+ ourName: "test",
451
+ storageName: "test-storage",
452
+ });
409
453
 
410
- const group = fixturesNode.createGroup();
454
+ const group = fixtures.node.createGroup();
411
455
 
412
456
  const knownState = group.core.knownState();
413
457
  group.addMember("everyone", "writer");
@@ -449,13 +493,17 @@ describe("StorageApiAsync", () => {
449
493
  });
450
494
 
451
495
  test("should handle invalid assumption when pushing multiple transactions with correction", async () => {
452
- const { node, storage } = await createTestNode();
496
+ const client = setupTestNode();
497
+ const storage = await createAsyncStorage({
498
+ nodeName: "test",
499
+ storageName: "test-storage",
500
+ });
453
501
 
454
- const core = node.createCoValue({
502
+ const core = client.node.createCoValue({
455
503
  type: "comap",
456
504
  ruleset: { type: "unsafeAllowAll" },
457
505
  meta: null,
458
- ...crypto.createdNowUnique(),
506
+ ...client.node.crypto.createdNowUnique(),
459
507
  });
460
508
 
461
509
  core.makeTransaction([{ count: 1 }], "trusting");
@@ -463,7 +511,7 @@ describe("StorageApiAsync", () => {
463
511
  await core.waitForSync();
464
512
 
465
513
  // Add storage later
466
- node.setStorage(storage);
514
+ client.addStorage({ storage });
467
515
 
468
516
  core.makeTransaction([{ count: 2 }], "trusting");
469
517
  core.makeTransaction([{ count: 3 }], "trusting");
@@ -492,20 +540,24 @@ describe("StorageApiAsync", () => {
492
540
  });
493
541
 
494
542
  test("should handle invalid assumption when pushing multiple transactions on different coValues with correction", async () => {
495
- const { node, storage } = await createTestNode();
543
+ const client = setupTestNode();
544
+ const storage = await createAsyncStorage({
545
+ nodeName: "test",
546
+ storageName: "test-storage",
547
+ });
496
548
 
497
- const core = node.createCoValue({
549
+ const core = client.node.createCoValue({
498
550
  type: "comap",
499
551
  ruleset: { type: "unsafeAllowAll" },
500
552
  meta: null,
501
- ...crypto.createdNowUnique(),
553
+ ...client.node.crypto.createdNowUnique(),
502
554
  });
503
555
 
504
- const core2 = node.createCoValue({
556
+ const core2 = client.node.createCoValue({
505
557
  type: "comap",
506
558
  ruleset: { type: "unsafeAllowAll" },
507
559
  meta: null,
508
- ...crypto.createdNowUnique(),
560
+ ...client.node.crypto.createdNowUnique(),
509
561
  });
510
562
 
511
563
  core.makeTransaction([{ count: 1 }], "trusting");
@@ -514,7 +566,7 @@ describe("StorageApiAsync", () => {
514
566
  await core.waitForSync();
515
567
 
516
568
  // Add storage later
517
- node.setStorage(storage);
569
+ client.addStorage({ storage });
518
570
 
519
571
  core.makeTransaction([{ count: 2 }], "trusting");
520
572
  core2.makeTransaction([{ count: 2 }], "trusting");
@@ -556,20 +608,24 @@ describe("StorageApiAsync", () => {
556
608
  });
557
609
 
558
610
  test("should handle close while pushing multiple transactions on different coValues with an invalid assumption", async () => {
559
- const { node, storage } = await createTestNode();
611
+ const client = setupTestNode();
612
+ const storage = await createAsyncStorage({
613
+ nodeName: "test",
614
+ storageName: "test-storage",
615
+ });
560
616
 
561
- const core = node.createCoValue({
617
+ const core = client.node.createCoValue({
562
618
  type: "comap",
563
619
  ruleset: { type: "unsafeAllowAll" },
564
620
  meta: null,
565
- ...crypto.createdNowUnique(),
621
+ ...client.node.crypto.createdNowUnique(),
566
622
  });
567
623
 
568
- const core2 = node.createCoValue({
624
+ const core2 = client.node.createCoValue({
569
625
  type: "comap",
570
626
  ruleset: { type: "unsafeAllowAll" },
571
627
  meta: null,
572
- ...crypto.createdNowUnique(),
628
+ ...client.node.crypto.createdNowUnique(),
573
629
  });
574
630
 
575
631
  core.makeTransaction([{ count: 1 }], "trusting");
@@ -578,7 +634,7 @@ describe("StorageApiAsync", () => {
578
634
  await core.waitForSync();
579
635
 
580
636
  // Add storage later
581
- node.setStorage(storage);
637
+ client.addStorage({ storage });
582
638
 
583
639
  core.makeTransaction([{ count: 2 }], "trusting");
584
640
  core2.makeTransaction([{ count: 2 }], "trusting");
@@ -623,15 +679,33 @@ describe("StorageApiAsync", () => {
623
679
  });
624
680
 
625
681
  test("should handle multiple sessions correctly", async () => {
626
- const { fixturesNode, dbPath } = await createFixturesNode();
627
- const { fixturesNode: fixtureNode2 } = await createFixturesNode(dbPath);
628
- const { node, storage } = await createTestNode();
682
+ const dbPath = getDbPath();
683
+
684
+ const fixtures = setupTestNode();
685
+ await fixtures.addAsyncStorage({
686
+ ourName: "test",
687
+ storageName: "test-storage",
688
+ filename: dbPath,
689
+ });
629
690
 
630
- const coValue = fixturesNode.createCoValue({
691
+ const fixture2 = setupTestNode();
692
+ await fixture2.addAsyncStorage({
693
+ ourName: "test",
694
+ storageName: "test-storage",
695
+ filename: dbPath,
696
+ });
697
+
698
+ const client = setupTestNode();
699
+ const storage = await createAsyncStorage({
700
+ nodeName: "test",
701
+ storageName: "test-storage",
702
+ });
703
+
704
+ const coValue = fixtures.node.createCoValue({
631
705
  type: "comap",
632
706
  ruleset: { type: "unsafeAllowAll" },
633
707
  meta: null,
634
- ...crypto.createdNowUnique(),
708
+ ...fixtures.node.crypto.createdNowUnique(),
635
709
  });
636
710
 
637
711
  coValue.makeTransaction(
@@ -646,7 +720,7 @@ describe("StorageApiAsync", () => {
646
720
  await coValue.waitForSync();
647
721
 
648
722
  const mapOnNode2 = await loadCoValueOrFail(
649
- fixtureNode2,
723
+ fixture2.node,
650
724
  coValue.id as CoID<RawCoMap>,
651
725
  );
652
726
 
@@ -670,27 +744,284 @@ describe("StorageApiAsync", () => {
670
744
  await storage.store(contentMessage, correctionCallback);
671
745
  await storage.waitForSync(mapOnNode2.id, mapOnNode2.core);
672
746
 
673
- node.setStorage(storage);
747
+ client.addStorage({ storage });
674
748
 
675
- const finalMap = await loadCoValueOrFail(node, mapOnNode2.id);
749
+ const finalMap = await loadCoValueOrFail(client.node, mapOnNode2.id);
676
750
  expect(finalMap.core.knownState()).toEqual(knownState);
677
751
  });
678
752
  });
679
753
 
754
+ describe("delete flow", () => {
755
+ test("deleteCoValue enqueues the coValue for erasure", async () => {
756
+ const storage = await createAsyncStorage({
757
+ nodeName: "test",
758
+ storageName: "test-storage",
759
+ });
760
+
761
+ const client = setupTestNode();
762
+ client.addStorage({ storage });
763
+
764
+ const group = client.node.createGroup();
765
+ const map = group.createMap();
766
+ map.core.deleteCoValue();
767
+ await map.core.waitForSync();
768
+
769
+ expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
770
+ });
771
+
772
+ test("background erasure doesn't run if not enabled", async () => {
773
+ const dbPath = getDbPath();
774
+
775
+ const node = setupTestNode();
776
+ const { storage } = await node.addAsyncStorage({
777
+ ourName: "test",
778
+ storageName: "test-storage",
779
+ filename: dbPath,
780
+ });
781
+
782
+ const group = node.node.createGroup();
783
+ const map = group.createMap();
784
+ map.set("k", "v");
785
+ await map.core.waitForSync();
786
+
787
+ vi.useFakeTimers();
788
+
789
+ map.core.deleteCoValue();
790
+ await map.core.waitForSync();
791
+
792
+ await vi.runAllTimersAsync();
793
+
794
+ expect(await getAllCoValuesWaitingForDelete(storage)).toContain(map.id);
795
+ });
796
+
797
+ test("background erasure runs if enabled", async () => {
798
+ const dbPath = getDbPath();
799
+
800
+ const node = setupTestNode();
801
+ const { storage } = await node.addAsyncStorage({
802
+ ourName: "test",
803
+ storageName: "test-storage",
804
+ filename: dbPath,
805
+ });
806
+
807
+ vi.useFakeTimers();
808
+ node.node.enableDeletedCoValuesErasure();
809
+
810
+ const group = node.node.createGroup();
811
+ const map = group.createMap();
812
+ map.set("k", "v");
813
+ await map.core.waitForSync();
814
+
815
+ map.core.deleteCoValue();
816
+ await map.core.waitForSync();
817
+
818
+ await vi.runAllTimersAsync();
819
+
820
+ expect(await getAllCoValuesWaitingForDelete(storage)).not.toContain(
821
+ map.id,
822
+ );
823
+
824
+ const sessionIDs = await getCoValueStoredSessions(storage, map.id);
825
+
826
+ expect(sessionIDs).toHaveLength(1);
827
+ expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
828
+ });
829
+
830
+ test("eraseAllDeletedCoValues deletes history but preserves tombstone", async () => {
831
+ const dbPath = getDbPath();
832
+
833
+ const node = setupTestNode();
834
+ const { storage } = await node.addAsyncStorage({
835
+ ourName: "test",
836
+ storageName: "test-storage",
837
+ filename: dbPath,
838
+ });
839
+
840
+ const group = node.node.createGroup();
841
+ const map = group.createMap();
842
+ map.set("k", "v");
843
+ await map.core.waitForSync();
844
+
845
+ map.core.deleteCoValue();
846
+ await map.core.waitForSync();
847
+
848
+ await waitFor(async () => {
849
+ const queued = await getAllCoValuesWaitingForDelete(storage);
850
+ expect(queued).toContain(map.id);
851
+ return true;
852
+ });
853
+
854
+ await storage.eraseAllDeletedCoValues();
855
+
856
+ const queued = await getAllCoValuesWaitingForDelete(storage);
857
+ expect(queued).not.toContain(map.id);
858
+
859
+ // Tombstone preserved + history erased when loaded from storage
860
+ const client = setupTestNode();
861
+ const { storage: clientStorage } = await client.addAsyncStorage({
862
+ ourName: "test",
863
+ storageName: "test-storage",
864
+ filename: dbPath,
865
+ });
866
+
867
+ const loaded = await loadCoValueOrFail(
868
+ client.node,
869
+ map.id as CoID<RawCoMap>,
870
+ );
871
+
872
+ expect(loaded.core.isDeleted).toBe(true);
873
+ expect(loaded.get("k")).toBeUndefined();
874
+
875
+ const sessionIDs = await getCoValueStoredSessions(clientStorage, map.id);
876
+
877
+ expect(sessionIDs).toHaveLength(1);
878
+ expect(sessionIDs[0]).toMatch(/_session_d[1-9A-HJ-NP-Za-km-z]+\$$/); // Delete session format
879
+ });
880
+
881
+ test("eraseAllDeletedCoValues does not break when called while a coValue is streaming from storage", async () => {
882
+ const dbPath = getDbPath();
883
+
884
+ const node = setupTestNode();
885
+ const { storage } = await node.addAsyncStorage({
886
+ ourName: "test",
887
+ storageName: "test-storage",
888
+ filename: dbPath,
889
+ });
890
+
891
+ const group = node.node.createGroup();
892
+ const map = group.createMap();
893
+ fillCoMapWithLargeData(map);
894
+ await map.core.waitForSync();
895
+ map.core.deleteCoValue();
896
+ await map.core.waitForSync();
897
+
898
+ storage.close();
899
+
900
+ const newSession = node.spawnNewSession();
901
+ const { storage: newStorage } = await newSession.addAsyncStorage({
902
+ ourName: "test",
903
+ storageName: "test-storage",
904
+ filename: dbPath,
905
+ });
906
+
907
+ const callback = vi.fn();
908
+
909
+ const loadPromise = new Promise((resolve) => {
910
+ newStorage.load(map.id, callback, resolve);
911
+ });
912
+ await newStorage.eraseAllDeletedCoValues();
913
+
914
+ expect(await loadPromise).toBe(true);
915
+ });
916
+
917
+ test("load interrupts eraseAllDeletedCoValues mid-run (resolves early, leaves some queued)", async () => {
918
+ const dbPath = getDbPath();
919
+
920
+ const node = setupTestNode();
921
+ const { storage } = await node.addAsyncStorage({
922
+ ourName: "test",
923
+ storageName: "test-storage",
924
+ filename: dbPath,
925
+ });
926
+
927
+ const group = node.node.createGroup();
928
+
929
+ const map1 = group.createMap();
930
+ map1.set("k", "v");
931
+ await map1.core.waitForSync();
932
+
933
+ const map2 = group.createMap();
934
+ map2.set("k", "v");
935
+ await map2.core.waitForSync();
936
+
937
+ map1.core.deleteCoValue();
938
+ map2.core.deleteCoValue();
939
+ await map1.core.waitForSync();
940
+ await map2.core.waitForSync();
941
+
942
+ await waitFor(async () => {
943
+ const queued = await getAllCoValuesWaitingForDelete(storage);
944
+ expect(queued).toEqual(expect.arrayContaining([map1.id, map2.id]));
945
+ return true;
946
+ });
947
+
948
+ let releaseBarrier!: () => void;
949
+ const barrier = new Promise<void>((resolve) => {
950
+ releaseBarrier = resolve;
951
+ });
952
+
953
+ let firstTxStartedResolve!: () => void;
954
+ const firstTxStarted = new Promise<void>((resolve) => {
955
+ firstTxStartedResolve = resolve;
956
+ });
957
+
958
+ // @ts-expect-error - dbClient is private
959
+ const dbClient = storage.dbClient;
960
+ const originalTransaction = dbClient.transaction.bind(dbClient);
961
+
962
+ let txCalls = 0;
963
+ const txSpy = vi
964
+ .spyOn(dbClient, "transaction")
965
+ .mockImplementation(async (callback) => {
966
+ txCalls += 1;
967
+ return originalTransaction(async (tx) => {
968
+ if (txCalls === 1) {
969
+ firstTxStartedResolve();
970
+ await barrier;
971
+ }
972
+ return callback(tx);
973
+ });
974
+ });
975
+
976
+ const erasePromise = storage.eraseAllDeletedCoValues();
977
+
978
+ // Ensure the eraser is in-flight and inside its first transaction.
979
+ await firstTxStarted;
980
+
981
+ // Trigger interruption. We don't await the load immediately to avoid doing
982
+ // DB reads while the transaction is being held open by the barrier.
983
+ const loadDone = new Promise<boolean>((resolve) => {
984
+ void storage.load("non-existent-id", () => {}, resolve);
985
+ });
986
+
987
+ releaseBarrier();
988
+
989
+ await erasePromise;
990
+ await loadDone;
991
+
992
+ const queuedAfter = await getAllCoValuesWaitingForDelete(storage);
993
+ expect(queuedAfter).toHaveLength(1);
994
+
995
+ txSpy.mockRestore();
996
+ });
997
+ });
998
+
680
999
  describe("dependencies", () => {
681
1000
  test("should push dependencies before the coValue", async () => {
682
- const { fixturesNode, dbPath } = await createFixturesNode();
683
- const { node, storage } = await createTestNode(dbPath);
1001
+ const dbPath = getDbPath();
1002
+ const fixtures = setupTestNode();
1003
+ await fixtures.addAsyncStorage({
1004
+ ourName: "test",
1005
+ storageName: "test-storage",
1006
+ filename: dbPath,
1007
+ });
1008
+
1009
+ const client = setupTestNode();
1010
+ const storage = await createAsyncStorage({
1011
+ nodeName: "test",
1012
+ storageName: "test-storage",
1013
+ filename: dbPath,
1014
+ });
684
1015
 
685
1016
  // Create a group and a map owned by that group to create dependencies
686
- const group = fixturesNode.createGroup();
1017
+ const group = fixtures.node.createGroup();
687
1018
  group.addMember("everyone", "reader");
688
1019
  const map = group.createMap({ test: "value" });
689
1020
  await group.core.waitForSync();
690
1021
  await map.core.waitForSync();
691
1022
 
692
1023
  const callback = vi.fn((content) =>
693
- node.syncManager.handleNewContent(content, "storage"),
1024
+ client.node.syncManager.handleNewContent(content, "storage"),
694
1025
  );
695
1026
  const done = vi.fn();
696
1027
 
@@ -725,24 +1056,36 @@ describe("StorageApiAsync", () => {
725
1056
  expect(updatedGroupKnownState).toEqual(group.core.knownState());
726
1057
  expect(updatedMapKnownState).toEqual(map.core.knownState());
727
1058
 
728
- node.setStorage(storage);
729
- const mapOnNode = await loadCoValueOrFail(node, map.id);
1059
+ client.addStorage({ storage });
1060
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
730
1061
  expect(mapOnNode.get("test")).toEqual("value");
731
1062
  });
732
1063
 
733
1064
  test("should handle dependencies that are already loaded correctly", async () => {
734
- const { fixturesNode, dbPath } = await createFixturesNode();
735
- const { node, storage } = await createTestNode(dbPath);
1065
+ const dbPath = getDbPath();
1066
+ const fixtures = setupTestNode();
1067
+ await fixtures.addAsyncStorage({
1068
+ ourName: "test",
1069
+ storageName: "test-storage",
1070
+ filename: dbPath,
1071
+ });
1072
+
1073
+ const client = setupTestNode();
1074
+ const storage = await createAsyncStorage({
1075
+ nodeName: "test",
1076
+ storageName: "test-storage",
1077
+ filename: dbPath,
1078
+ });
736
1079
 
737
1080
  // Create a group and a map owned by that group
738
- const group = fixturesNode.createGroup();
1081
+ const group = fixtures.node.createGroup();
739
1082
  group.addMember("everyone", "reader");
740
1083
  const map = group.createMap({ test: "value" });
741
1084
  await group.core.waitForSync();
742
1085
  await map.core.waitForSync();
743
1086
 
744
1087
  const callback = vi.fn((content) =>
745
- node.syncManager.handleNewContent(content, "storage"),
1088
+ client.node.syncManager.handleNewContent(content, "storage"),
746
1089
  );
747
1090
  const done = vi.fn();
748
1091
 
@@ -778,24 +1121,36 @@ describe("StorageApiAsync", () => {
778
1121
  const finalMapKnownState = storage.getKnownState(map.id);
779
1122
  expect(finalMapKnownState).toEqual(map.core.knownState());
780
1123
 
781
- node.setStorage(storage);
782
- const mapOnNode = await loadCoValueOrFail(node, map.id);
1124
+ client.addStorage({ storage });
1125
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
783
1126
  expect(mapOnNode.get("test")).toEqual("value");
784
1127
  });
785
1128
 
786
1129
  test("should load dependencies again if they were unmounted", async () => {
787
- const { fixturesNode, dbPath } = await createFixturesNode();
788
- const { node, storage } = await createTestNode(dbPath);
1130
+ const dbPath = getDbPath();
1131
+ const fixtures = setupTestNode();
1132
+ await fixtures.addAsyncStorage({
1133
+ ourName: "test",
1134
+ storageName: "test-storage",
1135
+ filename: dbPath,
1136
+ });
1137
+
1138
+ const client = setupTestNode();
1139
+ const { storage } = await client.addAsyncStorage({
1140
+ ourName: "test",
1141
+ storageName: "test-storage",
1142
+ filename: dbPath,
1143
+ });
789
1144
 
790
1145
  // Create a group and a map owned by that group
791
- const group = fixturesNode.createGroup();
1146
+ const group = fixtures.node.createGroup();
792
1147
  group.addMember("everyone", "reader");
793
1148
  const map = group.createMap({ test: "value" });
794
1149
  await group.core.waitForSync();
795
1150
  await map.core.waitForSync();
796
1151
 
797
1152
  const callback = vi.fn((content) =>
798
- node.syncManager.handleNewContent(content, "storage"),
1153
+ client.node.syncManager.handleNewContent(content, "storage"),
799
1154
  );
800
1155
  const done = vi.fn();
801
1156
 
@@ -827,19 +1182,31 @@ describe("StorageApiAsync", () => {
827
1182
 
828
1183
  expect(done).toHaveBeenCalledWith(true);
829
1184
 
830
- node.setStorage(storage);
831
- const mapOnNode = await loadCoValueOrFail(node, map.id);
1185
+ client.addStorage({ storage });
1186
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
832
1187
  expect(mapOnNode.get("test")).toEqual("value");
833
1188
  });
834
1189
  });
835
1190
 
836
1191
  describe("waitForSync", () => {
837
1192
  test("should resolve when the coValue is already synced", async () => {
838
- const { fixturesNode, dbPath } = await createFixturesNode();
839
- const { node, storage } = await createTestNode(dbPath);
1193
+ const dbPath = getDbPath();
1194
+ const fixtures = setupTestNode();
1195
+ await fixtures.addAsyncStorage({
1196
+ ourName: "test",
1197
+ storageName: "test-storage",
1198
+ filename: dbPath,
1199
+ });
1200
+
1201
+ const client = setupTestNode();
1202
+ const storage = await createAsyncStorage({
1203
+ nodeName: "test",
1204
+ storageName: "test-storage",
1205
+ filename: dbPath,
1206
+ });
840
1207
 
841
1208
  // Create a group and add a member
842
- const group = fixturesNode.createGroup();
1209
+ const group = fixtures.node.createGroup();
843
1210
  group.addMember("everyone", "reader");
844
1211
  await group.core.waitForSync();
845
1212
 
@@ -851,10 +1218,10 @@ describe("StorageApiAsync", () => {
851
1218
  const correctionCallback = vi.fn();
852
1219
  await storage.store(contentMessage, correctionCallback);
853
1220
 
854
- node.setStorage(storage);
1221
+ client.addStorage({ storage });
855
1222
 
856
1223
  // Load the group on the new node
857
- const groupOnNode = await loadCoValueOrFail(node, group.id);
1224
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
858
1225
 
859
1226
  // Wait for sync should resolve immediately since the coValue is already synced
860
1227
  await expect(
@@ -867,7 +1234,10 @@ describe("StorageApiAsync", () => {
867
1234
 
868
1235
  describe("close", () => {
869
1236
  test("should close without throwing an error", async () => {
870
- const { storage } = await createTestNode();
1237
+ const storage = await createAsyncStorage({
1238
+ nodeName: "test",
1239
+ storageName: "test-storage",
1240
+ });
871
1241
 
872
1242
  expect(() => storage.close()).not.toThrow();
873
1243
  });
@@ -875,11 +1245,23 @@ describe("StorageApiAsync", () => {
875
1245
 
876
1246
  describe("loadKnownState", () => {
877
1247
  test("should return cached knownState if available", async () => {
878
- const { fixturesNode, dbPath } = await createFixturesNode();
879
- const { storage } = await createTestNode(dbPath);
1248
+ const dbPath = getDbPath();
1249
+ const fixtures = setupTestNode();
1250
+ await fixtures.addAsyncStorage({
1251
+ ourName: "test",
1252
+ storageName: "test-storage",
1253
+ filename: dbPath,
1254
+ });
1255
+
1256
+ const client = setupTestNode();
1257
+ const { storage } = await client.addAsyncStorage({
1258
+ ourName: "test",
1259
+ storageName: "test-storage",
1260
+ filename: dbPath,
1261
+ });
880
1262
 
881
1263
  // Create a group to have data in the database
882
- const group = fixturesNode.createGroup();
1264
+ const group = fixtures.node.createGroup();
883
1265
  group.addMember("everyone", "reader");
884
1266
  await group.core.waitForSync();
885
1267
 
@@ -905,7 +1287,11 @@ describe("StorageApiAsync", () => {
905
1287
  });
906
1288
 
907
1289
  test("should return undefined for non-existent CoValue", async () => {
908
- const { storage } = await createTestNode();
1290
+ const client = setupTestNode();
1291
+ const { storage } = await client.addAsyncStorage({
1292
+ ourName: "test",
1293
+ storageName: "test-storage",
1294
+ });
909
1295
 
910
1296
  const result = await new Promise<CoValueKnownState | undefined>(
911
1297
  (resolve) => {
@@ -917,11 +1303,23 @@ describe("StorageApiAsync", () => {
917
1303
  });
918
1304
 
919
1305
  test("should deduplicate concurrent requests for the same ID", async () => {
920
- const { fixturesNode, dbPath } = await createFixturesNode();
921
- const { storage } = await createTestNode(dbPath);
1306
+ const dbPath = getDbPath();
1307
+ const fixtures = setupTestNode();
1308
+ await fixtures.addAsyncStorage({
1309
+ ourName: "test",
1310
+ storageName: "test-storage",
1311
+ filename: dbPath,
1312
+ });
1313
+
1314
+ const client = setupTestNode();
1315
+ const { storage } = await client.addAsyncStorage({
1316
+ ourName: "test",
1317
+ storageName: "test-storage",
1318
+ filename: dbPath,
1319
+ });
922
1320
 
923
1321
  // Create a group to have data in the database
924
- const group = fixturesNode.createGroup();
1322
+ const group = fixtures.node.createGroup();
925
1323
  group.addMember("everyone", "reader");
926
1324
  await group.core.waitForSync();
927
1325
 
@@ -959,11 +1357,23 @@ describe("StorageApiAsync", () => {
959
1357
  });
960
1358
 
961
1359
  test("should use cache and not query database when cache is populated", async () => {
962
- const { fixturesNode, dbPath } = await createFixturesNode();
963
- const { storage } = await createTestNode(dbPath);
1360
+ const dbPath = getDbPath();
1361
+ const fixtures = setupTestNode();
1362
+ await fixtures.addAsyncStorage({
1363
+ ourName: "test",
1364
+ storageName: "test-storage",
1365
+ filename: dbPath,
1366
+ });
1367
+
1368
+ const client = setupTestNode();
1369
+ const { storage } = await client.addAsyncStorage({
1370
+ ourName: "test",
1371
+ storageName: "test-storage",
1372
+ filename: dbPath,
1373
+ });
964
1374
 
965
1375
  // Create a group to have data in the database
966
- const group = fixturesNode.createGroup();
1376
+ const group = fixtures.node.createGroup();
967
1377
  group.addMember("everyone", "reader");
968
1378
  await group.core.waitForSync();
969
1379