cojson 0.13.17 → 0.13.18
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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +9 -0
- package/dist/PeerState.d.ts +3 -0
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +9 -0
- package/dist/PeerState.js.map +1 -1
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +2 -3
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValue.d.ts +4 -4
- package/dist/coValue.d.ts.map +1 -1
- package/dist/coValue.js +4 -4
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +143 -0
- package/dist/coValueCore/coValueCore.d.ts.map +1 -0
- package/dist/{coValueCore.js → coValueCore/coValueCore.js} +314 -246
- package/dist/coValueCore/coValueCore.js.map +1 -0
- package/dist/coValueCore/verifiedState.d.ts +65 -0
- package/dist/coValueCore/verifiedState.d.ts.map +1 -0
- package/dist/coValueCore/verifiedState.js +210 -0
- package/dist/coValueCore/verifiedState.js.map +1 -0
- package/dist/coValues/account.d.ts +8 -10
- package/dist/coValues/account.d.ts.map +1 -1
- package/dist/coValues/account.js +12 -13
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -3
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +6 -3
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -3
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +3 -3
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coPlainText.d.ts +2 -2
- package/dist/coValues/coPlainText.d.ts.map +1 -1
- package/dist/coValues/coPlainText.js +4 -4
- package/dist/coValues/coPlainText.js.map +1 -1
- package/dist/coValues/coStream.d.ts +3 -3
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +3 -3
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/group.d.ts +7 -2
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +29 -26
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.d.ts +2 -2
- package/dist/coreToCoValue.d.ts.map +1 -1
- package/dist/coreToCoValue.js +10 -14
- package/dist/coreToCoValue.js.map +1 -1
- package/dist/exports.d.ts +6 -5
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +3 -4
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts +30 -24
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +139 -170
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +15 -11
- package/dist/permissions.js.map +1 -1
- package/dist/priority.d.ts +1 -1
- package/dist/priority.d.ts.map +1 -1
- package/dist/sync.d.ts +2 -2
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +86 -55
- package/dist/sync.js.map +1 -1
- package/dist/tests/coList.test.js +19 -16
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coMap.test.js +12 -13
- package/dist/tests/coMap.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +9 -10
- package/dist/tests/coPlainText.test.js.map +1 -1
- package/dist/tests/coStream.test.js +22 -17
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +22 -28
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coValueCoreLoadingState.test.d.ts +2 -0
- package/dist/tests/coValueCoreLoadingState.test.d.ts.map +1 -0
- package/dist/tests/{coValueState.test.js → coValueCoreLoadingState.test.js} +62 -46
- package/dist/tests/coValueCoreLoadingState.test.js.map +1 -0
- package/dist/tests/group.test.js +42 -43
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.d.ts +2 -2
- package/dist/tests/messagesTestUtils.d.ts.map +1 -1
- package/dist/tests/messagesTestUtils.js +1 -1
- package/dist/tests/messagesTestUtils.js.map +1 -1
- package/dist/tests/permissions.test.js +224 -292
- package/dist/tests/permissions.test.js.map +1 -1
- package/dist/tests/priority.test.js +13 -14
- package/dist/tests/priority.test.js.map +1 -1
- package/dist/tests/sync.auth.test.d.ts +2 -0
- package/dist/tests/sync.auth.test.d.ts.map +1 -0
- package/dist/tests/sync.auth.test.js +141 -0
- package/dist/tests/sync.auth.test.js.map +1 -0
- package/dist/tests/sync.load.test.js +4 -4
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +25 -12
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +19 -19
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +20 -13
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.test.js +32 -39
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +126 -37
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +24 -15
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +88 -61
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/typeUtils/expectGroup.js +1 -1
- package/dist/typeUtils/expectGroup.js.map +1 -1
- package/package.json +1 -1
- package/src/PeerState.ts +11 -0
- package/src/SyncStateManager.ts +2 -3
- package/src/coValue.ts +11 -8
- package/src/{coValueCore.ts → coValueCore/coValueCore.ts} +469 -413
- package/src/coValueCore/verifiedState.ts +376 -0
- package/src/coValues/account.ts +20 -25
- package/src/coValues/coList.ts +12 -6
- package/src/coValues/coMap.ts +9 -6
- package/src/coValues/coPlainText.ts +9 -6
- package/src/coValues/coStream.ts +9 -6
- package/src/coValues/group.ts +50 -28
- package/src/coreToCoValue.ts +14 -15
- package/src/exports.ts +9 -7
- package/src/localNode.ts +227 -273
- package/src/permissions.ts +18 -12
- package/src/priority.ts +1 -1
- package/src/sync.ts +96 -63
- package/src/tests/coList.test.ts +21 -15
- package/src/tests/coMap.test.ts +12 -13
- package/src/tests/coPlainText.test.ts +12 -9
- package/src/tests/coStream.test.ts +25 -16
- package/src/tests/coValueCore.test.ts +30 -27
- package/src/tests/{coValueState.test.ts → coValueCoreLoadingState.test.ts} +67 -57
- package/src/tests/group.test.ts +44 -68
- package/src/tests/messagesTestUtils.ts +3 -8
- package/src/tests/permissions.test.ts +283 -449
- package/src/tests/priority.test.ts +17 -13
- package/src/tests/sync.auth.test.ts +188 -0
- package/src/tests/sync.load.test.ts +4 -4
- package/src/tests/sync.mesh.test.ts +25 -12
- package/src/tests/sync.peerReconciliation.test.ts +25 -25
- package/src/tests/sync.storage.test.ts +20 -13
- package/src/tests/sync.test.ts +43 -43
- package/src/tests/sync.upload.test.ts +157 -37
- package/src/tests/testUtils.ts +120 -74
- package/src/typeUtils/expectGroup.ts +1 -1
- package/dist/CoValuesStore.d.ts +0 -14
- package/dist/CoValuesStore.d.ts.map +0 -1
- package/dist/CoValuesStore.js +0 -32
- package/dist/CoValuesStore.js.map +0 -1
- package/dist/coValueCore.d.ts +0 -142
- package/dist/coValueCore.d.ts.map +0 -1
- package/dist/coValueCore.js.map +0 -1
- package/dist/coValueState.d.ts +0 -34
- package/dist/coValueState.d.ts.map +0 -1
- package/dist/coValueState.js +0 -190
- package/dist/coValueState.js.map +0 -1
- package/dist/tests/coValueState.test.d.ts +0 -2
- package/dist/tests/coValueState.test.d.ts.map +0 -1
- package/dist/tests/coValueState.test.js.map +0 -1
- package/src/CoValuesStore.ts +0 -41
- package/src/coValueState.ts +0 -245
package/src/localNode.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { Result, ResultAsync, err, ok, okAsync } from "neverthrow";
|
|
2
|
-
import { CoValuesStore } from "./CoValuesStore.js";
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
68
|
+
agentSecret: AgentSecret,
|
|
62
69
|
currentSessionID: SessionID,
|
|
63
70
|
crypto: CryptoProvider,
|
|
64
71
|
) {
|
|
65
|
-
this.
|
|
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
|
|
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<
|
|
199
|
+
migration?: RawAccountMigration<AccountMeta>;
|
|
81
200
|
crypto: CryptoProvider;
|
|
82
201
|
initialAgentSecret?: AgentSecret;
|
|
83
202
|
}): Promise<{
|
|
@@ -86,81 +205,38 @@ export class LocalNode {
|
|
|
86
205
|
accountSecret: AgentSecret;
|
|
87
206
|
sessionID: SessionID;
|
|
88
207
|
}> {
|
|
89
|
-
const
|
|
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
|
-
|
|
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(
|
|
216
|
+
await migration(account, node, creationProps);
|
|
114
217
|
} else {
|
|
115
|
-
const profileGroup =
|
|
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
|
-
|
|
223
|
+
account.set("profile", profile.id, "trusting");
|
|
121
224
|
}
|
|
122
225
|
|
|
123
|
-
|
|
124
|
-
accountOnNodeWithAccount.core,
|
|
125
|
-
accountOnNodeWithAccount.agentSecret,
|
|
126
|
-
);
|
|
127
|
-
|
|
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")) {
|
|
226
|
+
if (!account.get("profile")) {
|
|
136
227
|
throw new Error("Must set account profile in initial migration");
|
|
137
228
|
}
|
|
138
229
|
|
|
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
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
syncAllCoValuesAfterCreateAccount();
|
|
151
|
-
|
|
152
|
-
setTimeout(syncAllCoValuesAfterCreateAccount, 500);
|
|
153
|
-
|
|
154
230
|
return {
|
|
155
|
-
node
|
|
156
|
-
accountID:
|
|
157
|
-
accountSecret:
|
|
158
|
-
sessionID:
|
|
231
|
+
node,
|
|
232
|
+
accountID: account.id,
|
|
233
|
+
accountSecret: initialAgentSecret,
|
|
234
|
+
sessionID: node.currentSessionID,
|
|
159
235
|
};
|
|
160
236
|
}
|
|
161
237
|
|
|
162
238
|
/** @category 2. Node Creation */
|
|
163
|
-
static async withLoadedAccount
|
|
239
|
+
static async withLoadedAccount({
|
|
164
240
|
accountID,
|
|
165
241
|
accountSecret,
|
|
166
242
|
sessionID,
|
|
@@ -173,47 +249,25 @@ export class LocalNode {
|
|
|
173
249
|
sessionID: SessionID | undefined;
|
|
174
250
|
peersToLoadFrom: Peer[];
|
|
175
251
|
crypto: CryptoProvider;
|
|
176
|
-
migration?: RawAccountMigration<
|
|
252
|
+
migration?: RawAccountMigration<AccountMeta>;
|
|
177
253
|
}): Promise<LocalNode> {
|
|
178
254
|
try {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
crypto.newRandomSessionID(accountID),
|
|
255
|
+
const node = new LocalNode(
|
|
256
|
+
accountSecret,
|
|
257
|
+
sessionID || crypto.newRandomSessionID(accountID),
|
|
182
258
|
crypto,
|
|
183
259
|
);
|
|
184
260
|
|
|
185
261
|
for (const peer of peersToLoadFrom) {
|
|
186
|
-
|
|
262
|
+
node.syncManager.addPeer(peer);
|
|
187
263
|
}
|
|
188
264
|
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
const account = await accountPromise;
|
|
265
|
+
const account = await node.load(accountID);
|
|
192
266
|
|
|
193
267
|
if (account === "unavailable") {
|
|
194
268
|
throw new Error("Account unavailable from all peers");
|
|
195
269
|
}
|
|
196
270
|
|
|
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
271
|
const profileID = account.get("profile");
|
|
218
272
|
if (!profileID) {
|
|
219
273
|
throw new Error("Account has no profile");
|
|
@@ -225,11 +279,7 @@ export class LocalNode {
|
|
|
225
279
|
}
|
|
226
280
|
|
|
227
281
|
if (migration) {
|
|
228
|
-
await migration(
|
|
229
|
-
node.account = new RawControlledAccount(
|
|
230
|
-
controlledAccount.core,
|
|
231
|
-
controlledAccount.agentSecret,
|
|
232
|
-
);
|
|
282
|
+
await migration(account, node);
|
|
233
283
|
}
|
|
234
284
|
|
|
235
285
|
return node;
|
|
@@ -240,15 +290,19 @@ export class LocalNode {
|
|
|
240
290
|
}
|
|
241
291
|
|
|
242
292
|
/** @internal */
|
|
243
|
-
createCoValue(header: CoValueHeader):
|
|
293
|
+
createCoValue(header: CoValueHeader): AvailableCoValueCore {
|
|
244
294
|
if (this.crashed) {
|
|
245
295
|
throw new Error("Trying to create CoValue after node has crashed", {
|
|
246
296
|
cause: this.crashed,
|
|
247
297
|
});
|
|
248
298
|
}
|
|
249
299
|
|
|
250
|
-
const
|
|
251
|
-
|
|
300
|
+
const id = idforHeader(header, this.crypto);
|
|
301
|
+
|
|
302
|
+
const coValue = this.putCoValue(
|
|
303
|
+
id,
|
|
304
|
+
new VerifiedState(id, this.crypto, header, new Map()),
|
|
305
|
+
);
|
|
252
306
|
|
|
253
307
|
void this.syncManager.requestCoValueSync(coValue);
|
|
254
308
|
|
|
@@ -259,7 +313,7 @@ export class LocalNode {
|
|
|
259
313
|
async loadCoValueCore(
|
|
260
314
|
id: RawCoID,
|
|
261
315
|
skipLoadingFromPeer?: PeerID,
|
|
262
|
-
): Promise<CoValueCore
|
|
316
|
+
): Promise<CoValueCore> {
|
|
263
317
|
if (this.crashed) {
|
|
264
318
|
throw new Error("Trying to load CoValue after node has crashed", {
|
|
265
319
|
cause: this.crashed,
|
|
@@ -269,20 +323,20 @@ export class LocalNode {
|
|
|
269
323
|
let retries = 0;
|
|
270
324
|
|
|
271
325
|
while (true) {
|
|
272
|
-
const
|
|
326
|
+
const coValue = this.getCoValue(id);
|
|
273
327
|
|
|
274
328
|
if (
|
|
275
|
-
|
|
276
|
-
|
|
329
|
+
coValue.loadingState === "unknown" ||
|
|
330
|
+
coValue.loadingState === "unavailable"
|
|
277
331
|
) {
|
|
278
332
|
const peers =
|
|
279
333
|
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
|
280
334
|
|
|
281
335
|
if (peers.length === 0) {
|
|
282
|
-
return
|
|
336
|
+
return coValue;
|
|
283
337
|
}
|
|
284
338
|
|
|
285
|
-
|
|
339
|
+
coValue.loadFromPeers(peers).catch((e) => {
|
|
286
340
|
logger.error("Error loading from peers", {
|
|
287
341
|
id,
|
|
288
342
|
err: e,
|
|
@@ -290,9 +344,8 @@ export class LocalNode {
|
|
|
290
344
|
});
|
|
291
345
|
}
|
|
292
346
|
|
|
293
|
-
const result = await
|
|
294
|
-
|
|
295
|
-
if (result !== "unavailable" || retries >= 1) {
|
|
347
|
+
const result = await coValue.waitForAvailableOrUnavailable();
|
|
348
|
+
if (result.isAvailable() || retries >= 1) {
|
|
296
349
|
return result;
|
|
297
350
|
}
|
|
298
351
|
|
|
@@ -320,7 +373,7 @@ export class LocalNode {
|
|
|
320
373
|
|
|
321
374
|
const core = await this.loadCoValueCore(id);
|
|
322
375
|
|
|
323
|
-
if (core
|
|
376
|
+
if (!core.isAvailable()) {
|
|
324
377
|
return "unavailable";
|
|
325
378
|
}
|
|
326
379
|
|
|
@@ -328,10 +381,10 @@ export class LocalNode {
|
|
|
328
381
|
}
|
|
329
382
|
|
|
330
383
|
getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
|
|
331
|
-
const
|
|
384
|
+
const coValue = this.getCoValue(id);
|
|
332
385
|
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
386
|
+
if (coValue.isAvailable()) {
|
|
387
|
+
return coValue.getCurrentContent() as T;
|
|
335
388
|
}
|
|
336
389
|
|
|
337
390
|
return undefined;
|
|
@@ -369,7 +422,6 @@ export class LocalNode {
|
|
|
369
422
|
};
|
|
370
423
|
}
|
|
371
424
|
|
|
372
|
-
/** @deprecated Use Account.acceptInvite instead */
|
|
373
425
|
async acceptInvite<T extends RawCoValue>(
|
|
374
426
|
groupOrOwnedValueID: CoID<T>,
|
|
375
427
|
inviteSecret: InviteSecret,
|
|
@@ -382,12 +434,16 @@ export class LocalNode {
|
|
|
382
434
|
);
|
|
383
435
|
}
|
|
384
436
|
|
|
385
|
-
if (
|
|
437
|
+
if (
|
|
438
|
+
groupOrOwnedValue.core.verified.header.ruleset.type === "ownedByGroup"
|
|
439
|
+
) {
|
|
386
440
|
return this.acceptInvite(
|
|
387
|
-
groupOrOwnedValue.core.header.ruleset.group as CoID<RawGroup>,
|
|
441
|
+
groupOrOwnedValue.core.verified.header.ruleset.group as CoID<RawGroup>,
|
|
388
442
|
inviteSecret,
|
|
389
443
|
);
|
|
390
|
-
} else if (
|
|
444
|
+
} else if (
|
|
445
|
+
groupOrOwnedValue.core.verified.header.ruleset.type !== "group"
|
|
446
|
+
) {
|
|
391
447
|
throw new Error("Can only accept invites to groups");
|
|
392
448
|
}
|
|
393
449
|
|
|
@@ -415,7 +471,8 @@ export class LocalNode {
|
|
|
415
471
|
throw new Error("No invite found");
|
|
416
472
|
}
|
|
417
473
|
|
|
418
|
-
const
|
|
474
|
+
const account = this.getCurrentAgent();
|
|
475
|
+
const existingRole = group.get(account.id);
|
|
419
476
|
|
|
420
477
|
if (
|
|
421
478
|
existingRole === "admin" ||
|
|
@@ -429,16 +486,13 @@ export class LocalNode {
|
|
|
429
486
|
}
|
|
430
487
|
|
|
431
488
|
const groupAsInvite = expectGroup(
|
|
432
|
-
group.core
|
|
433
|
-
.
|
|
434
|
-
|
|
435
|
-
this.crypto.newRandomSessionID(inviteAgentID),
|
|
436
|
-
)
|
|
437
|
-
.getCurrentContent(),
|
|
489
|
+
group.core.contentInClonedNodeWithDifferentAccount(
|
|
490
|
+
new ControlledAgent(inviteAgentSecret, this.crypto),
|
|
491
|
+
),
|
|
438
492
|
);
|
|
439
493
|
|
|
440
494
|
groupAsInvite.addMemberInternal(
|
|
441
|
-
|
|
495
|
+
account,
|
|
442
496
|
inviteRole === "adminInvite"
|
|
443
497
|
? "admin"
|
|
444
498
|
: inviteRole === "writerInvite"
|
|
@@ -448,24 +502,25 @@ export class LocalNode {
|
|
|
448
502
|
: "reader",
|
|
449
503
|
);
|
|
450
504
|
|
|
451
|
-
group.core.
|
|
452
|
-
|
|
505
|
+
group.core.internalShamefullyCloneVerifiedStateFrom(
|
|
506
|
+
groupAsInvite.core.verified,
|
|
507
|
+
{ forceOverwrite: true },
|
|
508
|
+
);
|
|
509
|
+
group.core.internalShamefullyResetCachedContent();
|
|
453
510
|
|
|
454
|
-
|
|
455
|
-
groupListener(group.core.getCurrentContent());
|
|
456
|
-
}
|
|
511
|
+
group.core.notifyUpdate("immediate");
|
|
457
512
|
}
|
|
458
513
|
|
|
459
514
|
/** @internal */
|
|
460
|
-
expectCoValueLoaded(id: RawCoID, expectation?: string):
|
|
461
|
-
const
|
|
515
|
+
expectCoValueLoaded(id: RawCoID, expectation?: string): AvailableCoValueCore {
|
|
516
|
+
const coValue = this.getCoValue(id);
|
|
462
517
|
|
|
463
|
-
if (!
|
|
518
|
+
if (!coValue.isAvailable()) {
|
|
464
519
|
throw new Error(
|
|
465
|
-
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(
|
|
520
|
+
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(coValue)}`,
|
|
466
521
|
);
|
|
467
522
|
}
|
|
468
|
-
return
|
|
523
|
+
return coValue;
|
|
469
524
|
}
|
|
470
525
|
|
|
471
526
|
/** @internal */
|
|
@@ -483,49 +538,6 @@ export class LocalNode {
|
|
|
483
538
|
).getCurrentContent() as RawProfile;
|
|
484
539
|
}
|
|
485
540
|
|
|
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
541
|
/** @internal */
|
|
530
542
|
resolveAccountAgent(
|
|
531
543
|
id: RawAccountID | AgentID,
|
|
@@ -535,7 +547,7 @@ export class LocalNode {
|
|
|
535
547
|
return ok(id);
|
|
536
548
|
}
|
|
537
549
|
|
|
538
|
-
let coValue:
|
|
550
|
+
let coValue: AvailableCoValueCore;
|
|
539
551
|
|
|
540
552
|
try {
|
|
541
553
|
coValue = this.expectCoValueLoaded(id, expectation);
|
|
@@ -549,11 +561,11 @@ export class LocalNode {
|
|
|
549
561
|
}
|
|
550
562
|
|
|
551
563
|
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"
|
|
564
|
+
coValue.verified.header.type !== "comap" ||
|
|
565
|
+
coValue.verified.header.ruleset.type !== "group" ||
|
|
566
|
+
!coValue.verified.header.meta ||
|
|
567
|
+
!("type" in coValue.verified.header.meta) ||
|
|
568
|
+
coValue.verified.header.meta.type !== "account"
|
|
557
569
|
) {
|
|
558
570
|
return err({
|
|
559
571
|
type: "UnexpectedlyNotAccount",
|
|
@@ -565,75 +577,30 @@ export class LocalNode {
|
|
|
565
577
|
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
|
566
578
|
}
|
|
567
579
|
|
|
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
580
|
createGroup(
|
|
616
581
|
uniqueness: CoValueUniqueness = this.crypto.createdNowUnique(),
|
|
617
582
|
): RawGroup {
|
|
583
|
+
const account = this.getCurrentAgent();
|
|
584
|
+
|
|
618
585
|
const groupCoValue = this.createCoValue({
|
|
619
586
|
type: "comap",
|
|
620
|
-
ruleset: { type: "group", initialAdmin:
|
|
587
|
+
ruleset: { type: "group", initialAdmin: account.id },
|
|
621
588
|
meta: null,
|
|
622
589
|
...uniqueness,
|
|
623
590
|
});
|
|
624
591
|
|
|
625
592
|
const group = expectGroup(groupCoValue.getCurrentContent());
|
|
626
593
|
|
|
627
|
-
group.set(
|
|
594
|
+
group.set(account.id, "admin", "trusting");
|
|
628
595
|
|
|
629
596
|
const readKey = this.crypto.newRandomKeySecret();
|
|
630
597
|
|
|
631
598
|
group.set(
|
|
632
|
-
`${readKey.id}_for_${
|
|
599
|
+
`${readKey.id}_for_${account.id}`,
|
|
633
600
|
this.crypto.seal({
|
|
634
601
|
message: readKey.secret,
|
|
635
|
-
from:
|
|
636
|
-
to:
|
|
602
|
+
from: account.currentSealerSecret(),
|
|
603
|
+
to: account.currentSealerID(),
|
|
637
604
|
nOnceMaterial: {
|
|
638
605
|
in: groupCoValue.id,
|
|
639
606
|
tx: groupCoValue.nextTransactionID(),
|
|
@@ -648,24 +615,34 @@ export class LocalNode {
|
|
|
648
615
|
}
|
|
649
616
|
|
|
650
617
|
/** @internal */
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
currentSessionID: SessionID,
|
|
618
|
+
cloneWithDifferentAccount(
|
|
619
|
+
controlledAccountOrAgent: ControlledAccountOrAgent,
|
|
654
620
|
): LocalNode {
|
|
655
|
-
const newNode = new LocalNode(
|
|
621
|
+
const newNode = new LocalNode(
|
|
622
|
+
controlledAccountOrAgent.agentSecret,
|
|
623
|
+
this.crypto.newRandomSessionID(controlledAccountOrAgent.id),
|
|
624
|
+
this.crypto,
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
newNode.cloneVerifiedStateFrom(this);
|
|
628
|
+
|
|
629
|
+
return newNode;
|
|
630
|
+
}
|
|
656
631
|
|
|
657
|
-
|
|
632
|
+
/** @internal */
|
|
633
|
+
cloneVerifiedStateFrom(otherNode: LocalNode) {
|
|
634
|
+
const coValuesToCopy = Array.from(otherNode.coValues.entries());
|
|
658
635
|
|
|
659
636
|
while (coValuesToCopy.length > 0) {
|
|
660
|
-
const [coValueID,
|
|
637
|
+
const [coValueID, coValue] = coValuesToCopy[coValuesToCopy.length - 1]!;
|
|
661
638
|
|
|
662
|
-
if (!
|
|
639
|
+
if (!coValue.isAvailable()) {
|
|
663
640
|
coValuesToCopy.pop();
|
|
664
641
|
continue;
|
|
665
642
|
} else {
|
|
666
|
-
const allDepsCopied =
|
|
643
|
+
const allDepsCopied = coValue
|
|
667
644
|
.getDependedOnCoValues()
|
|
668
|
-
.every((dep) =>
|
|
645
|
+
.every((dep) => this.coValues.get(dep)?.isAvailable());
|
|
669
646
|
|
|
670
647
|
if (!allDepsCopied) {
|
|
671
648
|
// move to end of queue
|
|
@@ -673,34 +650,11 @@ export class LocalNode {
|
|
|
673
650
|
continue;
|
|
674
651
|
}
|
|
675
652
|
|
|
676
|
-
|
|
677
|
-
entry.core.header,
|
|
678
|
-
newNode,
|
|
679
|
-
new Map(entry.core.sessionLogs),
|
|
680
|
-
);
|
|
681
|
-
|
|
682
|
-
newNode.coValuesStore.internalMarkMagicallyAvailable(
|
|
683
|
-
coValueID,
|
|
684
|
-
newCoValue,
|
|
685
|
-
);
|
|
653
|
+
this.putCoValue(coValueID, coValue.verified);
|
|
686
654
|
|
|
687
655
|
coValuesToCopy.pop();
|
|
688
656
|
}
|
|
689
657
|
}
|
|
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
658
|
}
|
|
705
659
|
|
|
706
660
|
gracefulShutdown() {
|