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
@@ -70,7 +70,7 @@ describe("SyncManager.processQueues", () => {
70
70
  await loadCoValueOrFail(client.node, map.id);
71
71
 
72
72
  // Restart and load from storage
73
- client.restart();
73
+ await client.restart();
74
74
  client.connectToSyncServer();
75
75
  client.addStorage({ storage });
76
76
 
@@ -46,7 +46,7 @@ describe("SyncStateManager", () => {
46
46
  const newPeerState = client.node.syncManager.peers[peerState.id]!;
47
47
 
48
48
  expect(updateSpy).toHaveBeenCalledWith(
49
- peerState.id,
49
+ expect.objectContaining({ id: peerState.id }),
50
50
  newPeerState.getKnownState(map.core.id)!,
51
51
  { uploaded: true },
52
52
  );
@@ -5,8 +5,9 @@ import {
5
5
  setupTestNode,
6
6
  setupTestAccount,
7
7
  } from "./testUtils";
8
- import { stableStringify } from "../jsonStringify";
8
+ import { Stringified } from "../jsonStringify";
9
9
  import { WasmCrypto } from "../crypto/WasmCrypto";
10
+ import { JsonValue } from "../jsonValue";
10
11
 
11
12
  const wasmCrypto = await WasmCrypto.create();
12
13
  setCurrentTestCryptoProvider(wasmCrypto);
@@ -109,7 +110,9 @@ describe("WasmCrypto", () => {
109
110
  [
110
111
  {
111
112
  privacy: "trusting",
112
- changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
113
+ changes: JSON.stringify([
114
+ { op: "set", key: "count", value: 1 },
115
+ ]) as Stringified<JsonValue[]>,
113
116
  madeAt: Date.now(),
114
117
  },
115
118
  ],
@@ -203,7 +206,9 @@ describe("WasmCrypto", () => {
203
206
  [
204
207
  {
205
208
  privacy: "trusting",
206
- changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
209
+ changes: JSON.stringify([
210
+ { op: "set", key: "count", value: 1 },
211
+ ]) as Stringified<JsonValue[]>,
207
212
  madeAt: Date.now(),
208
213
  },
209
214
  ],
@@ -355,7 +355,7 @@ test("chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX
355
355
 
356
356
  await coValue.waitForSync();
357
357
 
358
- client.restart();
358
+ await client.restart();
359
359
  client.addStorage({
360
360
  storage,
361
361
  });
@@ -45,10 +45,17 @@ function createMockStorage(
45
45
  callback: (unsyncedCoValueIDs: RawCoID[]) => void,
46
46
  ) => void;
47
47
  stopTrackingSyncState?: (id: RawCoID) => void;
48
+ onCoValueUnmounted?: (id: RawCoID) => void;
48
49
  close?: () => Promise<unknown> | undefined;
50
+ markDeleteAsValid?: (id: RawCoID) => void;
51
+ enableDeletedCoValuesErasure?: () => void;
52
+ eraseAllDeletedCoValues?: () => Promise<void>;
49
53
  } = {},
50
54
  ): StorageAPI {
51
55
  return {
56
+ markDeleteAsValid: opts.markDeleteAsValid || vi.fn(),
57
+ enableDeletedCoValuesErasure: opts.enableDeletedCoValuesErasure || vi.fn(),
58
+ eraseAllDeletedCoValues: opts.eraseAllDeletedCoValues || vi.fn(),
52
59
  load: opts.load || vi.fn(),
53
60
  store: opts.store || vi.fn(),
54
61
  getKnownState: opts.getKnownState || vi.fn(),
@@ -58,6 +65,7 @@ function createMockStorage(
58
65
  trackCoValuesSyncState: opts.trackCoValuesSyncState || vi.fn(),
59
66
  getUnsyncedCoValueIDs: opts.getUnsyncedCoValueIDs || vi.fn(),
60
67
  stopTrackingSyncState: opts.stopTrackingSyncState || vi.fn(),
68
+ onCoValueUnmounted: opts.onCoValueUnmounted || vi.fn(),
61
69
  close: opts.close || vi.fn().mockResolvedValue(undefined),
62
70
  };
63
71
  }
@@ -9,13 +9,14 @@ import {
9
9
  } from "vitest";
10
10
  import { CoValueCore, idforHeader } from "../coValueCore/coValueCore.js";
11
11
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
12
- import { stableStringify } from "../jsonStringify.js";
12
+ import { Stringified } from "../jsonStringify.js";
13
13
  import { LocalNode } from "../localNode.js";
14
14
  import {
15
15
  agentAndSessionIDFromSecret,
16
16
  createTestMetricReader,
17
17
  createTestNode,
18
18
  createTwoConnectedNodes,
19
+ createUnloadedCoValue,
19
20
  loadCoValueOrFail,
20
21
  nodeWithRandomAgentAndSessionID,
21
22
  randomAgentAndSessionID,
@@ -25,7 +26,7 @@ import {
25
26
  waitFor,
26
27
  } from "./testUtils.js";
27
28
  import { CO_VALUE_PRIORITY } from "../priority.js";
28
- import { setMaxTxSizeBytes } from "../config.js";
29
+ import { JsonValue } from "../jsonValue.js";
29
30
 
30
31
  const Crypto = await WasmCrypto.create();
31
32
 
@@ -89,14 +90,6 @@ test("transactions with wrong signature are rejected", () => {
89
90
  });
90
91
 
91
92
  describe("transactions that exceed the byte size limit are rejected", () => {
92
- beforeEach(() => {
93
- setMaxTxSizeBytes(1 * 1024);
94
- });
95
-
96
- afterEach(() => {
97
- setMaxTxSizeBytes(1 * 1024 * 1024);
98
- });
99
-
100
93
  test("makeTransaction should throw error when transaction exceeds byte size limit", () => {
101
94
  const [agent, sessionID] = randomAgentAndSessionID();
102
95
  const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
@@ -108,7 +101,7 @@ describe("transactions that exceed the byte size limit are rejected", () => {
108
101
  ...Crypto.createdNowUnique(),
109
102
  });
110
103
 
111
- const largeBinaryData = "x".repeat(1024 + 100);
104
+ const largeBinaryData = "x".repeat(1024 * 1024 + 100);
112
105
 
113
106
  expect(() => {
114
107
  coValue.makeTransaction(
@@ -119,7 +112,9 @@ describe("transactions that exceed the byte size limit are rejected", () => {
119
112
  ],
120
113
  "trusting",
121
114
  );
122
- }).toThrow(/Transaction is too large to be synced/);
115
+ }).toThrow(
116
+ /Transaction too large to be synced: 1048689 bytes > 1048576 bytes limit/,
117
+ );
123
118
  });
124
119
 
125
120
  test("makeTransaction should work for transactions under byte size limit", () => {
@@ -357,7 +352,9 @@ test("changing parent and child group trigger only one invalidation on the local
357
352
  test("correctly records transactions", async () => {
358
353
  const node = nodeWithRandomAgentAndSessionID();
359
354
 
360
- const changes1 = stableStringify([{ hello: "world" }]);
355
+ const changes1 = JSON.stringify([{ hello: "world" }]) as Stringified<
356
+ JsonValue[]
357
+ >;
361
358
  node.syncManager.recordTransactionsSize(
362
359
  [
363
360
  {
@@ -376,7 +373,7 @@ test("correctly records transactions", async () => {
376
373
  expect(value.count).toBe(1);
377
374
  expect(value.sum).toBe(changes1.length);
378
375
 
379
- const changes2 = stableStringify([{ foo: "bar" }]);
376
+ const changes2 = JSON.stringify([{ foo: "bar" }]) as Stringified<JsonValue[]>;
380
377
  node.syncManager.recordTransactionsSize(
381
378
  [
382
379
  {
@@ -906,3 +903,41 @@ test("knownState should return the same object until the CoValue is modified", (
906
903
  expect(knownState6).not.toBe(knownState4);
907
904
  expect(knownState6).not.toBe(knownState1);
908
905
  });
906
+
907
+ describe("provideHeader uniqueness validation", () => {
908
+ test("should reject number uniqueness", () => {
909
+ const node = createTestNode();
910
+ const { coValue, header } = createUnloadedCoValue(node);
911
+
912
+ const invalidHeader = {
913
+ ...header,
914
+ uniqueness: 1.5 as any, // non-integer
915
+ };
916
+
917
+ expect(coValue.provideHeader(invalidHeader)).toBe(false);
918
+ });
919
+
920
+ test("should reject array uniqueness", () => {
921
+ const node = createTestNode();
922
+ const { coValue, header } = createUnloadedCoValue(node);
923
+
924
+ const invalidHeader = {
925
+ ...header,
926
+ uniqueness: [1, 2, 3] as any,
927
+ };
928
+
929
+ expect(coValue.provideHeader(invalidHeader)).toBe(false);
930
+ });
931
+
932
+ test("should reject object uniqueness with non-string values", () => {
933
+ const node = createTestNode();
934
+ const { coValue, header } = createUnloadedCoValue(node);
935
+
936
+ const invalidHeader = {
937
+ ...header,
938
+ uniqueness: { key: 123 } as any,
939
+ };
940
+
941
+ expect(coValue.provideHeader(invalidHeader)).toBe(false);
942
+ });
943
+ });
@@ -1,16 +1,10 @@
1
1
  import { assert, describe, expect, it } from "vitest";
2
2
  import { WasmCrypto } from "../crypto/WasmCrypto";
3
- import { JsonValue, LocalNode, SessionID } from "../exports";
4
- import {
5
- agentAndSessionIDFromSecret,
6
- randomAgentAndSessionID,
7
- } from "./testUtils";
8
- import { PureJSCrypto } from "../crypto/PureJSCrypto";
9
- import { Encrypted } from "../crypto/crypto";
10
- import { PrivateTransaction } from "../coValueCore/verifiedState";
3
+ import { LocalNode } from "../exports";
4
+ import { agentAndSessionIDFromSecret } from "./testUtils";
5
+ import { Transaction } from "../coValueCore/verifiedState";
11
6
 
12
7
  const wasmCrypto = await WasmCrypto.create();
13
- const jsCrypto = await PureJSCrypto.create();
14
8
 
15
9
  const agentSecret =
16
10
  "sealerSecret_zE3Nr7YFr1KkVbJSx4JDCzYn4ApYdm8kJ5ghNBxREHQya/signerSecret_z9fEu4eNG1eXHMak3YSzY7uLdoG8HESSJ8YW4xWdNNDSP";
@@ -20,7 +14,7 @@ function createTestNode() {
20
14
  return {
21
15
  agent,
22
16
  session,
23
- node: new LocalNode(agent.agentSecret, session, jsCrypto),
17
+ node: new LocalNode(agent.agentSecret, session, wasmCrypto),
24
18
  };
25
19
  }
26
20
 
@@ -140,4 +134,319 @@ describe("SessionLog WASM", () => {
140
134
 
141
135
  expect(decrypted).toEqual(fixtures.decrypted);
142
136
  });
137
+
138
+ function shuffleObjectKeys<T extends object>(obj: T): T {
139
+ const keys = Object.keys(obj);
140
+ // Fisher-Yates shuffle
141
+ for (let i = keys.length - 1; i > 0; i--) {
142
+ const j = Math.floor(Math.random() * (i + 1));
143
+ [keys[i], keys[j]] = [keys[j]!, keys[i]!];
144
+ }
145
+ const result = {} as T;
146
+ for (const key of keys) {
147
+ (result as any)[key] = (obj as any)[key];
148
+ }
149
+ return result;
150
+ }
151
+
152
+ function shuffleTransactions(transactions: Transaction[]): Transaction[] {
153
+ return transactions.map((t) => shuffleObjectKeys(t) as Transaction);
154
+ }
155
+
156
+ describe("Signature validation after shuffling transaction keys", () => {
157
+ it("trusting transactions with 100 k/v entries", () => {
158
+ const { agent, session, node } = createTestNode();
159
+
160
+ const group = node.createGroup();
161
+ const map = group.createMap();
162
+
163
+ // Create 100 trusting transactions with explicit test data
164
+ for (let i = 0; i < 100; i++) {
165
+ map.core.makeTransaction(
166
+ [{ op: "set", key: `key${i}`, value: `value${i}` }],
167
+ "trusting",
168
+ undefined,
169
+ i * 1000,
170
+ );
171
+ }
172
+
173
+ const sessionContent =
174
+ map.core.newContentSince(undefined)?.[0]?.new[session];
175
+ assert(sessionContent);
176
+ expect(sessionContent.newTransactions.length).toBe(100);
177
+
178
+ const log = wasmCrypto.createSessionLog(
179
+ map.id,
180
+ session,
181
+ agent.currentSignerID(),
182
+ );
183
+ const logShuffled = wasmCrypto.createSessionLog(
184
+ map.id,
185
+ session,
186
+ agent.currentSignerID(),
187
+ );
188
+
189
+ const shuffledTransactions = shuffleTransactions(
190
+ sessionContent.newTransactions,
191
+ );
192
+
193
+ expect(() =>
194
+ log.tryAdd(
195
+ sessionContent.newTransactions,
196
+ sessionContent.lastSignature,
197
+ false,
198
+ ),
199
+ ).not.toThrow();
200
+
201
+ expect(() =>
202
+ logShuffled.tryAdd(
203
+ shuffledTransactions,
204
+ sessionContent.lastSignature,
205
+ false,
206
+ ),
207
+ ).not.toThrow();
208
+ });
209
+
210
+ it("private transactions with 100 k/v entries", () => {
211
+ const { agent, session, node } = createTestNode();
212
+
213
+ const group = node.createGroup();
214
+ const map = group.createMap();
215
+
216
+ // Create 100 private transactions with explicit test data
217
+ for (let i = 0; i < 100; i++) {
218
+ map.core.makeTransaction(
219
+ [{ op: "set", key: `secretKey${i}`, value: `secretValue${i}` }],
220
+ "private",
221
+ undefined,
222
+ i * 1000,
223
+ );
224
+ }
225
+
226
+ const sessionContent =
227
+ map.core.newContentSince(undefined)?.[0]?.new[session];
228
+ assert(sessionContent);
229
+ expect(sessionContent.newTransactions.length).toBe(100);
230
+
231
+ // Verify transactions are actually private (encrypted)
232
+ const firstTx = sessionContent.newTransactions[0];
233
+ assert(firstTx);
234
+ expect(firstTx.privacy).toBe("private");
235
+ expect("encryptedChanges" in firstTx).toBe(true);
236
+
237
+ const log = wasmCrypto.createSessionLog(
238
+ map.id,
239
+ session,
240
+ agent.currentSignerID(),
241
+ );
242
+ const logShuffled = wasmCrypto.createSessionLog(
243
+ map.id,
244
+ session,
245
+ agent.currentSignerID(),
246
+ );
247
+
248
+ const shuffledTransactions = shuffleTransactions(
249
+ sessionContent.newTransactions,
250
+ );
251
+
252
+ expect(() =>
253
+ log.tryAdd(
254
+ sessionContent.newTransactions,
255
+ sessionContent.lastSignature,
256
+ false,
257
+ ),
258
+ ).not.toThrow();
259
+
260
+ expect(() =>
261
+ logShuffled.tryAdd(
262
+ shuffledTransactions,
263
+ sessionContent.lastSignature,
264
+ false,
265
+ ),
266
+ ).not.toThrow();
267
+ });
268
+
269
+ it("trusting transactions with metas", () => {
270
+ const { agent, session, node } = createTestNode();
271
+
272
+ const group = node.createGroup();
273
+ const map = group.createMap();
274
+
275
+ // Create transactions with metas
276
+ for (let i = 0; i < 50; i++) {
277
+ map.core.makeTransaction(
278
+ [{ op: "set", key: `key${i}`, value: `value${i}` }],
279
+ "trusting",
280
+ { index: i, timestamp: i * 1000, nested: { data: `meta${i}` } },
281
+ i * 1000,
282
+ );
283
+ }
284
+
285
+ const sessionContent =
286
+ map.core.newContentSince(undefined)?.[0]?.new[session];
287
+ assert(sessionContent);
288
+ expect(sessionContent.newTransactions.length).toBe(50);
289
+
290
+ // Verify metas are present
291
+ const firstTx = sessionContent.newTransactions[0];
292
+ assert(firstTx);
293
+ expect(firstTx.meta).toBeDefined();
294
+
295
+ const log = wasmCrypto.createSessionLog(
296
+ map.id,
297
+ session,
298
+ agent.currentSignerID(),
299
+ );
300
+ const logShuffled = wasmCrypto.createSessionLog(
301
+ map.id,
302
+ session,
303
+ agent.currentSignerID(),
304
+ );
305
+
306
+ const shuffledTransactions = shuffleTransactions(
307
+ sessionContent.newTransactions,
308
+ );
309
+
310
+ expect(() =>
311
+ log.tryAdd(
312
+ sessionContent.newTransactions,
313
+ sessionContent.lastSignature,
314
+ false,
315
+ ),
316
+ ).not.toThrow();
317
+
318
+ expect(() =>
319
+ logShuffled.tryAdd(
320
+ shuffledTransactions,
321
+ sessionContent.lastSignature,
322
+ false,
323
+ ),
324
+ ).not.toThrow();
325
+ });
326
+
327
+ it("private transactions with metas", () => {
328
+ const { agent, session, node } = createTestNode();
329
+
330
+ const group = node.createGroup();
331
+ const map = group.createMap();
332
+
333
+ // Create private transactions with encrypted metas
334
+ for (let i = 0; i < 50; i++) {
335
+ map.core.makeTransaction(
336
+ [{ op: "set", key: `secretKey${i}`, value: `secretValue${i}` }],
337
+ "private",
338
+ { index: i, secret: `confidential${i}` },
339
+ i * 1000,
340
+ );
341
+ }
342
+
343
+ const sessionContent =
344
+ map.core.newContentSince(undefined)?.[0]?.new[session];
345
+ assert(sessionContent);
346
+ expect(sessionContent.newTransactions.length).toBe(50);
347
+
348
+ // Verify transactions are private with encrypted metas
349
+ const firstTx = sessionContent.newTransactions[0];
350
+ assert(firstTx);
351
+ expect(firstTx.privacy).toBe("private");
352
+ expect("encryptedChanges" in firstTx).toBe(true);
353
+ expect(firstTx.meta).toBeDefined();
354
+
355
+ const log = wasmCrypto.createSessionLog(
356
+ map.id,
357
+ session,
358
+ agent.currentSignerID(),
359
+ );
360
+ const logShuffled = wasmCrypto.createSessionLog(
361
+ map.id,
362
+ session,
363
+ agent.currentSignerID(),
364
+ );
365
+
366
+ const shuffledTransactions = shuffleTransactions(
367
+ sessionContent.newTransactions,
368
+ );
369
+
370
+ expect(() =>
371
+ log.tryAdd(
372
+ sessionContent.newTransactions,
373
+ sessionContent.lastSignature,
374
+ false,
375
+ ),
376
+ ).not.toThrow();
377
+
378
+ expect(() =>
379
+ logShuffled.tryAdd(
380
+ shuffledTransactions,
381
+ sessionContent.lastSignature,
382
+ false,
383
+ ),
384
+ ).not.toThrow();
385
+ });
386
+
387
+ it("mixed trusting and private transactions across multiple sessions", () => {
388
+ const { agent, session, node } = createTestNode();
389
+
390
+ const group = node.createGroup();
391
+ const map = group.createMap();
392
+
393
+ // Create alternating trusting and private transactions
394
+ for (let i = 0; i < 50; i++) {
395
+ const privacy = i % 2 === 0 ? "trusting" : "private";
396
+ const hasMeta = i % 3 === 0;
397
+ map.core.makeTransaction(
398
+ [{ op: "set", key: `mixedKey${i}`, value: `mixedValue${i}` }],
399
+ privacy,
400
+ hasMeta ? { iteration: i, type: privacy } : undefined,
401
+ i * 1000,
402
+ );
403
+ }
404
+
405
+ const sessionContent =
406
+ map.core.newContentSince(undefined)?.[0]?.new[session];
407
+ assert(sessionContent);
408
+ expect(sessionContent.newTransactions.length).toBe(50);
409
+
410
+ // Verify we have a mix of trusting and private transactions
411
+ const trustingCount = sessionContent.newTransactions.filter(
412
+ (t) => t.privacy === "trusting",
413
+ ).length;
414
+ const privateCount = sessionContent.newTransactions.filter(
415
+ (t) => t.privacy === "private",
416
+ ).length;
417
+ expect(trustingCount).toBe(25);
418
+ expect(privateCount).toBe(25);
419
+
420
+ const log = wasmCrypto.createSessionLog(
421
+ map.id,
422
+ session,
423
+ agent.currentSignerID(),
424
+ );
425
+ const logShuffled = wasmCrypto.createSessionLog(
426
+ map.id,
427
+ session,
428
+ agent.currentSignerID(),
429
+ );
430
+
431
+ const shuffledTransactions = shuffleTransactions(
432
+ sessionContent.newTransactions,
433
+ );
434
+
435
+ expect(() =>
436
+ log.tryAdd(
437
+ sessionContent.newTransactions,
438
+ sessionContent.lastSignature,
439
+ false,
440
+ ),
441
+ ).not.toThrow();
442
+
443
+ expect(() =>
444
+ logShuffled.tryAdd(
445
+ shuffledTransactions,
446
+ sessionContent.lastSignature,
447
+ false,
448
+ ),
449
+ ).not.toThrow();
450
+ });
451
+ });
143
452
  });