cojson 0.18.29 → 0.18.30

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 (144) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +9 -0
  3. package/dist/PeerState.d.ts +23 -14
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +74 -23
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/SyncStateManager.d.ts +3 -3
  8. package/dist/SyncStateManager.d.ts.map +1 -1
  9. package/dist/SyncStateManager.js +18 -44
  10. package/dist/SyncStateManager.js.map +1 -1
  11. package/dist/coValueContentMessage.d.ts.map +1 -1
  12. package/dist/coValueContentMessage.js +2 -1
  13. package/dist/coValueContentMessage.js.map +1 -1
  14. package/dist/coValueCore/PeerKnownState.d.ts +21 -0
  15. package/dist/coValueCore/PeerKnownState.d.ts.map +1 -0
  16. package/dist/coValueCore/PeerKnownState.js +52 -0
  17. package/dist/coValueCore/PeerKnownState.js.map +1 -0
  18. package/dist/coValueCore/coValueCore.d.ts +39 -8
  19. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  20. package/dist/coValueCore/coValueCore.js +139 -40
  21. package/dist/coValueCore/coValueCore.js.map +1 -1
  22. package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts.map +1 -1
  23. package/dist/coValueCore/decryptTransactionChangesAndMeta.js +0 -5
  24. package/dist/coValueCore/decryptTransactionChangesAndMeta.js.map +1 -1
  25. package/dist/coValueCore/verifiedState.d.ts +0 -14
  26. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  27. package/dist/coValueCore/verifiedState.js +2 -32
  28. package/dist/coValueCore/verifiedState.js.map +1 -1
  29. package/dist/coValues/coList.d.ts +3 -4
  30. package/dist/coValues/coList.d.ts.map +1 -1
  31. package/dist/coValues/coList.js +4 -4
  32. package/dist/coValues/coList.js.map +1 -1
  33. package/dist/coValues/coMap.d.ts +3 -4
  34. package/dist/coValues/coMap.d.ts.map +1 -1
  35. package/dist/coValues/coMap.js +5 -4
  36. package/dist/coValues/coMap.js.map +1 -1
  37. package/dist/coValues/coStream.d.ts +3 -3
  38. package/dist/coValues/coStream.d.ts.map +1 -1
  39. package/dist/coValues/coStream.js +3 -4
  40. package/dist/coValues/coStream.js.map +1 -1
  41. package/dist/coValues/group.d.ts +3 -3
  42. package/dist/coValues/group.d.ts.map +1 -1
  43. package/dist/coValues/group.js +74 -52
  44. package/dist/coValues/group.js.map +1 -1
  45. package/dist/exports.d.ts +2 -2
  46. package/dist/exports.d.ts.map +1 -1
  47. package/dist/exports.js +2 -2
  48. package/dist/exports.js.map +1 -1
  49. package/dist/localNode.d.ts.map +1 -1
  50. package/dist/localNode.js +7 -5
  51. package/dist/localNode.js.map +1 -1
  52. package/dist/permissions.d.ts +5 -1
  53. package/dist/permissions.d.ts.map +1 -1
  54. package/dist/permissions.js +173 -109
  55. package/dist/permissions.js.map +1 -1
  56. package/dist/sync.d.ts.map +1 -1
  57. package/dist/sync.js +33 -44
  58. package/dist/sync.js.map +1 -1
  59. package/dist/tests/PeerKnownState.test.d.ts +2 -0
  60. package/dist/tests/PeerKnownState.test.d.ts.map +1 -0
  61. package/dist/tests/PeerKnownState.test.js +342 -0
  62. package/dist/tests/PeerKnownState.test.js.map +1 -0
  63. package/dist/tests/PeerState.test.js +17 -16
  64. package/dist/tests/PeerState.test.js.map +1 -1
  65. package/dist/tests/StorageApiAsync.test.js +12 -12
  66. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  67. package/dist/tests/StorageApiSync.test.js +11 -11
  68. package/dist/tests/StorageApiSync.test.js.map +1 -1
  69. package/dist/tests/SyncStateManager.test.js +16 -21
  70. package/dist/tests/SyncStateManager.test.js.map +1 -1
  71. package/dist/tests/coValueCore.dependencies.test.js +59 -0
  72. package/dist/tests/coValueCore.dependencies.test.js.map +1 -1
  73. package/dist/tests/coValueCore.test.js +41 -21
  74. package/dist/tests/coValueCore.test.js.map +1 -1
  75. package/dist/tests/group.addMember.test.js +266 -219
  76. package/dist/tests/group.addMember.test.js.map +1 -1
  77. package/dist/tests/group.inheritance.test.js +12 -0
  78. package/dist/tests/group.inheritance.test.js.map +1 -1
  79. package/dist/tests/group.invite.test.js +77 -0
  80. package/dist/tests/group.invite.test.js.map +1 -1
  81. package/dist/tests/group.removeMember.test.js +64 -7
  82. package/dist/tests/group.removeMember.test.js.map +1 -1
  83. package/dist/tests/group.roleOf.test.js +14 -4
  84. package/dist/tests/group.roleOf.test.js.map +1 -1
  85. package/dist/tests/permissions.test.js +51 -202
  86. package/dist/tests/permissions.test.js.map +1 -1
  87. package/dist/tests/sync.content.test.js +2 -2
  88. package/dist/tests/sync.content.test.js.map +1 -1
  89. package/dist/tests/sync.invite.test.js +6 -6
  90. package/dist/tests/sync.load.test.js +22 -22
  91. package/dist/tests/sync.mesh.test.js +9 -9
  92. package/dist/tests/sync.storage.test.js +13 -7
  93. package/dist/tests/sync.storage.test.js.map +1 -1
  94. package/dist/tests/sync.storageAsync.test.js +3 -3
  95. package/dist/tests/sync.test.js +13 -33
  96. package/dist/tests/sync.test.js.map +1 -1
  97. package/dist/tests/sync.upload.test.js +2 -2
  98. package/package.json +3 -3
  99. package/src/PeerState.ts +86 -34
  100. package/src/SyncStateManager.ts +25 -60
  101. package/src/coValueContentMessage.ts +3 -1
  102. package/src/coValueCore/PeerKnownState.ts +74 -0
  103. package/src/coValueCore/coValueCore.ts +180 -49
  104. package/src/coValueCore/decryptTransactionChangesAndMeta.ts +0 -6
  105. package/src/coValueCore/verifiedState.ts +2 -37
  106. package/src/coValues/coList.ts +7 -7
  107. package/src/coValues/coMap.ts +9 -7
  108. package/src/coValues/coStream.ts +6 -5
  109. package/src/coValues/group.ts +99 -60
  110. package/src/exports.ts +2 -1
  111. package/src/localNode.ts +7 -5
  112. package/src/permissions.ts +204 -123
  113. package/src/sync.ts +37 -53
  114. package/src/tests/PeerKnownState.test.ts +426 -0
  115. package/src/tests/PeerState.test.ts +24 -24
  116. package/src/tests/StorageApiAsync.test.ts +12 -12
  117. package/src/tests/StorageApiSync.test.ts +11 -11
  118. package/src/tests/SyncStateManager.test.ts +23 -53
  119. package/src/tests/coValueCore.dependencies.test.ts +87 -0
  120. package/src/tests/coValueCore.test.ts +64 -22
  121. package/src/tests/group.addMember.test.ts +384 -345
  122. package/src/tests/group.inheritance.test.ts +33 -0
  123. package/src/tests/group.invite.test.ts +117 -0
  124. package/src/tests/group.removeMember.test.ts +95 -9
  125. package/src/tests/group.roleOf.test.ts +16 -4
  126. package/src/tests/permissions.test.ts +56 -295
  127. package/src/tests/sync.content.test.ts +2 -2
  128. package/src/tests/sync.invite.test.ts +6 -6
  129. package/src/tests/sync.load.test.ts +22 -22
  130. package/src/tests/sync.mesh.test.ts +9 -9
  131. package/src/tests/sync.storage.test.ts +13 -8
  132. package/src/tests/sync.storageAsync.test.ts +3 -3
  133. package/src/tests/sync.test.ts +21 -50
  134. package/src/tests/sync.upload.test.ts +2 -2
  135. package/dist/PeerKnownStates.d.ts +0 -19
  136. package/dist/PeerKnownStates.d.ts.map +0 -1
  137. package/dist/PeerKnownStates.js +0 -64
  138. package/dist/PeerKnownStates.js.map +0 -1
  139. package/dist/tests/PeerKnownStates.test.d.ts +0 -2
  140. package/dist/tests/PeerKnownStates.test.d.ts.map +0 -1
  141. package/dist/tests/PeerKnownStates.test.js +0 -77
  142. package/dist/tests/PeerKnownStates.test.js.map +0 -1
  143. package/src/PeerKnownStates.ts +0 -93
  144. package/src/tests/PeerKnownStates.test.ts +0 -99
@@ -16,10 +16,8 @@ import {
16
16
  AgentID,
17
17
  ChildGroupReference,
18
18
  ParentGroupReference,
19
- getChildGroupId,
20
19
  getParentGroupId,
21
20
  isAgentID,
22
- isChildGroupReference,
23
21
  isParentGroupReference,
24
22
  } from "../ids.js";
25
23
  import { JsonObject } from "../jsonValue.js";
@@ -51,6 +49,7 @@ export type ParentGroupReferenceRole =
51
49
  | "extend"
52
50
  | "reader"
53
51
  | "writer"
52
+ | "manager"
54
53
  | "admin";
55
54
 
56
55
  export type GroupShape = {
@@ -123,6 +122,38 @@ function healMissingKeyForEveryone(group: RawGroup) {
123
122
  }
124
123
  }
125
124
 
125
+ function needsKeyRotation(group: RawGroup) {
126
+ const currentReadKeyId = group.get("readKey");
127
+
128
+ if (!currentReadKeyId) {
129
+ return false;
130
+ }
131
+
132
+ for (const parentGroup of group.getParentGroups()) {
133
+ const parentReadKeyId = parentGroup.get("readKey");
134
+
135
+ if (!parentReadKeyId) {
136
+ continue;
137
+ }
138
+
139
+ const hasKeyRevelation = group.get(
140
+ `${currentReadKeyId}_for_${parentReadKeyId}`,
141
+ );
142
+
143
+ if (!hasKeyRevelation) {
144
+ return true;
145
+ }
146
+ }
147
+
148
+ return false;
149
+ }
150
+
151
+ function rotateReadKeyIfNeeded(group: RawGroup) {
152
+ if (needsKeyRotation(group)) {
153
+ group.rotateReadKey();
154
+ }
155
+ }
156
+
126
157
  /** A `Group` is a scope for permissions of its members (`"reader" | "writer" | "admin"`), applying to objects owned by that group.
127
158
  *
128
159
  * A `Group` object exposes methods for permission management and allows you to create new CoValues owned by that group.
@@ -160,7 +191,11 @@ export class RawGroup<
160
191
  super(core, options);
161
192
  this.crypto = core.node.crypto;
162
193
 
163
- healMissingKeyForEveryone(this);
194
+ // Checks if this is not an account
195
+ if (core.isGroup()) {
196
+ rotateReadKeyIfNeeded(this);
197
+ healMissingKeyForEveryone(this);
198
+ }
164
199
  }
165
200
 
166
201
  /**
@@ -259,27 +294,20 @@ export class RawGroup<
259
294
  }
260
295
 
261
296
  forEachChildGroup(callback: (child: RawGroup) => void) {
262
- for (const key of this.keys()) {
263
- if (isChildGroupReference(key)) {
264
- // Check if the child group reference is revoked
265
- if (this.get(key) === "revoked") {
266
- continue;
267
- }
297
+ // When rotating the parent key, all the child groups loaded in memory rotate their key.
298
+ // The unloaded child groups will be rotated when they are loaded, by checking if their key has been revealed to the latest parent readKey.
299
+ for (const id of this.core.dependant) {
300
+ const dependant = this.core.node.getCoValue(id);
268
301
 
269
- const id = getChildGroupId(key);
270
- const child = this.core.node.getCoValue(id);
302
+ if (!dependant.isGroup()) {
303
+ continue;
304
+ }
271
305
 
272
- if (child.isAvailable()) {
273
- callback(expectGroup(child.getCurrentContent()));
274
- } else {
275
- this.core.node.load(id).then((child) => {
276
- if (child !== "unavailable") {
277
- callback(expectGroup(child));
278
- } else {
279
- logger.warn(`Unable to load child group ${id}, skipping`);
280
- }
281
- });
282
- }
306
+ const childGroup = expectGroup(dependant.getCurrentContent());
307
+ const reference = childGroup.get(`parent_${this.id}`);
308
+
309
+ if (reference && reference !== "revoked") {
310
+ callback(childGroup);
283
311
  }
284
312
  }
285
313
  }
@@ -311,7 +339,15 @@ export class RawGroup<
311
339
  account: RawAccount | ControlledAccountOrAgent | AgentID | Everyone,
312
340
  role: Role,
313
341
  ) {
314
- if (account === EVERYONE) {
342
+ const memberKey = typeof account === "string" ? account : account.id;
343
+ const previousRole = this.get(memberKey);
344
+
345
+ // if the role is the same, we don't need to do anything
346
+ if (previousRole === role) {
347
+ return;
348
+ }
349
+
350
+ if (memberKey === EVERYONE) {
315
351
  if (!(role === "reader" || role === "writer" || role === "writeOnly")) {
316
352
  throw new Error(
317
353
  "Can't make everyone something other than reader, writer or writeOnly",
@@ -323,22 +359,13 @@ export class RawGroup<
323
359
  throw new Error("Can't add member without read key secret");
324
360
  }
325
361
 
326
- const previousRole = this.get(account);
362
+ const previousRole = this.get(memberKey);
327
363
 
328
- this.set(account, role, "trusting");
364
+ this.set(memberKey, role, "trusting");
329
365
 
330
- if (this.get(account) !== role) {
331
- // The role was not set correctly; this presents three scenarios:
332
- // 1. The current user is an administrator trying to set another administrator to a lower role
333
- // 2. The current user is an administrator but something has gone wrong with the role assignment
334
- // 3. The current user is not an administrator and does not have sufficient permissions to set the role
335
- const myRole = this.myRole();
366
+ if (this.get(memberKey) !== role) {
336
367
  throw new Error(
337
- myRole === "admin"
338
- ? this.get(account) === "admin"
339
- ? "Administrators cannot demote other administrators in a group"
340
- : "Failed to set role"
341
- : `Failed to set role due to insufficient permissions (role of current account is ${myRole})`,
368
+ `Failed to set role ${role} to ${memberKey} (role of current account is ${this.myRole()})`,
342
369
  );
343
370
  }
344
371
 
@@ -359,10 +386,13 @@ export class RawGroup<
359
386
  return;
360
387
  }
361
388
 
362
- const memberKey = typeof account === "string" ? account : account.id;
363
389
  const agent =
364
390
  typeof account === "string" ? account : account.currentAgentID();
365
391
 
392
+ if (agent === EVERYONE) {
393
+ throw new Error("Agent should not be everyone");
394
+ }
395
+
366
396
  /**
367
397
  * WriteOnly members can only see their own changes.
368
398
  *
@@ -374,26 +404,23 @@ export class RawGroup<
374
404
  * invite.
375
405
  */
376
406
  if (role === "writeOnly" || role === "writeOnlyInvite") {
377
- const previousRole = this.get(memberKey);
378
-
379
- if (
380
- previousRole === "admin" &&
381
- memberKey !== this.core.node.getCurrentAgent().id
382
- ) {
383
- throw new Error(
384
- "Administrators cannot demote other administrators in a group",
385
- );
386
- }
387
-
388
407
  if (
389
408
  previousRole === "reader" ||
390
409
  previousRole === "writer" ||
410
+ previousRole === "manager" ||
391
411
  previousRole === "admin"
392
412
  ) {
393
413
  this.rotateReadKey(memberKey);
394
414
  }
395
415
 
396
416
  this.set(memberKey, role, "trusting");
417
+
418
+ if (this.get(memberKey) !== role) {
419
+ throw new Error(
420
+ `Failed to set role ${role} to ${memberKey} (role of current account is ${this.myRole()})`,
421
+ );
422
+ }
423
+
397
424
  this.internalCreateWriteOnlyKeyForMember(memberKey, agent);
398
425
  } else {
399
426
  const currentReadKey = this.getCurrentReadKey();
@@ -405,13 +432,8 @@ export class RawGroup<
405
432
  this.set(memberKey, role, "trusting");
406
433
 
407
434
  if (this.get(memberKey) !== role) {
408
- const myRole = this.myRole();
409
435
  throw new Error(
410
- myRole === "admin"
411
- ? this.get(memberKey) === "admin"
412
- ? "Administrators cannot demote other administrators in a group"
413
- : "Failed to set role"
414
- : `Failed to set role due to insufficient permissions (role of current account is ${myRole})`,
436
+ `Failed to set role ${role} to ${memberKey} (role of current account is ${this.myRole()})`,
415
437
  );
416
438
  }
417
439
 
@@ -458,6 +480,7 @@ export class RawGroup<
458
480
  memberRole === "reader" ||
459
481
  memberRole === "writer" ||
460
482
  memberRole === "admin" ||
483
+ memberRole === "manager" ||
461
484
  memberRole === "readerInvite" ||
462
485
  memberRole === "writerInvite" ||
463
486
  memberRole === "adminInvite"
@@ -960,7 +983,7 @@ export class RawGroup<
960
983
 
961
984
  extend(
962
985
  parent: RawGroup,
963
- role: "reader" | "writer" | "admin" | "inherit" = "inherit",
986
+ role: "reader" | "writer" | "manager" | "admin" | "inherit" = "inherit",
964
987
  ) {
965
988
  if (this.isSelfExtension(parent)) {
966
989
  return;
@@ -974,7 +997,6 @@ export class RawGroup<
974
997
 
975
998
  const value = role === "inherit" ? "extend" : role;
976
999
 
977
- parent.set(`child_${this.id}`, "extend", "trusting");
978
1000
  this.set(`parent_${parent.id}`, value, "trusting");
979
1001
 
980
1002
  const { id: childReadKeyID, secret: childReadKeySecret } =
@@ -1069,7 +1091,9 @@ export class RawGroup<
1069
1091
  this.set(`parent_${parent.id}`, "revoked", "trusting");
1070
1092
 
1071
1093
  // Set the child key on the parent group to `revoked`
1072
- parent.set(`child_${this.id}`, "revoked", "trusting");
1094
+ if (parent.get(`child_${this.id}`)) {
1095
+ parent.set(`child_${this.id}`, "revoked", "trusting");
1096
+ }
1073
1097
 
1074
1098
  // Rotate the keys on the child group
1075
1099
  this.rotateReadKey();
@@ -1085,11 +1109,18 @@ export class RawGroup<
1085
1109
  removeMember(account: RawAccount | ControlledAccountOrAgent | Everyone) {
1086
1110
  const memberKey = typeof account === "string" ? account : account.id;
1087
1111
 
1088
- if (this.myRole() === "admin") {
1112
+ if (this.myRole() === "admin" || this.myRole() === "manager") {
1089
1113
  this.rotateReadKey(memberKey);
1090
1114
  }
1091
1115
 
1092
1116
  this.set(memberKey, "revoked", "trusting");
1117
+
1118
+ // TODO: removeMember fails silently. Uncomment this will be a breaking change
1119
+ // if (this.get(memberKey) !== "revoked") {
1120
+ // throw new Error(
1121
+ // `Failed to revoke role to ${memberKey} (role of current account is ${this.myRole()})`,
1122
+ // );
1123
+ // }
1093
1124
  }
1094
1125
 
1095
1126
  /**
@@ -1254,23 +1285,30 @@ export class RawGroup<
1254
1285
 
1255
1286
  export function isInheritableRole(
1256
1287
  roleInParent: Role | undefined,
1257
- ): roleInParent is "revoked" | "admin" | "writer" | "reader" {
1288
+ ): roleInParent is "revoked" | "admin" | "manager" | "writer" | "reader" {
1258
1289
  return (
1259
1290
  roleInParent === "revoked" ||
1260
1291
  roleInParent === "admin" ||
1292
+ roleInParent === "manager" ||
1261
1293
  roleInParent === "writer" ||
1262
1294
  roleInParent === "reader"
1263
1295
  );
1264
1296
  }
1265
1297
 
1266
1298
  function isMorePermissiveAndShouldInherit(
1267
- roleInParent: "revoked" | "admin" | "writer" | "reader",
1299
+ roleInParent: "revoked" | "admin" | "manager" | "writer" | "reader",
1268
1300
  roleInChild: Role | undefined,
1269
1301
  ) {
1270
1302
  if (roleInParent === "revoked") {
1271
1303
  return true;
1272
1304
  }
1273
1305
 
1306
+ if (roleInParent === "manager") {
1307
+ return (
1308
+ !roleInChild || (roleInChild !== "manager" && roleInChild !== "admin")
1309
+ );
1310
+ }
1311
+
1274
1312
  if (roleInParent === "admin") {
1275
1313
  return !roleInChild || roleInChild !== "admin";
1276
1314
  }
@@ -1314,6 +1352,7 @@ const canRead = (
1314
1352
  const role = group.get(key);
1315
1353
  return (
1316
1354
  role === "admin" ||
1355
+ role === "manager" ||
1317
1356
  role === "writer" ||
1318
1357
  role === "reader" ||
1319
1358
  role === "adminInvite" ||
package/src/exports.ts CHANGED
@@ -60,7 +60,7 @@ import { AgentSecret, textDecoder, textEncoder } from "./crypto/crypto.js";
60
60
  import type { AgentID, RawCoID, SessionID } from "./ids.js";
61
61
  import type { JsonObject, JsonValue } from "./jsonValue.js";
62
62
  import type * as Media from "./media.js";
63
- import { disablePermissionErrors } from "./permissions.js";
63
+ import { disablePermissionErrors, isAccountRole } from "./permissions.js";
64
64
  import type { Peer, SyncMessage } from "./sync.js";
65
65
  import {
66
66
  DisconnectedError,
@@ -169,6 +169,7 @@ export {
169
169
  base64URLtoBytes,
170
170
  bytesToBase64url,
171
171
  hwrServerPeerSelector,
172
+ isAccountRole,
172
173
  };
173
174
 
174
175
  export type {
package/src/localNode.ts CHANGED
@@ -640,11 +640,13 @@ export class LocalNode {
640
640
  account,
641
641
  inviteRole === "adminInvite"
642
642
  ? "admin"
643
- : inviteRole === "writerInvite"
644
- ? "writer"
645
- : inviteRole === "writeOnlyInvite"
646
- ? "writeOnly"
647
- : "reader",
643
+ : inviteRole === "managerInvite"
644
+ ? "manager"
645
+ : inviteRole === "writerInvite"
646
+ ? "writer"
647
+ : inviteRole === "writeOnlyInvite"
648
+ ? "writeOnly"
649
+ : "reader",
648
650
  );
649
651
 
650
652
  const contentPieces =