cojson 0.20.7 → 0.20.8

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 (209) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/SyncStateManager.d.ts.map +1 -1
  4. package/dist/SyncStateManager.js +0 -2
  5. package/dist/SyncStateManager.js.map +1 -1
  6. package/dist/base64url.d.ts +15 -0
  7. package/dist/base64url.d.ts.map +1 -1
  8. package/dist/base64url.js +101 -5
  9. package/dist/base64url.js.map +1 -1
  10. package/dist/base64url.test.js +76 -1
  11. package/dist/base64url.test.js.map +1 -1
  12. package/dist/coValue.d.ts +2 -1
  13. package/dist/coValue.d.ts.map +1 -1
  14. package/dist/coValue.js.map +1 -1
  15. package/dist/coValueCore/coValueCore.d.ts +9 -11
  16. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  17. package/dist/coValueCore/coValueCore.js +92 -65
  18. package/dist/coValueCore/coValueCore.js.map +1 -1
  19. package/dist/coValueCore/verifiedState.d.ts +38 -7
  20. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  21. package/dist/coValueCore/verifiedState.js +226 -30
  22. package/dist/coValueCore/verifiedState.js.map +1 -1
  23. package/dist/coValues/binaryCoStream.d.ts +63 -0
  24. package/dist/coValues/binaryCoStream.d.ts.map +1 -0
  25. package/dist/coValues/binaryCoStream.js +125 -0
  26. package/dist/coValues/binaryCoStream.js.map +1 -0
  27. package/dist/coValues/coList.d.ts +3 -1
  28. package/dist/coValues/coList.d.ts.map +1 -1
  29. package/dist/coValues/coList.js +15 -6
  30. package/dist/coValues/coList.js.map +1 -1
  31. package/dist/coValues/coMap.d.ts +1 -1
  32. package/dist/coValues/coMap.d.ts.map +1 -1
  33. package/dist/coValues/coMap.js +2 -2
  34. package/dist/coValues/coMap.js.map +1 -1
  35. package/dist/coValues/coStream.d.ts +0 -38
  36. package/dist/coValues/coStream.d.ts.map +1 -1
  37. package/dist/coValues/coStream.js +0 -86
  38. package/dist/coValues/coStream.js.map +1 -1
  39. package/dist/coValues/group.d.ts +44 -6
  40. package/dist/coValues/group.d.ts.map +1 -1
  41. package/dist/coValues/group.js +198 -17
  42. package/dist/coValues/group.js.map +1 -1
  43. package/dist/coreToCoValue.d.ts +2 -1
  44. package/dist/coreToCoValue.d.ts.map +1 -1
  45. package/dist/coreToCoValue.js +2 -1
  46. package/dist/coreToCoValue.js.map +1 -1
  47. package/dist/crypto/NapiCrypto.d.ts +18 -24
  48. package/dist/crypto/NapiCrypto.d.ts.map +1 -1
  49. package/dist/crypto/NapiCrypto.js +98 -60
  50. package/dist/crypto/NapiCrypto.js.map +1 -1
  51. package/dist/crypto/RNCrypto.d.ts +16 -3
  52. package/dist/crypto/RNCrypto.d.ts.map +1 -1
  53. package/dist/crypto/RNCrypto.js +117 -54
  54. package/dist/crypto/RNCrypto.js.map +1 -1
  55. package/dist/crypto/WasmCrypto.d.ts +18 -24
  56. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  57. package/dist/crypto/WasmCrypto.js +100 -61
  58. package/dist/crypto/WasmCrypto.js.map +1 -1
  59. package/dist/crypto/crypto.d.ts +55 -19
  60. package/dist/crypto/crypto.d.ts.map +1 -1
  61. package/dist/crypto/crypto.js +14 -3
  62. package/dist/crypto/crypto.js.map +1 -1
  63. package/dist/exports.d.ts +7 -3
  64. package/dist/exports.d.ts.map +1 -1
  65. package/dist/exports.js +4 -2
  66. package/dist/exports.js.map +1 -1
  67. package/dist/localNode.d.ts +3 -1
  68. package/dist/localNode.d.ts.map +1 -1
  69. package/dist/localNode.js +10 -3
  70. package/dist/localNode.js.map +1 -1
  71. package/dist/media.d.ts +1 -1
  72. package/dist/media.d.ts.map +1 -1
  73. package/dist/permissions.d.ts +2 -1
  74. package/dist/permissions.d.ts.map +1 -1
  75. package/dist/permissions.js +19 -3
  76. package/dist/permissions.js.map +1 -1
  77. package/dist/storage/sqliteAsync/client.d.ts +24 -12
  78. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  79. package/dist/storage/sqliteAsync/client.js +70 -58
  80. package/dist/storage/sqliteAsync/client.js.map +1 -1
  81. package/dist/storage/sqliteAsync/types.d.ts +1 -1
  82. package/dist/storage/sqliteAsync/types.d.ts.map +1 -1
  83. package/dist/storage/types.d.ts +1 -0
  84. package/dist/storage/types.d.ts.map +1 -1
  85. package/dist/sync.d.ts.map +1 -1
  86. package/dist/sync.js +7 -1
  87. package/dist/sync.js.map +1 -1
  88. package/dist/tests/CojsonMessageChannel.test.js +2 -2
  89. package/dist/tests/SQLiteClientAsync.test.d.ts +2 -0
  90. package/dist/tests/SQLiteClientAsync.test.d.ts.map +1 -0
  91. package/dist/tests/SQLiteClientAsync.test.js +64 -0
  92. package/dist/tests/SQLiteClientAsync.test.js.map +1 -0
  93. package/dist/tests/StorageApiAsync.test.js +2 -8
  94. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  95. package/dist/tests/SyncStateManager.test.js +2 -2
  96. package/dist/tests/WasmCrypto.test.js +1 -15
  97. package/dist/tests/WasmCrypto.test.js.map +1 -1
  98. package/dist/tests/coList.test.js +24 -5
  99. package/dist/tests/coList.test.js.map +1 -1
  100. package/dist/tests/coStream.test.js +4 -3
  101. package/dist/tests/coStream.test.js.map +1 -1
  102. package/dist/tests/coValueCore.initTransaction.test.d.ts +2 -0
  103. package/dist/tests/coValueCore.initTransaction.test.d.ts.map +1 -0
  104. package/dist/tests/coValueCore.initTransaction.test.js +438 -0
  105. package/dist/tests/coValueCore.initTransaction.test.js.map +1 -0
  106. package/dist/tests/coValueCore.test.js +11 -19
  107. package/dist/tests/coValueCore.test.js.map +1 -1
  108. package/dist/tests/crypto.test.js +83 -0
  109. package/dist/tests/crypto.test.js.map +1 -1
  110. package/dist/tests/deleteCoValue.test.js +5 -5
  111. package/dist/tests/deleteCoValue.test.js.map +1 -1
  112. package/dist/tests/group.inheritance.test.js +11 -0
  113. package/dist/tests/group.inheritance.test.js.map +1 -1
  114. package/dist/tests/group.test.js +24 -1
  115. package/dist/tests/group.test.js.map +1 -1
  116. package/dist/tests/groupSealer.test.d.ts +2 -0
  117. package/dist/tests/groupSealer.test.d.ts.map +1 -0
  118. package/dist/tests/groupSealer.test.js +913 -0
  119. package/dist/tests/groupSealer.test.js.map +1 -0
  120. package/dist/tests/setup.js +5 -0
  121. package/dist/tests/setup.js.map +1 -1
  122. package/dist/tests/sync.auth.test.js +10 -10
  123. package/dist/tests/sync.concurrentLoad.test.js +12 -12
  124. package/dist/tests/sync.deleted.test.js +8 -8
  125. package/dist/tests/sync.garbageCollection.test.js +10 -10
  126. package/dist/tests/sync.invite.test.js +12 -12
  127. package/dist/tests/sync.known.test.js +2 -2
  128. package/dist/tests/sync.load.test.js +107 -107
  129. package/dist/tests/sync.mesh.test.js +164 -46
  130. package/dist/tests/sync.mesh.test.js.map +1 -1
  131. package/dist/tests/sync.multipleServers.test.js +43 -43
  132. package/dist/tests/sync.peerReconciliation.test.js +29 -29
  133. package/dist/tests/sync.sharding.test.js +3 -3
  134. package/dist/tests/sync.storage.test.js +104 -104
  135. package/dist/tests/sync.storage.test.js.map +1 -1
  136. package/dist/tests/sync.storageAsync.test.js +56 -56
  137. package/dist/tests/sync.upload.test.js +22 -22
  138. package/dist/tests/testStorage.d.ts +2 -0
  139. package/dist/tests/testStorage.d.ts.map +1 -1
  140. package/dist/tests/testStorage.js +30 -6
  141. package/dist/tests/testStorage.js.map +1 -1
  142. package/dist/typeUtils/isCoValue.js +1 -1
  143. package/dist/typeUtils/isCoValue.js.map +1 -1
  144. package/package.json +4 -4
  145. package/src/SyncStateManager.ts +0 -2
  146. package/src/base64url.test.ts +89 -1
  147. package/src/base64url.ts +134 -6
  148. package/src/coValue.ts +2 -1
  149. package/src/coValueCore/coValueCore.ts +126 -84
  150. package/src/coValueCore/verifiedState.ts +335 -53
  151. package/src/coValues/binaryCoStream.ts +217 -0
  152. package/src/coValues/coList.ts +21 -8
  153. package/src/coValues/coMap.ts +3 -0
  154. package/src/coValues/coStream.ts +0 -170
  155. package/src/coValues/group.ts +270 -21
  156. package/src/coreToCoValue.ts +2 -1
  157. package/src/crypto/NapiCrypto.ts +198 -95
  158. package/src/crypto/RNCrypto.ts +229 -102
  159. package/src/crypto/WasmCrypto.ts +201 -95
  160. package/src/crypto/crypto.ts +118 -45
  161. package/src/exports.ts +11 -5
  162. package/src/localNode.ts +17 -1
  163. package/src/media.ts +1 -1
  164. package/src/permissions.ts +30 -7
  165. package/src/storage/sqliteAsync/client.ts +136 -115
  166. package/src/storage/sqliteAsync/types.ts +3 -1
  167. package/src/storage/types.ts +4 -0
  168. package/src/sync.ts +10 -1
  169. package/src/tests/CojsonMessageChannel.test.ts +2 -2
  170. package/src/tests/SQLiteClientAsync.test.ts +75 -0
  171. package/src/tests/StorageApiAsync.test.ts +4 -9
  172. package/src/tests/SyncStateManager.test.ts +2 -2
  173. package/src/tests/WasmCrypto.test.ts +1 -25
  174. package/src/tests/coList.test.ts +39 -5
  175. package/src/tests/coStream.test.ts +4 -5
  176. package/src/tests/coValueCore.initTransaction.test.ts +836 -0
  177. package/src/tests/coValueCore.test.ts +11 -22
  178. package/src/tests/crypto.test.ts +107 -0
  179. package/src/tests/deleteCoValue.test.ts +5 -5
  180. package/src/tests/group.inheritance.test.ts +16 -0
  181. package/src/tests/group.test.ts +29 -1
  182. package/src/tests/groupSealer.test.ts +1473 -0
  183. package/src/tests/setup.ts +6 -0
  184. package/src/tests/sync.auth.test.ts +10 -10
  185. package/src/tests/sync.concurrentLoad.test.ts +12 -12
  186. package/src/tests/sync.deleted.test.ts +8 -8
  187. package/src/tests/sync.garbageCollection.test.ts +10 -10
  188. package/src/tests/sync.invite.test.ts +12 -12
  189. package/src/tests/sync.known.test.ts +2 -2
  190. package/src/tests/sync.load.test.ts +107 -107
  191. package/src/tests/sync.mesh.test.ts +189 -46
  192. package/src/tests/sync.multipleServers.test.ts +43 -43
  193. package/src/tests/sync.peerReconciliation.test.ts +29 -29
  194. package/src/tests/sync.sharding.test.ts +3 -3
  195. package/src/tests/sync.storage.test.ts +104 -104
  196. package/src/tests/sync.storageAsync.test.ts +56 -56
  197. package/src/tests/sync.upload.test.ts +22 -22
  198. package/src/tests/testStorage.ts +39 -9
  199. package/src/typeUtils/isCoValue.ts +1 -1
  200. package/dist/coValueCore/SessionMap.d.ts +0 -55
  201. package/dist/coValueCore/SessionMap.d.ts.map +0 -1
  202. package/dist/coValueCore/SessionMap.js +0 -206
  203. package/dist/coValueCore/SessionMap.js.map +0 -1
  204. package/dist/tests/coreWasm.test.d.ts +0 -2
  205. package/dist/tests/coreWasm.test.d.ts.map +0 -1
  206. package/dist/tests/coreWasm.test.js +0 -203
  207. package/dist/tests/coreWasm.test.js.map +0 -1
  208. package/src/coValueCore/SessionMap.ts +0 -394
  209. package/src/tests/coreWasm.test.ts +0 -452
@@ -403,7 +403,8 @@ test("(smoke test) records transactions from local node", async () => {
403
403
  });
404
404
 
405
405
  assert(typeof value !== "number" && !!value?.count);
406
- expect(value.count).toBe(3);
406
+ // Creating a group now creates 4 transactions: admin member, key revelation, readKey, and groupSealer
407
+ expect(value.count).toBe(4);
407
408
  });
408
409
 
409
410
  test("creating a coValue with a group should't trigger automatically a content creation (performance)", () => {
@@ -866,42 +867,30 @@ describe("markErrored and isErroredInPeer", () => {
866
867
  });
867
868
  });
868
869
 
869
- test("knownState should return the same object until the CoValue is modified", () => {
870
+ test("knownState should reflect modifications", () => {
870
871
  const [agent, sessionID] = randomAgentAndSessionID();
871
872
  const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
872
873
 
873
874
  const group = node.createGroup();
874
875
  const map = group.createMap();
875
876
 
876
- // Get the knownState for the first time
877
+ // Get the knownState before any modification
877
878
  const knownState1 = map.core.knownState();
878
879
 
879
- // Get the knownState again - should be the same object
880
- const knownState2 = map.core.knownState();
881
- expect(knownState2).toBe(knownState1);
882
-
883
- // Call it multiple times to ensure it's always the same object
884
- const knownState3 = map.core.knownState();
885
- expect(knownState3).toBe(knownState1);
886
-
887
880
  // Now modify the CoValue by making a transaction
888
881
  map.set("hello", "world");
889
882
 
890
- // Get the knownState after modification - should be a different object
891
- const knownState4 = map.core.knownState();
892
- expect(knownState4).not.toBe(knownState1);
893
-
894
- // Verify that subsequent calls still return the same (new) object
895
- const knownState5 = map.core.knownState();
896
- expect(knownState5).toBe(knownState4);
883
+ // Get the knownState after modification - should have updated sessions
884
+ const knownState2 = map.core.knownState();
885
+ expect(knownState2).not.toEqual(knownState1);
897
886
 
898
887
  // Make another modification
899
888
  map.set("foo", "bar");
900
889
 
901
- // Get the knownState after second modification - should be yet another different object
902
- const knownState6 = map.core.knownState();
903
- expect(knownState6).not.toBe(knownState4);
904
- expect(knownState6).not.toBe(knownState1);
890
+ // Get the knownState after second modification - should have updated sessions again
891
+ const knownState3 = map.core.knownState();
892
+ expect(knownState3).not.toEqual(knownState2);
893
+ expect(knownState3).not.toEqual(knownState1);
905
894
  });
906
895
 
907
896
  describe("provideHeader uniqueness validation", () => {
@@ -3,9 +3,11 @@ import { x25519 } from "@noble/curves/ed25519";
3
3
  import { blake3 } from "@noble/hashes/blake3";
4
4
  import { base58, base64url } from "@scure/base";
5
5
  import { expect, test, vi } from "vitest";
6
+ import { shortHashLength } from "../crypto/crypto.js";
6
7
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
7
8
  import { SessionID } from "../ids.js";
8
9
  import { stableStringify } from "../jsonStringify.js";
10
+ import { JsonValue } from "../jsonValue.js";
9
11
 
10
12
  const crypto = await WasmCrypto.create();
11
13
 
@@ -167,3 +169,108 @@ test(`Unsealing malformed JSON logs error [${name}]`, () => {
167
169
  "Failed to decrypt/parse sealed message",
168
170
  );
169
171
  });
172
+
173
+ // ============================================================================
174
+ // shortHash implementation consistency tests
175
+ // ============================================================================
176
+
177
+ /**
178
+ * Compute shortHash using the base TypeScript implementation (reference).
179
+ * This mirrors the CryptoProvider.shortHash method exactly.
180
+ */
181
+ function referenceShortHash(value: JsonValue): string {
182
+ const textEncoder = new TextEncoder();
183
+ return `shortHash_z${base58.encode(
184
+ blake3(textEncoder.encode(stableStringify(value))).slice(
185
+ 0,
186
+ shortHashLength,
187
+ ),
188
+ )}`;
189
+ }
190
+
191
+ test(`shortHash WASM implementation matches TypeScript reference [${name}]`, () => {
192
+ // Test with simple object
193
+ const simpleObj = { hello: "world" };
194
+ expect(crypto.shortHash(simpleObj)).toBe(referenceShortHash(simpleObj));
195
+
196
+ // Test with object where key order differs (should produce same hash due to stableStringify)
197
+ const objA = { b: "world", a: "hello" };
198
+ const objB = { a: "hello", b: "world" };
199
+ expect(crypto.shortHash(objA)).toBe(referenceShortHash(objA));
200
+ expect(crypto.shortHash(objB)).toBe(referenceShortHash(objB));
201
+ expect(crypto.shortHash(objA)).toBe(crypto.shortHash(objB));
202
+
203
+ // Test with nested objects
204
+ const nestedObj = { outer: { inner: { deep: "value" } }, top: 123 };
205
+ expect(crypto.shortHash(nestedObj)).toBe(referenceShortHash(nestedObj));
206
+
207
+ // Test with arrays
208
+ const arrayValue = [1, 2, 3, "four", { five: 5 }];
209
+ expect(crypto.shortHash(arrayValue)).toBe(referenceShortHash(arrayValue));
210
+
211
+ // Test with primitives
212
+ expect(crypto.shortHash("string")).toBe(referenceShortHash("string"));
213
+ expect(crypto.shortHash(42)).toBe(referenceShortHash(42));
214
+ expect(crypto.shortHash(true)).toBe(referenceShortHash(true));
215
+ expect(crypto.shortHash(null)).toBe(referenceShortHash(null));
216
+
217
+ // Test with empty structures
218
+ expect(crypto.shortHash({})).toBe(referenceShortHash({}));
219
+ expect(crypto.shortHash([])).toBe(referenceShortHash([]));
220
+ });
221
+
222
+ test(`shortHash produces correct format [${name}]`, () => {
223
+ const value = { test: "data" };
224
+ const hash = crypto.shortHash(value);
225
+
226
+ // Should start with "shortHash_z" prefix
227
+ expect(hash).toMatch(/^shortHash_z/);
228
+
229
+ // The base58 encoded part should be reasonable length (19 bytes base58 encoded)
230
+ const base58Part = hash.substring("shortHash_z".length);
231
+ expect(base58Part.length).toBeGreaterThan(0);
232
+
233
+ // Should only contain valid base58 characters
234
+ expect(base58Part).toMatch(
235
+ /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/,
236
+ );
237
+ });
238
+
239
+ test(`shortHash with complex nested structure matches reference [${name}]`, () => {
240
+ const complexValue = {
241
+ users: [
242
+ { id: 1, name: "Alice", active: true },
243
+ { id: 2, name: "Bob", active: false },
244
+ ],
245
+ metadata: {
246
+ created: 1234567890,
247
+ tags: ["important", "test"],
248
+ config: {
249
+ nested: {
250
+ deeply: {
251
+ value: "found",
252
+ },
253
+ },
254
+ },
255
+ },
256
+ nullField: null,
257
+ emptyArray: [],
258
+ emptyObject: {},
259
+ };
260
+
261
+ expect(crypto.shortHash(complexValue)).toBe(referenceShortHash(complexValue));
262
+ });
263
+
264
+ test(`shortHash with special characters matches reference [${name}]`, () => {
265
+ // Test with unicode
266
+ const unicodeValue = { emoji: "🎉", chinese: "你好", arabic: "مرحبا" };
267
+ expect(crypto.shortHash(unicodeValue)).toBe(referenceShortHash(unicodeValue));
268
+
269
+ // Test with escape sequences
270
+ const escapeValue = {
271
+ quote: 'has "quotes"',
272
+ newline: "has\nnewline",
273
+ tab: "has\ttab",
274
+ };
275
+ expect(crypto.shortHash(escapeValue)).toBe(referenceShortHash(escapeValue));
276
+ });
@@ -22,7 +22,7 @@ function makeDeleteMarkerTransaction(core: CoValueCore, madeAt?: number) {
22
22
  (sessionID) => isDeleteSessionID(sessionID as SessionID),
23
23
  ) as SessionID;
24
24
  assert(deleteSessionID);
25
- const log = core.verified?.sessions.get(deleteSessionID);
25
+ const log = core.verified?.getSession(deleteSessionID);
26
26
  assert(log?.lastSignature);
27
27
  const tx = log.transactions.at(-1);
28
28
  assert(tx);
@@ -123,7 +123,7 @@ test("rejects delete marker ingestion from non-admin (ownedByGroup, skipVerify=f
123
123
  reason: "NotAdmin",
124
124
  });
125
125
  expect(map.core.isDeleted).toBe(false);
126
- expect(map.core.verified?.sessions.get(deleteSessionID)).toBeUndefined();
126
+ expect(map.core.verified?.getSession(deleteSessionID)).toBeUndefined();
127
127
  });
128
128
 
129
129
  test("accepts delete marker ingestion from admin (ownedByGroup, skipVerify=false) and marks deleted", async () => {
@@ -187,7 +187,7 @@ test("rejects delete session ingestion when attempting to append a second transa
187
187
  expect(first).toBeUndefined();
188
188
  expect(mapOnBob.core.isDeleted).toBe(true);
189
189
  expect(
190
- mapOnBob.core.verified?.sessions.get(deleteSessionID)?.transactions,
190
+ mapOnBob.core.verified?.getSession(deleteSessionID)?.transactions,
191
191
  ).toHaveLength(1);
192
192
 
193
193
  const second = mapOnBob.core.tryAddTransactions(
@@ -210,7 +210,7 @@ test("rejects delete session ingestion when attempting to append a second transa
210
210
  );
211
211
  }
212
212
  expect(
213
- mapOnBob.core.verified?.sessions.get(deleteSessionID)?.transactions,
213
+ mapOnBob.core.verified?.getSession(deleteSessionID)?.transactions,
214
214
  ).toHaveLength(1);
215
215
  });
216
216
 
@@ -253,7 +253,7 @@ test("rejects delete session ingestion when attempting to add multiple delete tr
253
253
  /Delete transaction must be the only transaction in the session/,
254
254
  );
255
255
  }
256
- expect(mapOnBob.core.verified?.sessions.get(deleteSessionID)).toBeUndefined();
256
+ expect(mapOnBob.core.verified?.getSession(deleteSessionID)).toBeUndefined();
257
257
  });
258
258
 
259
259
  test("skipVerify=true ingestion marks deleted even for non-admin delete marker", async () => {
@@ -468,6 +468,9 @@ describe("extend", () => {
468
468
  const childGroup = node1.node.createGroup();
469
469
  childGroup.extend(parentGroup, "admin");
470
470
 
471
+ // Wait for childGroup to sync so node3 sees the groupSealer field
472
+ await childGroup.core.waitForSync();
473
+
471
474
  const sharedGroup = node3.node.createGroup();
472
475
 
473
476
  // Account3 does not have permissions over the childGroup being extended
@@ -485,6 +488,9 @@ describe("extend", () => {
485
488
  const testMap = sharedGroup.createMap();
486
489
  testMap.set("name", "Test");
487
490
 
491
+ // Wait for sharedGroup to sync so other nodes can resolve keys via groupSealer
492
+ await sharedGroup.core.waitForSync();
493
+
488
494
  // node1 should be able to access the map because it is admin of the childGroup
489
495
  const testMapOnNode1 = expectMap(
490
496
  await loadCoValueOrFail(node1.node, testMap.id),
@@ -520,6 +526,9 @@ describe("extend", () => {
520
526
  const childGroup = node1.node.createGroup();
521
527
  childGroup.extend(parentGroup, "admin");
522
528
 
529
+ // Wait for childGroup to sync so node3 sees the groupSealer field
530
+ await childGroup.core.waitForSync();
531
+
523
532
  const sharedGroup = node3.node.createGroup();
524
533
 
525
534
  // Account3 does not have permissions over the childGroup being extended
@@ -538,10 +547,17 @@ describe("extend", () => {
538
547
  newParentGroup.addMember(account4OnNode1, "admin");
539
548
  childGroup.extend(newParentGroup);
540
549
 
550
+ // Wait for sharedGroup and childGroup updates to sync
551
+ await sharedGroup.core.waitForSync();
552
+ await childGroup.core.waitForSync();
553
+
541
554
  // Create a map owned by sharedGroup
542
555
  const testMap = sharedGroup.createMap();
543
556
  testMap.set("name", "Test");
544
557
 
558
+ // Wait for map to sync
559
+ await testMap.core.waitForSync();
560
+
545
561
  // Account4 should be able to access the map because it is admin of newParentGroup
546
562
  const testMapOnNode4 = expectMap(
547
563
  await loadCoValueOrFail(node4.node, testMap.id),
@@ -2,7 +2,7 @@ import { describe, expect, test } from "vitest";
2
2
  import { RawCoList } from "../coValues/coList.js";
3
3
  import { RawCoMap } from "../coValues/coMap.js";
4
4
  import { RawCoStream } from "../coValues/coStream.js";
5
- import { RawBinaryCoStream } from "../coValues/coStream.js";
5
+ import { RawBinaryCoStream } from "../coValues/binaryCoStream.js";
6
6
  import type { RawCoValue, RawGroup } from "../exports.js";
7
7
  import type { NewContentMessage } from "../sync.js";
8
8
  import {
@@ -70,6 +70,34 @@ test("Can create a FileStream in a group", () => {
70
70
  expect(stream instanceof RawBinaryCoStream).toEqual(true);
71
71
  });
72
72
 
73
+ test("Group without name has name undefined", () => {
74
+ const node = nodeWithRandomAgentAndSessionID();
75
+ const group = node.createGroup();
76
+ expect(group.name).toBeUndefined();
77
+ });
78
+
79
+ test("Group created with name returns that name", () => {
80
+ const node = nodeWithRandomAgentAndSessionID();
81
+ const group = node.createGroup(undefined, { name: "My Group" });
82
+ expect(group.name).toBe("My Group");
83
+ });
84
+
85
+ test("Group created with empty name has name undefined (meta not set)", () => {
86
+ const node = nodeWithRandomAgentAndSessionID();
87
+ const group = node.createGroup(undefined, { name: "" });
88
+ expect(group.name).toBeUndefined();
89
+ });
90
+
91
+ test("Group with name persists after sync and load", async () => {
92
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
93
+ const group = node1.node.createGroup(undefined, { name: "Synced Group" });
94
+ expect(group.name).toBe("Synced Group");
95
+ await group.core.waitForSync();
96
+
97
+ const loaded = expectGroup(await loadCoValueOrFail(node2.node, group.id));
98
+ expect(loaded.name).toBe("Synced Group");
99
+ });
100
+
73
101
  test("Remove a member from a group where the admin role is inherited", async () => {
74
102
  const { node1, node2, node3 } = await createThreeConnectedNodes(
75
103
  "server",