cojson 0.17.8 → 0.17.10

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 (73) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/dist/coValueCore/SessionMap.d.ts +44 -0
  4. package/dist/coValueCore/SessionMap.d.ts.map +1 -0
  5. package/dist/coValueCore/SessionMap.js +118 -0
  6. package/dist/coValueCore/SessionMap.js.map +1 -0
  7. package/dist/coValueCore/coValueCore.d.ts +1 -0
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +40 -61
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/verifiedState.d.ts +14 -18
  12. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  13. package/dist/coValueCore/verifiedState.js +23 -86
  14. package/dist/coValueCore/verifiedState.js.map +1 -1
  15. package/dist/coValues/account.d.ts +4 -0
  16. package/dist/coValues/account.d.ts.map +1 -1
  17. package/dist/coValues/account.js +24 -4
  18. package/dist/coValues/account.js.map +1 -1
  19. package/dist/crypto/PureJSCrypto.d.ts +31 -3
  20. package/dist/crypto/PureJSCrypto.d.ts.map +1 -1
  21. package/dist/crypto/PureJSCrypto.js +112 -0
  22. package/dist/crypto/PureJSCrypto.js.map +1 -1
  23. package/dist/crypto/WasmCrypto.d.ts +23 -4
  24. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  25. package/dist/crypto/WasmCrypto.js +44 -2
  26. package/dist/crypto/WasmCrypto.js.map +1 -1
  27. package/dist/crypto/crypto.d.ts +17 -1
  28. package/dist/crypto/crypto.d.ts.map +1 -1
  29. package/dist/crypto/crypto.js.map +1 -1
  30. package/dist/localNode.d.ts +1 -0
  31. package/dist/localNode.d.ts.map +1 -1
  32. package/dist/localNode.js +10 -5
  33. package/dist/localNode.js.map +1 -1
  34. package/dist/sync.d.ts +2 -0
  35. package/dist/sync.d.ts.map +1 -1
  36. package/dist/sync.js +8 -1
  37. package/dist/sync.js.map +1 -1
  38. package/dist/tests/PureJSCrypto.test.d.ts +2 -0
  39. package/dist/tests/PureJSCrypto.test.d.ts.map +1 -0
  40. package/dist/tests/PureJSCrypto.test.js +88 -0
  41. package/dist/tests/PureJSCrypto.test.js.map +1 -0
  42. package/dist/tests/WasmCrypto.test.d.ts +2 -0
  43. package/dist/tests/WasmCrypto.test.d.ts.map +1 -0
  44. package/dist/tests/WasmCrypto.test.js +88 -0
  45. package/dist/tests/WasmCrypto.test.js.map +1 -0
  46. package/dist/tests/coValueCore.test.js +62 -187
  47. package/dist/tests/coValueCore.test.js.map +1 -1
  48. package/dist/tests/coreWasm.test.d.ts +2 -0
  49. package/dist/tests/coreWasm.test.d.ts.map +1 -0
  50. package/dist/tests/coreWasm.test.js +80 -0
  51. package/dist/tests/coreWasm.test.js.map +1 -0
  52. package/dist/tests/sync.test.js +19 -1
  53. package/dist/tests/sync.test.js.map +1 -1
  54. package/dist/tests/testUtils.d.ts +3 -0
  55. package/dist/tests/testUtils.d.ts.map +1 -1
  56. package/dist/tests/testUtils.js +4 -1
  57. package/dist/tests/testUtils.js.map +1 -1
  58. package/package.json +3 -3
  59. package/src/coValueCore/SessionMap.ts +230 -0
  60. package/src/coValueCore/coValueCore.ts +66 -91
  61. package/src/coValueCore/verifiedState.ts +60 -129
  62. package/src/coValues/account.ts +28 -4
  63. package/src/crypto/PureJSCrypto.ts +202 -2
  64. package/src/crypto/WasmCrypto.ts +95 -4
  65. package/src/crypto/crypto.ts +38 -1
  66. package/src/localNode.ts +18 -10
  67. package/src/sync.ts +10 -0
  68. package/src/tests/PureJSCrypto.test.ts +130 -0
  69. package/src/tests/WasmCrypto.test.ts +130 -0
  70. package/src/tests/coValueCore.test.ts +84 -292
  71. package/src/tests/coreWasm.test.ts +142 -0
  72. package/src/tests/sync.test.ts +32 -0
  73. package/src/tests/testUtils.ts +9 -1
@@ -0,0 +1,130 @@
1
+ import { assert, beforeEach, describe, expect, it } from "vitest";
2
+ import {
3
+ loadCoValueOrFail,
4
+ setCurrentTestCryptoProvider,
5
+ setupTestNode,
6
+ setupTestAccount,
7
+ } from "./testUtils";
8
+ import { stableStringify } from "../jsonStringify";
9
+ import { WasmCrypto } from "../crypto/WasmCrypto";
10
+
11
+ const wasmCrypto = await WasmCrypto.create();
12
+ setCurrentTestCryptoProvider(wasmCrypto);
13
+
14
+ let syncServer: ReturnType<typeof setupTestNode>;
15
+
16
+ beforeEach(() => {
17
+ syncServer = setupTestNode({ isSyncServer: true });
18
+ });
19
+
20
+ // A suite of tests focused on high-level tests that verify:
21
+ // - Keys creation and unsealing
22
+ // - Signature creation and verification
23
+ // - Encryption and decryption of values
24
+ describe("WasmCrypto", () => {
25
+ it("successfully creates a private CoValue and reads it in another session", async () => {
26
+ const client = setupTestNode({
27
+ connected: true,
28
+ });
29
+
30
+ const group = client.node.createGroup();
31
+ const map = group.createMap();
32
+ map.set("count", 0, "private");
33
+ map.set("count", 1, "private");
34
+ map.set("count", 2, "private");
35
+
36
+ const client2 = client.spawnNewSession();
37
+
38
+ const mapInTheOtherSession = await loadCoValueOrFail(client2.node, map.id);
39
+
40
+ expect(mapInTheOtherSession.get("count")).toEqual(2);
41
+ });
42
+
43
+ it("successfully updates a private CoValue and reads it in another session", async () => {
44
+ const client = setupTestNode({
45
+ connected: true,
46
+ });
47
+
48
+ const group = client.node.createGroup();
49
+ const map = group.createMap();
50
+ map.set("count", 0, "private");
51
+ map.set("count", 1, "private");
52
+ map.set("count", 2, "private");
53
+
54
+ const client2 = client.spawnNewSession();
55
+
56
+ const mapInTheOtherSession = await loadCoValueOrFail(client2.node, map.id);
57
+ mapInTheOtherSession.set("count", 3, "private");
58
+
59
+ await mapInTheOtherSession.core.waitForSync();
60
+
61
+ expect(mapInTheOtherSession.get("count")).toEqual(3);
62
+ });
63
+
64
+ it("can invite another account to a group and share a private CoValue", async () => {
65
+ const client = setupTestNode({
66
+ connected: true,
67
+ });
68
+ const account = await setupTestAccount({
69
+ connected: true,
70
+ });
71
+
72
+ const group = client.node.createGroup();
73
+ const invite = group.createInvite("admin");
74
+
75
+ await account.node.acceptInvite(group.id, invite);
76
+
77
+ const map = group.createMap();
78
+ map.set("secret", "private-data", "private");
79
+
80
+ // The other account should be able to read the private value
81
+ const mapInOtherSession = await loadCoValueOrFail(account.node, map.id);
82
+ expect(mapInOtherSession.get("secret")).toEqual("private-data");
83
+
84
+ mapInOtherSession.set("secret", "modified", "private");
85
+
86
+ await mapInOtherSession.core.waitForSync();
87
+
88
+ expect(map.get("secret")).toEqual("modified");
89
+ });
90
+
91
+ it("rejects sessions with invalid signatures", async () => {
92
+ const client = setupTestNode({
93
+ connected: true,
94
+ });
95
+
96
+ const group = client.node.createGroup();
97
+ const map = group.createMap();
98
+ map.set("count", 0, "trusting");
99
+
100
+ // Create a new session with the same agent
101
+ const client2 = client.spawnNewSession();
102
+
103
+ // This should work normally
104
+ const mapInOtherSession = await loadCoValueOrFail(client2.node, map.id);
105
+ expect(mapInOtherSession.get("count")).toEqual(0);
106
+
107
+ mapInOtherSession.core.tryAddTransactions(
108
+ client2.node.currentSessionID,
109
+ [
110
+ {
111
+ privacy: "trusting",
112
+ changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
113
+ madeAt: Date.now(),
114
+ },
115
+ ],
116
+ "hash_z12345678",
117
+ "signature_z12345678",
118
+ "immediate",
119
+ true,
120
+ );
121
+
122
+ const content =
123
+ mapInOtherSession.core.verified.newContentSince(undefined)?.[0];
124
+ assert(content);
125
+
126
+ client.node.syncManager.handleNewContent(content, "storage");
127
+
128
+ expect(map.get("count")).toEqual(0);
129
+ });
130
+ });
@@ -1,4 +1,3 @@
1
- import { encrypt } from "jazz-crypto-rs";
2
1
  import {
3
2
  assert,
4
3
  afterEach,
@@ -8,15 +7,13 @@ import {
8
7
  test,
9
8
  vi,
10
9
  } from "vitest";
11
- import { bytesToBase64url } from "../base64url.js";
12
10
  import { CoValueCore } from "../coValueCore/coValueCore.js";
13
11
  import { Transaction } from "../coValueCore/verifiedState.js";
14
- import { MapOpPayload } from "../coValues/coMap.js";
15
12
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
16
13
  import { stableStringify } from "../jsonStringify.js";
17
14
  import { LocalNode } from "../localNode.js";
18
- import { Role } from "../permissions.js";
19
15
  import {
16
+ agentAndSessionIDFromSecret,
20
17
  createTestMetricReader,
21
18
  createTestNode,
22
19
  createTwoConnectedNodes,
@@ -25,10 +22,13 @@ import {
25
22
  randomAgentAndSessionID,
26
23
  tearDownTestMetricReader,
27
24
  } from "./testUtils.js";
25
+ import { CO_VALUE_PRIORITY } from "../priority.js";
28
26
 
29
27
  const Crypto = await WasmCrypto.create();
30
28
 
31
29
  let metricReader: ReturnType<typeof createTestMetricReader>;
30
+ const agentSecret =
31
+ "sealerSecret_zE3Nr7YFr1KkVbJSx4JDCzYn4ApYdm8kJ5ghNBxREHQya/signerSecret_z9fEu4eNG1eXHMak3YSzY7uLdoG8HESSJ8YW4xWdNNDSP";
32
32
 
33
33
  beforeEach(() => {
34
34
  metricReader = createTestMetricReader();
@@ -38,47 +38,7 @@ afterEach(() => {
38
38
  tearDownTestMetricReader();
39
39
  });
40
40
 
41
- test("Can create coValue with new agent credentials and add transaction to it", () => {
42
- const [agent, sessionID] = randomAgentAndSessionID();
43
- const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
44
-
45
- const coValue = node.createCoValue({
46
- type: "costream",
47
- ruleset: { type: "unsafeAllowAll" },
48
- meta: null,
49
- ...Crypto.createdNowUnique(),
50
- });
51
-
52
- const transaction: Transaction = {
53
- privacy: "trusting",
54
- madeAt: Date.now(),
55
- changes: stableStringify([
56
- {
57
- hello: "world",
58
- },
59
- ]),
60
- };
61
-
62
- const { expectedNewHash } = coValue.verified.expectedNewHashAfter(
63
- node.currentSessionID,
64
- [transaction],
65
- );
66
-
67
- expect(
68
- coValue
69
- .tryAddTransactions(
70
- node.currentSessionID,
71
- [transaction],
72
- expectedNewHash,
73
- Crypto.sign(agent.currentSignerSecret(), expectedNewHash),
74
- "immediate",
75
- )
76
- ._unsafeUnwrap(),
77
- ).toBe(true);
78
- });
79
-
80
41
  test("transactions with wrong signature are rejected", () => {
81
- const wrongAgent = Crypto.newRandomAgentSecret();
82
42
  const node = nodeWithRandomAgentAndSessionID();
83
43
 
84
44
  const coValue = node.createCoValue({
@@ -88,88 +48,53 @@ test("transactions with wrong signature are rejected", () => {
88
48
  ...Crypto.createdNowUnique(),
89
49
  });
90
50
 
91
- const transaction: Transaction = {
92
- privacy: "trusting",
93
- madeAt: Date.now(),
94
- changes: stableStringify([
95
- {
96
- hello: "world",
97
- },
98
- ]),
99
- };
100
-
101
- const { expectedNewHash } = coValue.verified.expectedNewHashAfter(
102
- node.currentSessionID,
103
- [transaction],
104
- );
105
-
106
- // eslint-disable-next-line neverthrow/must-use-result
107
- coValue
108
- .tryAddTransactions(
51
+ const { transaction, signature } =
52
+ coValue.verified.makeNewTrustingTransaction(
109
53
  node.currentSessionID,
110
- [transaction],
111
- expectedNewHash,
112
- Crypto.sign(Crypto.getAgentSignerSecret(wrongAgent), expectedNewHash),
113
- "immediate",
114
- )
115
- ._unsafeUnwrapErr({ withStackTrace: true });
116
- });
54
+ node.getCurrentAgent(),
55
+ [{ hello: "world" }],
56
+ );
117
57
 
118
- test("transactions with correctly signed, but wrong hash are rejected", () => {
119
- const [agent, sessionID] = randomAgentAndSessionID();
120
- const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
58
+ transaction.madeAt = Date.now() + 1000;
121
59
 
122
- const coValue = node.createCoValue({
123
- type: "costream",
124
- ruleset: { type: "unsafeAllowAll" },
125
- meta: null,
126
- ...Crypto.createdNowUnique(),
127
- });
60
+ // Delete the transaction from the coValue
61
+ node.internalDeleteCoValue(coValue.id);
62
+ node.syncManager.handleNewContent(
63
+ {
64
+ action: "content",
65
+ id: coValue.id,
66
+ header: coValue.verified.header,
67
+ priority: CO_VALUE_PRIORITY.LOW,
68
+ new: {},
69
+ },
70
+ "import",
71
+ );
128
72
 
129
- const transaction: Transaction = {
130
- privacy: "trusting",
131
- madeAt: Date.now(),
132
- changes: stableStringify([
133
- {
134
- hello: "world",
135
- },
136
- ]),
137
- };
73
+ const newEntry = node.getCoValue(coValue.id);
138
74
 
139
- const { expectedNewHash } = coValue.verified.expectedNewHashAfter(
75
+ // eslint-disable-next-line neverthrow/must-use-result
76
+ const result = newEntry.tryAddTransactions(
140
77
  node.currentSessionID,
141
- [
142
- {
143
- privacy: "trusting",
144
- madeAt: Date.now(),
145
- changes: stableStringify([
146
- {
147
- hello: "wrong",
148
- },
149
- ]),
150
- },
151
- ],
78
+ [transaction],
79
+ undefined,
80
+ signature,
81
+ "immediate",
152
82
  );
153
83
 
154
- // eslint-disable-next-line neverthrow/must-use-result
155
- coValue
156
- .tryAddTransactions(
157
- node.currentSessionID,
158
- [transaction],
159
- expectedNewHash,
160
- Crypto.sign(agent.currentSignerSecret(), expectedNewHash),
161
- "immediate",
162
- )
163
- ._unsafeUnwrapErr({ withStackTrace: true });
84
+ expect(result.isErr()).toBe(true);
85
+ expect(newEntry.getValidSortedTransactions().length).toBe(0);
164
86
  });
165
87
 
166
88
  test("New transactions in a group correctly update owned values, including subscriptions", async () => {
167
89
  const [agent, sessionID] = randomAgentAndSessionID();
168
90
  const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
169
91
 
170
- const group = node.createGroup();
92
+ const timeBeforeEdit = Date.now() - 1000;
93
+ const dateNowMock = vi
94
+ .spyOn(Date, "now")
95
+ .mockImplementation(() => timeBeforeEdit);
171
96
 
172
- const timeBeforeEdit = Date.now();
97
+ const group = node.createGroup();
173
98
 
174
99
  await new Promise((resolve) => setTimeout(resolve, 10));
175
100
 
@@ -179,49 +104,31 @@ test("New transactions in a group correctly update owned values, including subsc
179
104
 
180
105
  const listener = vi.fn();
181
106
 
182
- map.subscribe(listener);
107
+ map.subscribe((map) => {
108
+ listener(map.get("hello"));
109
+ });
183
110
 
184
- expect(listener.mock.calls[0]?.[0].get("hello")).toBe("world");
111
+ expect(listener).toHaveBeenLastCalledWith("world");
185
112
 
186
- const resignationThatWeJustLearnedAbout = {
187
- privacy: "trusting",
188
- madeAt: timeBeforeEdit,
189
- changes: stableStringify([
113
+ expect(map.core.getValidSortedTransactions().length).toBe(1);
114
+ expect(group.get(agent.id)).toBe("admin");
115
+
116
+ group.core.makeTransaction(
117
+ [
190
118
  {
191
119
  op: "set",
192
120
  key: agent.id,
193
121
  value: "revoked",
194
- } satisfies MapOpPayload<typeof agent.id, Role>,
195
- ]),
196
- } satisfies Transaction;
197
-
198
- const { expectedNewHash } = group.core.verified.expectedNewHashAfter(
199
- sessionID,
200
- [resignationThatWeJustLearnedAbout],
122
+ },
123
+ ],
124
+ "trusting",
201
125
  );
202
126
 
203
- const signature = Crypto.sign(
204
- node.getCurrentAgent().currentSignerSecret(),
205
- expectedNewHash,
206
- );
207
-
208
- expect(map.core.getValidSortedTransactions().length).toBe(1);
209
-
210
- const manuallyAdddedTxSuccess = group.core
211
- .tryAddTransactions(
212
- node.currentSessionID,
213
- [resignationThatWeJustLearnedAbout],
214
- expectedNewHash,
215
- signature,
216
- "immediate",
217
- )
218
- ._unsafeUnwrap({ withStackTrace: true });
219
-
220
- expect(manuallyAdddedTxSuccess).toBe(true);
221
-
222
- expect(listener.mock.calls.length).toBe(2);
223
- expect(listener.mock.calls[1]?.[0].get("hello")).toBe(undefined);
127
+ expect(group.get(agent.id)).toBe("revoked");
128
+ dateNowMock.mockReset();
224
129
 
130
+ expect(listener).toHaveBeenCalledTimes(2);
131
+ expect(listener).toHaveBeenLastCalledWith(undefined);
225
132
  expect(map.core.getValidSortedTransactions().length).toBe(0);
226
133
  });
227
134
 
@@ -359,166 +266,51 @@ test("listeners are notified even if the previous listener threw an error", asyn
359
266
  errorLog.mockRestore();
360
267
  });
361
268
 
362
- test("getValidTransactions should skip trusting transactions with invalid JSON", () => {
363
- const [agent, sessionID] = randomAgentAndSessionID();
364
- const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
365
-
366
- const coValue = node.createCoValue({
367
- type: "costream",
368
- ruleset: { type: "unsafeAllowAll" },
369
- meta: null,
370
- ...Crypto.createdNowUnique(),
371
- });
372
-
373
- // Create a valid transaction first
374
- const validTransaction: Transaction = {
375
- privacy: "trusting",
376
- madeAt: Date.now(),
377
- changes: stableStringify([{ hello: "world" }]),
378
- };
379
-
380
- const { expectedNewHash: expectedNewHash1 } =
381
- coValue.verified.expectedNewHashAfter(node.currentSessionID, [
382
- validTransaction,
383
- ]);
384
-
385
- coValue
386
- .tryAddTransactions(
387
- node.currentSessionID,
388
- [validTransaction],
389
- expectedNewHash1,
390
- Crypto.sign(agent.currentSignerSecret(), expectedNewHash1),
391
- "immediate",
392
- )
393
- ._unsafeUnwrap();
394
-
395
- // Create an invalid transaction with malformed JSON
396
- const invalidTransaction: Transaction = {
397
- privacy: "trusting",
398
- madeAt: Date.now() + 1,
399
- changes: '{"invalid": json}' as any, // Invalid JSON string
400
- };
401
-
402
- const { expectedNewHash: expectedNewHash2 } =
403
- coValue.verified.expectedNewHashAfter(node.currentSessionID, [
404
- invalidTransaction,
405
- ]);
406
-
407
- coValue
408
- .tryAddTransactions(
409
- node.currentSessionID,
410
- [invalidTransaction],
411
- expectedNewHash2,
412
- Crypto.sign(agent.currentSignerSecret(), expectedNewHash2),
413
- "immediate",
414
- )
415
- ._unsafeUnwrap();
416
-
417
- // Get valid transactions - should only include the valid one
418
- const validTransactions = coValue.getValidTransactions();
419
-
420
- expect(validTransactions).toHaveLength(1);
421
- expect(validTransactions[0]?.changes).toEqual([{ hello: "world" }]);
422
- });
423
-
424
269
  test("getValidTransactions should skip private transactions with invalid JSON", () => {
425
- const [agent, sessionID] = randomAgentAndSessionID();
270
+ const [agent, sessionID] = agentAndSessionIDFromSecret(agentSecret);
426
271
  const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
427
272
 
428
- const group = node.createGroup();
429
- group.addMember("everyone", "writer");
430
-
431
- const coValue = node.createCoValue({
432
- type: "costream",
433
- ruleset: { type: "ownedByGroup", group: group.id },
434
- meta: null,
435
- ...Crypto.createdNowUnique(),
436
- });
437
-
438
- const { secret: keySecret, id: keyID } = coValue.getCurrentReadKey();
439
-
440
- assert(keySecret);
441
-
442
- const encrypted = Crypto.encryptForTransaction(
443
- [{ hello: "world" }],
444
- keySecret,
445
- {
446
- in: coValue.id,
447
- tx: coValue.nextTransactionID(),
273
+ const fixtures = {
274
+ id: "co_zWwrEiushQLvbkWd6Z3L8WxTU1r",
275
+ signature:
276
+ "signature_z3ktW7wxMnW7VYExCGZv4Ug2UJSW3ag6zLDiP8GpZThzif6veJt7JipYpUgshhuGbgHtLcWywWSWysV7hChxFypDt",
277
+ decrypted:
278
+ '[{"after":"start","op":"app","value":"co_zMphsnYN6GU8nn2HDY5suvyGufY"}]',
279
+ key: {
280
+ secret: "keySecret_z3dU66SsyQkkGKpNCJW6NX74MnfVGHUyY7r85b4M8X88L",
281
+ id: "key_z5XUAHyoqUV9zXWvMK",
448
282
  },
449
- );
450
-
451
- // Create a valid private transaction first
452
- const validTransaction: Transaction = {
453
- privacy: "private",
454
- madeAt: Date.now(),
455
- keyUsed: keyID,
456
- encryptedChanges: encrypted as any,
457
- };
283
+ transaction: {
284
+ privacy: "private",
285
+ madeAt: 0,
286
+ encryptedChanges:
287
+ "encrypted_UNAxqdUSGRZ2rzuLU99AFPKCe2C0HwsTzMWQreXZqLr6RpWrSMa-5lwgwIev7xPHTgZFq5UyUgMFrO9zlHJHJGgjJcDzFihY=" as any,
288
+ keyUsed: "key_z5XUAHyoqUV9zXWvMK",
289
+ },
290
+ session:
291
+ "sealer_z5yhsCCe2XwLTZC4254mUoMASshm3Diq49JrefPpjTktp/signer_z7gVGDpNz9qUtsRxAkHMuu4DYdtVVCG4XELTKPYdoYLPr_session_z9mDP8FoonSA",
292
+ } as const;
458
293
 
459
- const { expectedNewHash: expectedNewHash1 } =
460
- coValue.verified.expectedNewHashAfter(node.currentSessionID, [
461
- validTransaction,
462
- ]);
294
+ const group = node.createGroup();
295
+ const map = group.createMap();
463
296
 
464
- coValue
465
- .tryAddTransactions(
466
- node.currentSessionID,
467
- [validTransaction],
468
- expectedNewHash1,
469
- Crypto.sign(agent.currentSignerSecret(), expectedNewHash1),
470
- "immediate",
471
- )
472
- ._unsafeUnwrap();
297
+ map.set("hello", "world");
473
298
 
474
- const textEncoder = new TextEncoder();
475
- const brokenChange = `encrypted_U${bytesToBase64url(
476
- encrypt(
477
- textEncoder.encode('{"invalid": json}'),
478
- keySecret,
479
- textEncoder.encode(
480
- stableStringify({
481
- in: coValue.id,
482
- tx: coValue.nextTransactionID(),
483
- }),
484
- ),
485
- ),
486
- )}`;
487
-
488
- // Create an invalid private transaction with malformed JSON after decryption
489
- const invalidTransaction: Transaction = {
490
- privacy: "private",
491
- madeAt: Date.now() + 1,
492
- keyUsed: keyID,
493
- encryptedChanges: brokenChange as any,
494
- };
495
-
496
- const { expectedNewHash: expectedNewHash2 } =
497
- coValue.verified.expectedNewHashAfter(node.currentSessionID, [
498
- invalidTransaction,
499
- ]);
500
-
501
- coValue
299
+ // This should fail silently, because the encryptedChanges will be outputted as gibberish
300
+ map.core
502
301
  .tryAddTransactions(
503
- node.currentSessionID,
504
- [invalidTransaction],
505
- expectedNewHash2,
506
- Crypto.sign(agent.currentSignerSecret(), expectedNewHash2),
302
+ fixtures.session,
303
+ [fixtures.transaction],
304
+ undefined,
305
+ fixtures.signature,
507
306
  "immediate",
508
307
  )
509
308
  ._unsafeUnwrap();
510
309
 
511
- // Get valid transactions - should skip the invalid one
512
- const validTransactions = coValue.getValidTransactions({
513
- ignorePrivateTransactions: false,
514
- });
310
+ // Get valid transactions - should only include the valid one
311
+ const validTransactions = map.core.getValidTransactions();
515
312
 
516
- // Since we can't easily create valid private transactions in this test setup,
517
- // we just verify that the method doesn't crash and handles the invalid JSON gracefully
518
- expect(validTransactions).toBeDefined();
519
- expect(Array.isArray(validTransactions)).toBe(true);
520
- expect(validTransactions.length).toBe(1);
521
- expect(validTransactions[0]?.changes).toEqual([{ hello: "world" }]);
313
+ expect(validTransactions).toHaveLength(1);
522
314
  });
523
315
 
524
316
  describe("markErrored and isErroredInPeer", () => {