cojson 0.8.21 → 0.8.27
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/CHANGELOG.md +13 -0
- package/dist/native/CoValuesStore.js +31 -0
- package/dist/native/CoValuesStore.js.map +1 -0
- package/dist/native/PeerState.js +3 -0
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/SyncStateSubscriptionManager.js +2 -2
- package/dist/native/SyncStateSubscriptionManager.js.map +1 -1
- package/dist/native/coValueState.js +183 -27
- package/dist/native/coValueState.js.map +1 -1
- package/dist/native/localNode.js +20 -41
- package/dist/native/localNode.js.map +1 -1
- package/dist/native/sync.js +43 -82
- package/dist/native/sync.js.map +1 -1
- package/dist/web/CoValuesStore.js +31 -0
- package/dist/web/CoValuesStore.js.map +1 -0
- package/dist/web/PeerState.js +3 -0
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/SyncStateSubscriptionManager.js +2 -2
- package/dist/web/SyncStateSubscriptionManager.js.map +1 -1
- package/dist/web/coValueState.js +183 -27
- package/dist/web/coValueState.js.map +1 -1
- package/dist/web/localNode.js +20 -41
- package/dist/web/localNode.js.map +1 -1
- package/dist/web/sync.js +43 -82
- package/dist/web/sync.js.map +1 -1
- package/package.json +1 -1
- package/src/CoValuesStore.ts +38 -0
- package/src/PeerState.ts +4 -0
- package/src/SyncStateSubscriptionManager.ts +2 -2
- package/src/coValueState.ts +260 -42
- package/src/localNode.ts +28 -56
- package/src/sync.ts +58 -104
- package/src/tests/coValueState.test.ts +427 -0
- package/src/tests/group.test.ts +1 -1
- package/src/tests/sync.test.ts +123 -9
package/src/coValueState.ts
CHANGED
|
@@ -1,54 +1,44 @@
|
|
|
1
|
+
import { PeerState } from "./PeerState.js";
|
|
1
2
|
import { CoValueCore } from "./coValueCore.js";
|
|
3
|
+
import { RawCoID } from "./ids.js";
|
|
2
4
|
import { PeerID } from "./sync.js";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
let resolve!: (value: T) => void;
|
|
6
|
+
export const CO_VALUE_LOADING_MAX_RETRIES = 5;
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
return { promise, resolve };
|
|
8
|
+
export class CoValueUnknownState {
|
|
9
|
+
type = "unknown" as const;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
class
|
|
15
|
-
type = "
|
|
16
|
-
private peers
|
|
12
|
+
export class CoValueLoadingState {
|
|
13
|
+
type = "loading" as const;
|
|
14
|
+
private peers = new Map<
|
|
17
15
|
PeerID,
|
|
18
|
-
ReturnType<typeof createResolvablePromise<
|
|
19
|
-
|
|
20
|
-
private
|
|
16
|
+
ReturnType<typeof createResolvablePromise<void>>
|
|
17
|
+
>();
|
|
18
|
+
private resolveResult: (value: CoValueCore | "unavailable") => void;
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
result: Promise<CoValueCore | "unavailable">;
|
|
23
21
|
|
|
24
22
|
constructor(peersIds: Iterable<PeerID>) {
|
|
25
23
|
this.peers = new Map();
|
|
26
24
|
|
|
27
25
|
for (const peerId of peersIds) {
|
|
28
|
-
this.peers.set(
|
|
29
|
-
peerId,
|
|
30
|
-
createResolvablePromise<"available" | "unavailable">(),
|
|
31
|
-
);
|
|
26
|
+
this.peers.set(peerId, createResolvablePromise<void>());
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
const { resolve, promise } = createResolvablePromise<
|
|
35
|
-
|
|
30
|
+
CoValueCore | "unavailable"
|
|
36
31
|
>();
|
|
37
32
|
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
33
|
+
this.result = promise;
|
|
34
|
+
this.resolveResult = resolve;
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
markAsUnavailable(peerId: PeerID) {
|
|
43
38
|
const entry = this.peers.get(peerId);
|
|
44
39
|
|
|
45
40
|
if (entry) {
|
|
46
|
-
entry.resolve(
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (value === "available") {
|
|
50
|
-
this.resolve("available");
|
|
51
|
-
return;
|
|
41
|
+
entry.resolve();
|
|
52
42
|
}
|
|
53
43
|
|
|
54
44
|
this.peers.delete(peerId);
|
|
@@ -59,6 +49,14 @@ class CoValueUnknownState {
|
|
|
59
49
|
}
|
|
60
50
|
}
|
|
61
51
|
|
|
52
|
+
resolve(value: CoValueCore | "unavailable") {
|
|
53
|
+
this.resolveResult(value);
|
|
54
|
+
for (const entry of this.peers.values()) {
|
|
55
|
+
entry.resolve();
|
|
56
|
+
}
|
|
57
|
+
this.peers.clear();
|
|
58
|
+
}
|
|
59
|
+
|
|
62
60
|
// Wait for a specific peer to have a known state
|
|
63
61
|
waitForPeer(peerId: PeerID) {
|
|
64
62
|
const entry = this.peers.get(peerId);
|
|
@@ -71,47 +69,267 @@ class CoValueUnknownState {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
class CoValueAvailableState {
|
|
72
|
+
export class CoValueAvailableState {
|
|
75
73
|
type = "available" as const;
|
|
76
74
|
|
|
77
75
|
constructor(public coValue: CoValueCore) {}
|
|
78
76
|
}
|
|
79
77
|
|
|
78
|
+
export class CoValueUnavailableState {
|
|
79
|
+
type = "unavailable" as const;
|
|
80
|
+
}
|
|
81
|
+
|
|
80
82
|
type CoValueStateAction =
|
|
81
83
|
| {
|
|
82
|
-
type: "
|
|
83
|
-
|
|
84
|
+
type: "load-requested";
|
|
85
|
+
peersIds: PeerID[];
|
|
84
86
|
}
|
|
85
87
|
| {
|
|
86
|
-
type: "found";
|
|
88
|
+
type: "not-found-in-peer";
|
|
87
89
|
peerId: PeerID;
|
|
90
|
+
}
|
|
91
|
+
| {
|
|
92
|
+
type: "available";
|
|
88
93
|
coValue: CoValueCore;
|
|
89
94
|
};
|
|
90
95
|
|
|
96
|
+
type CoValueStateType =
|
|
97
|
+
| CoValueUnknownState
|
|
98
|
+
| CoValueLoadingState
|
|
99
|
+
| CoValueAvailableState
|
|
100
|
+
| CoValueUnavailableState;
|
|
101
|
+
|
|
91
102
|
export class CoValueState {
|
|
92
|
-
|
|
103
|
+
promise?: Promise<CoValueCore | "unavailable">;
|
|
104
|
+
private resolve?: (value: CoValueCore | "unavailable") => void;
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
public id: RawCoID,
|
|
108
|
+
public state: CoValueStateType,
|
|
109
|
+
) {}
|
|
93
110
|
|
|
94
|
-
static Unknown(
|
|
95
|
-
return new CoValueState(new CoValueUnknownState(
|
|
111
|
+
static Unknown(id: RawCoID) {
|
|
112
|
+
return new CoValueState(id, new CoValueUnknownState());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static Loading(id: RawCoID, peersIds: Iterable<PeerID>) {
|
|
116
|
+
return new CoValueState(id, new CoValueLoadingState(peersIds));
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
static Available(coValue: CoValueCore) {
|
|
99
|
-
return new CoValueState(new CoValueAvailableState(coValue));
|
|
120
|
+
return new CoValueState(coValue.id, new CoValueAvailableState(coValue));
|
|
100
121
|
}
|
|
101
122
|
|
|
102
|
-
|
|
123
|
+
static Unavailable(id: RawCoID) {
|
|
124
|
+
return new CoValueState(id, new CoValueUnavailableState());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async getCoValue() {
|
|
103
128
|
if (this.state.type === "available") {
|
|
129
|
+
return this.state.coValue;
|
|
130
|
+
}
|
|
131
|
+
if (this.state.type === "unavailable") {
|
|
132
|
+
return "unavailable";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// If we don't have a resolved state we return a new promise
|
|
136
|
+
// that will be resolved when the state will move to available or unavailable
|
|
137
|
+
if (!this.promise) {
|
|
138
|
+
const { promise, resolve } = createResolvablePromise<
|
|
139
|
+
CoValueCore | "unavailable"
|
|
140
|
+
>();
|
|
141
|
+
|
|
142
|
+
this.promise = promise;
|
|
143
|
+
this.resolve = resolve;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.promise;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private moveToState(value: CoValueStateType) {
|
|
150
|
+
this.state = value;
|
|
151
|
+
|
|
152
|
+
if (!this.resolve) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// If the state is available we resolve the promise
|
|
157
|
+
// and clear it to handle the possible transition from unavailable to available
|
|
158
|
+
if (value.type === "available") {
|
|
159
|
+
this.resolve(value.coValue);
|
|
160
|
+
this.clearPromise();
|
|
161
|
+
} else if (value.type === "unavailable") {
|
|
162
|
+
this.resolve("unavailable");
|
|
163
|
+
this.clearPromise();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private clearPromise() {
|
|
168
|
+
this.promise = undefined;
|
|
169
|
+
this.resolve = undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async loadFromPeers(peers: PeerState[]) {
|
|
173
|
+
const state = this.state;
|
|
174
|
+
|
|
175
|
+
if (state.type !== "unknown" && state.type !== "unavailable") {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (peers.length === 0) {
|
|
104
180
|
return;
|
|
105
181
|
}
|
|
106
182
|
|
|
183
|
+
const doLoad = async (peersToLoadFrom: PeerState[]) => {
|
|
184
|
+
const peersWithoutErrors = getPeersWithoutErrors(
|
|
185
|
+
peersToLoadFrom,
|
|
186
|
+
this.id,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// If we are in the loading state we move to a new loading state
|
|
190
|
+
// to reset all the loading promises
|
|
191
|
+
if (this.state.type === "loading" || this.state.type === "unknown") {
|
|
192
|
+
this.moveToState(
|
|
193
|
+
new CoValueLoadingState(peersWithoutErrors.map((p) => p.id)),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Assign the current state to a variable to not depend on the state changes
|
|
198
|
+
// that may happen while we wait for loadCoValueFromPeers to complete
|
|
199
|
+
const currentState = this.state;
|
|
200
|
+
|
|
201
|
+
// If we entered successfully the loading state, we load the coValue from the peers
|
|
202
|
+
//
|
|
203
|
+
// We may not enter the loading state if the coValue has become available in between
|
|
204
|
+
// of the retries
|
|
205
|
+
if (currentState.type === "loading") {
|
|
206
|
+
await loadCoValueFromPeers(this, peersWithoutErrors);
|
|
207
|
+
|
|
208
|
+
const result = await currentState.result;
|
|
209
|
+
return result !== "unavailable";
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return currentState.type === "available";
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
await doLoad(peers);
|
|
216
|
+
|
|
217
|
+
// Retry loading from peers that have the retry flag enabled
|
|
218
|
+
const peersWithRetry = peers.filter((p) =>
|
|
219
|
+
p.shouldRetryUnavailableCoValues(),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
if (peersWithRetry.length > 0) {
|
|
223
|
+
// We want to exit early if the coValue becomes available in between the retries
|
|
224
|
+
await Promise.race([
|
|
225
|
+
this.getCoValue(),
|
|
226
|
+
runWithRetry(
|
|
227
|
+
() => doLoad(peersWithRetry),
|
|
228
|
+
CO_VALUE_LOADING_MAX_RETRIES,
|
|
229
|
+
),
|
|
230
|
+
]);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// If after the retries the coValue is still loading, we consider the load failed
|
|
234
|
+
if (this.state.type === "loading") {
|
|
235
|
+
this.moveToState(new CoValueUnavailableState());
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
dispatch(action: CoValueStateAction) {
|
|
240
|
+
const currentState = this.state;
|
|
241
|
+
|
|
107
242
|
switch (action.type) {
|
|
108
|
-
case "
|
|
109
|
-
|
|
243
|
+
case "available":
|
|
244
|
+
if (currentState.type === "loading") {
|
|
245
|
+
currentState.resolve(action.coValue);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// It should be always possible to move to the available state
|
|
249
|
+
this.moveToState(new CoValueAvailableState(action.coValue));
|
|
250
|
+
|
|
110
251
|
break;
|
|
111
|
-
case "found":
|
|
112
|
-
|
|
113
|
-
|
|
252
|
+
case "not-found-in-peer":
|
|
253
|
+
if (currentState.type === "loading") {
|
|
254
|
+
currentState.markAsUnavailable(action.peerId);
|
|
255
|
+
}
|
|
256
|
+
|
|
114
257
|
break;
|
|
115
258
|
}
|
|
116
259
|
}
|
|
117
260
|
}
|
|
261
|
+
|
|
262
|
+
async function loadCoValueFromPeers(
|
|
263
|
+
coValueEntry: CoValueState,
|
|
264
|
+
peers: PeerState[],
|
|
265
|
+
) {
|
|
266
|
+
for (const peer of peers) {
|
|
267
|
+
if (coValueEntry.state.type === "loading") {
|
|
268
|
+
await peer.pushOutgoingMessage({
|
|
269
|
+
action: "load",
|
|
270
|
+
id: coValueEntry.id,
|
|
271
|
+
header: false,
|
|
272
|
+
sessions: {},
|
|
273
|
+
});
|
|
274
|
+
} else if (coValueEntry.state.type === "available") {
|
|
275
|
+
await peer.pushOutgoingMessage({
|
|
276
|
+
action: "load",
|
|
277
|
+
...coValueEntry.state.coValue.knownState(),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (coValueEntry.state.type === "loading") {
|
|
282
|
+
await coValueEntry.state.waitForPeer(peer.id);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function runWithRetry<T>(fn: () => Promise<T>, maxRetries: number) {
|
|
288
|
+
let retries = 1;
|
|
289
|
+
|
|
290
|
+
while (retries < maxRetries) {
|
|
291
|
+
/**
|
|
292
|
+
* With maxRetries of 5 we should wait:
|
|
293
|
+
* 300ms
|
|
294
|
+
* 900ms
|
|
295
|
+
* 2700ms
|
|
296
|
+
* 8100ms
|
|
297
|
+
*/
|
|
298
|
+
await sleep(3 ** retries * 100);
|
|
299
|
+
|
|
300
|
+
const result = await fn();
|
|
301
|
+
|
|
302
|
+
if (result === true) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
retries++;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function createResolvablePromise<T>() {
|
|
311
|
+
let resolve!: (value: T) => void;
|
|
312
|
+
|
|
313
|
+
const promise = new Promise<T>((res) => {
|
|
314
|
+
resolve = res;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return { promise, resolve };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function sleep(ms: number) {
|
|
321
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function getPeersWithoutErrors(peers: PeerState[], coValueId: RawCoID) {
|
|
325
|
+
return peers.filter((p) => {
|
|
326
|
+
if (p.erroredCoValues.has(coValueId)) {
|
|
327
|
+
console.error(
|
|
328
|
+
`Skipping load on errored coValue ${coValueId} from peer ${p.id}`,
|
|
329
|
+
);
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return true;
|
|
334
|
+
});
|
|
335
|
+
}
|
package/src/localNode.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Result, ResultAsync, err, ok, okAsync } from "neverthrow";
|
|
2
|
+
import { CoValuesStore } from "./CoValuesStore.js";
|
|
2
3
|
import { CoID } from "./coValue.js";
|
|
3
4
|
import { RawCoValue } from "./coValue.js";
|
|
4
5
|
import {
|
|
@@ -6,7 +7,6 @@ import {
|
|
|
6
7
|
CoValueHeader,
|
|
7
8
|
CoValueUniqueness,
|
|
8
9
|
} from "./coValueCore.js";
|
|
9
|
-
import { CoValueState } from "./coValueState.js";
|
|
10
10
|
import {
|
|
11
11
|
AccountMeta,
|
|
12
12
|
ControlledAccountOrAgent,
|
|
@@ -45,7 +45,7 @@ export class LocalNode {
|
|
|
45
45
|
/** @internal */
|
|
46
46
|
crypto: CryptoProvider;
|
|
47
47
|
/** @internal */
|
|
48
|
-
|
|
48
|
+
coValuesStore = new CoValuesStore();
|
|
49
49
|
/** @category 3. Low-level */
|
|
50
50
|
account: ControlledAccountOrAgent;
|
|
51
51
|
/** @category 3. Low-level */
|
|
@@ -125,7 +125,8 @@ export class LocalNode {
|
|
|
125
125
|
);
|
|
126
126
|
|
|
127
127
|
nodeWithAccount.account = controlledAccount;
|
|
128
|
-
nodeWithAccount.
|
|
128
|
+
nodeWithAccount.coValuesStore.setAsAvailable(
|
|
129
|
+
controlledAccount.id,
|
|
129
130
|
controlledAccount.core,
|
|
130
131
|
);
|
|
131
132
|
controlledAccount.core._cachedContent = undefined;
|
|
@@ -136,7 +137,7 @@ export class LocalNode {
|
|
|
136
137
|
|
|
137
138
|
// we shouldn't need this, but it fixes account data not syncing for new accounts
|
|
138
139
|
function syncAllCoValuesAfterCreateAccount() {
|
|
139
|
-
for (const coValueEntry of
|
|
140
|
+
for (const coValueEntry of nodeWithAccount.coValuesStore.getValues()) {
|
|
140
141
|
if (coValueEntry.state.type === "available") {
|
|
141
142
|
void nodeWithAccount.syncManager.syncCoValue(
|
|
142
143
|
coValueEntry.state.coValue,
|
|
@@ -206,7 +207,7 @@ export class LocalNode {
|
|
|
206
207
|
node.syncManager.local = node;
|
|
207
208
|
|
|
208
209
|
controlledAccount.core.node = node;
|
|
209
|
-
node.
|
|
210
|
+
node.coValuesStore.setAsAvailable(accountID, controlledAccount.core);
|
|
210
211
|
controlledAccount.core._cachedContent = undefined;
|
|
211
212
|
|
|
212
213
|
const profileID = account.get("profile");
|
|
@@ -243,7 +244,7 @@ export class LocalNode {
|
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
const coValue = new CoValueCore(header, this);
|
|
246
|
-
this.
|
|
247
|
+
this.coValuesStore.setAsAvailable(coValue.id, coValue);
|
|
247
248
|
|
|
248
249
|
void this.syncManager.syncCoValue(coValue);
|
|
249
250
|
|
|
@@ -253,10 +254,7 @@ export class LocalNode {
|
|
|
253
254
|
/** @internal */
|
|
254
255
|
async loadCoValueCore(
|
|
255
256
|
id: RawCoID,
|
|
256
|
-
|
|
257
|
-
dontLoadFrom?: PeerID;
|
|
258
|
-
dontWaitFor?: PeerID;
|
|
259
|
-
} = {},
|
|
257
|
+
skipLoadingFromPeer?: PeerID,
|
|
260
258
|
): Promise<CoValueCore | "unavailable"> {
|
|
261
259
|
if (this.crashed) {
|
|
262
260
|
throw new Error("Trying to load CoValue after node has crashed", {
|
|
@@ -264,40 +262,18 @@ export class LocalNode {
|
|
|
264
262
|
});
|
|
265
263
|
}
|
|
266
264
|
|
|
267
|
-
|
|
268
|
-
if (!entry) {
|
|
269
|
-
const peersToWaitFor = new Set(
|
|
270
|
-
Object.values(this.syncManager.peers)
|
|
271
|
-
.filter((peer) => peer.isServerOrStoragePeer())
|
|
272
|
-
.map((peer) => peer.id),
|
|
273
|
-
);
|
|
274
|
-
if (options.dontWaitFor) peersToWaitFor.delete(options.dontWaitFor);
|
|
275
|
-
entry = CoValueState.Unknown(peersToWaitFor);
|
|
276
|
-
|
|
277
|
-
this.coValues[id] = entry;
|
|
265
|
+
const entry = this.coValuesStore.get(id);
|
|
278
266
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
id,
|
|
267
|
+
if (entry.state.type === "unknown" || entry.state.type === "unavailable") {
|
|
268
|
+
const peers =
|
|
269
|
+
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
|
283
270
|
|
|
284
|
-
|
|
285
|
-
);
|
|
271
|
+
await entry.loadFromPeers(peers).catch((e) => {
|
|
272
|
+
console.error("Error loading from peers", id, e);
|
|
286
273
|
});
|
|
287
274
|
}
|
|
288
|
-
if (entry.state.type === "available") {
|
|
289
|
-
return Promise.resolve(entry.state.coValue);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
await entry.state.ready;
|
|
293
|
-
|
|
294
|
-
const updatedEntry = this.coValues[id];
|
|
295
|
-
|
|
296
|
-
if (updatedEntry?.state.type === "available") {
|
|
297
|
-
return Promise.resolve(updatedEntry.state.coValue);
|
|
298
|
-
}
|
|
299
275
|
|
|
300
|
-
return
|
|
276
|
+
return entry.getCoValue();
|
|
301
277
|
}
|
|
302
278
|
|
|
303
279
|
/**
|
|
@@ -318,13 +294,12 @@ export class LocalNode {
|
|
|
318
294
|
}
|
|
319
295
|
|
|
320
296
|
getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
|
|
321
|
-
const entry = this.
|
|
322
|
-
|
|
323
|
-
return undefined;
|
|
324
|
-
}
|
|
297
|
+
const entry = this.coValuesStore.get(id);
|
|
298
|
+
|
|
325
299
|
if (entry.state.type === "available") {
|
|
326
300
|
return entry.state.coValue.getCurrentContent() as T;
|
|
327
301
|
}
|
|
302
|
+
|
|
328
303
|
return undefined;
|
|
329
304
|
}
|
|
330
305
|
|
|
@@ -448,15 +423,11 @@ export class LocalNode {
|
|
|
448
423
|
|
|
449
424
|
/** @internal */
|
|
450
425
|
expectCoValueLoaded(id: RawCoID, expectation?: string): CoValueCore {
|
|
451
|
-
const entry = this.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
`${expectation ? expectation + ": " : ""}Unknown CoValue ${id}`,
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
if (entry.state.type === "unknown") {
|
|
426
|
+
const entry = this.coValuesStore.get(id);
|
|
427
|
+
|
|
428
|
+
if (entry.state.type !== "available") {
|
|
458
429
|
throw new Error(
|
|
459
|
-
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded`,
|
|
430
|
+
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${entry.state.type}`,
|
|
460
431
|
);
|
|
461
432
|
}
|
|
462
433
|
return entry.state.coValue;
|
|
@@ -650,18 +621,20 @@ export class LocalNode {
|
|
|
650
621
|
): LocalNode {
|
|
651
622
|
const newNode = new LocalNode(account, currentSessionID, this.crypto);
|
|
652
623
|
|
|
653
|
-
const coValuesToCopy =
|
|
624
|
+
const coValuesToCopy = Array.from(this.coValuesStore.getEntries());
|
|
654
625
|
|
|
655
626
|
while (coValuesToCopy.length > 0) {
|
|
656
627
|
const [coValueID, entry] = coValuesToCopy[coValuesToCopy.length - 1]!;
|
|
657
628
|
|
|
658
|
-
if (entry.state.type
|
|
629
|
+
if (entry.state.type !== "available") {
|
|
659
630
|
coValuesToCopy.pop();
|
|
660
631
|
continue;
|
|
661
632
|
} else {
|
|
662
633
|
const allDepsCopied = entry.state.coValue
|
|
663
634
|
.getDependedOnCoValues()
|
|
664
|
-
.every(
|
|
635
|
+
.every(
|
|
636
|
+
(dep) => newNode.coValuesStore.get(dep).state.type === "available",
|
|
637
|
+
);
|
|
665
638
|
|
|
666
639
|
if (!allDepsCopied) {
|
|
667
640
|
// move to end of queue
|
|
@@ -675,8 +648,7 @@ export class LocalNode {
|
|
|
675
648
|
new Map(entry.state.coValue.sessionLogs),
|
|
676
649
|
);
|
|
677
650
|
|
|
678
|
-
newNode.
|
|
679
|
-
CoValueState.Available(newCoValue);
|
|
651
|
+
newNode.coValuesStore.setAsAvailable(coValueID, newCoValue);
|
|
680
652
|
|
|
681
653
|
coValuesToCopy.pop();
|
|
682
654
|
}
|