cojson 0.13.16 → 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 +16 -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 +6 -4
- package/dist/coValue.d.ts.map +1 -1
- package/dist/coValue.js +5 -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 +10 -6
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +41 -15
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +4 -3
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +5 -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 +5 -5
- package/dist/coValues/coPlainText.js.map +1 -1
- package/dist/coValues/coStream.d.ts +5 -4
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +5 -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 +4 -3
- 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 +153 -177
- 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 +133 -13
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coMap.test.js +43 -14
- 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 +49 -18
- 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/coValueCoreLoadingState.test.js +227 -0
- 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 +60 -2
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +70 -10
- 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 +14 -8
- package/src/{coValueCore.ts → coValueCore/coValueCore.ts} +470 -413
- package/src/coValueCore/verifiedState.ts +376 -0
- package/src/coValues/account.ts +20 -25
- package/src/coValues/coList.ts +63 -29
- package/src/coValues/coMap.ts +13 -6
- package/src/coValues/coPlainText.ts +10 -8
- package/src/coValues/coStream.ts +12 -7
- package/src/coValues/group.ts +50 -28
- package/src/coreToCoValue.ts +14 -15
- package/src/exports.ts +9 -7
- package/src/localNode.ts +248 -283
- package/src/permissions.ts +18 -12
- package/src/priority.ts +1 -1
- package/src/sync.ts +96 -63
- package/src/tests/coList.test.ts +200 -12
- package/src/tests/coMap.test.ts +65 -14
- package/src/tests/coPlainText.test.ts +12 -9
- package/src/tests/coStream.test.ts +80 -17
- package/src/tests/coValueCore.test.ts +30 -27
- package/src/tests/coValueCoreLoadingState.test.ts +337 -0
- 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 +79 -2
- package/src/tests/sync.mesh.test.ts +89 -9
- 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 -141
- 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 -228
- 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 +0 -344
- package/dist/tests/coValueState.test.js.map +0 -1
- package/src/CoValuesStore.ts +0 -41
- package/src/coValueState.ts +0 -300
- package/src/tests/coValueState.test.ts +0 -525
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,36 +313,46 @@ 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,
|
|
266
320
|
});
|
|
267
321
|
}
|
|
268
322
|
|
|
269
|
-
|
|
323
|
+
let retries = 0;
|
|
270
324
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
entry.highLevelState === "unavailable"
|
|
274
|
-
) {
|
|
275
|
-
const peers =
|
|
276
|
-
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
|
325
|
+
while (true) {
|
|
326
|
+
const coValue = this.getCoValue(id);
|
|
277
327
|
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
328
|
+
if (
|
|
329
|
+
coValue.loadingState === "unknown" ||
|
|
330
|
+
coValue.loadingState === "unavailable"
|
|
331
|
+
) {
|
|
332
|
+
const peers =
|
|
333
|
+
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
|
281
334
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
335
|
+
if (peers.length === 0) {
|
|
336
|
+
return coValue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
coValue.loadFromPeers(peers).catch((e) => {
|
|
340
|
+
logger.error("Error loading from peers", {
|
|
341
|
+
id,
|
|
342
|
+
err: e,
|
|
343
|
+
});
|
|
286
344
|
});
|
|
287
|
-
}
|
|
288
|
-
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const result = await coValue.waitForAvailableOrUnavailable();
|
|
348
|
+
if (result.isAvailable() || retries >= 1) {
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
289
353
|
|
|
290
|
-
|
|
291
|
-
|
|
354
|
+
retries++;
|
|
355
|
+
}
|
|
292
356
|
}
|
|
293
357
|
|
|
294
358
|
/**
|
|
@@ -309,7 +373,7 @@ export class LocalNode {
|
|
|
309
373
|
|
|
310
374
|
const core = await this.loadCoValueCore(id);
|
|
311
375
|
|
|
312
|
-
if (core
|
|
376
|
+
if (!core.isAvailable()) {
|
|
313
377
|
return "unavailable";
|
|
314
378
|
}
|
|
315
379
|
|
|
@@ -317,10 +381,10 @@ export class LocalNode {
|
|
|
317
381
|
}
|
|
318
382
|
|
|
319
383
|
getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
|
|
320
|
-
const
|
|
384
|
+
const coValue = this.getCoValue(id);
|
|
321
385
|
|
|
322
|
-
if (
|
|
323
|
-
return
|
|
386
|
+
if (coValue.isAvailable()) {
|
|
387
|
+
return coValue.getCurrentContent() as T;
|
|
324
388
|
}
|
|
325
389
|
|
|
326
390
|
return undefined;
|
|
@@ -358,7 +422,6 @@ export class LocalNode {
|
|
|
358
422
|
};
|
|
359
423
|
}
|
|
360
424
|
|
|
361
|
-
/** @deprecated Use Account.acceptInvite instead */
|
|
362
425
|
async acceptInvite<T extends RawCoValue>(
|
|
363
426
|
groupOrOwnedValueID: CoID<T>,
|
|
364
427
|
inviteSecret: InviteSecret,
|
|
@@ -371,12 +434,16 @@ export class LocalNode {
|
|
|
371
434
|
);
|
|
372
435
|
}
|
|
373
436
|
|
|
374
|
-
if (
|
|
437
|
+
if (
|
|
438
|
+
groupOrOwnedValue.core.verified.header.ruleset.type === "ownedByGroup"
|
|
439
|
+
) {
|
|
375
440
|
return this.acceptInvite(
|
|
376
|
-
groupOrOwnedValue.core.header.ruleset.group as CoID<RawGroup>,
|
|
441
|
+
groupOrOwnedValue.core.verified.header.ruleset.group as CoID<RawGroup>,
|
|
377
442
|
inviteSecret,
|
|
378
443
|
);
|
|
379
|
-
} else if (
|
|
444
|
+
} else if (
|
|
445
|
+
groupOrOwnedValue.core.verified.header.ruleset.type !== "group"
|
|
446
|
+
) {
|
|
380
447
|
throw new Error("Can only accept invites to groups");
|
|
381
448
|
}
|
|
382
449
|
|
|
@@ -404,7 +471,8 @@ export class LocalNode {
|
|
|
404
471
|
throw new Error("No invite found");
|
|
405
472
|
}
|
|
406
473
|
|
|
407
|
-
const
|
|
474
|
+
const account = this.getCurrentAgent();
|
|
475
|
+
const existingRole = group.get(account.id);
|
|
408
476
|
|
|
409
477
|
if (
|
|
410
478
|
existingRole === "admin" ||
|
|
@@ -418,16 +486,13 @@ export class LocalNode {
|
|
|
418
486
|
}
|
|
419
487
|
|
|
420
488
|
const groupAsInvite = expectGroup(
|
|
421
|
-
group.core
|
|
422
|
-
.
|
|
423
|
-
|
|
424
|
-
this.crypto.newRandomSessionID(inviteAgentID),
|
|
425
|
-
)
|
|
426
|
-
.getCurrentContent(),
|
|
489
|
+
group.core.contentInClonedNodeWithDifferentAccount(
|
|
490
|
+
new ControlledAgent(inviteAgentSecret, this.crypto),
|
|
491
|
+
),
|
|
427
492
|
);
|
|
428
493
|
|
|
429
494
|
groupAsInvite.addMemberInternal(
|
|
430
|
-
|
|
495
|
+
account,
|
|
431
496
|
inviteRole === "adminInvite"
|
|
432
497
|
? "admin"
|
|
433
498
|
: inviteRole === "writerInvite"
|
|
@@ -437,24 +502,25 @@ export class LocalNode {
|
|
|
437
502
|
: "reader",
|
|
438
503
|
);
|
|
439
504
|
|
|
440
|
-
group.core.
|
|
441
|
-
|
|
505
|
+
group.core.internalShamefullyCloneVerifiedStateFrom(
|
|
506
|
+
groupAsInvite.core.verified,
|
|
507
|
+
{ forceOverwrite: true },
|
|
508
|
+
);
|
|
509
|
+
group.core.internalShamefullyResetCachedContent();
|
|
442
510
|
|
|
443
|
-
|
|
444
|
-
groupListener(group.core.getCurrentContent());
|
|
445
|
-
}
|
|
511
|
+
group.core.notifyUpdate("immediate");
|
|
446
512
|
}
|
|
447
513
|
|
|
448
514
|
/** @internal */
|
|
449
|
-
expectCoValueLoaded(id: RawCoID, expectation?: string):
|
|
450
|
-
const
|
|
515
|
+
expectCoValueLoaded(id: RawCoID, expectation?: string): AvailableCoValueCore {
|
|
516
|
+
const coValue = this.getCoValue(id);
|
|
451
517
|
|
|
452
|
-
if (!
|
|
518
|
+
if (!coValue.isAvailable()) {
|
|
453
519
|
throw new Error(
|
|
454
|
-
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(
|
|
520
|
+
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(coValue)}`,
|
|
455
521
|
);
|
|
456
522
|
}
|
|
457
|
-
return
|
|
523
|
+
return coValue;
|
|
458
524
|
}
|
|
459
525
|
|
|
460
526
|
/** @internal */
|
|
@@ -472,49 +538,6 @@ export class LocalNode {
|
|
|
472
538
|
).getCurrentContent() as RawProfile;
|
|
473
539
|
}
|
|
474
540
|
|
|
475
|
-
/** @internal */
|
|
476
|
-
createAccount(
|
|
477
|
-
agentSecret = this.crypto.newRandomAgentSecret(),
|
|
478
|
-
): RawControlledAccount {
|
|
479
|
-
const accountAgentID = this.crypto.getAgentID(agentSecret);
|
|
480
|
-
const account = expectGroup(
|
|
481
|
-
this.createCoValue(
|
|
482
|
-
accountHeaderForInitialAgentSecret(agentSecret, this.crypto),
|
|
483
|
-
)
|
|
484
|
-
.testWithDifferentAccount(
|
|
485
|
-
new ControlledAgent(agentSecret, this.crypto),
|
|
486
|
-
this.crypto.newRandomSessionID(accountAgentID),
|
|
487
|
-
)
|
|
488
|
-
.getCurrentContent(),
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
account.set(accountAgentID, "admin", "trusting");
|
|
492
|
-
|
|
493
|
-
const readKey = this.crypto.newRandomKeySecret();
|
|
494
|
-
|
|
495
|
-
const sealed = this.crypto.seal({
|
|
496
|
-
message: readKey.secret,
|
|
497
|
-
from: this.crypto.getAgentSealerSecret(agentSecret),
|
|
498
|
-
to: this.crypto.getAgentSealerID(accountAgentID),
|
|
499
|
-
nOnceMaterial: {
|
|
500
|
-
in: account.id,
|
|
501
|
-
tx: account.core.nextTransactionID(),
|
|
502
|
-
},
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
account.set(`${readKey.id}_for_${accountAgentID}`, sealed, "trusting");
|
|
506
|
-
|
|
507
|
-
account.set("readKey", readKey.id, "trusting");
|
|
508
|
-
|
|
509
|
-
const accountOnThisNode = this.expectCoValueLoaded(account.id);
|
|
510
|
-
|
|
511
|
-
accountOnThisNode._sessionLogs = new Map(account.core.sessionLogs);
|
|
512
|
-
|
|
513
|
-
accountOnThisNode._cachedContent = undefined;
|
|
514
|
-
|
|
515
|
-
return new RawControlledAccount(accountOnThisNode, agentSecret);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
541
|
/** @internal */
|
|
519
542
|
resolveAccountAgent(
|
|
520
543
|
id: RawAccountID | AgentID,
|
|
@@ -524,7 +547,7 @@ export class LocalNode {
|
|
|
524
547
|
return ok(id);
|
|
525
548
|
}
|
|
526
549
|
|
|
527
|
-
let coValue:
|
|
550
|
+
let coValue: AvailableCoValueCore;
|
|
528
551
|
|
|
529
552
|
try {
|
|
530
553
|
coValue = this.expectCoValueLoaded(id, expectation);
|
|
@@ -538,11 +561,11 @@ export class LocalNode {
|
|
|
538
561
|
}
|
|
539
562
|
|
|
540
563
|
if (
|
|
541
|
-
coValue.header.type !== "comap" ||
|
|
542
|
-
coValue.header.ruleset.type !== "group" ||
|
|
543
|
-
!coValue.header.meta ||
|
|
544
|
-
!("type" in coValue.header.meta) ||
|
|
545
|
-
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"
|
|
546
569
|
) {
|
|
547
570
|
return err({
|
|
548
571
|
type: "UnexpectedlyNotAccount",
|
|
@@ -554,75 +577,30 @@ export class LocalNode {
|
|
|
554
577
|
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
|
555
578
|
}
|
|
556
579
|
|
|
557
|
-
resolveAccountAgentAsync(
|
|
558
|
-
id: RawAccountID | AgentID,
|
|
559
|
-
expectation?: string,
|
|
560
|
-
): ResultAsync<AgentID, ResolveAccountAgentError> {
|
|
561
|
-
if (isAgentID(id)) {
|
|
562
|
-
return okAsync(id);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return ResultAsync.fromPromise(
|
|
566
|
-
this.loadCoValueCore(id),
|
|
567
|
-
(e) =>
|
|
568
|
-
({
|
|
569
|
-
type: "ErrorLoadingCoValueCore",
|
|
570
|
-
expectation,
|
|
571
|
-
id,
|
|
572
|
-
error: e,
|
|
573
|
-
}) satisfies LoadCoValueCoreError,
|
|
574
|
-
).andThen((coValue) => {
|
|
575
|
-
if (coValue === "unavailable") {
|
|
576
|
-
return err({
|
|
577
|
-
type: "AccountUnavailableFromAllPeers" as const,
|
|
578
|
-
expectation,
|
|
579
|
-
id,
|
|
580
|
-
} satisfies AccountUnavailableFromAllPeersError);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
if (
|
|
584
|
-
coValue.header.type !== "comap" ||
|
|
585
|
-
coValue.header.ruleset.type !== "group" ||
|
|
586
|
-
!coValue.header.meta ||
|
|
587
|
-
!("type" in coValue.header.meta) ||
|
|
588
|
-
coValue.header.meta.type !== "account"
|
|
589
|
-
) {
|
|
590
|
-
return err({
|
|
591
|
-
type: "UnexpectedlyNotAccount" as const,
|
|
592
|
-
expectation,
|
|
593
|
-
id,
|
|
594
|
-
} satisfies UnexpectedlyNotAccountError);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
return ok((coValue.getCurrentContent() as RawAccount).currentAgentID());
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* @deprecated use Account.createGroup() instead
|
|
603
|
-
*/
|
|
604
580
|
createGroup(
|
|
605
581
|
uniqueness: CoValueUniqueness = this.crypto.createdNowUnique(),
|
|
606
582
|
): RawGroup {
|
|
583
|
+
const account = this.getCurrentAgent();
|
|
584
|
+
|
|
607
585
|
const groupCoValue = this.createCoValue({
|
|
608
586
|
type: "comap",
|
|
609
|
-
ruleset: { type: "group", initialAdmin:
|
|
587
|
+
ruleset: { type: "group", initialAdmin: account.id },
|
|
610
588
|
meta: null,
|
|
611
589
|
...uniqueness,
|
|
612
590
|
});
|
|
613
591
|
|
|
614
592
|
const group = expectGroup(groupCoValue.getCurrentContent());
|
|
615
593
|
|
|
616
|
-
group.set(
|
|
594
|
+
group.set(account.id, "admin", "trusting");
|
|
617
595
|
|
|
618
596
|
const readKey = this.crypto.newRandomKeySecret();
|
|
619
597
|
|
|
620
598
|
group.set(
|
|
621
|
-
`${readKey.id}_for_${
|
|
599
|
+
`${readKey.id}_for_${account.id}`,
|
|
622
600
|
this.crypto.seal({
|
|
623
601
|
message: readKey.secret,
|
|
624
|
-
from:
|
|
625
|
-
to:
|
|
602
|
+
from: account.currentSealerSecret(),
|
|
603
|
+
to: account.currentSealerID(),
|
|
626
604
|
nOnceMaterial: {
|
|
627
605
|
in: groupCoValue.id,
|
|
628
606
|
tx: groupCoValue.nextTransactionID(),
|
|
@@ -637,24 +615,34 @@ export class LocalNode {
|
|
|
637
615
|
}
|
|
638
616
|
|
|
639
617
|
/** @internal */
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
currentSessionID: SessionID,
|
|
618
|
+
cloneWithDifferentAccount(
|
|
619
|
+
controlledAccountOrAgent: ControlledAccountOrAgent,
|
|
643
620
|
): LocalNode {
|
|
644
|
-
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
|
+
}
|
|
645
631
|
|
|
646
|
-
|
|
632
|
+
/** @internal */
|
|
633
|
+
cloneVerifiedStateFrom(otherNode: LocalNode) {
|
|
634
|
+
const coValuesToCopy = Array.from(otherNode.coValues.entries());
|
|
647
635
|
|
|
648
636
|
while (coValuesToCopy.length > 0) {
|
|
649
|
-
const [coValueID,
|
|
637
|
+
const [coValueID, coValue] = coValuesToCopy[coValuesToCopy.length - 1]!;
|
|
650
638
|
|
|
651
|
-
if (!
|
|
639
|
+
if (!coValue.isAvailable()) {
|
|
652
640
|
coValuesToCopy.pop();
|
|
653
641
|
continue;
|
|
654
642
|
} else {
|
|
655
|
-
const allDepsCopied =
|
|
643
|
+
const allDepsCopied = coValue
|
|
656
644
|
.getDependedOnCoValues()
|
|
657
|
-
.every((dep) =>
|
|
645
|
+
.every((dep) => this.coValues.get(dep)?.isAvailable());
|
|
658
646
|
|
|
659
647
|
if (!allDepsCopied) {
|
|
660
648
|
// move to end of queue
|
|
@@ -662,34 +650,11 @@ export class LocalNode {
|
|
|
662
650
|
continue;
|
|
663
651
|
}
|
|
664
652
|
|
|
665
|
-
|
|
666
|
-
entry.core.header,
|
|
667
|
-
newNode,
|
|
668
|
-
new Map(entry.core.sessionLogs),
|
|
669
|
-
);
|
|
670
|
-
|
|
671
|
-
newNode.coValuesStore.internalMarkMagicallyAvailable(
|
|
672
|
-
coValueID,
|
|
673
|
-
newCoValue,
|
|
674
|
-
);
|
|
653
|
+
this.putCoValue(coValueID, coValue.verified);
|
|
675
654
|
|
|
676
655
|
coValuesToCopy.pop();
|
|
677
656
|
}
|
|
678
657
|
}
|
|
679
|
-
|
|
680
|
-
if (account instanceof RawControlledAccount) {
|
|
681
|
-
// To make sure that when we edit the account, we're modifying the correct sessions
|
|
682
|
-
const accountInNode = new RawControlledAccount(
|
|
683
|
-
newNode.expectCoValueLoaded(account.id),
|
|
684
|
-
account.agentSecret,
|
|
685
|
-
);
|
|
686
|
-
if (accountInNode.core.node !== newNode) {
|
|
687
|
-
throw new Error("Account's node is not the new node");
|
|
688
|
-
}
|
|
689
|
-
newNode.account = accountInNode;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
return newNode;
|
|
693
658
|
}
|
|
694
659
|
|
|
695
660
|
gracefulShutdown() {
|