cojson 0.13.5 → 0.13.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 (88) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE.txt +1 -1
  4. package/dist/PeerState.d.ts +6 -0
  5. package/dist/PeerState.d.ts.map +1 -1
  6. package/dist/PeerState.js +43 -0
  7. package/dist/PeerState.js.map +1 -1
  8. package/dist/coValueCore.d.ts +3 -1
  9. package/dist/coValueCore.d.ts.map +1 -1
  10. package/dist/coValueCore.js +26 -8
  11. package/dist/coValueCore.js.map +1 -1
  12. package/dist/coValueState.d.ts +1 -0
  13. package/dist/coValueState.d.ts.map +1 -1
  14. package/dist/coValueState.js +27 -2
  15. package/dist/coValueState.js.map +1 -1
  16. package/dist/coValues/group.d.ts +1 -0
  17. package/dist/coValues/group.d.ts.map +1 -1
  18. package/dist/coValues/group.js +45 -21
  19. package/dist/coValues/group.js.map +1 -1
  20. package/dist/crypto/crypto.d.ts +2 -2
  21. package/dist/crypto/crypto.d.ts.map +1 -1
  22. package/dist/permissions.d.ts +1 -0
  23. package/dist/permissions.d.ts.map +1 -1
  24. package/dist/permissions.js +19 -3
  25. package/dist/permissions.js.map +1 -1
  26. package/dist/storage/FileSystem.d.ts +2 -2
  27. package/dist/storage/FileSystem.d.ts.map +1 -1
  28. package/dist/sync.d.ts +14 -4
  29. package/dist/sync.d.ts.map +1 -1
  30. package/dist/sync.js +146 -146
  31. package/dist/sync.js.map +1 -1
  32. package/dist/tests/SyncStateManager.test.js +51 -46
  33. package/dist/tests/SyncStateManager.test.js.map +1 -1
  34. package/dist/tests/coValueCore.test.js +66 -4
  35. package/dist/tests/coValueCore.test.js.map +1 -1
  36. package/dist/tests/coValueState.test.js +31 -4
  37. package/dist/tests/coValueState.test.js.map +1 -1
  38. package/dist/tests/group.test.js +135 -2
  39. package/dist/tests/group.test.js.map +1 -1
  40. package/dist/tests/messagesTestUtils.d.ts +13 -0
  41. package/dist/tests/messagesTestUtils.d.ts.map +1 -0
  42. package/dist/tests/messagesTestUtils.js +42 -0
  43. package/dist/tests/messagesTestUtils.js.map +1 -0
  44. package/dist/tests/sync.load.test.d.ts +2 -0
  45. package/dist/tests/sync.load.test.d.ts.map +1 -0
  46. package/dist/tests/sync.load.test.js +249 -0
  47. package/dist/tests/sync.load.test.js.map +1 -0
  48. package/dist/tests/sync.mesh.test.d.ts +2 -0
  49. package/dist/tests/sync.mesh.test.d.ts.map +1 -0
  50. package/dist/tests/sync.mesh.test.js +157 -0
  51. package/dist/tests/sync.mesh.test.js.map +1 -0
  52. package/dist/tests/sync.peerReconciliation.test.d.ts +2 -0
  53. package/dist/tests/sync.peerReconciliation.test.d.ts.map +1 -0
  54. package/dist/tests/sync.peerReconciliation.test.js +166 -0
  55. package/dist/tests/sync.peerReconciliation.test.js.map +1 -0
  56. package/dist/tests/sync.storage.test.d.ts +2 -0
  57. package/dist/tests/sync.storage.test.d.ts.map +1 -0
  58. package/dist/tests/sync.storage.test.js +201 -0
  59. package/dist/tests/sync.storage.test.js.map +1 -0
  60. package/dist/tests/sync.test.js +139 -1048
  61. package/dist/tests/sync.test.js.map +1 -1
  62. package/dist/tests/sync.upload.test.d.ts +2 -0
  63. package/dist/tests/sync.upload.test.d.ts.map +1 -0
  64. package/dist/tests/sync.upload.test.js +156 -0
  65. package/dist/tests/sync.upload.test.js.map +1 -0
  66. package/dist/tests/testUtils.d.ts +76 -33
  67. package/dist/tests/testUtils.d.ts.map +1 -1
  68. package/dist/tests/testUtils.js +154 -47
  69. package/dist/tests/testUtils.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/PeerState.ts +59 -1
  72. package/src/coValueCore.ts +37 -7
  73. package/src/coValueState.ts +34 -3
  74. package/src/coValues/group.ts +83 -45
  75. package/src/permissions.ts +31 -3
  76. package/src/sync.ts +169 -185
  77. package/src/tests/SyncStateManager.test.ts +58 -70
  78. package/src/tests/coValueCore.test.ts +90 -3
  79. package/src/tests/coValueState.test.ts +59 -5
  80. package/src/tests/group.test.ts +250 -2
  81. package/src/tests/messagesTestUtils.ts +75 -0
  82. package/src/tests/sync.load.test.ts +327 -0
  83. package/src/tests/sync.mesh.test.ts +219 -0
  84. package/src/tests/sync.peerReconciliation.test.ts +251 -0
  85. package/src/tests/sync.storage.test.ts +259 -0
  86. package/src/tests/sync.test.ts +170 -1245
  87. package/src/tests/sync.upload.test.ts +202 -0
  88. package/src/tests/testUtils.ts +215 -61
@@ -258,29 +258,38 @@ export class RawGroup<
258
258
  role: Role,
259
259
  ) {
260
260
  if (account === EVERYONE) {
261
- if (!(role === "reader" || role === "writer")) {
261
+ if (!(role === "reader" || role === "writer" || role === "writeOnly")) {
262
262
  throw new Error(
263
- "Can't make everyone something other than reader or writer",
263
+ "Can't make everyone something other than reader, writer or writeOnly",
264
264
  );
265
265
  }
266
-
267
266
  const currentReadKey = this.core.getCurrentReadKey();
268
267
 
269
268
  if (!currentReadKey.secret) {
270
269
  throw new Error("Can't add member without read key secret");
271
270
  }
272
271
 
272
+ const previousRole = this.get(account);
273
+
273
274
  this.set(account, role, "trusting");
274
275
 
275
276
  if (this.get(account) !== role) {
276
277
  throw new Error("Failed to set role");
277
278
  }
278
279
 
279
- this.set(
280
- `${currentReadKey.id}_for_${EVERYONE}`,
281
- currentReadKey.secret,
282
- "trusting",
283
- );
280
+ if (role === "writeOnly") {
281
+ if (previousRole === "reader" || previousRole === "writer") {
282
+ this.rotateReadKey();
283
+ }
284
+
285
+ this.delete(`${currentReadKey.id}_for_${EVERYONE}`);
286
+ } else {
287
+ this.set(
288
+ `${currentReadKey.id}_for_${EVERYONE}`,
289
+ currentReadKey.secret,
290
+ "trusting",
291
+ );
292
+ }
284
293
 
285
294
  return;
286
295
  }
@@ -299,44 +308,18 @@ export class RawGroup<
299
308
  * invite.
300
309
  */
301
310
  if (role === "writeOnly" || role === "writeOnlyInvite") {
302
- const writeKeyForNewMember = this.core.crypto.newRandomKeySecret();
311
+ const previousRole = this.get(memberKey);
303
312
 
304
- this.set(memberKey, role, "trusting");
305
- this.set(`writeKeyFor_${memberKey}`, writeKeyForNewMember.id, "trusting");
306
-
307
- this.storeKeyRevelationForMember(
308
- memberKey,
309
- agent,
310
- writeKeyForNewMember.id,
311
- writeKeyForNewMember.secret,
312
- );
313
-
314
- for (const otherMemberKey of this.getMemberKeys()) {
315
- const memberRole = this.get(otherMemberKey);
316
-
317
- if (
318
- memberRole === "reader" ||
319
- memberRole === "writer" ||
320
- memberRole === "admin" ||
321
- memberRole === "readerInvite" ||
322
- memberRole === "writerInvite" ||
323
- memberRole === "adminInvite"
324
- ) {
325
- const otherMemberAgent = this.core.node
326
- .resolveAccountAgent(
327
- otherMemberKey,
328
- "Expected member agent to be loaded",
329
- )
330
- ._unsafeUnwrap({ withStackTrace: true });
331
-
332
- this.storeKeyRevelationForMember(
333
- otherMemberKey,
334
- otherMemberAgent,
335
- writeKeyForNewMember.id,
336
- writeKeyForNewMember.secret,
337
- );
338
- }
313
+ if (
314
+ previousRole === "reader" ||
315
+ previousRole === "writer" ||
316
+ previousRole === "admin"
317
+ ) {
318
+ this.rotateReadKey();
339
319
  }
320
+
321
+ this.set(memberKey, role, "trusting");
322
+ this.internalCreateWriteOnlyKeyForMember(memberKey, agent);
340
323
  } else {
341
324
  const currentReadKey = this.core.getCurrentReadKey();
342
325
 
@@ -370,6 +353,49 @@ export class RawGroup<
370
353
  }
371
354
  }
372
355
 
356
+ internalCreateWriteOnlyKeyForMember(
357
+ memberKey: RawAccountID | AgentID,
358
+ agent: AgentID,
359
+ ) {
360
+ const writeKeyForNewMember = this.core.crypto.newRandomKeySecret();
361
+
362
+ this.set(`writeKeyFor_${memberKey}`, writeKeyForNewMember.id, "trusting");
363
+
364
+ this.storeKeyRevelationForMember(
365
+ memberKey,
366
+ agent,
367
+ writeKeyForNewMember.id,
368
+ writeKeyForNewMember.secret,
369
+ );
370
+
371
+ for (const otherMemberKey of this.getMemberKeys()) {
372
+ const memberRole = this.get(otherMemberKey);
373
+
374
+ if (
375
+ memberRole === "reader" ||
376
+ memberRole === "writer" ||
377
+ memberRole === "admin" ||
378
+ memberRole === "readerInvite" ||
379
+ memberRole === "writerInvite" ||
380
+ memberRole === "adminInvite"
381
+ ) {
382
+ const otherMemberAgent = this.core.node
383
+ .resolveAccountAgent(
384
+ otherMemberKey,
385
+ "Expected member agent to be loaded",
386
+ )
387
+ ._unsafeUnwrap({ withStackTrace: true });
388
+
389
+ this.storeKeyRevelationForMember(
390
+ otherMemberKey,
391
+ otherMemberAgent,
392
+ writeKeyForNewMember.id,
393
+ writeKeyForNewMember.secret,
394
+ );
395
+ }
396
+ }
397
+ }
398
+
373
399
  private storeKeyRevelationForMember(
374
400
  memberKey: RawAccountID | AgentID,
375
401
  agent: AgentID,
@@ -409,7 +435,19 @@ export class RawGroup<
409
435
  if (this.myRole() === "writeOnly") {
410
436
  const accountId = this.core.node.account.id;
411
437
 
412
- return this.get(`writeKeyFor_${accountId}`) as KeyID;
438
+ const key = this.get(`writeKeyFor_${accountId}`) as KeyID;
439
+
440
+ // When everyone is writeOnly, we need to create a writeOnly key for the current account if missing
441
+ if (!key && this.get("everyone") === "writeOnly") {
442
+ this.internalCreateWriteOnlyKeyForMember(
443
+ accountId,
444
+ this.core.node.account.currentAgentID(),
445
+ );
446
+
447
+ return this.get(`writeKeyFor_${accountId}`) as KeyID;
448
+ }
449
+
450
+ return key;
413
451
  }
414
452
 
415
453
  return this.get("readKey");
@@ -225,6 +225,7 @@ function determineValidTransactionsForGroup(
225
225
  });
226
226
 
227
227
  const memberState: MemberState = {};
228
+ const writeOnlyKeys: Record<RawAccountID | AgentID, KeyID> = {};
228
229
  const validTransactions: ValidTransactionsResult[] = [];
229
230
 
230
231
  const keyRevelations = new Set<string>();
@@ -302,7 +303,8 @@ function determineValidTransactionsForGroup(
302
303
  memberState[transactor] !== "adminInvite" &&
303
304
  memberState[transactor] !== "writerInvite" &&
304
305
  memberState[transactor] !== "readerInvite" &&
305
- memberState[transactor] !== "writeOnlyInvite"
306
+ memberState[transactor] !== "writeOnlyInvite" &&
307
+ !isOwnWriteKeyRevelation(change.key, transactor, writeOnlyKeys)
306
308
  ) {
307
309
  logPermissionError("Only admins can reveal keys");
308
310
  continue;
@@ -370,14 +372,19 @@ function determineValidTransactionsForGroup(
370
372
  validTransactions.push({ txID: { sessionID, txIndex }, tx });
371
373
  continue;
372
374
  } else if (isWriteKeyForMember(change.key)) {
375
+ const memberKey = getAccountOrAgentFromWriteKeyForMember(change.key);
376
+
373
377
  if (
374
378
  memberState[transactor] !== "admin" &&
375
- memberState[transactor] !== "writeOnlyInvite"
379
+ memberState[transactor] !== "writeOnlyInvite" &&
380
+ memberKey !== transactor
376
381
  ) {
377
382
  logPermissionError("Only admins can set writeKeys");
378
383
  continue;
379
384
  }
380
385
 
386
+ writeOnlyKeys[memberKey] = change.value as KeyID;
387
+
381
388
  /**
382
389
  * writeOnlyInvite need to be able to set writeKeys because every new writeOnly
383
390
  * member comes with their own write key.
@@ -422,11 +429,12 @@ function determineValidTransactionsForGroup(
422
429
  !(
423
430
  change.value === "reader" ||
424
431
  change.value === "writer" ||
432
+ change.value === "writeOnly" ||
425
433
  change.value === "revoked"
426
434
  )
427
435
  ) {
428
436
  logPermissionError(
429
- "Everyone can only be set to reader, writer or revoked",
437
+ "Everyone can only be set to reader, writer, writeOnly or revoked",
430
438
  );
431
439
  continue;
432
440
  }
@@ -499,6 +507,12 @@ export function isWriteKeyForMember(
499
507
  return co.startsWith("writeKeyFor_");
500
508
  }
501
509
 
510
+ export function getAccountOrAgentFromWriteKeyForMember(
511
+ co: `writeKeyFor_${RawAccountID | AgentID}`,
512
+ ): RawAccountID | AgentID {
513
+ return co.slice("writeKeyFor_".length) as RawAccountID | AgentID;
514
+ }
515
+
502
516
  export function isKeyForKeyField(co: string): co is `${KeyID}_for_${KeyID}` {
503
517
  return co.startsWith("key_") && co.includes("_for_key");
504
518
  }
@@ -520,3 +534,17 @@ function isParentExtension(key: string): key is `parent_${CoID<RawGroup>}` {
520
534
  function isChildExtension(key: string): key is `child_${CoID<RawGroup>}` {
521
535
  return key.startsWith("child_");
522
536
  }
537
+
538
+ function isOwnWriteKeyRevelation(
539
+ key: `${KeyID}_for_${string}`,
540
+ memberKey: RawAccountID | AgentID,
541
+ writeOnlyKeys: Record<RawAccountID | AgentID, KeyID>,
542
+ ): key is `${KeyID}_for_${RawAccountID | AgentID}` {
543
+ if (Object.keys(writeOnlyKeys).length === 0) {
544
+ return false;
545
+ }
546
+
547
+ const keyID = key.slice(0, key.indexOf("_for_"));
548
+
549
+ return writeOnlyKeys[memberKey] === keyID;
550
+ }