cojson 0.13.17 → 0.13.20

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 (180) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/PeerState.d.ts +4 -1
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +16 -36
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/SyncStateManager.d.ts.map +1 -1
  8. package/dist/SyncStateManager.js +2 -3
  9. package/dist/SyncStateManager.js.map +1 -1
  10. package/dist/coValue.d.ts +4 -4
  11. package/dist/coValue.d.ts.map +1 -1
  12. package/dist/coValue.js +4 -4
  13. package/dist/coValue.js.map +1 -1
  14. package/dist/coValueCore/coValueCore.d.ts +143 -0
  15. package/dist/coValueCore/coValueCore.d.ts.map +1 -0
  16. package/dist/{coValueCore.js → coValueCore/coValueCore.js} +325 -253
  17. package/dist/coValueCore/coValueCore.js.map +1 -0
  18. package/dist/coValueCore/verifiedState.d.ts +65 -0
  19. package/dist/coValueCore/verifiedState.d.ts.map +1 -0
  20. package/dist/coValueCore/verifiedState.js +210 -0
  21. package/dist/coValueCore/verifiedState.js.map +1 -0
  22. package/dist/coValues/account.d.ts +8 -10
  23. package/dist/coValues/account.d.ts.map +1 -1
  24. package/dist/coValues/account.js +12 -13
  25. package/dist/coValues/account.js.map +1 -1
  26. package/dist/coValues/coList.d.ts +3 -3
  27. package/dist/coValues/coList.d.ts.map +1 -1
  28. package/dist/coValues/coList.js +6 -3
  29. package/dist/coValues/coList.js.map +1 -1
  30. package/dist/coValues/coMap.d.ts +3 -3
  31. package/dist/coValues/coMap.d.ts.map +1 -1
  32. package/dist/coValues/coMap.js +3 -3
  33. package/dist/coValues/coMap.js.map +1 -1
  34. package/dist/coValues/coPlainText.d.ts +2 -2
  35. package/dist/coValues/coPlainText.d.ts.map +1 -1
  36. package/dist/coValues/coPlainText.js +4 -4
  37. package/dist/coValues/coPlainText.js.map +1 -1
  38. package/dist/coValues/coStream.d.ts +3 -3
  39. package/dist/coValues/coStream.d.ts.map +1 -1
  40. package/dist/coValues/coStream.js +3 -3
  41. package/dist/coValues/coStream.js.map +1 -1
  42. package/dist/coValues/group.d.ts +7 -2
  43. package/dist/coValues/group.d.ts.map +1 -1
  44. package/dist/coValues/group.js +29 -26
  45. package/dist/coValues/group.js.map +1 -1
  46. package/dist/coreToCoValue.d.ts +2 -2
  47. package/dist/coreToCoValue.d.ts.map +1 -1
  48. package/dist/coreToCoValue.js +10 -14
  49. package/dist/coreToCoValue.js.map +1 -1
  50. package/dist/exports.d.ts +6 -5
  51. package/dist/exports.d.ts.map +1 -1
  52. package/dist/exports.js +3 -4
  53. package/dist/exports.js.map +1 -1
  54. package/dist/localNode.d.ts +30 -24
  55. package/dist/localNode.d.ts.map +1 -1
  56. package/dist/localNode.js +147 -173
  57. package/dist/localNode.js.map +1 -1
  58. package/dist/permissions.d.ts +2 -1
  59. package/dist/permissions.d.ts.map +1 -1
  60. package/dist/permissions.js +15 -11
  61. package/dist/permissions.js.map +1 -1
  62. package/dist/priority.d.ts +1 -1
  63. package/dist/priority.d.ts.map +1 -1
  64. package/dist/streamUtils.d.ts +5 -5
  65. package/dist/streamUtils.d.ts.map +1 -1
  66. package/dist/streamUtils.js +5 -20
  67. package/dist/streamUtils.js.map +1 -1
  68. package/dist/sync.d.ts +8 -6
  69. package/dist/sync.d.ts.map +1 -1
  70. package/dist/sync.js +121 -74
  71. package/dist/sync.js.map +1 -1
  72. package/dist/tests/PeerState.test.js +0 -31
  73. package/dist/tests/PeerState.test.js.map +1 -1
  74. package/dist/tests/SyncStateManager.test.js +41 -6
  75. package/dist/tests/SyncStateManager.test.js.map +1 -1
  76. package/dist/tests/account.test.js +16 -0
  77. package/dist/tests/account.test.js.map +1 -1
  78. package/dist/tests/coList.test.js +19 -16
  79. package/dist/tests/coList.test.js.map +1 -1
  80. package/dist/tests/coMap.test.js +12 -13
  81. package/dist/tests/coMap.test.js.map +1 -1
  82. package/dist/tests/coPlainText.test.js +9 -10
  83. package/dist/tests/coPlainText.test.js.map +1 -1
  84. package/dist/tests/coStream.test.js +22 -17
  85. package/dist/tests/coStream.test.js.map +1 -1
  86. package/dist/tests/coValueCore.test.js +22 -28
  87. package/dist/tests/coValueCore.test.js.map +1 -1
  88. package/dist/tests/coValueCoreLoadingState.test.d.ts +2 -0
  89. package/dist/tests/coValueCoreLoadingState.test.d.ts.map +1 -0
  90. package/dist/tests/{coValueState.test.js → coValueCoreLoadingState.test.js} +62 -46
  91. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -0
  92. package/dist/tests/group.test.js +42 -43
  93. package/dist/tests/group.test.js.map +1 -1
  94. package/dist/tests/messagesTestUtils.d.ts +2 -2
  95. package/dist/tests/messagesTestUtils.d.ts.map +1 -1
  96. package/dist/tests/messagesTestUtils.js +1 -1
  97. package/dist/tests/messagesTestUtils.js.map +1 -1
  98. package/dist/tests/permissions.test.js +224 -292
  99. package/dist/tests/permissions.test.js.map +1 -1
  100. package/dist/tests/priority.test.js +13 -14
  101. package/dist/tests/priority.test.js.map +1 -1
  102. package/dist/tests/sync.auth.test.d.ts +2 -0
  103. package/dist/tests/sync.auth.test.d.ts.map +1 -0
  104. package/dist/tests/sync.auth.test.js +190 -0
  105. package/dist/tests/sync.auth.test.js.map +1 -0
  106. package/dist/tests/sync.load.test.js +6 -6
  107. package/dist/tests/sync.load.test.js.map +1 -1
  108. package/dist/tests/sync.mesh.test.js +25 -12
  109. package/dist/tests/sync.mesh.test.js.map +1 -1
  110. package/dist/tests/sync.peerReconciliation.test.js +19 -19
  111. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  112. package/dist/tests/sync.storage.test.js +20 -13
  113. package/dist/tests/sync.storage.test.js.map +1 -1
  114. package/dist/tests/sync.test.js +32 -39
  115. package/dist/tests/sync.test.js.map +1 -1
  116. package/dist/tests/sync.upload.test.js +126 -37
  117. package/dist/tests/sync.upload.test.js.map +1 -1
  118. package/dist/tests/testUtils.d.ts +35 -17
  119. package/dist/tests/testUtils.d.ts.map +1 -1
  120. package/dist/tests/testUtils.js +103 -79
  121. package/dist/tests/testUtils.js.map +1 -1
  122. package/dist/typeUtils/expectGroup.js +1 -1
  123. package/dist/typeUtils/expectGroup.js.map +1 -1
  124. package/package.json +1 -1
  125. package/src/PeerState.ts +19 -40
  126. package/src/SyncStateManager.ts +2 -3
  127. package/src/coValue.ts +11 -8
  128. package/src/{coValueCore.ts → coValueCore/coValueCore.ts} +478 -422
  129. package/src/coValueCore/verifiedState.ts +376 -0
  130. package/src/coValues/account.ts +20 -25
  131. package/src/coValues/coList.ts +12 -6
  132. package/src/coValues/coMap.ts +9 -6
  133. package/src/coValues/coPlainText.ts +9 -6
  134. package/src/coValues/coStream.ts +9 -6
  135. package/src/coValues/group.ts +50 -28
  136. package/src/coreToCoValue.ts +14 -15
  137. package/src/exports.ts +9 -7
  138. package/src/localNode.ts +236 -275
  139. package/src/permissions.ts +18 -12
  140. package/src/priority.ts +1 -1
  141. package/src/streamUtils.ts +7 -34
  142. package/src/sync.ts +146 -84
  143. package/src/tests/PeerState.test.ts +0 -37
  144. package/src/tests/SyncStateManager.test.ts +56 -6
  145. package/src/tests/account.test.ts +24 -0
  146. package/src/tests/coList.test.ts +21 -15
  147. package/src/tests/coMap.test.ts +12 -13
  148. package/src/tests/coPlainText.test.ts +12 -9
  149. package/src/tests/coStream.test.ts +25 -16
  150. package/src/tests/coValueCore.test.ts +30 -27
  151. package/src/tests/{coValueState.test.ts → coValueCoreLoadingState.test.ts} +67 -57
  152. package/src/tests/group.test.ts +44 -69
  153. package/src/tests/messagesTestUtils.ts +3 -8
  154. package/src/tests/permissions.test.ts +283 -449
  155. package/src/tests/priority.test.ts +17 -13
  156. package/src/tests/sync.auth.test.ts +246 -0
  157. package/src/tests/sync.load.test.ts +7 -6
  158. package/src/tests/sync.mesh.test.ts +25 -12
  159. package/src/tests/sync.peerReconciliation.test.ts +25 -25
  160. package/src/tests/sync.storage.test.ts +20 -13
  161. package/src/tests/sync.test.ts +43 -43
  162. package/src/tests/sync.upload.test.ts +157 -37
  163. package/src/tests/testUtils.ts +143 -96
  164. package/src/typeUtils/expectGroup.ts +1 -1
  165. package/dist/CoValuesStore.d.ts +0 -14
  166. package/dist/CoValuesStore.d.ts.map +0 -1
  167. package/dist/CoValuesStore.js +0 -32
  168. package/dist/CoValuesStore.js.map +0 -1
  169. package/dist/coValueCore.d.ts +0 -142
  170. package/dist/coValueCore.d.ts.map +0 -1
  171. package/dist/coValueCore.js.map +0 -1
  172. package/dist/coValueState.d.ts +0 -34
  173. package/dist/coValueState.d.ts.map +0 -1
  174. package/dist/coValueState.js +0 -190
  175. package/dist/coValueState.js.map +0 -1
  176. package/dist/tests/coValueState.test.d.ts +0 -2
  177. package/dist/tests/coValueState.test.d.ts.map +0 -1
  178. package/dist/tests/coValueState.test.js.map +0 -1
  179. package/src/CoValuesStore.ts +0 -41
  180. package/src/coValueState.ts +0 -245
package/src/localNode.ts CHANGED
@@ -1,14 +1,19 @@
1
- import { Result, ResultAsync, err, ok, okAsync } from "neverthrow";
2
- import { CoValuesStore } from "./CoValuesStore.js";
1
+ import { Result, err, ok } from "neverthrow";
3
2
  import { CoID } from "./coValue.js";
4
3
  import { RawCoValue } from "./coValue.js";
5
4
  import {
5
+ AvailableCoValueCore,
6
6
  CoValueCore,
7
+ idforHeader,
8
+ } from "./coValueCore/coValueCore.js";
9
+ import {
7
10
  CoValueHeader,
8
11
  CoValueUniqueness,
9
- } from "./coValueCore.js";
12
+ VerifiedState,
13
+ } from "./coValueCore/verifiedState.js";
10
14
  import {
11
15
  AccountMeta,
16
+ ControlledAccount,
12
17
  ControlledAccountOrAgent,
13
18
  ControlledAgent,
14
19
  InvalidAccountAgentIDError,
@@ -16,9 +21,9 @@ import {
16
21
  RawAccount,
17
22
  RawAccountID,
18
23
  RawAccountMigration,
19
- RawControlledAccount,
20
24
  RawProfile,
21
25
  accountHeaderForInitialAgentSecret,
26
+ expectAccount,
22
27
  } from "./coValues/account.js";
23
28
  import {
24
29
  InviteSecret,
@@ -29,6 +34,7 @@ import { AgentSecret, CryptoProvider } from "./crypto/crypto.js";
29
34
  import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
30
35
  import { logger } from "./logger.js";
31
36
  import { Peer, PeerID, SyncManager } from "./sync.js";
37
+ import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
32
38
  import { expectGroup } from "./typeUtils/expectGroup.js";
33
39
 
34
40
  /** A `LocalNode` represents a local view of a set of loaded `CoValue`s, from the perspective of a particular account (or primitive cryptographic agent).
@@ -46,11 +52,12 @@ export class LocalNode {
46
52
  /** @internal */
47
53
  crypto: CryptoProvider;
48
54
  /** @internal */
49
- coValuesStore = new CoValuesStore();
50
- /** @category 3. Low-level */
51
- account: ControlledAccountOrAgent;
55
+ private readonly coValues = new Map<RawCoID, CoValueCore>();
56
+
52
57
  /** @category 3. Low-level */
53
- currentSessionID: SessionID;
58
+ readonly currentSessionID: SessionID;
59
+ readonly agentSecret: AgentSecret;
60
+
54
61
  /** @category 3. Low-level */
55
62
  syncManager = new SyncManager(this);
56
63
 
@@ -58,17 +65,129 @@ export class LocalNode {
58
65
 
59
66
  /** @category 3. Low-level */
60
67
  constructor(
61
- account: ControlledAccountOrAgent,
68
+ agentSecret: AgentSecret,
62
69
  currentSessionID: SessionID,
63
70
  crypto: CryptoProvider,
64
71
  ) {
65
- this.account = account;
72
+ this.agentSecret = agentSecret;
66
73
  this.currentSessionID = currentSessionID;
67
74
  this.crypto = crypto;
68
75
  }
69
76
 
77
+ getCoValue(id: RawCoID) {
78
+ let entry = this.coValues.get(id);
79
+
80
+ if (!entry) {
81
+ entry = CoValueCore.fromID(id, this);
82
+ this.coValues.set(id, entry);
83
+ }
84
+
85
+ return entry;
86
+ }
87
+
88
+ allCoValues() {
89
+ return this.coValues.values();
90
+ }
91
+
92
+ private putCoValue(
93
+ id: RawCoID,
94
+ verified: VerifiedState,
95
+ { forceOverwrite = false }: { forceOverwrite?: boolean } = {},
96
+ ): AvailableCoValueCore {
97
+ const entry = this.getCoValue(id);
98
+ entry.internalMarkMagicallyAvailable(verified, { forceOverwrite });
99
+ return entry as AvailableCoValueCore;
100
+ }
101
+
102
+ internalDeleteCoValue(id: RawCoID) {
103
+ this.coValues.delete(id);
104
+ }
105
+
106
+ getCurrentAgent(): ControlledAccountOrAgent {
107
+ const accountOrAgent = accountOrAgentIDfromSessionID(this.currentSessionID);
108
+ if (isAgentID(accountOrAgent)) {
109
+ return new ControlledAgent(this.agentSecret, this.crypto);
110
+ }
111
+ return new ControlledAccount(
112
+ expectAccount(
113
+ this.expectCoValueLoaded(accountOrAgent).getCurrentContent(),
114
+ ),
115
+ this.agentSecret,
116
+ );
117
+ }
118
+
119
+ expectCurrentAccountID(reason: string): RawAccountID {
120
+ const accountOrAgent = accountOrAgentIDfromSessionID(this.currentSessionID);
121
+ if (isAgentID(accountOrAgent)) {
122
+ throw new Error(
123
+ "Current account is an agent, but expected an account: " + reason,
124
+ );
125
+ }
126
+ return accountOrAgent;
127
+ }
128
+
129
+ expectCurrentAccount(reason: string): RawAccount {
130
+ const accountID = this.expectCurrentAccountID(reason);
131
+ return expectAccount(
132
+ this.expectCoValueLoaded(accountID).getCurrentContent(),
133
+ );
134
+ }
135
+
136
+ static internalCreateAccount(opts: {
137
+ crypto: CryptoProvider;
138
+ initialAgentSecret?: AgentSecret;
139
+ peersToLoadFrom?: Peer[];
140
+ }): RawAccount {
141
+ const {
142
+ crypto,
143
+ initialAgentSecret = crypto.newRandomAgentSecret(),
144
+ peersToLoadFrom = [],
145
+ } = opts;
146
+ const accountHeader = accountHeaderForInitialAgentSecret(
147
+ initialAgentSecret,
148
+ crypto,
149
+ );
150
+ const accountID = idforHeader(accountHeader, crypto);
151
+
152
+ const node = new LocalNode(
153
+ initialAgentSecret,
154
+ crypto.newRandomSessionID(accountID as RawAccountID),
155
+ crypto,
156
+ );
157
+
158
+ for (const peer of peersToLoadFrom) {
159
+ node.syncManager.addPeer(peer);
160
+ }
161
+
162
+ const accountAgentID = crypto.getAgentID(initialAgentSecret);
163
+
164
+ const rawAccount = expectGroup(
165
+ node.createCoValue(accountHeader).getCurrentContent(),
166
+ );
167
+
168
+ rawAccount.set(accountAgentID, "admin", "trusting");
169
+
170
+ const readKey = crypto.newRandomKeySecret();
171
+
172
+ const sealed = crypto.seal({
173
+ message: readKey.secret,
174
+ from: crypto.getAgentSealerSecret(initialAgentSecret),
175
+ to: crypto.getAgentSealerID(accountAgentID),
176
+ nOnceMaterial: {
177
+ in: rawAccount.id,
178
+ tx: rawAccount.core.nextTransactionID(),
179
+ },
180
+ });
181
+
182
+ rawAccount.set(`${readKey.id}_for_${accountAgentID}`, sealed, "trusting");
183
+
184
+ rawAccount.set("readKey", readKey.id, "trusting");
185
+
186
+ return node.expectCurrentAccount("after creation");
187
+ }
188
+
70
189
  /** @category 2. Node Creation */
71
- static async withNewlyCreatedAccount<Meta extends AccountMeta = AccountMeta>({
190
+ static async withNewlyCreatedAccount({
72
191
  creationProps,
73
192
  peersToLoadFrom,
74
193
  migration,
@@ -77,7 +196,7 @@ export class LocalNode {
77
196
  }: {
78
197
  creationProps: { name: string };
79
198
  peersToLoadFrom?: Peer[];
80
- migration?: RawAccountMigration<Meta>;
199
+ migration?: RawAccountMigration<AccountMeta>;
81
200
  crypto: CryptoProvider;
82
201
  initialAgentSecret?: AgentSecret;
83
202
  }): Promise<{
@@ -86,81 +205,47 @@ export class LocalNode {
86
205
  accountSecret: AgentSecret;
87
206
  sessionID: SessionID;
88
207
  }> {
89
- const throwawayAgent = crypto.newRandomAgentSecret();
90
- const setupNode = new LocalNode(
91
- new ControlledAgent(throwawayAgent, crypto),
92
- crypto.newRandomSessionID(crypto.getAgentID(throwawayAgent)),
208
+ const account = LocalNode.internalCreateAccount({
93
209
  crypto,
94
- );
95
-
96
- const account = setupNode.createAccount(initialAgentSecret);
97
-
98
- const nodeWithAccount = account.core.node.testWithDifferentAccount(
99
- account,
100
- crypto.newRandomSessionID(account.id),
101
- );
102
-
103
- const accountOnNodeWithAccount =
104
- nodeWithAccount.account as RawControlledAccount<Meta>;
105
-
106
- if (peersToLoadFrom) {
107
- for (const peer of peersToLoadFrom) {
108
- nodeWithAccount.syncManager.addPeer(peer);
109
- }
110
- }
210
+ initialAgentSecret,
211
+ peersToLoadFrom,
212
+ });
213
+ const node = account.core.node;
111
214
 
112
215
  if (migration) {
113
- await migration(accountOnNodeWithAccount, nodeWithAccount, creationProps);
216
+ await migration(account, node, creationProps);
114
217
  } else {
115
- const profileGroup = accountOnNodeWithAccount.createGroup();
218
+ const profileGroup = node.createGroup();
116
219
  profileGroup.addMember("everyone", "reader");
117
220
  const profile = profileGroup.createMap<Profile>({
118
221
  name: creationProps.name,
119
222
  });
120
- accountOnNodeWithAccount.set("profile", profile.id, "trusting");
223
+ account.set("profile", profile.id, "trusting");
121
224
  }
122
225
 
123
- const controlledAccount = new RawControlledAccount(
124
- accountOnNodeWithAccount.core,
125
- accountOnNodeWithAccount.agentSecret,
126
- );
226
+ const profileId = account.get("profile");
127
227
 
128
- nodeWithAccount.account = controlledAccount;
129
- nodeWithAccount.coValuesStore.internalMarkMagicallyAvailable(
130
- controlledAccount.id,
131
- controlledAccount.core,
132
- );
133
- controlledAccount.core._cachedContent = undefined;
134
-
135
- if (!controlledAccount.get("profile")) {
228
+ if (!profileId) {
136
229
  throw new Error("Must set account profile in initial migration");
137
230
  }
138
231
 
139
- // we shouldn't need this, but it fixes account data not syncing for new accounts
140
- function syncAllCoValuesAfterCreateAccount() {
141
- for (const coValueEntry of nodeWithAccount.coValuesStore.getValues()) {
142
- if (coValueEntry.isAvailable()) {
143
- void nodeWithAccount.syncManager.requestCoValueSync(
144
- coValueEntry.core,
145
- );
146
- }
147
- }
232
+ if (node.syncManager.hasStoragePeers()) {
233
+ await Promise.all([
234
+ node.syncManager.waitForStorageSync(account.id),
235
+ node.syncManager.waitForStorageSync(profileId),
236
+ ]);
148
237
  }
149
238
 
150
- syncAllCoValuesAfterCreateAccount();
151
-
152
- setTimeout(syncAllCoValuesAfterCreateAccount, 500);
153
-
154
239
  return {
155
- node: nodeWithAccount,
156
- accountID: accountOnNodeWithAccount.id,
157
- accountSecret: accountOnNodeWithAccount.agentSecret,
158
- sessionID: nodeWithAccount.currentSessionID,
240
+ node,
241
+ accountID: account.id,
242
+ accountSecret: initialAgentSecret,
243
+ sessionID: node.currentSessionID,
159
244
  };
160
245
  }
161
246
 
162
247
  /** @category 2. Node Creation */
163
- static async withLoadedAccount<Meta extends AccountMeta = AccountMeta>({
248
+ static async withLoadedAccount({
164
249
  accountID,
165
250
  accountSecret,
166
251
  sessionID,
@@ -173,63 +258,35 @@ export class LocalNode {
173
258
  sessionID: SessionID | undefined;
174
259
  peersToLoadFrom: Peer[];
175
260
  crypto: CryptoProvider;
176
- migration?: RawAccountMigration<Meta>;
261
+ migration?: RawAccountMigration<AccountMeta>;
177
262
  }): Promise<LocalNode> {
178
263
  try {
179
- const loadingNode = new LocalNode(
180
- new ControlledAgent(accountSecret, crypto),
181
- crypto.newRandomSessionID(accountID),
264
+ const node = new LocalNode(
265
+ accountSecret,
266
+ sessionID || crypto.newRandomSessionID(accountID),
182
267
  crypto,
183
268
  );
184
269
 
185
270
  for (const peer of peersToLoadFrom) {
186
- loadingNode.syncManager.addPeer(peer);
271
+ node.syncManager.addPeer(peer);
187
272
  }
188
273
 
189
- const accountPromise = loadingNode.load(accountID);
190
-
191
- const account = await accountPromise;
274
+ const account = await node.load(accountID);
192
275
 
193
276
  if (account === "unavailable") {
194
277
  throw new Error("Account unavailable from all peers");
195
278
  }
196
279
 
197
- const controlledAccount = new RawControlledAccount(
198
- account.core,
199
- accountSecret,
200
- );
201
-
202
- // since this is all synchronous, we can just swap out nodes for the SyncManager
203
- const node = loadingNode.testWithDifferentAccount(
204
- controlledAccount,
205
- sessionID || crypto.newRandomSessionID(accountID),
206
- );
207
- node.syncManager = loadingNode.syncManager;
208
- node.syncManager.local = node;
209
-
210
- controlledAccount.core.node = node;
211
- node.coValuesStore.internalMarkMagicallyAvailable(
212
- accountID,
213
- controlledAccount.core,
214
- );
215
- controlledAccount.core._cachedContent = undefined;
216
-
217
280
  const profileID = account.get("profile");
218
281
  if (!profileID) {
219
282
  throw new Error("Account has no profile");
220
283
  }
221
- const profile = await node.load(profileID);
222
284
 
223
- if (profile === "unavailable") {
224
- throw new Error("Profile unavailable from all peers");
225
- }
285
+ // Preload the profile
286
+ await node.load(profileID);
226
287
 
227
288
  if (migration) {
228
- await migration(controlledAccount as RawControlledAccount<Meta>, node);
229
- node.account = new RawControlledAccount(
230
- controlledAccount.core,
231
- controlledAccount.agentSecret,
232
- );
289
+ await migration(account, node);
233
290
  }
234
291
 
235
292
  return node;
@@ -240,15 +297,19 @@ export class LocalNode {
240
297
  }
241
298
 
242
299
  /** @internal */
243
- createCoValue(header: CoValueHeader): CoValueCore {
300
+ createCoValue(header: CoValueHeader): AvailableCoValueCore {
244
301
  if (this.crashed) {
245
302
  throw new Error("Trying to create CoValue after node has crashed", {
246
303
  cause: this.crashed,
247
304
  });
248
305
  }
249
306
 
250
- const coValue = new CoValueCore(header, this);
251
- this.coValuesStore.internalMarkMagicallyAvailable(coValue.id, coValue);
307
+ const id = idforHeader(header, this.crypto);
308
+
309
+ const coValue = this.putCoValue(
310
+ id,
311
+ new VerifiedState(id, this.crypto, header, new Map()),
312
+ );
252
313
 
253
314
  void this.syncManager.requestCoValueSync(coValue);
254
315
 
@@ -259,7 +320,7 @@ export class LocalNode {
259
320
  async loadCoValueCore(
260
321
  id: RawCoID,
261
322
  skipLoadingFromPeer?: PeerID,
262
- ): Promise<CoValueCore | "unavailable"> {
323
+ ): Promise<CoValueCore> {
263
324
  if (this.crashed) {
264
325
  throw new Error("Trying to load CoValue after node has crashed", {
265
326
  cause: this.crashed,
@@ -269,20 +330,20 @@ export class LocalNode {
269
330
  let retries = 0;
270
331
 
271
332
  while (true) {
272
- const entry = this.coValuesStore.get(id);
333
+ const coValue = this.getCoValue(id);
273
334
 
274
335
  if (
275
- entry.highLevelState === "unknown" ||
276
- entry.highLevelState === "unavailable"
336
+ coValue.loadingState === "unknown" ||
337
+ coValue.loadingState === "unavailable"
277
338
  ) {
278
339
  const peers =
279
340
  this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
280
341
 
281
342
  if (peers.length === 0) {
282
- return "unavailable";
343
+ return coValue;
283
344
  }
284
345
 
285
- entry.loadFromPeers(peers).catch((e) => {
346
+ coValue.loadFromPeers(peers).catch((e) => {
286
347
  logger.error("Error loading from peers", {
287
348
  id,
288
349
  err: e,
@@ -290,9 +351,8 @@ export class LocalNode {
290
351
  });
291
352
  }
292
353
 
293
- const result = await entry.getCoValue();
294
-
295
- if (result !== "unavailable" || retries >= 1) {
354
+ const result = await coValue.waitForAvailableOrUnavailable();
355
+ if (result.isAvailable() || retries >= 1) {
296
356
  return result;
297
357
  }
298
358
 
@@ -320,7 +380,7 @@ export class LocalNode {
320
380
 
321
381
  const core = await this.loadCoValueCore(id);
322
382
 
323
- if (core === "unavailable") {
383
+ if (!core.isAvailable()) {
324
384
  return "unavailable";
325
385
  }
326
386
 
@@ -328,10 +388,10 @@ export class LocalNode {
328
388
  }
329
389
 
330
390
  getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
331
- const entry = this.coValuesStore.get(id);
391
+ const coValue = this.getCoValue(id);
332
392
 
333
- if (entry.isAvailable()) {
334
- return entry.core.getCurrentContent() as T;
393
+ if (coValue.isAvailable()) {
394
+ return coValue.getCurrentContent() as T;
335
395
  }
336
396
 
337
397
  return undefined;
@@ -369,7 +429,6 @@ export class LocalNode {
369
429
  };
370
430
  }
371
431
 
372
- /** @deprecated Use Account.acceptInvite instead */
373
432
  async acceptInvite<T extends RawCoValue>(
374
433
  groupOrOwnedValueID: CoID<T>,
375
434
  inviteSecret: InviteSecret,
@@ -382,12 +441,16 @@ export class LocalNode {
382
441
  );
383
442
  }
384
443
 
385
- if (groupOrOwnedValue.core.header.ruleset.type === "ownedByGroup") {
444
+ if (
445
+ groupOrOwnedValue.core.verified.header.ruleset.type === "ownedByGroup"
446
+ ) {
386
447
  return this.acceptInvite(
387
- groupOrOwnedValue.core.header.ruleset.group as CoID<RawGroup>,
448
+ groupOrOwnedValue.core.verified.header.ruleset.group as CoID<RawGroup>,
388
449
  inviteSecret,
389
450
  );
390
- } else if (groupOrOwnedValue.core.header.ruleset.type !== "group") {
451
+ } else if (
452
+ groupOrOwnedValue.core.verified.header.ruleset.type !== "group"
453
+ ) {
391
454
  throw new Error("Can only accept invites to groups");
392
455
  }
393
456
 
@@ -415,7 +478,8 @@ export class LocalNode {
415
478
  throw new Error("No invite found");
416
479
  }
417
480
 
418
- const existingRole = group.get(this.account.id);
481
+ const account = this.getCurrentAgent();
482
+ const existingRole = group.get(account.id);
419
483
 
420
484
  if (
421
485
  existingRole === "admin" ||
@@ -429,16 +493,13 @@ export class LocalNode {
429
493
  }
430
494
 
431
495
  const groupAsInvite = expectGroup(
432
- group.core
433
- .testWithDifferentAccount(
434
- new ControlledAgent(inviteAgentSecret, this.crypto),
435
- this.crypto.newRandomSessionID(inviteAgentID),
436
- )
437
- .getCurrentContent(),
496
+ group.core.contentInClonedNodeWithDifferentAccount(
497
+ new ControlledAgent(inviteAgentSecret, this.crypto),
498
+ ),
438
499
  );
439
500
 
440
501
  groupAsInvite.addMemberInternal(
441
- this.account,
502
+ account,
442
503
  inviteRole === "adminInvite"
443
504
  ? "admin"
444
505
  : inviteRole === "writerInvite"
@@ -448,24 +509,25 @@ export class LocalNode {
448
509
  : "reader",
449
510
  );
450
511
 
451
- group.core._sessionLogs = groupAsInvite.core.sessionLogs;
452
- group.core._cachedContent = undefined;
512
+ group.core.internalShamefullyCloneVerifiedStateFrom(
513
+ groupAsInvite.core.verified,
514
+ { forceOverwrite: true },
515
+ );
516
+ group.core.internalShamefullyResetCachedContent();
453
517
 
454
- for (const groupListener of group.core.listeners) {
455
- groupListener(group.core.getCurrentContent());
456
- }
518
+ group.core.notifyUpdate("immediate");
457
519
  }
458
520
 
459
521
  /** @internal */
460
- expectCoValueLoaded(id: RawCoID, expectation?: string): CoValueCore {
461
- const entry = this.coValuesStore.get(id);
522
+ expectCoValueLoaded(id: RawCoID, expectation?: string): AvailableCoValueCore {
523
+ const coValue = this.getCoValue(id);
462
524
 
463
- if (!entry.isAvailable()) {
525
+ if (!coValue.isAvailable()) {
464
526
  throw new Error(
465
- `${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(entry)}`,
527
+ `${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(coValue)}`,
466
528
  );
467
529
  }
468
- return entry.core;
530
+ return coValue;
469
531
  }
470
532
 
471
533
  /** @internal */
@@ -483,49 +545,6 @@ export class LocalNode {
483
545
  ).getCurrentContent() as RawProfile;
484
546
  }
485
547
 
486
- /** @internal */
487
- createAccount(
488
- agentSecret = this.crypto.newRandomAgentSecret(),
489
- ): RawControlledAccount {
490
- const accountAgentID = this.crypto.getAgentID(agentSecret);
491
- const account = expectGroup(
492
- this.createCoValue(
493
- accountHeaderForInitialAgentSecret(agentSecret, this.crypto),
494
- )
495
- .testWithDifferentAccount(
496
- new ControlledAgent(agentSecret, this.crypto),
497
- this.crypto.newRandomSessionID(accountAgentID),
498
- )
499
- .getCurrentContent(),
500
- );
501
-
502
- account.set(accountAgentID, "admin", "trusting");
503
-
504
- const readKey = this.crypto.newRandomKeySecret();
505
-
506
- const sealed = this.crypto.seal({
507
- message: readKey.secret,
508
- from: this.crypto.getAgentSealerSecret(agentSecret),
509
- to: this.crypto.getAgentSealerID(accountAgentID),
510
- nOnceMaterial: {
511
- in: account.id,
512
- tx: account.core.nextTransactionID(),
513
- },
514
- });
515
-
516
- account.set(`${readKey.id}_for_${accountAgentID}`, sealed, "trusting");
517
-
518
- account.set("readKey", readKey.id, "trusting");
519
-
520
- const accountOnThisNode = this.expectCoValueLoaded(account.id);
521
-
522
- accountOnThisNode._sessionLogs = new Map(account.core.sessionLogs);
523
-
524
- accountOnThisNode._cachedContent = undefined;
525
-
526
- return new RawControlledAccount(accountOnThisNode, agentSecret);
527
- }
528
-
529
548
  /** @internal */
530
549
  resolveAccountAgent(
531
550
  id: RawAccountID | AgentID,
@@ -535,7 +554,7 @@ export class LocalNode {
535
554
  return ok(id);
536
555
  }
537
556
 
538
- let coValue: CoValueCore;
557
+ let coValue: AvailableCoValueCore;
539
558
 
540
559
  try {
541
560
  coValue = this.expectCoValueLoaded(id, expectation);
@@ -549,11 +568,11 @@ export class LocalNode {
549
568
  }
550
569
 
551
570
  if (
552
- coValue.header.type !== "comap" ||
553
- coValue.header.ruleset.type !== "group" ||
554
- !coValue.header.meta ||
555
- !("type" in coValue.header.meta) ||
556
- coValue.header.meta.type !== "account"
571
+ coValue.verified.header.type !== "comap" ||
572
+ coValue.verified.header.ruleset.type !== "group" ||
573
+ !coValue.verified.header.meta ||
574
+ !("type" in coValue.verified.header.meta) ||
575
+ coValue.verified.header.meta.type !== "account"
557
576
  ) {
558
577
  return err({
559
578
  type: "UnexpectedlyNotAccount",
@@ -565,75 +584,30 @@ export class LocalNode {
565
584
  return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
566
585
  }
567
586
 
568
- resolveAccountAgentAsync(
569
- id: RawAccountID | AgentID,
570
- expectation?: string,
571
- ): ResultAsync<AgentID, ResolveAccountAgentError> {
572
- if (isAgentID(id)) {
573
- return okAsync(id);
574
- }
575
-
576
- return ResultAsync.fromPromise(
577
- this.loadCoValueCore(id),
578
- (e) =>
579
- ({
580
- type: "ErrorLoadingCoValueCore",
581
- expectation,
582
- id,
583
- error: e,
584
- }) satisfies LoadCoValueCoreError,
585
- ).andThen((coValue) => {
586
- if (coValue === "unavailable") {
587
- return err({
588
- type: "AccountUnavailableFromAllPeers" as const,
589
- expectation,
590
- id,
591
- } satisfies AccountUnavailableFromAllPeersError);
592
- }
593
-
594
- if (
595
- coValue.header.type !== "comap" ||
596
- coValue.header.ruleset.type !== "group" ||
597
- !coValue.header.meta ||
598
- !("type" in coValue.header.meta) ||
599
- coValue.header.meta.type !== "account"
600
- ) {
601
- return err({
602
- type: "UnexpectedlyNotAccount" as const,
603
- expectation,
604
- id,
605
- } satisfies UnexpectedlyNotAccountError);
606
- }
607
-
608
- return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
609
- });
610
- }
611
-
612
- /**
613
- * @deprecated use Account.createGroup() instead
614
- */
615
587
  createGroup(
616
588
  uniqueness: CoValueUniqueness = this.crypto.createdNowUnique(),
617
589
  ): RawGroup {
590
+ const account = this.getCurrentAgent();
591
+
618
592
  const groupCoValue = this.createCoValue({
619
593
  type: "comap",
620
- ruleset: { type: "group", initialAdmin: this.account.id },
594
+ ruleset: { type: "group", initialAdmin: account.id },
621
595
  meta: null,
622
596
  ...uniqueness,
623
597
  });
624
598
 
625
599
  const group = expectGroup(groupCoValue.getCurrentContent());
626
600
 
627
- group.set(this.account.id, "admin", "trusting");
601
+ group.set(account.id, "admin", "trusting");
628
602
 
629
603
  const readKey = this.crypto.newRandomKeySecret();
630
604
 
631
605
  group.set(
632
- `${readKey.id}_for_${this.account.id}`,
606
+ `${readKey.id}_for_${account.id}`,
633
607
  this.crypto.seal({
634
608
  message: readKey.secret,
635
- from: this.account.currentSealerSecret(),
636
- to: this.account.currentSealerID(),
609
+ from: account.currentSealerSecret(),
610
+ to: account.currentSealerID(),
637
611
  nOnceMaterial: {
638
612
  in: groupCoValue.id,
639
613
  tx: groupCoValue.nextTransactionID(),
@@ -648,24 +622,34 @@ export class LocalNode {
648
622
  }
649
623
 
650
624
  /** @internal */
651
- testWithDifferentAccount(
652
- account: ControlledAccountOrAgent,
653
- currentSessionID: SessionID,
625
+ cloneWithDifferentAccount(
626
+ controlledAccountOrAgent: ControlledAccountOrAgent,
654
627
  ): LocalNode {
655
- const newNode = new LocalNode(account, currentSessionID, this.crypto);
628
+ const newNode = new LocalNode(
629
+ controlledAccountOrAgent.agentSecret,
630
+ this.crypto.newRandomSessionID(controlledAccountOrAgent.id),
631
+ this.crypto,
632
+ );
633
+
634
+ newNode.cloneVerifiedStateFrom(this);
635
+
636
+ return newNode;
637
+ }
656
638
 
657
- const coValuesToCopy = Array.from(this.coValuesStore.getEntries());
639
+ /** @internal */
640
+ cloneVerifiedStateFrom(otherNode: LocalNode) {
641
+ const coValuesToCopy = Array.from(otherNode.coValues.entries());
658
642
 
659
643
  while (coValuesToCopy.length > 0) {
660
- const [coValueID, entry] = coValuesToCopy[coValuesToCopy.length - 1]!;
644
+ const [coValueID, coValue] = coValuesToCopy[coValuesToCopy.length - 1]!;
661
645
 
662
- if (!entry.isAvailable()) {
646
+ if (!coValue.isAvailable()) {
663
647
  coValuesToCopy.pop();
664
648
  continue;
665
649
  } else {
666
- const allDepsCopied = entry.core
650
+ const allDepsCopied = coValue
667
651
  .getDependedOnCoValues()
668
- .every((dep) => newNode.coValuesStore.get(dep).isAvailable());
652
+ .every((dep) => this.coValues.get(dep)?.isAvailable());
669
653
 
670
654
  if (!allDepsCopied) {
671
655
  // move to end of queue
@@ -673,34 +657,11 @@ export class LocalNode {
673
657
  continue;
674
658
  }
675
659
 
676
- const newCoValue = new CoValueCore(
677
- entry.core.header,
678
- newNode,
679
- new Map(entry.core.sessionLogs),
680
- );
681
-
682
- newNode.coValuesStore.internalMarkMagicallyAvailable(
683
- coValueID,
684
- newCoValue,
685
- );
660
+ this.putCoValue(coValueID, coValue.verified);
686
661
 
687
662
  coValuesToCopy.pop();
688
663
  }
689
664
  }
690
-
691
- if (account instanceof RawControlledAccount) {
692
- // To make sure that when we edit the account, we're modifying the correct sessions
693
- const accountInNode = new RawControlledAccount(
694
- newNode.expectCoValueLoaded(account.id),
695
- account.agentSecret,
696
- );
697
- if (accountInNode.core.node !== newNode) {
698
- throw new Error("Account's node is not the new node");
699
- }
700
- newNode.account = accountInNode;
701
- }
702
-
703
- return newNode;
704
665
  }
705
666
 
706
667
  gracefulShutdown() {