cojson 0.16.4 → 0.16.6

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 (150) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/GarbageCollector.d.ts +12 -0
  4. package/dist/GarbageCollector.d.ts.map +1 -0
  5. package/dist/GarbageCollector.js +37 -0
  6. package/dist/GarbageCollector.js.map +1 -0
  7. package/dist/coValue.d.ts +1 -1
  8. package/dist/coValue.d.ts.map +1 -1
  9. package/dist/coValue.js.map +1 -1
  10. package/dist/coValueContentMessage.d.ts +1 -0
  11. package/dist/coValueContentMessage.d.ts.map +1 -1
  12. package/dist/coValueContentMessage.js +11 -3
  13. package/dist/coValueContentMessage.js.map +1 -1
  14. package/dist/coValueCore/coValueCore.d.ts +8 -11
  15. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  16. package/dist/coValueCore/coValueCore.js +30 -122
  17. package/dist/coValueCore/coValueCore.js.map +1 -1
  18. package/dist/coValueCore/utils.d.ts.map +1 -1
  19. package/dist/coValueCore/utils.js.map +1 -1
  20. package/dist/coValueCore/verifiedState.d.ts +1 -0
  21. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  22. package/dist/coValueCore/verifiedState.js.map +1 -1
  23. package/dist/coValues/coMap.d.ts +3 -3
  24. package/dist/coValues/coPlainText.d.ts +1 -0
  25. package/dist/coValues/coPlainText.d.ts.map +1 -1
  26. package/dist/coValues/coPlainText.js +27 -8
  27. package/dist/coValues/coPlainText.js.map +1 -1
  28. package/dist/coValues/coStream.d.ts +2 -2
  29. package/dist/coValues/group.d.ts +19 -11
  30. package/dist/coValues/group.d.ts.map +1 -1
  31. package/dist/coValues/group.js +221 -59
  32. package/dist/coValues/group.js.map +1 -1
  33. package/dist/config.d.ts +10 -1
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/config.js +16 -1
  36. package/dist/config.js.map +1 -1
  37. package/dist/exports.d.ts +11 -4
  38. package/dist/exports.d.ts.map +1 -1
  39. package/dist/exports.js +8 -3
  40. package/dist/exports.js.map +1 -1
  41. package/dist/ids.d.ts +3 -3
  42. package/dist/ids.d.ts.map +1 -1
  43. package/dist/ids.js.map +1 -1
  44. package/dist/localNode.d.ts +8 -5
  45. package/dist/localNode.d.ts.map +1 -1
  46. package/dist/localNode.js +11 -0
  47. package/dist/localNode.js.map +1 -1
  48. package/dist/queue/LocalTransactionsSyncQueue.js +1 -1
  49. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
  50. package/dist/queue/StoreQueue.d.ts +7 -1
  51. package/dist/queue/StoreQueue.d.ts.map +1 -1
  52. package/dist/queue/StoreQueue.js +35 -13
  53. package/dist/queue/StoreQueue.js.map +1 -1
  54. package/dist/storage/sqlite/client.d.ts +4 -4
  55. package/dist/storage/sqlite/client.d.ts.map +1 -1
  56. package/dist/storage/sqlite/client.js +13 -4
  57. package/dist/storage/sqlite/client.js.map +1 -1
  58. package/dist/storage/sqliteAsync/client.d.ts +3 -3
  59. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  60. package/dist/storage/sqliteAsync/client.js +12 -3
  61. package/dist/storage/sqliteAsync/client.js.map +1 -1
  62. package/dist/storage/storageAsync.d.ts.map +1 -1
  63. package/dist/storage/storageAsync.js +2 -7
  64. package/dist/storage/storageAsync.js.map +1 -1
  65. package/dist/storage/storageSync.d.ts.map +1 -1
  66. package/dist/storage/storageSync.js +2 -7
  67. package/dist/storage/storageSync.js.map +1 -1
  68. package/dist/storage/types.d.ts +2 -2
  69. package/dist/storage/types.d.ts.map +1 -1
  70. package/dist/sync.d.ts.map +1 -1
  71. package/dist/sync.js +17 -3
  72. package/dist/sync.js.map +1 -1
  73. package/dist/tests/GarbageCollector.test.d.ts +2 -0
  74. package/dist/tests/GarbageCollector.test.d.ts.map +1 -0
  75. package/dist/tests/GarbageCollector.test.js +85 -0
  76. package/dist/tests/GarbageCollector.test.js.map +1 -0
  77. package/dist/tests/coPlainText.test.js +142 -4
  78. package/dist/tests/coPlainText.test.js.map +1 -1
  79. package/dist/tests/coStream.test.js +3 -3
  80. package/dist/tests/coStream.test.js.map +1 -1
  81. package/dist/tests/group.inheritance.test.js +195 -0
  82. package/dist/tests/group.inheritance.test.js.map +1 -1
  83. package/dist/tests/group.removeMember.test.js +152 -1
  84. package/dist/tests/group.removeMember.test.js.map +1 -1
  85. package/dist/tests/group.roleOf.test.js +2 -2
  86. package/dist/tests/group.roleOf.test.js.map +1 -1
  87. package/dist/tests/group.test.js +81 -3
  88. package/dist/tests/group.test.js.map +1 -1
  89. package/dist/tests/sync.garbageCollection.test.d.ts +2 -0
  90. package/dist/tests/sync.garbageCollection.test.d.ts.map +1 -0
  91. package/dist/tests/sync.garbageCollection.test.js +133 -0
  92. package/dist/tests/sync.garbageCollection.test.js.map +1 -0
  93. package/dist/tests/sync.load.test.js +6 -3
  94. package/dist/tests/sync.load.test.js.map +1 -1
  95. package/dist/tests/sync.mesh.test.js +48 -34
  96. package/dist/tests/sync.mesh.test.js.map +1 -1
  97. package/dist/tests/sync.storage.test.js +31 -21
  98. package/dist/tests/sync.storage.test.js.map +1 -1
  99. package/dist/tests/sync.storageAsync.test.js +76 -29
  100. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  101. package/dist/tests/testStorage.d.ts +1 -0
  102. package/dist/tests/testStorage.d.ts.map +1 -1
  103. package/dist/tests/testStorage.js +1 -1
  104. package/dist/tests/testStorage.js.map +1 -1
  105. package/dist/tests/testUtils.d.ts +2 -0
  106. package/dist/tests/testUtils.d.ts.map +1 -1
  107. package/dist/tests/testUtils.js +6 -0
  108. package/dist/tests/testUtils.js.map +1 -1
  109. package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts +2 -2
  110. package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts.map +1 -1
  111. package/dist/typeUtils/expectGroup.d.ts.map +1 -1
  112. package/dist/typeUtils/expectGroup.js +6 -5
  113. package/dist/typeUtils/expectGroup.js.map +1 -1
  114. package/package.json +1 -1
  115. package/src/GarbageCollector.ts +48 -0
  116. package/src/coValue.ts +1 -4
  117. package/src/coValueContentMessage.ts +16 -3
  118. package/src/coValueCore/coValueCore.ts +50 -198
  119. package/src/coValueCore/utils.ts +1 -0
  120. package/src/coValueCore/verifiedState.ts +1 -0
  121. package/src/coValues/coPlainText.ts +40 -8
  122. package/src/coValues/group.ts +310 -91
  123. package/src/config.ts +20 -1
  124. package/src/exports.ts +13 -5
  125. package/src/ids.ts +3 -3
  126. package/src/localNode.ts +22 -8
  127. package/src/queue/LocalTransactionsSyncQueue.ts +1 -1
  128. package/src/queue/StoreQueue.ts +45 -12
  129. package/src/storage/sqlite/client.ts +24 -10
  130. package/src/storage/sqliteAsync/client.ts +26 -5
  131. package/src/storage/storageAsync.ts +5 -9
  132. package/src/storage/storageSync.ts +2 -9
  133. package/src/storage/types.ts +7 -4
  134. package/src/sync.ts +19 -3
  135. package/src/tests/GarbageCollector.test.ts +127 -0
  136. package/src/tests/coPlainText.test.ts +176 -4
  137. package/src/tests/coStream.test.ts +7 -3
  138. package/src/tests/group.inheritance.test.ts +279 -0
  139. package/src/tests/group.removeMember.test.ts +244 -1
  140. package/src/tests/group.roleOf.test.ts +2 -2
  141. package/src/tests/group.test.ts +105 -5
  142. package/src/tests/sync.garbageCollection.test.ts +178 -0
  143. package/src/tests/sync.load.test.ts +6 -3
  144. package/src/tests/sync.mesh.test.ts +49 -34
  145. package/src/tests/sync.storage.test.ts +31 -21
  146. package/src/tests/sync.storageAsync.test.ts +81 -29
  147. package/src/tests/testStorage.ts +11 -3
  148. package/src/tests/testUtils.ts +9 -1
  149. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +2 -2
  150. package/src/typeUtils/expectGroup.ts +8 -5
@@ -1,10 +1,21 @@
1
- import { afterEach, expect, test, vi } from "vitest";
1
+ import { afterEach, beforeEach, expect, test, vi } from "vitest";
2
2
  import { expectPlainText } from "../coValue.js";
3
+ import { setMaxRecommendedTxSize } from "../config.js";
3
4
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
4
- import { nodeWithRandomAgentAndSessionID } from "./testUtils.js";
5
+ import {
6
+ SyncMessagesLog,
7
+ loadCoValueOrFail,
8
+ nodeWithRandomAgentAndSessionID,
9
+ setupTestNode,
10
+ } from "./testUtils.js";
5
11
 
6
12
  const Crypto = await WasmCrypto.create();
7
13
 
14
+ beforeEach(() => {
15
+ setMaxRecommendedTxSize(100 * 1024);
16
+ SyncMessagesLog.clear();
17
+ });
18
+
8
19
  afterEach(() => void vi.unstubAllGlobals());
9
20
 
10
21
  test("Empty CoPlainText works", () => {
@@ -185,8 +196,8 @@ test("insertBefore and insertAfter work as expected", () => {
185
196
  expect(content.toString()).toEqual("hey");
186
197
 
187
198
  // Insert '!' at start
188
- content.insertBefore(0, "!", "trusting"); // "!hey"
189
- expect(content.toString()).toEqual("!hey");
199
+ content.insertBefore(0, "!?", "trusting"); // "!?hey"
200
+ expect(content.toString()).toEqual("!?hey");
190
201
  });
191
202
 
192
203
  test("Can delete a single grapheme", () => {
@@ -286,3 +297,164 @@ test("Splits into and from grapheme string arrays", () => {
286
297
  const text = content.fromGraphemes(graphemes);
287
298
  expect(text).toEqual("👋 안녕!");
288
299
  });
300
+
301
+ test("chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE", async () => {
302
+ setMaxRecommendedTxSize(5);
303
+
304
+ const client = setupTestNode();
305
+ const { storage } = client.addStorage();
306
+
307
+ const coValue = client.node.createCoValue({
308
+ type: "coplaintext",
309
+ ruleset: { type: "unsafeAllowAll" },
310
+ meta: null,
311
+ ...Crypto.createdNowUnique(),
312
+ });
313
+
314
+ const content = expectPlainText(coValue.getCurrentContent());
315
+
316
+ content.insertAfter(
317
+ content.entries().length,
318
+ "I'm writing you to test that coplaintext",
319
+ "trusting",
320
+ );
321
+ content.insertAfter(
322
+ content.entries().length,
323
+ " chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE.",
324
+ "trusting",
325
+ );
326
+ content.insertAfter(
327
+ content.entries().length,
328
+ "This is required because when a user paste 1Mb of text, we can split it in multiple websocket messages.",
329
+ "trusting",
330
+ );
331
+
332
+ content.insertBefore(0, "Dear reader,\n", "trusting");
333
+
334
+ expect(content.toString()).toMatchInlineSnapshot(
335
+ `"Dear reader,\nI'm writing you to test that coplaintext chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE.This is required because when a user paste 1Mb of text, we can split it in multiple websocket messages."`,
336
+ );
337
+
338
+ await coValue.waitForSync();
339
+
340
+ client.restart();
341
+ client.addStorage({
342
+ storage,
343
+ });
344
+
345
+ const loaded = await loadCoValueOrFail(client.node, content.id);
346
+ await loaded.core.waitForSync();
347
+
348
+ expect(loaded.toString()).toEqual(content.toString());
349
+
350
+ expect(
351
+ SyncMessagesLog.getMessages({
352
+ CoPlainText: coValue,
353
+ }),
354
+ ).toMatchInlineSnapshot(`
355
+ [
356
+ "client -> storage | CONTENT CoPlainText header: true new: ",
357
+ "client -> storage | CONTENT CoPlainText header: false new: After: 0 New: 1",
358
+ "client -> storage | CONTENT CoPlainText header: false new: After: 1 New: 1",
359
+ "client -> storage | CONTENT CoPlainText header: false new: After: 2 New: 1",
360
+ "client -> storage | CONTENT CoPlainText header: false new: After: 3 New: 1",
361
+ "client -> storage | CONTENT CoPlainText header: false new: After: 4 New: 1",
362
+ "client -> storage | CONTENT CoPlainText header: false new: After: 5 New: 1",
363
+ "client -> storage | CONTENT CoPlainText header: false new: After: 6 New: 1",
364
+ "client -> storage | CONTENT CoPlainText header: false new: After: 7 New: 1",
365
+ "client -> storage | CONTENT CoPlainText header: false new: After: 8 New: 1",
366
+ "client -> storage | CONTENT CoPlainText header: false new: After: 9 New: 1",
367
+ "client -> storage | CONTENT CoPlainText header: false new: After: 10 New: 1",
368
+ "client -> storage | CONTENT CoPlainText header: false new: After: 11 New: 1",
369
+ "client -> storage | CONTENT CoPlainText header: false new: After: 12 New: 1",
370
+ "client -> storage | CONTENT CoPlainText header: false new: After: 13 New: 1",
371
+ "client -> storage | CONTENT CoPlainText header: false new: After: 14 New: 1",
372
+ "client -> storage | CONTENT CoPlainText header: false new: After: 15 New: 1",
373
+ "client -> storage | CONTENT CoPlainText header: false new: After: 16 New: 1",
374
+ "client -> storage | CONTENT CoPlainText header: false new: After: 17 New: 1",
375
+ "client -> storage | CONTENT CoPlainText header: false new: After: 18 New: 1",
376
+ "client -> storage | CONTENT CoPlainText header: false new: After: 19 New: 1",
377
+ "client -> storage | CONTENT CoPlainText header: false new: After: 20 New: 1",
378
+ "client -> storage | CONTENT CoPlainText header: false new: After: 21 New: 1",
379
+ "client -> storage | CONTENT CoPlainText header: false new: After: 22 New: 1",
380
+ "client -> storage | CONTENT CoPlainText header: false new: After: 23 New: 1",
381
+ "client -> storage | CONTENT CoPlainText header: false new: After: 24 New: 1",
382
+ "client -> storage | CONTENT CoPlainText header: false new: After: 25 New: 1",
383
+ "client -> storage | CONTENT CoPlainText header: false new: After: 26 New: 1",
384
+ "client -> storage | CONTENT CoPlainText header: false new: After: 27 New: 1",
385
+ "client -> storage | CONTENT CoPlainText header: false new: After: 28 New: 1",
386
+ "client -> storage | CONTENT CoPlainText header: false new: After: 29 New: 1",
387
+ "client -> storage | CONTENT CoPlainText header: false new: After: 30 New: 1",
388
+ "client -> storage | CONTENT CoPlainText header: false new: After: 31 New: 1",
389
+ "client -> storage | CONTENT CoPlainText header: false new: After: 32 New: 1",
390
+ "client -> storage | CONTENT CoPlainText header: false new: After: 33 New: 1",
391
+ "client -> storage | CONTENT CoPlainText header: false new: After: 34 New: 1",
392
+ "client -> storage | CONTENT CoPlainText header: false new: After: 35 New: 1",
393
+ "client -> storage | CONTENT CoPlainText header: false new: After: 36 New: 1",
394
+ "client -> storage | CONTENT CoPlainText header: false new: After: 37 New: 1",
395
+ "client -> storage | CONTENT CoPlainText header: false new: After: 38 New: 1",
396
+ "client -> storage | CONTENT CoPlainText header: false new: After: 39 New: 1",
397
+ "client -> storage | CONTENT CoPlainText header: false new: After: 40 New: 1",
398
+ "client -> storage | CONTENT CoPlainText header: false new: After: 41 New: 1",
399
+ "client -> storage | CONTENT CoPlainText header: false new: After: 42 New: 1",
400
+ "client -> storage | CONTENT CoPlainText header: false new: After: 43 New: 1",
401
+ "client -> storage | CONTENT CoPlainText header: false new: After: 44 New: 1",
402
+ "client -> storage | CONTENT CoPlainText header: false new: After: 45 New: 1",
403
+ "client -> storage | CONTENT CoPlainText header: false new: After: 46 New: 1",
404
+ "client -> storage | CONTENT CoPlainText header: false new: After: 47 New: 1",
405
+ "client -> storage | CONTENT CoPlainText header: false new: After: 48 New: 1",
406
+ "client -> storage | CONTENT CoPlainText header: false new: After: 49 New: 1",
407
+ "client -> storage | LOAD CoPlainText sessions: empty",
408
+ "storage -> client | CONTENT CoPlainText header: true new: After: 0 New: 1 expectContentUntil: header/50",
409
+ "storage -> client | CONTENT CoPlainText header: true new: After: 1 New: 1",
410
+ "storage -> client | CONTENT CoPlainText header: true new: After: 2 New: 1",
411
+ "storage -> client | CONTENT CoPlainText header: true new: After: 3 New: 1",
412
+ "storage -> client | CONTENT CoPlainText header: true new: After: 4 New: 1",
413
+ "storage -> client | CONTENT CoPlainText header: true new: After: 5 New: 1",
414
+ "storage -> client | CONTENT CoPlainText header: true new: After: 6 New: 1",
415
+ "storage -> client | CONTENT CoPlainText header: true new: After: 7 New: 1",
416
+ "storage -> client | CONTENT CoPlainText header: true new: After: 8 New: 1",
417
+ "storage -> client | CONTENT CoPlainText header: true new: After: 9 New: 1",
418
+ "storage -> client | CONTENT CoPlainText header: true new: After: 10 New: 1",
419
+ "storage -> client | CONTENT CoPlainText header: true new: After: 11 New: 1",
420
+ "storage -> client | CONTENT CoPlainText header: true new: After: 12 New: 1",
421
+ "storage -> client | CONTENT CoPlainText header: true new: After: 13 New: 1",
422
+ "storage -> client | CONTENT CoPlainText header: true new: After: 14 New: 1",
423
+ "storage -> client | CONTENT CoPlainText header: true new: After: 15 New: 1",
424
+ "storage -> client | CONTENT CoPlainText header: true new: After: 16 New: 1",
425
+ "storage -> client | CONTENT CoPlainText header: true new: After: 17 New: 1",
426
+ "storage -> client | CONTENT CoPlainText header: true new: After: 18 New: 1",
427
+ "storage -> client | CONTENT CoPlainText header: true new: After: 19 New: 1",
428
+ "storage -> client | CONTENT CoPlainText header: true new: After: 20 New: 1",
429
+ "storage -> client | CONTENT CoPlainText header: true new: After: 21 New: 1",
430
+ "storage -> client | CONTENT CoPlainText header: true new: After: 22 New: 1",
431
+ "storage -> client | CONTENT CoPlainText header: true new: After: 23 New: 1",
432
+ "storage -> client | CONTENT CoPlainText header: true new: After: 24 New: 1",
433
+ "storage -> client | CONTENT CoPlainText header: true new: After: 25 New: 1",
434
+ "storage -> client | CONTENT CoPlainText header: true new: After: 26 New: 1",
435
+ "storage -> client | CONTENT CoPlainText header: true new: After: 27 New: 1",
436
+ "storage -> client | CONTENT CoPlainText header: true new: After: 28 New: 1",
437
+ "storage -> client | CONTENT CoPlainText header: true new: After: 29 New: 1",
438
+ "storage -> client | CONTENT CoPlainText header: true new: After: 30 New: 1",
439
+ "storage -> client | CONTENT CoPlainText header: true new: After: 31 New: 1",
440
+ "storage -> client | CONTENT CoPlainText header: true new: After: 32 New: 1",
441
+ "storage -> client | CONTENT CoPlainText header: true new: After: 33 New: 1",
442
+ "storage -> client | CONTENT CoPlainText header: true new: After: 34 New: 1",
443
+ "storage -> client | CONTENT CoPlainText header: true new: After: 35 New: 1",
444
+ "storage -> client | CONTENT CoPlainText header: true new: After: 36 New: 1",
445
+ "storage -> client | CONTENT CoPlainText header: true new: After: 37 New: 1",
446
+ "storage -> client | CONTENT CoPlainText header: true new: After: 38 New: 1",
447
+ "storage -> client | CONTENT CoPlainText header: true new: After: 39 New: 1",
448
+ "storage -> client | CONTENT CoPlainText header: true new: After: 40 New: 1",
449
+ "storage -> client | CONTENT CoPlainText header: true new: After: 41 New: 1",
450
+ "storage -> client | CONTENT CoPlainText header: true new: After: 42 New: 1",
451
+ "storage -> client | CONTENT CoPlainText header: true new: After: 43 New: 1",
452
+ "storage -> client | CONTENT CoPlainText header: true new: After: 44 New: 1",
453
+ "storage -> client | CONTENT CoPlainText header: true new: After: 45 New: 1",
454
+ "storage -> client | CONTENT CoPlainText header: true new: After: 46 New: 1",
455
+ "storage -> client | CONTENT CoPlainText header: true new: After: 47 New: 1",
456
+ "storage -> client | CONTENT CoPlainText header: true new: After: 48 New: 1",
457
+ "storage -> client | CONTENT CoPlainText header: true new: After: 49 New: 1",
458
+ ]
459
+ `);
460
+ });
@@ -6,7 +6,7 @@ import {
6
6
  RawBinaryCoStream,
7
7
  RawCoStreamView,
8
8
  } from "../coValues/coStream.js";
9
- import { MAX_RECOMMENDED_TX_SIZE } from "../config.js";
9
+ import { TRANSACTION_CONFIG } from "../config.js";
10
10
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
11
11
  import { SessionID } from "../ids.js";
12
12
  import {
@@ -153,7 +153,9 @@ test("When adding large transactions (small fraction of MAX_RECOMMENDED_TX_SIZE)
153
153
  );
154
154
 
155
155
  for (let i = 0; i < 10; i++) {
156
- const chunk = new Uint8Array(MAX_RECOMMENDED_TX_SIZE / 3 + 100);
156
+ const chunk = new Uint8Array(
157
+ TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE / 3 + 100,
158
+ );
157
159
 
158
160
  content.pushBinaryStreamChunk(chunk, "trusting");
159
161
  }
@@ -226,7 +228,9 @@ test("When adding large transactions (bigger than MAX_RECOMMENDED_TX_SIZE), we s
226
228
  "trusting",
227
229
  );
228
230
 
229
- const chunk = new Uint8Array(MAX_RECOMMENDED_TX_SIZE + 100);
231
+ const chunk = new Uint8Array(
232
+ TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE + 100,
233
+ );
230
234
 
231
235
  for (let i = 0; i < 3; i++) {
232
236
  content.pushBinaryStreamChunk(chunk, "trusting");
@@ -1,4 +1,6 @@
1
1
  import { beforeEach, describe, expect, test } from "vitest";
2
+ import type { CoID, RawGroup } from "../exports";
3
+ import { NewContentMessage } from "../sync";
2
4
  import {
3
5
  SyncMessagesLog,
4
6
  createThreeConnectedNodes,
@@ -96,6 +98,32 @@ describe("extend", () => {
96
98
  expect(mapOnNode2.get("test")).toEqual("Written from node2");
97
99
  });
98
100
 
101
+ test("inherited everyone roles should work correctly", async () => {
102
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
103
+
104
+ const group = node1.node.createGroup();
105
+ group.addMember("everyone", "writer");
106
+
107
+ const childGroup = node1.node.createGroup();
108
+ childGroup.extend(group);
109
+
110
+ expect(childGroup.roleOf("everyone")).toEqual("writer");
111
+
112
+ const map = childGroup.createMap();
113
+ map.set("test", "Written from the admin");
114
+
115
+ await map.core.waitForSync();
116
+
117
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
118
+
119
+ // The writer role should be able to see the edits from the admin
120
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
121
+
122
+ mapOnNode2.set("hello", "from node 2");
123
+
124
+ expect(mapOnNode2.get("hello")).toEqual("from node 2");
125
+ });
126
+
99
127
  test("a user should be able to extend a group when his role on the parent group is writeOnly", async () => {
100
128
  const { node1, node2 } = await createTwoConnectedNodes("server", "server");
101
129
 
@@ -315,6 +343,257 @@ describe("extend", () => {
315
343
 
316
344
  expect(childGroup.roleOf(alice.id)).toBe("writer");
317
345
  });
346
+
347
+ test("should be possible to extend a group after getting revoked from the parent group", async () => {
348
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
349
+ "server",
350
+ "server",
351
+ "server",
352
+ );
353
+
354
+ const parentGroup = node1.node.createGroup();
355
+
356
+ const alice = await loadCoValueOrFail(node1.node, node3.accountID);
357
+ const bob = await loadCoValueOrFail(node1.node, node2.accountID);
358
+ parentGroup.addMember(alice, "writer");
359
+ parentGroup.addMember(bob, "reader");
360
+ parentGroup.removeMember(bob);
361
+
362
+ const parentGroupOnNode2 = await loadCoValueOrFail(
363
+ node2.node,
364
+ parentGroup.id,
365
+ );
366
+
367
+ const childGroup = node2.node.createGroup();
368
+ childGroup.extend(parentGroupOnNode2);
369
+
370
+ expect(childGroup.roleOf(alice.id)).toBe("writer");
371
+ });
372
+
373
+ test("should be possible to extend when access is everyone reader and the account is revoked from the parent group", async () => {
374
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
375
+ "server",
376
+ "server",
377
+ "server",
378
+ );
379
+
380
+ const parentGroup = node1.node.createGroup();
381
+ parentGroup.addMember("everyone", "reader");
382
+ const alice = await loadCoValueOrFail(node1.node, node3.accountID);
383
+ const bob = await loadCoValueOrFail(node1.node, node2.accountID);
384
+ parentGroup.addMember(alice, "writer");
385
+ parentGroup.addMember(bob, "reader");
386
+ parentGroup.removeMember(bob);
387
+
388
+ const parentGroupOnNode2 = await loadCoValueOrFail(
389
+ node2.node,
390
+ parentGroup.id,
391
+ );
392
+
393
+ const childGroup = node2.node.createGroup();
394
+ childGroup.extend(parentGroupOnNode2);
395
+
396
+ expect(childGroup.roleOf(alice.id)).toBe("writer");
397
+ });
398
+
399
+ test("should be able to extend when the last read key is healed", async () => {
400
+ const clientWithAccess = setupTestNode({
401
+ secret:
402
+ "sealerSecret_zBTPp7U58Fzq9o7EvJpu4KEziepi8QVf2Xaxuy5xmmXFx/signerSecret_z62DuviZdXCjz4EZWofvr9vaLYFXDeTaC9KWhoQiQjzKk",
403
+ connected: true,
404
+ });
405
+ const clientWithoutAccess = setupTestNode({
406
+ connected: true,
407
+ });
408
+
409
+ const brokenGroupContent = {
410
+ action: "content",
411
+ id: "co_zW7F36Nnop9A7Er4gUzBcUXnZCK",
412
+ header: {
413
+ type: "comap",
414
+ ruleset: {
415
+ type: "group",
416
+ initialAdmin:
417
+ "sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv",
418
+ },
419
+ meta: null,
420
+ createdAt: "2025-08-06T10:14:39.617Z",
421
+ uniqueness: "z3LJjnuPiPJaf5Qb9A",
422
+ },
423
+ priority: 0,
424
+ new: {
425
+ "sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv_session_zYLsz2CiW9pW":
426
+ {
427
+ after: 0,
428
+ newTransactions: [
429
+ {
430
+ privacy: "trusting",
431
+ madeAt: 1754475279619,
432
+ changes:
433
+ '[{"key":"sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv","op":"set","value":"admin"}]',
434
+ },
435
+ {
436
+ privacy: "trusting",
437
+ madeAt: 1754475279621,
438
+ changes:
439
+ '[{"key":"key_z5CVahfMkEWPj1B3zH_for_sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv","op":"set","value":"sealed_UCg4UkytXF-W8PaIvaDffO3pZ3d9hdXUuNkQQEikPTAuOD9us92Pqb5Vgu7lx1Fpb0X8V5BJ2yxz6_D5WOzK3qjWBSsc7J1xDJA=="}]',
440
+ },
441
+ {
442
+ privacy: "trusting",
443
+ madeAt: 1754475279621,
444
+ changes:
445
+ '[{"key":"readKey","op":"set","value":"key_z5CVahfMkEWPj1B3zH"}]',
446
+ },
447
+ {
448
+ privacy: "trusting",
449
+ madeAt: 1754475279622,
450
+ changes: '[{"key":"everyone","op":"set","value":"reader"}]',
451
+ },
452
+ {
453
+ privacy: "trusting",
454
+ madeAt: 1754475279623,
455
+ changes:
456
+ '[{"key":"key_z5CVahfMkEWPj1B3zH_for_everyone","op":"set","value":"keySecret_z9U9gzkahQXCxDoSw7isiUnbobXwuLdcSkL9Ci6ZEEkaL"}]',
457
+ },
458
+ {
459
+ privacy: "trusting",
460
+ madeAt: 1754475279623,
461
+ changes:
462
+ '[{"key":"key_z4Fi7hZNBx7XoVAKkP_for_sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv","op":"set","value":"sealed_UuCBBfZkTnRTrGraqWWlzm9JE-VFduhsfu7WaZjpCbJYOTXpPhSNOnzGeS8qVuIsG6dORbME22lc5ltLxPjRqofQdDCNGQehCeQ=="}]',
463
+ },
464
+ {
465
+ privacy: "trusting",
466
+ madeAt: 1754475279624,
467
+ changes:
468
+ '[{"key":"key_z5CVahfMkEWPj1B3zH_for_key_z4Fi7hZNBx7XoVAKkP","op":"set","value":"encrypted_USTrBuobwTCORwy5yHxy4sFZ7swfrafP6k5ZwcTf76f0MBu9Ie-JmsX3mNXad4mluI47gvGXzi8I_"}]',
469
+ },
470
+ {
471
+ privacy: "trusting",
472
+ madeAt: 1754475279624,
473
+ changes:
474
+ '[{"key":"readKey","op":"set","value":"key_z4Fi7hZNBx7XoVAKkP"}]',
475
+ },
476
+ ],
477
+ lastSignature:
478
+ "signature_z3tsE7U1JaeNeUmZ4EY3Xq5uQ9jq9jDi6Rkhdt7T7b7z4NCnpMgB4bo8TwLXYVCrRdBm6PoyyPdK8fYFzHJUh5EzA",
479
+ },
480
+ },
481
+ } as unknown as NewContentMessage;
482
+
483
+ clientWithAccess.node.syncManager.handleNewContent(
484
+ brokenGroupContent,
485
+ "import",
486
+ );
487
+
488
+ // Load the CoValue to recover the key_for_everyone
489
+ await loadCoValueOrFail(
490
+ clientWithAccess.node,
491
+ brokenGroupContent.id as CoID<RawGroup>,
492
+ );
493
+
494
+ const group = await loadCoValueOrFail(
495
+ clientWithoutAccess.node,
496
+ brokenGroupContent.id as CoID<RawGroup>,
497
+ );
498
+ const childGroup = clientWithoutAccess.node.createGroup();
499
+ childGroup.extend(group);
500
+
501
+ expect(childGroup.getParentGroups()).toEqual([group]);
502
+ });
503
+
504
+ test("should be able to extend when the last read key is missing", async () => {
505
+ const clientWithoutAccess = setupTestNode({
506
+ connected: true,
507
+ });
508
+
509
+ const brokenGroupContent = {
510
+ action: "content",
511
+ id: "co_zW7F36Nnop9A7Er4gUzBcUXnZCK",
512
+ header: {
513
+ type: "comap",
514
+ ruleset: {
515
+ type: "group",
516
+ initialAdmin:
517
+ "sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv",
518
+ },
519
+ meta: null,
520
+ createdAt: "2025-08-06T10:14:39.617Z",
521
+ uniqueness: "z3LJjnuPiPJaf5Qb9A",
522
+ },
523
+ priority: 0,
524
+ new: {
525
+ "sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv_session_zYLsz2CiW9pW":
526
+ {
527
+ after: 0,
528
+ newTransactions: [
529
+ {
530
+ privacy: "trusting",
531
+ madeAt: 1754475279619,
532
+ changes:
533
+ '[{"key":"sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv","op":"set","value":"admin"}]',
534
+ },
535
+ {
536
+ privacy: "trusting",
537
+ madeAt: 1754475279621,
538
+ changes:
539
+ '[{"key":"key_z5CVahfMkEWPj1B3zH_for_sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv","op":"set","value":"sealed_UCg4UkytXF-W8PaIvaDffO3pZ3d9hdXUuNkQQEikPTAuOD9us92Pqb5Vgu7lx1Fpb0X8V5BJ2yxz6_D5WOzK3qjWBSsc7J1xDJA=="}]',
540
+ },
541
+ {
542
+ privacy: "trusting",
543
+ madeAt: 1754475279621,
544
+ changes:
545
+ '[{"key":"readKey","op":"set","value":"key_z5CVahfMkEWPj1B3zH"}]',
546
+ },
547
+ {
548
+ privacy: "trusting",
549
+ madeAt: 1754475279622,
550
+ changes: '[{"key":"everyone","op":"set","value":"reader"}]',
551
+ },
552
+ {
553
+ privacy: "trusting",
554
+ madeAt: 1754475279623,
555
+ changes:
556
+ '[{"key":"key_z5CVahfMkEWPj1B3zH_for_everyone","op":"set","value":"keySecret_z9U9gzkahQXCxDoSw7isiUnbobXwuLdcSkL9Ci6ZEEkaL"}]',
557
+ },
558
+ {
559
+ privacy: "trusting",
560
+ madeAt: 1754475279623,
561
+ changes:
562
+ '[{"key":"key_z4Fi7hZNBx7XoVAKkP_for_sealer_z12QDazYB3ygPZtBV7sMm7iYKMRnNZ6Aaj1dfLXR7LSBm/signer_z2AskZQbc82qxo7iA3oiXoNExHLsAEXC2pHbwJzRnATWv","op":"set","value":"sealed_UuCBBfZkTnRTrGraqWWlzm9JE-VFduhsfu7WaZjpCbJYOTXpPhSNOnzGeS8qVuIsG6dORbME22lc5ltLxPjRqofQdDCNGQehCeQ=="}]',
563
+ },
564
+ {
565
+ privacy: "trusting",
566
+ madeAt: 1754475279624,
567
+ changes:
568
+ '[{"key":"key_z5CVahfMkEWPj1B3zH_for_key_z4Fi7hZNBx7XoVAKkP","op":"set","value":"encrypted_USTrBuobwTCORwy5yHxy4sFZ7swfrafP6k5ZwcTf76f0MBu9Ie-JmsX3mNXad4mluI47gvGXzi8I_"}]',
569
+ },
570
+ {
571
+ privacy: "trusting",
572
+ madeAt: 1754475279624,
573
+ changes:
574
+ '[{"key":"readKey","op":"set","value":"key_z4Fi7hZNBx7XoVAKkP"}]',
575
+ },
576
+ ],
577
+ lastSignature:
578
+ "signature_z3tsE7U1JaeNeUmZ4EY3Xq5uQ9jq9jDi6Rkhdt7T7b7z4NCnpMgB4bo8TwLXYVCrRdBm6PoyyPdK8fYFzHJUh5EzA",
579
+ },
580
+ },
581
+ } as unknown as NewContentMessage;
582
+
583
+ clientWithoutAccess.node.syncManager.handleNewContent(
584
+ brokenGroupContent,
585
+ "import",
586
+ );
587
+
588
+ const group = await loadCoValueOrFail(
589
+ clientWithoutAccess.node,
590
+ brokenGroupContent.id as CoID<RawGroup>,
591
+ );
592
+ const childGroup = clientWithoutAccess.node.createGroup();
593
+ childGroup.extend(group);
594
+
595
+ expect(childGroup.getParentGroups()).toEqual([group]);
596
+ });
318
597
  });
319
598
 
320
599
  describe("unextend", () => {