cojson 0.18.0 → 0.18.2

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 (77) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/coValueCore/branching.d.ts +36 -0
  4. package/dist/coValueCore/branching.d.ts.map +1 -0
  5. package/dist/coValueCore/branching.js +122 -0
  6. package/dist/coValueCore/branching.js.map +1 -0
  7. package/dist/coValueCore/coValueCore.d.ts +71 -5
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +162 -53
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts +3 -0
  12. package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts.map +1 -0
  13. package/dist/coValueCore/decodeTransactionChangesAndMeta.js +59 -0
  14. package/dist/coValueCore/decodeTransactionChangesAndMeta.js.map +1 -0
  15. package/dist/coValueCore/utils.d.ts.map +1 -1
  16. package/dist/coValueCore/utils.js +3 -0
  17. package/dist/coValueCore/utils.js.map +1 -1
  18. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  19. package/dist/coValues/coList.d.ts +3 -3
  20. package/dist/coValues/coList.d.ts.map +1 -1
  21. package/dist/coValues/coList.js +4 -7
  22. package/dist/coValues/coList.js.map +1 -1
  23. package/dist/coValues/coMap.d.ts +3 -3
  24. package/dist/coValues/coMap.d.ts.map +1 -1
  25. package/dist/coValues/coMap.js +6 -6
  26. package/dist/coValues/coMap.js.map +1 -1
  27. package/dist/coValues/coStream.d.ts +3 -3
  28. package/dist/coValues/coStream.d.ts.map +1 -1
  29. package/dist/coValues/coStream.js +4 -4
  30. package/dist/coValues/coStream.js.map +1 -1
  31. package/dist/ids.d.ts.map +1 -1
  32. package/dist/ids.js.map +1 -1
  33. package/dist/jsonStringify.d.ts +1 -0
  34. package/dist/jsonStringify.d.ts.map +1 -1
  35. package/dist/jsonStringify.js +8 -0
  36. package/dist/jsonStringify.js.map +1 -1
  37. package/dist/localNode.d.ts +6 -0
  38. package/dist/localNode.d.ts.map +1 -1
  39. package/dist/localNode.js +27 -0
  40. package/dist/localNode.js.map +1 -1
  41. package/dist/permissions.d.ts +2 -7
  42. package/dist/permissions.d.ts.map +1 -1
  43. package/dist/permissions.js +75 -71
  44. package/dist/permissions.js.map +1 -1
  45. package/dist/tests/branching.test.d.ts +2 -0
  46. package/dist/tests/branching.test.d.ts.map +1 -0
  47. package/dist/tests/branching.test.js +287 -0
  48. package/dist/tests/branching.test.js.map +1 -0
  49. package/dist/tests/coValueCore.test.js +2 -3
  50. package/dist/tests/coValueCore.test.js.map +1 -1
  51. package/dist/tests/group.removeMember.test.js +63 -116
  52. package/dist/tests/group.removeMember.test.js.map +1 -1
  53. package/dist/tests/sync.load.test.js +36 -0
  54. package/dist/tests/sync.load.test.js.map +1 -1
  55. package/dist/tests/sync.storage.test.js +39 -3
  56. package/dist/tests/sync.storage.test.js.map +1 -1
  57. package/dist/tests/sync.upload.test.js +39 -3
  58. package/dist/tests/sync.upload.test.js.map +1 -1
  59. package/package.json +2 -2
  60. package/src/coValueCore/branching.ts +198 -0
  61. package/src/coValueCore/coValueCore.ts +255 -72
  62. package/src/coValueCore/decodeTransactionChangesAndMeta.ts +81 -0
  63. package/src/coValueCore/utils.ts +4 -0
  64. package/src/coValueCore/verifiedState.ts +1 -1
  65. package/src/coValues/coList.ts +8 -10
  66. package/src/coValues/coMap.ts +8 -11
  67. package/src/coValues/coStream.ts +7 -8
  68. package/src/ids.ts +4 -1
  69. package/src/jsonStringify.ts +8 -0
  70. package/src/localNode.ts +40 -0
  71. package/src/permissions.ts +83 -90
  72. package/src/tests/branching.test.ts +425 -0
  73. package/src/tests/coValueCore.test.ts +2 -3
  74. package/src/tests/group.removeMember.test.ts +116 -214
  75. package/src/tests/sync.load.test.ts +48 -0
  76. package/src/tests/sync.storage.test.ts +54 -3
  77. package/src/tests/sync.upload.test.ts +53 -3
@@ -77,10 +77,7 @@ function logPermissionError(
77
77
  logger.debug("Permission error: " + message, attributes);
78
78
  }
79
79
 
80
- export function determineValidTransactions(
81
- coValue: CoValueCore,
82
- knownTransactions?: CoValueKnownState["sessions"],
83
- ): { txID: TransactionID; tx: Transaction }[] {
80
+ export function determineValidTransactions(coValue: CoValueCore) {
84
81
  if (!coValue.isAvailable()) {
85
82
  throw new Error("determineValidTransactions CoValue is not available");
86
83
  }
@@ -91,8 +88,7 @@ export function determineValidTransactions(
91
88
  throw new Error("Group must have initialAdmin");
92
89
  }
93
90
 
94
- return determineValidTransactionsForGroup(coValue, initialAdmin)
95
- .validTransactions;
91
+ determineValidTransactionsForGroup(coValue, initialAdmin);
96
92
  } else if (coValue.verified.header.ruleset.type === "ownedByGroup") {
97
93
  const groupContent = expectGroup(
98
94
  coValue.node
@@ -107,58 +103,44 @@ export function determineValidTransactions(
107
103
  throw new Error("Group must be a map");
108
104
  }
109
105
 
110
- const validTransactions: ValidTransactionsResult[] = [];
111
-
112
- for (const [sessionID, sessionLog] of coValue.verified.sessions.entries()) {
113
- const transactor = accountOrAgentIDfromSessionID(sessionID);
114
- const knownTransactionsForSession = knownTransactions?.[sessionID] ?? -1;
106
+ for (const tx of coValue.verifiedTransactions) {
107
+ if (tx.isValidated) {
108
+ continue;
109
+ }
115
110
 
116
- sessionLog.transactions.forEach((tx, txIndex) => {
117
- if (knownTransactionsForSession >= txIndex) {
118
- return;
119
- }
111
+ tx.isValidated = true;
112
+ const wasValid = tx.isValid;
120
113
 
121
- const groupAtTime = groupContent.atTime(tx.madeAt);
122
- const effectiveTransactor = agentInAccountOrMemberInGroup(
123
- transactor,
124
- groupAtTime,
125
- );
114
+ const groupAtTime = groupContent.atTime(tx.madeAt);
115
+ const effectiveTransactor = agentInAccountOrMemberInGroup(
116
+ tx.author,
117
+ groupAtTime,
118
+ );
126
119
 
127
- if (!effectiveTransactor) {
128
- return;
129
- }
120
+ if (!effectiveTransactor) {
121
+ tx.isValid = false;
122
+ continue;
123
+ }
130
124
 
131
- const transactorRoleAtTxTime =
132
- groupAtTime.roleOfInternal(effectiveTransactor);
125
+ const transactorRoleAtTxTime =
126
+ groupAtTime.roleOfInternal(effectiveTransactor);
133
127
 
134
- if (
135
- transactorRoleAtTxTime !== "admin" &&
136
- transactorRoleAtTxTime !== "writer" &&
137
- transactorRoleAtTxTime !== "writeOnly"
138
- ) {
139
- return;
140
- }
128
+ if (
129
+ transactorRoleAtTxTime !== "admin" &&
130
+ transactorRoleAtTxTime !== "writer" &&
131
+ transactorRoleAtTxTime !== "writeOnly"
132
+ ) {
133
+ tx.isValid = false;
134
+ continue;
135
+ }
141
136
 
142
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
143
- });
137
+ tx.isValid = true;
144
138
  }
145
-
146
- return validTransactions;
147
139
  } else if (coValue.verified.header.ruleset.type === "unsafeAllowAll") {
148
- const validTransactions: ValidTransactionsResult[] = [];
149
-
150
- for (const [sessionID, sessionLog] of coValue.verified.sessions.entries()) {
151
- const knownTransactionsForSession = knownTransactions?.[sessionID] ?? -1;
152
-
153
- sessionLog.transactions.forEach((tx, txIndex) => {
154
- if (knownTransactionsForSession >= txIndex) {
155
- return;
156
- }
157
-
158
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
159
- });
140
+ for (const tx of coValue.verifiedTransactions) {
141
+ tx.isValid = true;
142
+ tx.isValidated = true;
160
143
  }
161
- return validTransactions;
162
144
  } else {
163
145
  throw new Error(
164
146
  "Unknown ruleset type " +
@@ -228,22 +210,9 @@ function determineValidTransactionsForGroup(
228
210
  coValue: CoValueCore,
229
211
  initialAdmin: RawAccountID | AgentID,
230
212
  extendChain?: Set<CoValueCore["id"]>,
231
- ): { validTransactions: ValidTransactionsResult[]; memberState: MemberState } {
232
- const allTransactionsSorted: {
233
- sessionID: SessionID;
234
- txIndex: number;
235
- tx: Transaction;
236
- }[] = [];
237
-
238
- for (const [sessionID, sessionLog] of coValue.verified?.sessions.entries() ??
239
- []) {
240
- sessionLog.transactions.forEach((tx, txIndex) => {
241
- allTransactionsSorted.push({ sessionID, txIndex, tx });
242
- });
243
- }
244
-
245
- allTransactionsSorted.sort((a, b) => {
246
- return a.tx.madeAt - b.tx.madeAt;
213
+ ): { memberState: MemberState } {
214
+ coValue.verifiedTransactions.sort((a, b) => {
215
+ return a.madeAt - b.madeAt;
247
216
  });
248
217
 
249
218
  const memberState: MemberState = {};
@@ -252,15 +221,16 @@ function determineValidTransactionsForGroup(
252
221
 
253
222
  const writeKeys = new Set<string>();
254
223
 
255
- for (const { sessionID, txIndex, tx } of allTransactionsSorted) {
256
- const transactor = accountOrAgentIDfromSessionID(sessionID);
224
+ for (const transaction of coValue.verifiedTransactions) {
225
+ const transactor = transaction.author;
226
+
227
+ transaction.isValidated = true;
228
+
229
+ const tx = transaction.tx;
257
230
 
258
231
  if (tx.privacy === "private") {
259
232
  if (memberState[transactor] === "admin") {
260
- validTransactions.push({
261
- txID: { sessionID, txIndex },
262
- tx,
263
- });
233
+ transaction.isValid = true;
264
234
  continue;
265
235
  } else {
266
236
  logPermissionError(
@@ -270,16 +240,20 @@ function determineValidTransactionsForGroup(
270
240
  }
271
241
  }
272
242
 
273
- let changes;
243
+ let changes = transaction.changes;
274
244
 
275
- try {
276
- changes = parseJSON(tx.changes);
277
- } catch (e) {
278
- logPermissionError("Invalid JSON in transaction", {
279
- id: coValue.id,
280
- tx,
281
- });
282
- continue;
245
+ if (!changes) {
246
+ try {
247
+ changes = parseJSON(tx.changes);
248
+ transaction.changes = changes;
249
+ } catch (e) {
250
+ logPermissionError("Invalid JSON in transaction", {
251
+ id: coValue.id,
252
+ tx,
253
+ });
254
+ transaction.hasInvalidChanges = true;
255
+ continue;
256
+ }
283
257
  }
284
258
 
285
259
  const change = changes[0] as
@@ -292,29 +266,33 @@ function determineValidTransactionsForGroup(
292
266
 
293
267
  if (changes.length !== 1) {
294
268
  logPermissionError("Group transaction must have exactly one change");
269
+ transaction.isValid = false;
295
270
  continue;
296
271
  }
297
272
 
298
273
  if (change.op !== "set") {
299
274
  logPermissionError("Group transaction must set a role or readKey");
275
+ transaction.isValid = false;
300
276
  continue;
301
277
  }
302
278
 
303
279
  if (change.key === "readKey") {
304
280
  if (memberState[transactor] !== "admin") {
305
281
  logPermissionError("Only admins can set readKeys");
282
+ transaction.isValid = false;
306
283
  continue;
307
284
  }
308
285
 
309
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
286
+ transaction.isValid = true;
310
287
  continue;
311
288
  } else if (change.key === "profile") {
312
289
  if (memberState[transactor] !== "admin") {
313
290
  logPermissionError("Only admins can set profile");
291
+ transaction.isValid = false;
314
292
  continue;
315
293
  }
316
294
 
317
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
295
+ transaction.isValid = true;
318
296
  continue;
319
297
  } else if (change.key === "root") {
320
298
  if (memberState[transactor] !== "admin") {
@@ -322,7 +300,7 @@ function determineValidTransactionsForGroup(
322
300
  continue;
323
301
  }
324
302
 
325
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
303
+ transaction.isValid = true;
326
304
  continue;
327
305
  } else if (
328
306
  isKeyForKeyField(change.key) ||
@@ -337,15 +315,17 @@ function determineValidTransactionsForGroup(
337
315
  !isOwnWriteKeyRevelation(change.key, transactor, writeOnlyKeys)
338
316
  ) {
339
317
  logPermissionError("Only admins can reveal keys");
318
+ transaction.isValid = false;
340
319
  continue;
341
320
  }
342
321
 
343
322
  // TODO: check validity of agents who the key is revealed to?
344
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
323
+ transaction.isValid = true;
345
324
  continue;
346
325
  } else if (isParentExtension(change.key)) {
347
326
  if (memberState[transactor] !== "admin") {
348
327
  logPermissionError("Only admins can set parent extensions");
328
+ transaction.isValid = false;
349
329
  continue;
350
330
  }
351
331
 
@@ -364,13 +344,14 @@ function determineValidTransactionsForGroup(
364
344
  logPermissionError(
365
345
  "Circular extend detected, dropping the transaction",
366
346
  );
347
+ transaction.isValid = false;
367
348
  continue;
368
349
  }
369
350
 
370
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
351
+ transaction.isValid = true;
371
352
  continue;
372
353
  } else if (isChildExtension(change.key)) {
373
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
354
+ transaction.isValid = true;
374
355
  continue;
375
356
  } else if (isWriteKeyForMember(change.key)) {
376
357
  const memberKey = getAccountOrAgentFromWriteKeyForMember(change.key);
@@ -381,6 +362,7 @@ function determineValidTransactionsForGroup(
381
362
  memberKey !== transactor
382
363
  ) {
383
364
  logPermissionError("Only admins can set writeKeys");
365
+ transaction.isValid = false;
384
366
  continue;
385
367
  }
386
368
 
@@ -398,12 +380,13 @@ function determineValidTransactionsForGroup(
398
380
  logPermissionError(
399
381
  "Write key already exists and can't be overridden by invite",
400
382
  );
383
+ transaction.isValid = false;
401
384
  continue;
402
385
  }
403
386
 
404
387
  writeKeys.add(change.key);
405
388
 
406
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
389
+ transaction.isValid = true;
407
390
  continue;
408
391
  }
409
392
 
@@ -422,6 +405,7 @@ function determineValidTransactionsForGroup(
422
405
  change.value !== "writeOnlyInvite"
423
406
  ) {
424
407
  logPermissionError("Group transaction must set a valid role");
408
+ transaction.isValid = false;
425
409
  continue;
426
410
  }
427
411
 
@@ -437,6 +421,7 @@ function determineValidTransactionsForGroup(
437
421
  logPermissionError(
438
422
  "Everyone can only be set to reader, writer, writeOnly or revoked",
439
423
  );
424
+ transaction.isValid = false;
440
425
  continue;
441
426
  }
442
427
 
@@ -450,7 +435,9 @@ function determineValidTransactionsForGroup(
450
435
  const currentAccountId = coValue.node.getCurrentAccountOrAgentID();
451
436
 
452
437
  const isSelfRevoke =
453
- currentAccountId === change.key && change.value === "revoked";
438
+ currentAccountId === change.key &&
439
+ transactor === currentAccountId &&
440
+ change.value === "revoked";
454
441
 
455
442
  if (!isFirstSelfAppointment && !isSelfRevoke) {
456
443
  if (memberState[transactor] === "admin") {
@@ -460,41 +447,47 @@ function determineValidTransactionsForGroup(
460
447
  assignedRole !== "admin"
461
448
  ) {
462
449
  logPermissionError("Admins can only demote themselves.");
450
+ transaction.isValid = false;
463
451
  continue;
464
452
  }
465
453
  } else if (memberState[transactor] === "adminInvite") {
466
454
  if (change.value !== "admin") {
467
455
  logPermissionError("AdminInvites can only create admins.");
456
+ transaction.isValid = false;
468
457
  continue;
469
458
  }
470
459
  } else if (memberState[transactor] === "writerInvite") {
471
460
  if (change.value !== "writer") {
472
461
  logPermissionError("WriterInvites can only create writers.");
462
+ transaction.isValid = false;
473
463
  continue;
474
464
  }
475
465
  } else if (memberState[transactor] === "readerInvite") {
476
466
  if (change.value !== "reader") {
477
467
  logPermissionError("ReaderInvites can only create reader.");
468
+ transaction.isValid = false;
478
469
  continue;
479
470
  }
480
471
  } else if (memberState[transactor] === "writeOnlyInvite") {
481
472
  if (change.value !== "writeOnly") {
482
473
  logPermissionError("WriteOnlyInvites can only create writeOnly.");
474
+ transaction.isValid = false;
483
475
  continue;
484
476
  }
485
477
  } else {
486
478
  logPermissionError(
487
479
  "Group transaction must be made by current admin or invite",
488
480
  );
481
+ transaction.isValid = false;
489
482
  continue;
490
483
  }
491
484
  }
492
485
 
493
486
  memberState[affectedMember] = change.value;
494
- validTransactions.push({ txID: { sessionID, txIndex }, tx });
487
+ transaction.isValid = true;
495
488
  }
496
489
 
497
- return { validTransactions, memberState };
490
+ return { memberState };
498
491
  }
499
492
 
500
493
  function agentInAccountOrMemberInGroup(