cojson 0.17.14 → 0.18.1

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 (83) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +34 -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/verifiedState.d.ts.map +1 -1
  16. package/dist/coValues/account.d.ts +2 -1
  17. package/dist/coValues/account.d.ts.map +1 -1
  18. package/dist/coValues/account.js +6 -0
  19. package/dist/coValues/account.js.map +1 -1
  20. package/dist/coValues/coList.d.ts +3 -3
  21. package/dist/coValues/coList.d.ts.map +1 -1
  22. package/dist/coValues/coList.js +4 -7
  23. package/dist/coValues/coList.js.map +1 -1
  24. package/dist/coValues/coMap.d.ts +3 -3
  25. package/dist/coValues/coMap.d.ts.map +1 -1
  26. package/dist/coValues/coMap.js +6 -6
  27. package/dist/coValues/coMap.js.map +1 -1
  28. package/dist/coValues/coStream.d.ts +3 -3
  29. package/dist/coValues/coStream.d.ts.map +1 -1
  30. package/dist/coValues/coStream.js +4 -4
  31. package/dist/coValues/coStream.js.map +1 -1
  32. package/dist/ids.d.ts.map +1 -1
  33. package/dist/ids.js.map +1 -1
  34. package/dist/jsonStringify.d.ts +1 -0
  35. package/dist/jsonStringify.d.ts.map +1 -1
  36. package/dist/jsonStringify.js +8 -0
  37. package/dist/jsonStringify.js.map +1 -1
  38. package/dist/permissions.d.ts +2 -7
  39. package/dist/permissions.d.ts.map +1 -1
  40. package/dist/permissions.js +72 -70
  41. package/dist/permissions.js.map +1 -1
  42. package/dist/sync.d.ts +2 -1
  43. package/dist/sync.d.ts.map +1 -1
  44. package/dist/sync.js +16 -5
  45. package/dist/sync.js.map +1 -1
  46. package/dist/tests/account.test.js +20 -0
  47. package/dist/tests/account.test.js.map +1 -1
  48. package/dist/tests/branching.test.d.ts +2 -0
  49. package/dist/tests/branching.test.d.ts.map +1 -0
  50. package/dist/tests/branching.test.js +99 -0
  51. package/dist/tests/branching.test.js.map +1 -0
  52. package/dist/tests/coValueCore.test.js +2 -3
  53. package/dist/tests/coValueCore.test.js.map +1 -1
  54. package/dist/tests/sync.sharding.test.js +63 -0
  55. package/dist/tests/sync.sharding.test.js.map +1 -1
  56. package/dist/tests/sync.storage.test.js +1 -3
  57. package/dist/tests/sync.storage.test.js.map +1 -1
  58. package/dist/tests/sync.upload.test.js +1 -3
  59. package/dist/tests/sync.upload.test.js.map +1 -1
  60. package/dist/tests/testUtils.d.ts +1 -0
  61. package/dist/tests/testUtils.d.ts.map +1 -1
  62. package/dist/tests/testUtils.js +1 -1
  63. package/dist/tests/testUtils.js.map +1 -1
  64. package/package.json +2 -2
  65. package/src/coValueCore/branching.ts +198 -0
  66. package/src/coValueCore/coValueCore.ts +255 -72
  67. package/src/coValueCore/decodeTransactionChangesAndMeta.ts +81 -0
  68. package/src/coValueCore/verifiedState.ts +1 -1
  69. package/src/coValues/account.ts +11 -9
  70. package/src/coValues/coList.ts +8 -10
  71. package/src/coValues/coMap.ts +8 -11
  72. package/src/coValues/coStream.ts +7 -8
  73. package/src/ids.ts +4 -1
  74. package/src/jsonStringify.ts +8 -0
  75. package/src/permissions.ts +80 -89
  76. package/src/sync.ts +21 -6
  77. package/src/tests/account.test.ts +24 -0
  78. package/src/tests/branching.test.ts +141 -0
  79. package/src/tests/coValueCore.test.ts +2 -3
  80. package/src/tests/sync.sharding.test.ts +72 -0
  81. package/src/tests/sync.storage.test.ts +3 -3
  82. package/src/tests/sync.upload.test.ts +3 -3
  83. package/src/tests/testUtils.ts +2 -1
@@ -15,9 +15,9 @@ import {
15
15
  Signature,
16
16
  SignerID,
17
17
  } from "../crypto/crypto.js";
18
- import { RawCoID, SessionID, TransactionID } from "../ids.js";
18
+ import { AgentID, RawCoID, SessionID, TransactionID } from "../ids.js";
19
19
  import { JsonObject, JsonValue } from "../jsonValue.js";
20
- import { parseJSON } from "../jsonStringify.js";
20
+ import { parseJSON, safeParseJSON } from "../jsonStringify.js";
21
21
  import { LocalNode, ResolveAccountAgentError } from "../localNode.js";
22
22
  import { logger } from "../logger.js";
23
23
  import { determineValidTransactions } from "../permissions.js";
@@ -27,6 +27,15 @@ import { expectGroup } from "../typeUtils/expectGroup.js";
27
27
  import { getDependedOnCoValuesFromRawData } from "./utils.js";
28
28
  import { CoValueHeader, Transaction, VerifiedState } from "./verifiedState.js";
29
29
  import { SessionMap } from "./SessionMap.js";
30
+ import {
31
+ MergeCommit,
32
+ createBranch,
33
+ getBranchId,
34
+ getBranchSource,
35
+ mergeBranch,
36
+ } from "./branching.js";
37
+ import { type RawAccountID } from "../coValues/account.js";
38
+ import { decodeTransactionChangesAndMeta } from "./decodeTransactionChangesAndMeta.js";
30
39
 
31
40
  export function idforHeader(
32
41
  header: CoValueHeader,
@@ -36,11 +45,38 @@ export function idforHeader(
36
45
  return `co_z${hash.slice("shortHash_z".length)}`;
37
46
  }
38
47
 
48
+ export type VerifiedTransaction = {
49
+ // The account or agent that made the transaction
50
+ author: RawAccountID | AgentID;
51
+ // An object containing the session ID and the transaction index
52
+ txID: TransactionID;
53
+ tx: Transaction;
54
+ // The Unix time when the transaction was made
55
+ madeAt: number;
56
+ // Whether the transaction has been validated, used to track if determinedValidTransactions needs to be check this
57
+ isValidated: boolean;
58
+ // The decoded changes of the transaction
59
+ changes: JsonValue[] | undefined;
60
+ // The decoded meta information of the transaction
61
+ meta: JsonObject | undefined;
62
+
63
+ // Whether the transaction is valid, as per membership rules
64
+ isValid: boolean;
65
+
66
+ // True if we encountered an error while decoding the changes
67
+ hasInvalidChanges: boolean;
68
+ // True if we encountered an error while parsing the meta
69
+ hasInvalidMeta: boolean;
70
+
71
+ // True if the meta information has been parsed and loaded in the CoValueCore
72
+ hasMetaBeenParsed: boolean;
73
+ };
74
+
39
75
  export type DecryptedTransaction = {
40
76
  txID: TransactionID;
41
77
  changes: JsonValue[];
42
78
  madeAt: number;
43
- trusting?: boolean;
79
+ tx: Transaction;
44
80
  };
45
81
 
46
82
  export type AvailableCoValueCore = CoValueCore & { verified: VerifiedState };
@@ -82,10 +118,6 @@ export class CoValueCore {
82
118
  private _cachedContent?: RawCoValue;
83
119
  readonly listeners: Set<(core: CoValueCore, unsub: () => void) => void> =
84
120
  new Set();
85
- private readonly _decryptionCache: {
86
- [key: Encrypted<JsonValue[], JsonValue>]: JsonValue[] | undefined;
87
- [key: Encrypted<JsonObject, JsonValue>]: JsonObject | undefined;
88
- } = {};
89
121
  private _cachedDependentOn?: Set<RawCoID>;
90
122
  private counter: UpDownCounter;
91
123
 
@@ -340,13 +372,13 @@ export class CoValueCore {
340
372
  );
341
373
  }
342
374
  this._verified = state.clone();
343
- this._cachedContent = undefined;
344
- this._cachedDependentOn = undefined;
375
+ this.internalShamefullyResetCachedContent();
345
376
  }
346
377
 
347
378
  internalShamefullyResetCachedContent() {
348
379
  this._cachedContent = undefined;
349
380
  this._cachedDependentOn = undefined;
381
+ this.resetParsedTransactions();
350
382
  }
351
383
 
352
384
  groupInvalidationSubscription?: () => void;
@@ -368,7 +400,8 @@ export class CoValueCore {
368
400
 
369
401
  if (entry.isAvailable()) {
370
402
  this.groupInvalidationSubscription = entry.subscribe((_groupUpdate) => {
371
- this._cachedContent = undefined;
403
+ // When the group is updated, we need to reset the cached content because the transactions validity might have changed
404
+ this.internalShamefullyResetCachedContent();
372
405
  this.notifyUpdate("immediate");
373
406
  }, false);
374
407
  } else {
@@ -588,14 +621,6 @@ export class CoValueCore {
588
621
  keySecret,
589
622
  meta,
590
623
  );
591
-
592
- if (result.transaction.privacy === "private") {
593
- this._decryptionCache[result.transaction.encryptedChanges] = changes;
594
-
595
- if (result.transaction.meta) {
596
- this._decryptionCache[result.transaction.meta] = meta;
597
- }
598
- }
599
624
  } else {
600
625
  result = this.verified.makeNewTrustingTransaction(
601
626
  sessionID,
@@ -609,6 +634,9 @@ export class CoValueCore {
609
634
 
610
635
  this.node.syncManager.recordTransactionsSize([transaction], "local");
611
636
 
637
+ // We pre-populate the parsed transactions and meta for the new transaction, to skip the parsing step later
638
+ this.loadVerifiedTransactionsFromLogs({ transaction, changes, meta });
639
+
612
640
  const session = this.verified.sessions.get(sessionID);
613
641
  const txIdx = session ? session.transactions.length - 1 : 0;
614
642
 
@@ -646,86 +674,235 @@ export class CoValueCore {
646
674
  return newContent;
647
675
  }
648
676
 
677
+ // The starting point of the branch, in case this CoValue is a branch
678
+ branchStart:
679
+ | { branch: CoValueKnownState["sessions"]; madeAt: number }
680
+ | undefined;
681
+
682
+ // The list of merge commits that have been made
683
+ mergeCommits: { commit: MergeCommit; madeAt: number }[] = [];
684
+
685
+ // Reset the parsed transactions and branches, to validate them again from scratch when the group is updated
686
+ resetParsedTransactions() {
687
+ this.branchStart = undefined;
688
+ this.mergeCommits = [];
689
+
690
+ for (const transaction of this.verifiedTransactions) {
691
+ transaction.isValidated = false;
692
+ transaction.hasMetaBeenParsed = false;
693
+ }
694
+ }
695
+
696
+ verifiedTransactions: VerifiedTransaction[] = [];
697
+ private verifiedTransactionsKnownSessions: CoValueKnownState["sessions"] = {};
698
+
699
+ /**
700
+ * Loads the new transaction from the SessionMap into verifiedTransactions as a VerifiedTransaction.
701
+ *
702
+ * If the transaction is already loaded from the SessionMap in the past, it will not be loaded again.
703
+ *
704
+ * Used to have a fast way to iterate over the CoValue transactions, and track their validation/decoding state.
705
+ *
706
+ * @param preload - Optional preload object containing the transaction, changes, and meta.
707
+ * If provided, the transaction will be preloaded with the given changes and meta.
708
+ *
709
+ * @internal
710
+ * */
711
+ loadVerifiedTransactionsFromLogs(preload?: {
712
+ transaction: Transaction;
713
+ changes: JsonValue[];
714
+ meta: JsonObject | undefined;
715
+ }) {
716
+ if (!this.verified) {
717
+ return;
718
+ }
719
+
720
+ for (const [sessionID, sessionLog] of this.verified.sessions.entries()) {
721
+ const count = this.verifiedTransactionsKnownSessions[sessionID] ?? 0;
722
+
723
+ sessionLog.transactions.forEach((tx, txIndex) => {
724
+ if (txIndex < count) {
725
+ return;
726
+ }
727
+
728
+ this.verifiedTransactions.push({
729
+ author: accountOrAgentIDfromSessionID(sessionID),
730
+ txID: {
731
+ sessionID,
732
+ txIndex,
733
+ },
734
+ madeAt: tx.madeAt,
735
+ isValidated: false,
736
+ isValid: false,
737
+ changes: tx === preload?.transaction ? preload.changes : undefined,
738
+ meta: tx === preload?.transaction ? preload.meta : undefined,
739
+ hasInvalidChanges: false,
740
+ hasInvalidMeta: false,
741
+ hasMetaBeenParsed: false,
742
+ tx,
743
+ });
744
+ });
745
+
746
+ this.verifiedTransactionsKnownSessions[sessionID] =
747
+ sessionLog.transactions.length;
748
+ }
749
+ }
750
+
751
+ /**
752
+ * Iterates over the verifiedTransactions and marks them as valid or invalid, based on the group membership of the authors of the transactions .
753
+ */
754
+ private determineValidTransactions() {
755
+ determineValidTransactions(this);
756
+ }
757
+
758
+ /**
759
+ * Parses the meta information of a transaction, and set the branchStart and mergeCommits.
760
+ */
761
+ private parseMetaInformation(transaction: VerifiedTransaction) {
762
+ if (
763
+ !transaction.meta ||
764
+ !transaction.isValid ||
765
+ transaction.hasMetaBeenParsed
766
+ ) {
767
+ return;
768
+ }
769
+
770
+ transaction.hasMetaBeenParsed = true;
771
+
772
+ if (
773
+ transaction.meta?.["branch"] &&
774
+ (!this.branchStart || transaction.madeAt < this.branchStart.madeAt)
775
+ ) {
776
+ this.branchStart = {
777
+ branch: transaction.meta.branch as CoValueKnownState["sessions"],
778
+ madeAt: transaction.madeAt,
779
+ };
780
+ }
781
+
782
+ if (transaction.meta?.["merge"]) {
783
+ const mergeCommit = transaction.meta as MergeCommit;
784
+
785
+ this.mergeCommits.push({
786
+ commit: mergeCommit,
787
+ madeAt: transaction.madeAt,
788
+ });
789
+ }
790
+ }
791
+
792
+ /**
793
+ * Loads the new transactions from SessionMap and:
794
+ * - Validates each transaction based on the group membership of the authors
795
+ * - Decodes the changes & meta for each transaction
796
+ * - Parses the meta information of the transaction
797
+ */
798
+ private parseNewTransactions(ignorePrivateTransactions: boolean) {
799
+ if (!this.isAvailable()) {
800
+ return;
801
+ }
802
+
803
+ this.loadVerifiedTransactionsFromLogs();
804
+ this.determineValidTransactions();
805
+
806
+ for (const transaction of this.verifiedTransactions) {
807
+ decodeTransactionChangesAndMeta(
808
+ this,
809
+ transaction,
810
+ ignorePrivateTransactions,
811
+ );
812
+ this.parseMetaInformation(transaction);
813
+ }
814
+ }
815
+
816
+ /**
817
+ * Returns the valid transactions matching the criteria specified in the options
818
+ */
649
819
  getValidTransactions(options?: {
650
820
  ignorePrivateTransactions: boolean;
651
- knownTransactions?: CoValueKnownState["sessions"];
821
+ // The range, described as knownState sessions, to filter the transactions returned
822
+ from?: CoValueKnownState["sessions"];
823
+ to?: CoValueKnownState["sessions"];
824
+
825
+ // The transactions that have already been processed, used for the incremental builds of the content views
826
+ knownTransactions?: Set<Transaction>;
827
+
828
+ // If true, the branch source transactions will be skipped. Used to gather the transactions for the merge operation.
829
+ skipBranchSource?: boolean;
652
830
  }): DecryptedTransaction[] {
653
831
  if (!this.verified) {
654
- throw new Error(
655
- "CoValueCore: getValidTransactions called on coValue without verified state",
656
- );
832
+ return [];
657
833
  }
658
834
 
659
- const validTransactions = determineValidTransactions(
660
- this,
661
- options?.knownTransactions,
662
- );
835
+ this.parseNewTransactions(options?.ignorePrivateTransactions ?? false);
663
836
 
664
- const allTransactions: DecryptedTransaction[] = [];
837
+ const matchingTransactions: DecryptedTransaction[] = [];
665
838
 
666
- for (const { txID, tx } of validTransactions) {
667
- if (options?.knownTransactions?.[txID.sessionID]! >= txID.txIndex) {
839
+ for (const transaction of this.verifiedTransactions) {
840
+ if (!isValidTransactionWithChanges(transaction)) {
668
841
  continue;
669
842
  }
670
843
 
671
- let changes: JsonValue[];
844
+ if (options?.knownTransactions?.has(transaction.tx)) {
845
+ continue;
846
+ }
672
847
 
673
- if (tx.privacy === "private") {
674
- if (options?.ignorePrivateTransactions) {
675
- continue;
676
- }
848
+ options?.knownTransactions?.add(transaction.tx);
677
849
 
678
- const readKey = this.getReadKey(tx.keyUsed);
850
+ const { txID, madeAt } = transaction;
679
851
 
680
- if (!readKey) {
681
- continue;
682
- }
852
+ const from = options?.from?.[txID.sessionID] ?? -1;
853
+ const to = options?.to?.[txID.sessionID] ?? Infinity;
683
854
 
684
- let decryptedChanges = this._decryptionCache[tx.encryptedChanges];
855
+ // The txIndex starts at 0 and from/to are referring to the count of transactions
856
+ if (from > txID.txIndex || to < txID.txIndex) {
857
+ continue;
858
+ }
685
859
 
686
- if (!decryptedChanges) {
687
- decryptedChanges = this.verified.decryptTransaction(
688
- txID.sessionID,
689
- txID.txIndex,
690
- readKey,
691
- );
860
+ matchingTransactions.push(transaction);
861
+ }
692
862
 
693
- this._decryptionCache[tx.encryptedChanges] = decryptedChanges;
694
- }
863
+ const source = getBranchSource(this);
695
864
 
696
- if (!decryptedChanges) {
697
- logger.error("Failed to decrypt transaction despite having key", {
698
- err: new Error("Failed to decrypt transaction despite having key"),
699
- });
700
- continue;
701
- }
865
+ // If this is a branch, we load the valid transactions from the source
866
+ if (source && this.branchStart && !options?.skipBranchSource) {
867
+ const sourceTransactions = source.getValidTransactions({
868
+ to: this.branchStart.branch,
869
+ ignorePrivateTransactions: options?.ignorePrivateTransactions ?? false,
870
+ knownTransactions: options?.knownTransactions,
871
+ });
702
872
 
703
- changes = decryptedChanges;
704
- } else {
705
- try {
706
- changes = parseJSON(tx.changes);
707
- } catch (e) {
708
- logger.error("Failed to parse trusting transaction on " + this.id, {
709
- err: e,
710
- });
711
- continue;
712
- }
873
+ for (const { changes, tx, madeAt, txID } of sourceTransactions) {
874
+ matchingTransactions.push({
875
+ txID: {
876
+ sessionID: `${txID.sessionID}_branch_${source.id}`,
877
+ txIndex: txID.txIndex,
878
+ },
879
+ madeAt,
880
+ changes,
881
+ tx,
882
+ });
713
883
  }
714
-
715
- allTransactions.push({
716
- txID,
717
- madeAt: tx.madeAt,
718
- changes,
719
- trusting: tx.privacy === "trusting",
720
- });
721
884
  }
722
885
 
723
- return allTransactions;
886
+ return matchingTransactions;
887
+ }
888
+
889
+ createBranch(name: string, ownerId: RawCoID) {
890
+ return createBranch(this, name, ownerId);
891
+ }
892
+
893
+ mergeBranch() {
894
+ return mergeBranch(this);
895
+ }
896
+
897
+ getBranchId(name: string, ownerId: RawCoID) {
898
+ return getBranchId(this, name, ownerId);
724
899
  }
725
900
 
726
901
  getValidSortedTransactions(options?: {
727
902
  ignorePrivateTransactions: boolean;
728
- knownTransactions: CoValueKnownState["sessions"];
903
+
904
+ // The transactions that have already been processed, used for the incremental builds of the content views
905
+ knownTransactions?: Set<Transaction>;
729
906
  }): DecryptedTransaction[] {
730
907
  const allTransactions = this.getValidTransactions(options);
731
908
 
@@ -1001,3 +1178,9 @@ export type TryAddTransactionsError =
1001
1178
  | ResolveAccountAgentError
1002
1179
  | InvalidHashError
1003
1180
  | InvalidSignatureError;
1181
+
1182
+ function isValidTransactionWithChanges(
1183
+ transaction: VerifiedTransaction,
1184
+ ): transaction is VerifiedTransaction & { changes: JsonValue[] } {
1185
+ return Boolean(transaction.isValid && transaction.changes);
1186
+ }
@@ -0,0 +1,81 @@
1
+ import { AvailableCoValueCore, VerifiedTransaction } from "./coValueCore.js";
2
+ import { safeParseJSON } from "../jsonStringify.js";
3
+
4
+ export function decodeTransactionChangesAndMeta(
5
+ coValue: AvailableCoValueCore,
6
+ transaction: VerifiedTransaction,
7
+ ignorePrivateTransactions: boolean,
8
+ ) {
9
+ if (!transaction.isValid) {
10
+ return;
11
+ }
12
+
13
+ if (transaction.tx.privacy === "private" && ignorePrivateTransactions) {
14
+ return;
15
+ }
16
+
17
+ const needsChagesParsing =
18
+ !transaction.hasInvalidChanges && !transaction.changes;
19
+ const needsMetaParsing =
20
+ !transaction.hasInvalidMeta && !transaction.meta && transaction.tx.meta;
21
+
22
+ if (!needsChagesParsing && !needsMetaParsing) {
23
+ return;
24
+ }
25
+
26
+ if (transaction.tx.privacy === "private") {
27
+ const readKey = coValue.getReadKey(transaction.tx.keyUsed);
28
+
29
+ if (!readKey) {
30
+ return;
31
+ }
32
+
33
+ if (needsChagesParsing) {
34
+ const changes = coValue.verified.decryptTransaction(
35
+ transaction.txID.sessionID,
36
+ transaction.txID.txIndex,
37
+ readKey,
38
+ );
39
+
40
+ if (!changes) {
41
+ transaction.hasInvalidChanges = true;
42
+ } else {
43
+ transaction.changes = changes;
44
+ }
45
+ }
46
+
47
+ if (needsMetaParsing) {
48
+ const meta = coValue.verified.decryptTransactionMeta(
49
+ transaction.txID.sessionID,
50
+ transaction.txID.txIndex,
51
+ readKey,
52
+ );
53
+
54
+ if (!meta) {
55
+ transaction.hasInvalidMeta = true;
56
+ } else {
57
+ transaction.meta = meta;
58
+ }
59
+ }
60
+ } else {
61
+ if (needsChagesParsing) {
62
+ const changes = safeParseJSON(transaction.tx.changes);
63
+
64
+ if (!changes) {
65
+ transaction.hasInvalidChanges = true;
66
+ } else {
67
+ transaction.changes = changes;
68
+ }
69
+ }
70
+
71
+ if (needsMetaParsing && transaction.tx.meta) {
72
+ const meta = safeParseJSON(transaction.tx.meta);
73
+
74
+ if (!meta) {
75
+ transaction.hasInvalidMeta = true;
76
+ } else {
77
+ transaction.meta = meta;
78
+ }
79
+ }
80
+ }
81
+ }
@@ -21,6 +21,7 @@ import { CoValueKnownState, NewContentMessage } from "../sync.js";
21
21
  import { TryAddTransactionsError } from "./coValueCore.js";
22
22
  import { SessionLog, SessionMap } from "./SessionMap.js";
23
23
  import { ControlledAccountOrAgent } from "../coValues/account.js";
24
+ import { logger } from "../logger.js";
24
25
 
25
26
  export type CoValueHeader = {
26
27
  type: AnyRawCoValue["type"];
@@ -40,7 +41,6 @@ export type PrivateTransaction = {
40
41
  encryptedChanges: Encrypted<JsonValue[], { in: RawCoID; tx: TransactionID }>;
41
42
  meta?: Encrypted<JsonObject, { in: RawCoID; tx: TransactionID }>;
42
43
  };
43
-
44
44
  export type TrustingTransaction = {
45
45
  privacy: "trusting";
46
46
  madeAt: number;
@@ -1,12 +1,5 @@
1
1
  import { CoID, RawCoValue } from "../coValue.js";
2
- import {
3
- AvailableCoValueCore,
4
- CoValueCore,
5
- } from "../coValueCore/coValueCore.js";
6
- import {
7
- CoValueHeader,
8
- CoValueUniqueness,
9
- } from "../coValueCore/verifiedState.js";
2
+ import { CoValueHeader } from "../coValueCore/verifiedState.js";
10
3
  import {
11
4
  AgentSecret,
12
5
  CryptoProvider,
@@ -21,7 +14,7 @@ import { LocalNode } from "../localNode.js";
21
14
  import { logger } from "../logger.js";
22
15
  import type { AccountRole, Role } from "../permissions.js";
23
16
  import { RawCoMap } from "./coMap.js";
24
- import { Everyone, InviteSecret, RawGroup } from "./group.js";
17
+ import { Everyone, EVERYONE, InviteSecret, RawGroup } from "./group.js";
25
18
 
26
19
  export function accountHeaderForInitialAgentSecret(
27
20
  agentSecret: AgentSecret,
@@ -75,6 +68,15 @@ export class RawAccount<
75
68
  throw new Error("Cannot create invite from an account");
76
69
  }
77
70
 
71
+ override roleOfInternal(
72
+ accountID: RawAccountID | AgentID | typeof EVERYONE,
73
+ ): Role | undefined {
74
+ if (accountID === this.id) {
75
+ return "admin";
76
+ }
77
+ return super.roleOfInternal(accountID);
78
+ }
79
+
78
80
  override addMember(
79
81
  account: RawAccount | ControlledAccountOrAgent | Everyone,
80
82
  role: Role,
@@ -10,6 +10,7 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
10
10
  import { isCoValue } from "../typeUtils/isCoValue.js";
11
11
  import { RawAccountID } from "./account.js";
12
12
  import { RawGroup } from "./group.js";
13
+ import { Transaction } from "../coValueCore/verifiedState.js";
13
14
 
14
15
  export type OpID = TransactionID & { changeIdx: number };
15
16
 
@@ -86,8 +87,12 @@ export class RawCoList<
86
87
  opID: OpID;
87
88
  }[];
88
89
  /** @internal */
89
- totalValidTransactions = 0;
90
- knownTransactions: CoValueKnownState["sessions"] = {};
90
+ knownTransactions: Set<Transaction>;
91
+
92
+ get totalValidTransactions() {
93
+ return this.knownTransactions.size;
94
+ }
95
+
91
96
  lastValidTransaction: number | undefined;
92
97
 
93
98
  /** @internal */
@@ -99,7 +104,7 @@ export class RawCoList<
99
104
  this.deletionsByInsertion = {};
100
105
  this.afterStart = [];
101
106
  this.beforeEnd = [];
102
- this.knownTransactions = {};
107
+ this.knownTransactions = new Set<Transaction>();
103
108
 
104
109
  this.processNewTransactions();
105
110
  }
@@ -114,7 +119,6 @@ export class RawCoList<
114
119
  return;
115
120
  }
116
121
 
117
- this.totalValidTransactions += transactions.length;
118
122
  let lastValidTransaction: number | undefined = undefined;
119
123
  let oldestValidTransaction: number | undefined = undefined;
120
124
  this._cachedEntries = undefined;
@@ -126,11 +130,6 @@ export class RawCoList<
126
130
  madeAt,
127
131
  );
128
132
 
129
- this.knownTransactions[txID.sessionID] = Math.max(
130
- this.knownTransactions[txID.sessionID] ?? 0,
131
- txID.txIndex,
132
- );
133
-
134
133
  for (const [changeIdx, changeUntyped] of changes.entries()) {
135
134
  const change = changeUntyped as ListOpPayload<Item>;
136
135
 
@@ -624,7 +623,6 @@ export class RawCoList<
624
623
  this.afterStart = listAfter.afterStart;
625
624
  this.beforeEnd = listAfter.beforeEnd;
626
625
  this.insertions = listAfter.insertions;
627
- this.totalValidTransactions = listAfter.totalValidTransactions;
628
626
  this.lastValidTransaction = listAfter.lastValidTransaction;
629
627
  this.knownTransactions = listAfter.knownTransactions;
630
628
  this.deletionsByInsertion = listAfter.deletionsByInsertion;
@@ -10,6 +10,7 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
10
10
  import { isCoValue } from "../typeUtils/isCoValue.js";
11
11
  import { RawAccountID } from "./account.js";
12
12
  import type { RawGroup } from "./group.js";
13
+ import { Transaction } from "../coValueCore/verifiedState.js";
13
14
 
14
15
  type MapOp<K extends string, V extends JsonValue | undefined> = {
15
16
  txID: TransactionID;
@@ -57,7 +58,7 @@ export class RawCoMapView<
57
58
  [Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
58
59
  };
59
60
  /** @internal */
60
- knownTransactions: CoValueKnownState["sessions"];
61
+ knownTransactions: Set<Transaction>;
61
62
 
62
63
  /** @internal */
63
64
  ignorePrivateTransactions: boolean;
@@ -66,7 +67,9 @@ export class RawCoMapView<
66
67
  /** @category 6. Meta */
67
68
  readonly _shape!: Shape;
68
69
 
69
- totalValidTransactions = 0;
70
+ get totalValidTransactions() {
71
+ return this.knownTransactions.size;
72
+ }
70
73
 
71
74
  /** @internal */
72
75
  constructor(
@@ -83,7 +86,7 @@ export class RawCoMapView<
83
86
  options?.ignorePrivateTransactions ?? false;
84
87
  this.ops = {};
85
88
  this.latest = {};
86
- this.knownTransactions = {};
89
+ this.knownTransactions = new Set<Transaction>();
87
90
 
88
91
  this.processNewTransactions();
89
92
  }
@@ -113,7 +116,7 @@ export class RawCoMapView<
113
116
  NonNullable<(typeof ops)[keyof typeof ops]>
114
117
  >();
115
118
 
116
- for (const { txID, changes, madeAt, trusting } of newValidTransactions) {
119
+ for (const { txID, changes, madeAt, tx } of newValidTransactions) {
117
120
  if (madeAt > this.latestTxMadeAt) {
118
121
  this.latestTxMadeAt = madeAt;
119
122
  }
@@ -128,7 +131,7 @@ export class RawCoMapView<
128
131
  madeAt,
129
132
  changeIdx,
130
133
  change,
131
- trusting,
134
+ trusting: tx.privacy === "trusting",
132
135
  };
133
136
 
134
137
  const entries = ops[change.key];
@@ -140,15 +143,9 @@ export class RawCoMapView<
140
143
  entries.push(entry);
141
144
  changedEntries.set(change.key, entries);
142
145
  }
143
- this.knownTransactions[txID.sessionID] = Math.max(
144
- this.knownTransactions[txID.sessionID] ?? 0,
145
- txID.txIndex,
146
- );
147
146
  }
148
147
  }
149
148
 
150
- this.totalValidTransactions += newValidTransactions.length;
151
-
152
149
  for (const entries of changedEntries.values()) {
153
150
  entries.sort(this.core.compareTransactions);
154
151
  }