cojson 0.19.21 → 0.20.0

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 (254) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +67 -0
  3. package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts +42 -0
  4. package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts.map +1 -0
  5. package/dist/CojsonMessageChannel/CojsonMessageChannel.js +261 -0
  6. package/dist/CojsonMessageChannel/CojsonMessageChannel.js.map +1 -0
  7. package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts +18 -0
  8. package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts.map +1 -0
  9. package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js +37 -0
  10. package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js.map +1 -0
  11. package/dist/CojsonMessageChannel/index.d.ts +3 -0
  12. package/dist/CojsonMessageChannel/index.d.ts.map +1 -0
  13. package/dist/CojsonMessageChannel/index.js +2 -0
  14. package/dist/CojsonMessageChannel/index.js.map +1 -0
  15. package/dist/CojsonMessageChannel/types.d.ts +149 -0
  16. package/dist/CojsonMessageChannel/types.d.ts.map +1 -0
  17. package/dist/CojsonMessageChannel/types.js +36 -0
  18. package/dist/CojsonMessageChannel/types.js.map +1 -0
  19. package/dist/GarbageCollector.d.ts +4 -2
  20. package/dist/GarbageCollector.d.ts.map +1 -1
  21. package/dist/GarbageCollector.js +5 -3
  22. package/dist/GarbageCollector.js.map +1 -1
  23. package/dist/SyncStateManager.d.ts +3 -3
  24. package/dist/SyncStateManager.d.ts.map +1 -1
  25. package/dist/SyncStateManager.js +4 -4
  26. package/dist/SyncStateManager.js.map +1 -1
  27. package/dist/coValueContentMessage.d.ts +0 -2
  28. package/dist/coValueContentMessage.d.ts.map +1 -1
  29. package/dist/coValueContentMessage.js +0 -8
  30. package/dist/coValueContentMessage.js.map +1 -1
  31. package/dist/coValueCore/SessionMap.d.ts +4 -2
  32. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  33. package/dist/coValueCore/SessionMap.js +30 -0
  34. package/dist/coValueCore/SessionMap.js.map +1 -1
  35. package/dist/coValueCore/coValueCore.d.ts +86 -4
  36. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  37. package/dist/coValueCore/coValueCore.js +318 -17
  38. package/dist/coValueCore/coValueCore.js.map +1 -1
  39. package/dist/coValueCore/verifiedState.d.ts +6 -1
  40. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  41. package/dist/coValueCore/verifiedState.js +9 -0
  42. package/dist/coValueCore/verifiedState.js.map +1 -1
  43. package/dist/coValues/coList.d.ts +3 -2
  44. package/dist/coValues/coList.d.ts.map +1 -1
  45. package/dist/coValues/coList.js.map +1 -1
  46. package/dist/coValues/group.d.ts.map +1 -1
  47. package/dist/coValues/group.js +3 -6
  48. package/dist/coValues/group.js.map +1 -1
  49. package/dist/config.d.ts +0 -6
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/config.js +0 -8
  52. package/dist/config.js.map +1 -1
  53. package/dist/crypto/NapiCrypto.d.ts +1 -2
  54. package/dist/crypto/NapiCrypto.d.ts.map +1 -1
  55. package/dist/crypto/NapiCrypto.js +19 -4
  56. package/dist/crypto/NapiCrypto.js.map +1 -1
  57. package/dist/crypto/RNCrypto.d.ts.map +1 -1
  58. package/dist/crypto/RNCrypto.js +19 -4
  59. package/dist/crypto/RNCrypto.js.map +1 -1
  60. package/dist/crypto/WasmCrypto.d.ts +11 -4
  61. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  62. package/dist/crypto/WasmCrypto.js +52 -10
  63. package/dist/crypto/WasmCrypto.js.map +1 -1
  64. package/dist/crypto/WasmCryptoEdge.d.ts +1 -0
  65. package/dist/crypto/WasmCryptoEdge.d.ts.map +1 -1
  66. package/dist/crypto/WasmCryptoEdge.js +4 -1
  67. package/dist/crypto/WasmCryptoEdge.js.map +1 -1
  68. package/dist/crypto/crypto.d.ts +3 -3
  69. package/dist/crypto/crypto.d.ts.map +1 -1
  70. package/dist/crypto/crypto.js +6 -1
  71. package/dist/crypto/crypto.js.map +1 -1
  72. package/dist/exports.d.ts +3 -2
  73. package/dist/exports.d.ts.map +1 -1
  74. package/dist/exports.js +3 -1
  75. package/dist/exports.js.map +1 -1
  76. package/dist/ids.d.ts +4 -1
  77. package/dist/ids.d.ts.map +1 -1
  78. package/dist/ids.js +4 -0
  79. package/dist/ids.js.map +1 -1
  80. package/dist/knownState.d.ts +2 -0
  81. package/dist/knownState.d.ts.map +1 -1
  82. package/dist/localNode.d.ts +13 -3
  83. package/dist/localNode.d.ts.map +1 -1
  84. package/dist/localNode.js +17 -2
  85. package/dist/localNode.js.map +1 -1
  86. package/dist/platformUtils.d.ts +3 -0
  87. package/dist/platformUtils.d.ts.map +1 -0
  88. package/dist/platformUtils.js +24 -0
  89. package/dist/platformUtils.js.map +1 -0
  90. package/dist/storage/DeletedCoValuesEraserScheduler.d.ts +30 -0
  91. package/dist/storage/DeletedCoValuesEraserScheduler.d.ts.map +1 -0
  92. package/dist/storage/DeletedCoValuesEraserScheduler.js +84 -0
  93. package/dist/storage/DeletedCoValuesEraserScheduler.js.map +1 -0
  94. package/dist/storage/sqlite/client.d.ts +3 -0
  95. package/dist/storage/sqlite/client.d.ts.map +1 -1
  96. package/dist/storage/sqlite/client.js +44 -0
  97. package/dist/storage/sqlite/client.js.map +1 -1
  98. package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
  99. package/dist/storage/sqlite/sqliteMigrations.js +7 -0
  100. package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
  101. package/dist/storage/sqliteAsync/client.d.ts +3 -0
  102. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  103. package/dist/storage/sqliteAsync/client.js +42 -0
  104. package/dist/storage/sqliteAsync/client.js.map +1 -1
  105. package/dist/storage/storageAsync.d.ts +15 -3
  106. package/dist/storage/storageAsync.d.ts.map +1 -1
  107. package/dist/storage/storageAsync.js +60 -3
  108. package/dist/storage/storageAsync.js.map +1 -1
  109. package/dist/storage/storageSync.d.ts +14 -3
  110. package/dist/storage/storageSync.d.ts.map +1 -1
  111. package/dist/storage/storageSync.js +54 -3
  112. package/dist/storage/storageSync.js.map +1 -1
  113. package/dist/storage/types.d.ts +64 -0
  114. package/dist/storage/types.d.ts.map +1 -1
  115. package/dist/storage/types.js +12 -1
  116. package/dist/storage/types.js.map +1 -1
  117. package/dist/sync.d.ts +6 -0
  118. package/dist/sync.d.ts.map +1 -1
  119. package/dist/sync.js +69 -15
  120. package/dist/sync.js.map +1 -1
  121. package/dist/tests/CojsonMessageChannel.test.d.ts +2 -0
  122. package/dist/tests/CojsonMessageChannel.test.d.ts.map +1 -0
  123. package/dist/tests/CojsonMessageChannel.test.js +236 -0
  124. package/dist/tests/CojsonMessageChannel.test.js.map +1 -0
  125. package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts +2 -0
  126. package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts.map +1 -0
  127. package/dist/tests/DeletedCoValuesEraserScheduler.test.js +149 -0
  128. package/dist/tests/DeletedCoValuesEraserScheduler.test.js.map +1 -0
  129. package/dist/tests/GarbageCollector.test.js +91 -18
  130. package/dist/tests/GarbageCollector.test.js.map +1 -1
  131. package/dist/tests/StorageApiAsync.test.js +510 -146
  132. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  133. package/dist/tests/StorageApiSync.test.js +531 -130
  134. package/dist/tests/StorageApiSync.test.js.map +1 -1
  135. package/dist/tests/SyncManager.processQueues.test.js +1 -1
  136. package/dist/tests/SyncManager.processQueues.test.js.map +1 -1
  137. package/dist/tests/SyncStateManager.test.js +1 -1
  138. package/dist/tests/SyncStateManager.test.js.map +1 -1
  139. package/dist/tests/WasmCrypto.test.js +6 -3
  140. package/dist/tests/WasmCrypto.test.js.map +1 -1
  141. package/dist/tests/coPlainText.test.js +1 -1
  142. package/dist/tests/coPlainText.test.js.map +1 -1
  143. package/dist/tests/coValueCore.loadFromStorage.test.js +4 -0
  144. package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
  145. package/dist/tests/coValueCore.test.js +34 -13
  146. package/dist/tests/coValueCore.test.js.map +1 -1
  147. package/dist/tests/coreWasm.test.js +127 -4
  148. package/dist/tests/coreWasm.test.js.map +1 -1
  149. package/dist/tests/crypto.test.js +89 -93
  150. package/dist/tests/crypto.test.js.map +1 -1
  151. package/dist/tests/deleteCoValue.test.d.ts +2 -0
  152. package/dist/tests/deleteCoValue.test.d.ts.map +1 -0
  153. package/dist/tests/deleteCoValue.test.js +313 -0
  154. package/dist/tests/deleteCoValue.test.js.map +1 -0
  155. package/dist/tests/group.removeMember.test.js +18 -30
  156. package/dist/tests/group.removeMember.test.js.map +1 -1
  157. package/dist/tests/knownState.lazyLoading.test.js +4 -0
  158. package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
  159. package/dist/tests/sync.deleted.test.d.ts +2 -0
  160. package/dist/tests/sync.deleted.test.d.ts.map +1 -0
  161. package/dist/tests/sync.deleted.test.js +214 -0
  162. package/dist/tests/sync.deleted.test.js.map +1 -0
  163. package/dist/tests/sync.garbageCollection.test.js +56 -32
  164. package/dist/tests/sync.garbageCollection.test.js.map +1 -1
  165. package/dist/tests/sync.load.test.js +3 -5
  166. package/dist/tests/sync.load.test.js.map +1 -1
  167. package/dist/tests/sync.mesh.test.js +4 -3
  168. package/dist/tests/sync.mesh.test.js.map +1 -1
  169. package/dist/tests/sync.peerReconciliation.test.js +3 -3
  170. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  171. package/dist/tests/sync.storage.test.js +12 -11
  172. package/dist/tests/sync.storage.test.js.map +1 -1
  173. package/dist/tests/sync.storageAsync.test.js +7 -7
  174. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  175. package/dist/tests/sync.test.js +3 -2
  176. package/dist/tests/sync.test.js.map +1 -1
  177. package/dist/tests/sync.tracking.test.js +35 -4
  178. package/dist/tests/sync.tracking.test.js.map +1 -1
  179. package/dist/tests/testStorage.d.ts +3 -0
  180. package/dist/tests/testStorage.d.ts.map +1 -1
  181. package/dist/tests/testStorage.js +16 -2
  182. package/dist/tests/testStorage.js.map +1 -1
  183. package/dist/tests/testUtils.d.ts +29 -4
  184. package/dist/tests/testUtils.d.ts.map +1 -1
  185. package/dist/tests/testUtils.js +84 -9
  186. package/dist/tests/testUtils.js.map +1 -1
  187. package/package.json +6 -16
  188. package/src/CojsonMessageChannel/CojsonMessageChannel.ts +332 -0
  189. package/src/CojsonMessageChannel/MessagePortOutgoingChannel.ts +52 -0
  190. package/src/CojsonMessageChannel/index.ts +9 -0
  191. package/src/CojsonMessageChannel/types.ts +200 -0
  192. package/src/GarbageCollector.ts +5 -5
  193. package/src/SyncStateManager.ts +6 -6
  194. package/src/coValueContentMessage.ts +0 -14
  195. package/src/coValueCore/SessionMap.ts +43 -1
  196. package/src/coValueCore/coValueCore.ts +430 -15
  197. package/src/coValueCore/verifiedState.ts +26 -3
  198. package/src/coValues/coList.ts +5 -3
  199. package/src/coValues/group.ts +5 -6
  200. package/src/config.ts +0 -9
  201. package/src/crypto/NapiCrypto.ts +29 -13
  202. package/src/crypto/RNCrypto.ts +29 -11
  203. package/src/crypto/WasmCrypto.ts +67 -20
  204. package/src/crypto/WasmCryptoEdge.ts +5 -1
  205. package/src/crypto/crypto.ts +16 -4
  206. package/src/exports.ts +3 -0
  207. package/src/ids.ts +11 -1
  208. package/src/localNode.ts +18 -5
  209. package/src/platformUtils.ts +26 -0
  210. package/src/storage/DeletedCoValuesEraserScheduler.ts +124 -0
  211. package/src/storage/sqlite/client.ts +77 -0
  212. package/src/storage/sqlite/sqliteMigrations.ts +7 -0
  213. package/src/storage/sqliteAsync/client.ts +75 -0
  214. package/src/storage/storageAsync.ts +77 -4
  215. package/src/storage/storageSync.ts +73 -4
  216. package/src/storage/types.ts +75 -0
  217. package/src/sync.ts +84 -15
  218. package/src/tests/CojsonMessageChannel.test.ts +306 -0
  219. package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
  220. package/src/tests/GarbageCollector.test.ts +119 -22
  221. package/src/tests/StorageApiAsync.test.ts +615 -156
  222. package/src/tests/StorageApiSync.test.ts +623 -137
  223. package/src/tests/SyncManager.processQueues.test.ts +1 -1
  224. package/src/tests/SyncStateManager.test.ts +1 -1
  225. package/src/tests/WasmCrypto.test.ts +8 -3
  226. package/src/tests/coPlainText.test.ts +1 -1
  227. package/src/tests/coValueCore.loadFromStorage.test.ts +8 -0
  228. package/src/tests/coValueCore.test.ts +49 -14
  229. package/src/tests/coreWasm.test.ts +319 -10
  230. package/src/tests/crypto.test.ts +141 -150
  231. package/src/tests/deleteCoValue.test.ts +528 -0
  232. package/src/tests/group.removeMember.test.ts +35 -35
  233. package/src/tests/knownState.lazyLoading.test.ts +8 -0
  234. package/src/tests/sync.deleted.test.ts +294 -0
  235. package/src/tests/sync.garbageCollection.test.ts +69 -36
  236. package/src/tests/sync.load.test.ts +3 -5
  237. package/src/tests/sync.mesh.test.ts +6 -3
  238. package/src/tests/sync.peerReconciliation.test.ts +3 -3
  239. package/src/tests/sync.storage.test.ts +14 -11
  240. package/src/tests/sync.storageAsync.test.ts +7 -7
  241. package/src/tests/sync.test.ts +5 -2
  242. package/src/tests/sync.tracking.test.ts +54 -4
  243. package/src/tests/testStorage.ts +30 -3
  244. package/src/tests/testUtils.ts +113 -15
  245. package/dist/crypto/PureJSCrypto.d.ts +0 -77
  246. package/dist/crypto/PureJSCrypto.d.ts.map +0 -1
  247. package/dist/crypto/PureJSCrypto.js +0 -236
  248. package/dist/crypto/PureJSCrypto.js.map +0 -1
  249. package/dist/tests/PureJSCrypto.test.d.ts +0 -2
  250. package/dist/tests/PureJSCrypto.test.d.ts.map +0 -1
  251. package/dist/tests/PureJSCrypto.test.js +0 -145
  252. package/dist/tests/PureJSCrypto.test.js.map +0 -1
  253. package/src/crypto/PureJSCrypto.ts +0 -429
  254. 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
- 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
+ });
103
55
 
104
- const id = fixturesNode.createGroup().id;
56
+ const client = setupTestNode();
57
+ const { storage } = await client.addAsyncStorage({
58
+ ourName: "test",
59
+ storageName: "test-storage",
60
+ });
61
+
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
+ });
300
+
301
+ const client = setupTestNode();
302
+ const storage = await createAsyncStorage({
303
+ nodeName: "test",
304
+ storageName: "test-storage",
305
+ });
289
306
 
290
- const group = fixturesNode.createGroup();
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();
629
683
 
630
- const coValue = fixturesNode.createCoValue({
684
+ const fixtures = setupTestNode();
685
+ await fixtures.addAsyncStorage({
686
+ ourName: "test",
687
+ storageName: "test-storage",
688
+ filename: dbPath,
689
+ });
690
+
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,19 +1121,92 @@ 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);
1126
+ expect(mapOnNode.get("test")).toEqual("value");
1127
+ });
1128
+
1129
+ test("should load dependencies again if they were unmounted", async () => {
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
+ });
1144
+
1145
+ // Create a group and a map owned by that group
1146
+ const group = fixtures.node.createGroup();
1147
+ group.addMember("everyone", "reader");
1148
+ const map = group.createMap({ test: "value" });
1149
+ await group.core.waitForSync();
1150
+ await map.core.waitForSync();
1151
+
1152
+ const callback = vi.fn((content) =>
1153
+ client.node.syncManager.handleNewContent(content, "storage"),
1154
+ );
1155
+ const done = vi.fn();
1156
+
1157
+ // Load the map (and its group)
1158
+ await storage.load(map.id, callback, done);
1159
+ callback.mockClear();
1160
+ done.mockClear();
1161
+
1162
+ // Unmount the map and its group
1163
+ storage.onCoValueUnmounted(map.id);
1164
+ storage.onCoValueUnmounted(group.id);
1165
+
1166
+ // Load the map. The group dependency should be loaded again
1167
+ await storage.load(map.id, callback, done);
1168
+
1169
+ expect(callback).toHaveBeenCalledTimes(2);
1170
+ expect(callback).toHaveBeenNthCalledWith(
1171
+ 1,
1172
+ expect.objectContaining({
1173
+ id: group.id,
1174
+ }),
1175
+ );
1176
+ expect(callback).toHaveBeenNthCalledWith(
1177
+ 2,
1178
+ expect.objectContaining({
1179
+ id: map.id,
1180
+ }),
1181
+ );
1182
+
1183
+ expect(done).toHaveBeenCalledWith(true);
1184
+
1185
+ client.addStorage({ storage });
1186
+ const mapOnNode = await loadCoValueOrFail(client.node, map.id);
783
1187
  expect(mapOnNode.get("test")).toEqual("value");
784
1188
  });
785
1189
  });
786
1190
 
787
1191
  describe("waitForSync", () => {
788
1192
  test("should resolve when the coValue is already synced", async () => {
789
- const { fixturesNode, dbPath } = await createFixturesNode();
790
- 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
+ });
791
1207
 
792
1208
  // Create a group and add a member
793
- const group = fixturesNode.createGroup();
1209
+ const group = fixtures.node.createGroup();
794
1210
  group.addMember("everyone", "reader");
795
1211
  await group.core.waitForSync();
796
1212
 
@@ -802,10 +1218,10 @@ describe("StorageApiAsync", () => {
802
1218
  const correctionCallback = vi.fn();
803
1219
  await storage.store(contentMessage, correctionCallback);
804
1220
 
805
- node.setStorage(storage);
1221
+ client.addStorage({ storage });
806
1222
 
807
1223
  // Load the group on the new node
808
- const groupOnNode = await loadCoValueOrFail(node, group.id);
1224
+ const groupOnNode = await loadCoValueOrFail(client.node, group.id);
809
1225
 
810
1226
  // Wait for sync should resolve immediately since the coValue is already synced
811
1227
  await expect(
@@ -818,7 +1234,10 @@ describe("StorageApiAsync", () => {
818
1234
 
819
1235
  describe("close", () => {
820
1236
  test("should close without throwing an error", async () => {
821
- const { storage } = await createTestNode();
1237
+ const storage = await createAsyncStorage({
1238
+ nodeName: "test",
1239
+ storageName: "test-storage",
1240
+ });
822
1241
 
823
1242
  expect(() => storage.close()).not.toThrow();
824
1243
  });
@@ -826,11 +1245,23 @@ describe("StorageApiAsync", () => {
826
1245
 
827
1246
  describe("loadKnownState", () => {
828
1247
  test("should return cached knownState if available", async () => {
829
- const { fixturesNode, dbPath } = await createFixturesNode();
830
- 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
+ });
831
1262
 
832
1263
  // Create a group to have data in the database
833
- const group = fixturesNode.createGroup();
1264
+ const group = fixtures.node.createGroup();
834
1265
  group.addMember("everyone", "reader");
835
1266
  await group.core.waitForSync();
836
1267
 
@@ -856,7 +1287,11 @@ describe("StorageApiAsync", () => {
856
1287
  });
857
1288
 
858
1289
  test("should return undefined for non-existent CoValue", async () => {
859
- const { storage } = await createTestNode();
1290
+ const client = setupTestNode();
1291
+ const { storage } = await client.addAsyncStorage({
1292
+ ourName: "test",
1293
+ storageName: "test-storage",
1294
+ });
860
1295
 
861
1296
  const result = await new Promise<CoValueKnownState | undefined>(
862
1297
  (resolve) => {
@@ -868,11 +1303,23 @@ describe("StorageApiAsync", () => {
868
1303
  });
869
1304
 
870
1305
  test("should deduplicate concurrent requests for the same ID", async () => {
871
- const { fixturesNode, dbPath } = await createFixturesNode();
872
- 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
+ });
873
1320
 
874
1321
  // Create a group to have data in the database
875
- const group = fixturesNode.createGroup();
1322
+ const group = fixtures.node.createGroup();
876
1323
  group.addMember("everyone", "reader");
877
1324
  await group.core.waitForSync();
878
1325
 
@@ -910,11 +1357,23 @@ describe("StorageApiAsync", () => {
910
1357
  });
911
1358
 
912
1359
  test("should use cache and not query database when cache is populated", async () => {
913
- const { fixturesNode, dbPath } = await createFixturesNode();
914
- 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
+ });
915
1374
 
916
1375
  // Create a group to have data in the database
917
- const group = fixturesNode.createGroup();
1376
+ const group = fixtures.node.createGroup();
918
1377
  group.addMember("everyone", "reader");
919
1378
  await group.core.waitForSync();
920
1379