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/permissions.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CoID } from "./coValue.js";
|
|
2
|
-
import { CoValueCore
|
|
2
|
+
import { CoValueCore } from "./coValueCore/coValueCore.js";
|
|
3
|
+
import { Transaction } from "./coValueCore/verifiedState.js";
|
|
3
4
|
import { RawAccount, RawAccountID, RawProfile } from "./coValues/account.js";
|
|
4
5
|
import { MapOpPayload } from "./coValues/coMap.js";
|
|
5
6
|
import {
|
|
@@ -64,19 +65,23 @@ export function determineValidTransactions(
|
|
|
64
65
|
coValue: CoValueCore,
|
|
65
66
|
knownTransactions?: CoValueKnownState["sessions"],
|
|
66
67
|
): { txID: TransactionID; tx: Transaction }[] {
|
|
67
|
-
if (coValue.
|
|
68
|
-
|
|
68
|
+
if (!coValue.isAvailable()) {
|
|
69
|
+
throw new Error("determineValidTransactions CoValue is not available");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (coValue.verified.header.ruleset.type === "group") {
|
|
73
|
+
const initialAdmin = coValue.verified.header.ruleset.initialAdmin;
|
|
69
74
|
if (!initialAdmin) {
|
|
70
75
|
throw new Error("Group must have initialAdmin");
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
return determineValidTransactionsForGroup(coValue, initialAdmin)
|
|
74
79
|
.validTransactions;
|
|
75
|
-
} else if (coValue.header.ruleset.type === "ownedByGroup") {
|
|
80
|
+
} else if (coValue.verified.header.ruleset.type === "ownedByGroup") {
|
|
76
81
|
const groupContent = expectGroup(
|
|
77
82
|
coValue.node
|
|
78
83
|
.expectCoValueLoaded(
|
|
79
|
-
coValue.header.ruleset.group,
|
|
84
|
+
coValue.verified.header.ruleset.group,
|
|
80
85
|
"Determining valid transaction in owned object but its group wasn't loaded",
|
|
81
86
|
)
|
|
82
87
|
.getCurrentContent(),
|
|
@@ -88,7 +93,7 @@ export function determineValidTransactions(
|
|
|
88
93
|
|
|
89
94
|
const validTransactions: ValidTransactionsResult[] = [];
|
|
90
95
|
|
|
91
|
-
for (const [sessionID, sessionLog] of coValue.
|
|
96
|
+
for (const [sessionID, sessionLog] of coValue.verified.sessions.entries()) {
|
|
92
97
|
const transactor = accountOrAgentIDfromSessionID(sessionID);
|
|
93
98
|
const knownTransactionsForSession = knownTransactions?.[sessionID] ?? -1;
|
|
94
99
|
|
|
@@ -123,10 +128,10 @@ export function determineValidTransactions(
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
return validTransactions;
|
|
126
|
-
} else if (coValue.header.ruleset.type === "unsafeAllowAll") {
|
|
131
|
+
} else if (coValue.verified.header.ruleset.type === "unsafeAllowAll") {
|
|
127
132
|
const validTransactions: ValidTransactionsResult[] = [];
|
|
128
133
|
|
|
129
|
-
for (const [sessionID, sessionLog] of coValue.
|
|
134
|
+
for (const [sessionID, sessionLog] of coValue.verified.sessions.entries()) {
|
|
130
135
|
const knownTransactionsForSession = knownTransactions?.[sessionID] ?? -1;
|
|
131
136
|
|
|
132
137
|
sessionLog.transactions.forEach((tx, txIndex) => {
|
|
@@ -141,7 +146,7 @@ export function determineValidTransactions(
|
|
|
141
146
|
} else {
|
|
142
147
|
throw new Error(
|
|
143
148
|
"Unknown ruleset type " +
|
|
144
|
-
(coValue.header.ruleset as { type: string }).type,
|
|
149
|
+
(coValue.verified.header.ruleset as { type: string }).type,
|
|
145
150
|
);
|
|
146
151
|
}
|
|
147
152
|
}
|
|
@@ -167,7 +172,7 @@ function resolveMemberStateFromParentReference(
|
|
|
167
172
|
"Expected parent group to be loaded",
|
|
168
173
|
);
|
|
169
174
|
|
|
170
|
-
if (parentGroup.header.ruleset.type !== "group") {
|
|
175
|
+
if (parentGroup.verified.header.ruleset.type !== "group") {
|
|
171
176
|
return;
|
|
172
177
|
}
|
|
173
178
|
|
|
@@ -176,7 +181,7 @@ function resolveMemberStateFromParentReference(
|
|
|
176
181
|
return;
|
|
177
182
|
}
|
|
178
183
|
|
|
179
|
-
const initialAdmin = parentGroup.header.ruleset.initialAdmin;
|
|
184
|
+
const initialAdmin = parentGroup.verified.header.ruleset.initialAdmin;
|
|
180
185
|
|
|
181
186
|
if (!initialAdmin) {
|
|
182
187
|
throw new Error("Group must have initialAdmin");
|
|
@@ -214,7 +219,8 @@ function determineValidTransactionsForGroup(
|
|
|
214
219
|
tx: Transaction;
|
|
215
220
|
}[] = [];
|
|
216
221
|
|
|
217
|
-
for (const [sessionID, sessionLog] of coValue.
|
|
222
|
+
for (const [sessionID, sessionLog] of coValue.verified?.sessions.entries() ??
|
|
223
|
+
[]) {
|
|
218
224
|
sessionLog.transactions.forEach((tx, txIndex) => {
|
|
219
225
|
allTransactionsSorted.push({ sessionID, txIndex, tx });
|
|
220
226
|
});
|
package/src/priority.ts
CHANGED
package/src/sync.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Histogram, ValueType, metrics } from "@opentelemetry/api";
|
|
2
2
|
import { PeerState } from "./PeerState.js";
|
|
3
3
|
import { SyncStateManager } from "./SyncStateManager.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import {
|
|
5
|
+
AvailableCoValueCore,
|
|
6
|
+
CoValueCore,
|
|
7
|
+
} from "./coValueCore/coValueCore.js";
|
|
8
|
+
import { CoValueHeader, Transaction } from "./coValueCore/verifiedState.js";
|
|
7
9
|
import { Signature } from "./crypto/crypto.js";
|
|
8
10
|
import { RawCoID, SessionID } from "./ids.js";
|
|
9
11
|
import { LocalNode } from "./localNode.js";
|
|
@@ -158,7 +160,7 @@ export class SyncManager {
|
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
handleSyncMessage(msg: SyncMessage, peer: PeerState) {
|
|
161
|
-
if (this.local.
|
|
163
|
+
if (this.local.getCoValue(msg.id).isErroredInPeer(peer.id)) {
|
|
162
164
|
logger.warn(
|
|
163
165
|
`Skipping message ${msg.action} on errored coValue ${msg.id} from peer ${peer.id}`,
|
|
164
166
|
);
|
|
@@ -197,13 +199,17 @@ export class SyncManager {
|
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
sendNewContentIncludingDependencies(id: RawCoID, peer: PeerState) {
|
|
200
|
-
const coValue = this.local.
|
|
202
|
+
const coValue = this.local.getCoValue(id);
|
|
203
|
+
|
|
204
|
+
if (!coValue.isAvailable()) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
201
207
|
|
|
202
208
|
coValue
|
|
203
209
|
.getDependedOnCoValues()
|
|
204
210
|
.map((id) => this.sendNewContentIncludingDependencies(id, peer));
|
|
205
211
|
|
|
206
|
-
const newContentPieces = coValue.newContentSince(
|
|
212
|
+
const newContentPieces = coValue.verified.newContentSince(
|
|
207
213
|
peer.optimisticKnownStates.get(id),
|
|
208
214
|
);
|
|
209
215
|
|
|
@@ -212,16 +218,15 @@ export class SyncManager {
|
|
|
212
218
|
this.trySendToPeer(peer, piece);
|
|
213
219
|
}
|
|
214
220
|
|
|
215
|
-
peer.toldKnownState.add(id);
|
|
216
221
|
peer.combineOptimisticWith(id, coValue.knownState());
|
|
217
222
|
} else if (!peer.toldKnownState.has(id)) {
|
|
218
223
|
this.trySendToPeer(peer, {
|
|
219
224
|
action: "known",
|
|
220
225
|
...coValue.knownState(),
|
|
221
226
|
});
|
|
222
|
-
|
|
223
|
-
peer.toldKnownState.add(id);
|
|
224
227
|
}
|
|
228
|
+
|
|
229
|
+
peer.trackToldKnownState(id);
|
|
225
230
|
}
|
|
226
231
|
|
|
227
232
|
startPeerReconciliation(peer: PeerState) {
|
|
@@ -237,40 +242,38 @@ export class SyncManager {
|
|
|
237
242
|
gathered.add(coValue.id);
|
|
238
243
|
|
|
239
244
|
for (const id of coValue.getDependedOnCoValues()) {
|
|
240
|
-
const
|
|
245
|
+
const coValue = this.local.getCoValue(id);
|
|
241
246
|
|
|
242
|
-
if (
|
|
243
|
-
buildOrderedCoValueList(
|
|
247
|
+
if (coValue.isAvailable()) {
|
|
248
|
+
buildOrderedCoValueList(coValue);
|
|
244
249
|
}
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
coValuesOrderedByDependency.push(coValue);
|
|
248
253
|
};
|
|
249
254
|
|
|
250
|
-
for (const
|
|
251
|
-
if (!
|
|
255
|
+
for (const coValue of this.local.allCoValues()) {
|
|
256
|
+
if (!coValue.isAvailable()) {
|
|
252
257
|
// If the coValue is unavailable and we never tried this peer
|
|
253
258
|
// we try to load it from the peer
|
|
254
|
-
if (!peer.
|
|
255
|
-
peer.
|
|
259
|
+
if (!peer.loadRequestSent.has(coValue.id)) {
|
|
260
|
+
peer.trackLoadRequestSent(coValue.id);
|
|
256
261
|
this.trySendToPeer(peer, {
|
|
257
262
|
action: "load",
|
|
258
263
|
header: false,
|
|
259
|
-
id:
|
|
264
|
+
id: coValue.id,
|
|
260
265
|
sessions: {},
|
|
261
266
|
});
|
|
262
267
|
}
|
|
263
268
|
} else {
|
|
264
|
-
const coValue = entry.core;
|
|
265
|
-
|
|
266
269
|
// Build the list of coValues ordered by dependency
|
|
267
270
|
// so we can send the load message in the correct order
|
|
268
271
|
buildOrderedCoValueList(coValue);
|
|
269
272
|
}
|
|
270
273
|
|
|
271
274
|
// Fill the missing known states with empty known states
|
|
272
|
-
if (!peer.optimisticKnownStates.has(
|
|
273
|
-
peer.setOptimisticKnownState(
|
|
275
|
+
if (!peer.optimisticKnownStates.has(coValue.id)) {
|
|
276
|
+
peer.setOptimisticKnownState(coValue.id, "empty");
|
|
274
277
|
}
|
|
275
278
|
}
|
|
276
279
|
|
|
@@ -281,7 +284,7 @@ export class SyncManager {
|
|
|
281
284
|
* - Start the sync process in case we or the other peer
|
|
282
285
|
* lacks some transactions
|
|
283
286
|
*/
|
|
284
|
-
peer.
|
|
287
|
+
peer.trackLoadRequestSent(coValue.id);
|
|
285
288
|
this.trySendToPeer(peer, {
|
|
286
289
|
action: "load",
|
|
287
290
|
...coValue.knownState(),
|
|
@@ -368,11 +371,11 @@ export class SyncManager {
|
|
|
368
371
|
*
|
|
369
372
|
*/
|
|
370
373
|
peer.setKnownState(msg.id, knownStateIn(msg));
|
|
371
|
-
const
|
|
374
|
+
const coValue = this.local.getCoValue(msg.id);
|
|
372
375
|
|
|
373
376
|
if (
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
coValue.loadingState === "unknown" ||
|
|
378
|
+
coValue.loadingState === "unavailable"
|
|
376
379
|
) {
|
|
377
380
|
const eligiblePeers = this.getServerAndStoragePeers(peer.id);
|
|
378
381
|
|
|
@@ -380,8 +383,7 @@ export class SyncManager {
|
|
|
380
383
|
// We don't have any eligible peers to load the coValue from
|
|
381
384
|
// so we send a known state back to the sender to let it know
|
|
382
385
|
// that the coValue is unavailable
|
|
383
|
-
peer.
|
|
384
|
-
|
|
386
|
+
peer.trackToldKnownState(msg.id);
|
|
385
387
|
this.trySendToPeer(peer, {
|
|
386
388
|
action: "known",
|
|
387
389
|
id: msg.id,
|
|
@@ -398,17 +400,16 @@ export class SyncManager {
|
|
|
398
400
|
}
|
|
399
401
|
}
|
|
400
402
|
|
|
401
|
-
if (
|
|
403
|
+
if (coValue.loadingState === "loading") {
|
|
402
404
|
// We need to return from handleLoad immediately and wait for the CoValue to be loaded
|
|
403
405
|
// in a new task, otherwise we might block further incoming content messages that would
|
|
404
406
|
// resolve the CoValue as available. This can happen when we receive fresh
|
|
405
407
|
// content from a client, but we are a server with our own upstream server(s)
|
|
406
|
-
|
|
407
|
-
.
|
|
408
|
+
coValue
|
|
409
|
+
.waitForAvailableOrUnavailable()
|
|
408
410
|
.then(async (value) => {
|
|
409
|
-
if (value
|
|
410
|
-
peer.
|
|
411
|
-
|
|
411
|
+
if (!value.isAvailable()) {
|
|
412
|
+
peer.trackToldKnownState(msg.id);
|
|
412
413
|
this.trySendToPeer(peer, {
|
|
413
414
|
action: "known",
|
|
414
415
|
id: msg.id,
|
|
@@ -426,9 +427,10 @@ export class SyncManager {
|
|
|
426
427
|
err: e,
|
|
427
428
|
});
|
|
428
429
|
});
|
|
429
|
-
} else if (
|
|
430
|
+
} else if (coValue.isAvailable()) {
|
|
430
431
|
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
431
432
|
} else {
|
|
433
|
+
peer.trackToldKnownState(msg.id);
|
|
432
434
|
this.trySendToPeer(peer, {
|
|
433
435
|
action: "known",
|
|
434
436
|
id: msg.id,
|
|
@@ -439,7 +441,7 @@ export class SyncManager {
|
|
|
439
441
|
}
|
|
440
442
|
|
|
441
443
|
handleKnownState(msg: KnownStateMessage, peer: PeerState) {
|
|
442
|
-
const
|
|
444
|
+
const coValue = this.local.getCoValue(msg.id);
|
|
443
445
|
|
|
444
446
|
peer.combineWith(msg.id, knownStateIn(msg));
|
|
445
447
|
|
|
@@ -448,10 +450,10 @@ export class SyncManager {
|
|
|
448
450
|
const availableOnPeer = peer.optimisticKnownStates.get(msg.id)?.header;
|
|
449
451
|
|
|
450
452
|
if (!availableOnPeer) {
|
|
451
|
-
|
|
453
|
+
coValue.markNotFoundInPeer(peer.id);
|
|
452
454
|
}
|
|
453
455
|
|
|
454
|
-
if (
|
|
456
|
+
if (coValue.isAvailable()) {
|
|
455
457
|
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
456
458
|
}
|
|
457
459
|
}
|
|
@@ -470,11 +472,9 @@ export class SyncManager {
|
|
|
470
472
|
}
|
|
471
473
|
|
|
472
474
|
handleNewContent(msg: NewContentMessage, peer: PeerState) {
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
let coValue: CoValueCore;
|
|
475
|
+
const coValue = this.local.getCoValue(msg.id);
|
|
476
476
|
|
|
477
|
-
if (!
|
|
477
|
+
if (!coValue.isAvailable()) {
|
|
478
478
|
if (!msg.header) {
|
|
479
479
|
this.trySendToPeer(peer, {
|
|
480
480
|
action: "known",
|
|
@@ -487,12 +487,11 @@ export class SyncManager {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
peer.updateHeader(msg.id, true);
|
|
490
|
+
coValue.markAvailable(msg.header, peer.id);
|
|
491
|
+
}
|
|
490
492
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
entry.markAvailable(coValue, peer.id);
|
|
494
|
-
} else {
|
|
495
|
-
coValue = entry.core;
|
|
493
|
+
if (!coValue.isAvailable()) {
|
|
494
|
+
throw new Error("Unreachable: CoValue should be available in every case");
|
|
496
495
|
}
|
|
497
496
|
|
|
498
497
|
let invalidStateAssumed = false;
|
|
@@ -502,7 +501,7 @@ export class SyncManager {
|
|
|
502
501
|
SessionNewContent,
|
|
503
502
|
][]) {
|
|
504
503
|
const ourKnownTxIdx =
|
|
505
|
-
coValue.
|
|
504
|
+
coValue.verified.sessions.get(sessionID)?.transactions.length;
|
|
506
505
|
const theirFirstNewTxIdx = newContentForSession.after;
|
|
507
506
|
|
|
508
507
|
if ((ourKnownTxIdx || 0) < theirFirstNewTxIdx) {
|
|
@@ -526,16 +525,17 @@ export class SyncManager {
|
|
|
526
525
|
newTransactions,
|
|
527
526
|
undefined,
|
|
528
527
|
newContentForSession.lastSignature,
|
|
528
|
+
"immediate", // TODO: can we change this to deferred?
|
|
529
529
|
);
|
|
530
530
|
|
|
531
531
|
if (result.isErr()) {
|
|
532
|
-
|
|
532
|
+
console.error("Failed to add transactions", {
|
|
533
533
|
peerId: peer.id,
|
|
534
534
|
peerRole: peer.role,
|
|
535
535
|
id: msg.id,
|
|
536
536
|
err: result.error,
|
|
537
537
|
});
|
|
538
|
-
|
|
538
|
+
coValue.markErrored(peer.id, result.error);
|
|
539
539
|
continue;
|
|
540
540
|
}
|
|
541
541
|
|
|
@@ -555,7 +555,7 @@ export class SyncManager {
|
|
|
555
555
|
isCorrection: true,
|
|
556
556
|
...coValue.knownState(),
|
|
557
557
|
});
|
|
558
|
-
peer.
|
|
558
|
+
peer.trackToldKnownState(msg.id);
|
|
559
559
|
} else {
|
|
560
560
|
/**
|
|
561
561
|
* We are sending a known state message to the peer to acknowledge the
|
|
@@ -568,15 +568,50 @@ export class SyncManager {
|
|
|
568
568
|
action: "known",
|
|
569
569
|
...coValue.knownState(),
|
|
570
570
|
});
|
|
571
|
-
peer.
|
|
571
|
+
peer.trackToldKnownState(msg.id);
|
|
572
572
|
}
|
|
573
573
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
574
|
+
const sourcePeer = peer;
|
|
575
|
+
const syncedPeers = [];
|
|
576
|
+
|
|
577
|
+
for (const peer of this.peersInPriorityOrder()) {
|
|
578
|
+
/**
|
|
579
|
+
* We sync the content against the source peer if it is a client or server peers
|
|
580
|
+
* to upload any content that is available on the current node and not on the source peer.
|
|
581
|
+
*
|
|
582
|
+
* We don't need to do this with storage peers because we don't get updates from those peers,
|
|
583
|
+
* only load and store content.
|
|
584
|
+
*/
|
|
585
|
+
if (peer.id === sourcePeer.id && sourcePeer.role === "storage") continue;
|
|
586
|
+
if (peer.closed) continue;
|
|
587
|
+
if (coValue.isErroredInPeer(peer.id)) continue;
|
|
588
|
+
|
|
589
|
+
// We directly forward the new content to peers that have an active subscription
|
|
590
|
+
if (peer.optimisticKnownStates.has(coValue.id)) {
|
|
591
|
+
this.sendNewContentIncludingDependencies(coValue.id, peer);
|
|
592
|
+
syncedPeers.push(peer);
|
|
593
|
+
} else if (
|
|
594
|
+
peer.isServerOrStoragePeer() &&
|
|
595
|
+
!peer.loadRequestSent.has(coValue.id)
|
|
596
|
+
) {
|
|
597
|
+
const state = coValue.getStateForPeer(peer.id)?.type;
|
|
598
|
+
|
|
599
|
+
// Check if there is a inflight load operation and we
|
|
600
|
+
// are waiting for other peers to send the load request
|
|
601
|
+
if (state === "unknown" || state === undefined) {
|
|
602
|
+
this.trySendToPeer(peer, {
|
|
603
|
+
action: "load",
|
|
604
|
+
...coValue.knownState(),
|
|
605
|
+
});
|
|
606
|
+
peer.trackLoadRequestSent(coValue.id);
|
|
607
|
+
syncedPeers.push(peer);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
for (const peer of syncedPeers) {
|
|
613
|
+
this.syncState.triggerUpdate(peer.id, coValue.id);
|
|
614
|
+
}
|
|
580
615
|
}
|
|
581
616
|
|
|
582
617
|
handleCorrection(msg: KnownStateMessage, peer: PeerState) {
|
|
@@ -609,11 +644,9 @@ export class SyncManager {
|
|
|
609
644
|
}
|
|
610
645
|
|
|
611
646
|
async syncCoValue(coValue: CoValueCore) {
|
|
612
|
-
const entry = this.local.coValuesStore.get(coValue.id);
|
|
613
|
-
|
|
614
647
|
for (const peer of this.peersInPriorityOrder()) {
|
|
615
648
|
if (peer.closed) continue;
|
|
616
|
-
if (
|
|
649
|
+
if (coValue.isErroredInPeer(peer.id)) continue;
|
|
617
650
|
|
|
618
651
|
// Only subscribed CoValues are synced to clients
|
|
619
652
|
if (
|
|
@@ -669,11 +702,11 @@ export class SyncManager {
|
|
|
669
702
|
}
|
|
670
703
|
|
|
671
704
|
async waitForAllCoValuesSync(timeout = 60_000) {
|
|
672
|
-
const coValues = this.local.
|
|
705
|
+
const coValues = this.local.allCoValues();
|
|
673
706
|
const validCoValues = Array.from(coValues).filter(
|
|
674
707
|
(coValue) =>
|
|
675
|
-
coValue.
|
|
676
|
-
coValue.
|
|
708
|
+
coValue.loadingState === "available" ||
|
|
709
|
+
coValue.loadingState === "loading",
|
|
677
710
|
);
|
|
678
711
|
|
|
679
712
|
return Promise.all(
|
package/src/tests/coList.test.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { beforeEach, expect, test } from "vitest";
|
|
|
2
2
|
import { expectList } from "../coValue.js";
|
|
3
3
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
4
4
|
import { LocalNode } from "../localNode.js";
|
|
5
|
+
import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
5
6
|
import {
|
|
6
7
|
loadCoValueOrFail,
|
|
7
|
-
|
|
8
|
+
nodeWithRandomAgentAndSessionID,
|
|
9
|
+
randomAgentAndSessionID,
|
|
8
10
|
setupTestNode,
|
|
9
11
|
waitFor,
|
|
10
12
|
} from "./testUtils.js";
|
|
@@ -16,7 +18,7 @@ beforeEach(async () => {
|
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
test("Empty CoList works", () => {
|
|
19
|
-
const node =
|
|
21
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
20
22
|
|
|
21
23
|
const coValue = node.createCoValue({
|
|
22
24
|
type: "colist",
|
|
@@ -32,7 +34,7 @@ test("Empty CoList works", () => {
|
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
test("Can append, prepend, delete and replace items in CoList", () => {
|
|
35
|
-
const node =
|
|
37
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
36
38
|
|
|
37
39
|
const coValue = node.createCoValue({
|
|
38
40
|
type: "colist",
|
|
@@ -63,7 +65,7 @@ test("Can append, prepend, delete and replace items in CoList", () => {
|
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
test("Push is equivalent to append after last item", () => {
|
|
66
|
-
const node =
|
|
68
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
67
69
|
|
|
68
70
|
const coValue = node.createCoValue({
|
|
69
71
|
type: "colist",
|
|
@@ -85,7 +87,7 @@ test("Push is equivalent to append after last item", () => {
|
|
|
85
87
|
});
|
|
86
88
|
|
|
87
89
|
test("appendItems add an array of items at the end of the list", () => {
|
|
88
|
-
const node =
|
|
90
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
89
91
|
|
|
90
92
|
const coValue = node.createCoValue({
|
|
91
93
|
type: "colist",
|
|
@@ -105,7 +107,7 @@ test("appendItems add an array of items at the end of the list", () => {
|
|
|
105
107
|
});
|
|
106
108
|
|
|
107
109
|
test("appendItems at index", () => {
|
|
108
|
-
const node =
|
|
110
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
109
111
|
|
|
110
112
|
const coValue = node.createCoValue({
|
|
111
113
|
type: "colist",
|
|
@@ -135,7 +137,7 @@ test("appendItems at index", () => {
|
|
|
135
137
|
});
|
|
136
138
|
|
|
137
139
|
test("appendItems at index", () => {
|
|
138
|
-
const node =
|
|
140
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
139
141
|
|
|
140
142
|
const coValue = node.createCoValue({
|
|
141
143
|
type: "colist",
|
|
@@ -157,7 +159,7 @@ test("appendItems at index", () => {
|
|
|
157
159
|
});
|
|
158
160
|
|
|
159
161
|
test("appendItems with negative index", () => {
|
|
160
|
-
const node =
|
|
162
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
161
163
|
|
|
162
164
|
const coValue = node.createCoValue({
|
|
163
165
|
type: "colist",
|
|
@@ -175,7 +177,7 @@ test("appendItems with negative index", () => {
|
|
|
175
177
|
});
|
|
176
178
|
|
|
177
179
|
test("Can push into empty list", () => {
|
|
178
|
-
const node =
|
|
180
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
179
181
|
|
|
180
182
|
const coValue = node.createCoValue({
|
|
181
183
|
type: "colist",
|
|
@@ -193,7 +195,7 @@ test("Can push into empty list", () => {
|
|
|
193
195
|
});
|
|
194
196
|
|
|
195
197
|
test("init the list correctly", () => {
|
|
196
|
-
const node =
|
|
198
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
197
199
|
|
|
198
200
|
const group = node.createGroup();
|
|
199
201
|
|
|
@@ -213,7 +215,7 @@ test("init the list correctly", () => {
|
|
|
213
215
|
});
|
|
214
216
|
|
|
215
217
|
test("Items prepended to start appear with latest first", () => {
|
|
216
|
-
const node =
|
|
218
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
217
219
|
|
|
218
220
|
const coValue = node.createCoValue({
|
|
219
221
|
type: "colist",
|
|
@@ -232,7 +234,7 @@ test("Items prepended to start appear with latest first", () => {
|
|
|
232
234
|
});
|
|
233
235
|
|
|
234
236
|
test("mixing prepend and append", () => {
|
|
235
|
-
const node =
|
|
237
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
236
238
|
|
|
237
239
|
const coValue = node.createCoValue({
|
|
238
240
|
type: "colist",
|
|
@@ -251,7 +253,7 @@ test("mixing prepend and append", () => {
|
|
|
251
253
|
});
|
|
252
254
|
|
|
253
255
|
test("Items appended to start", () => {
|
|
254
|
-
const node =
|
|
256
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
255
257
|
|
|
256
258
|
const coValue = node.createCoValue({
|
|
257
259
|
type: "colist",
|
|
@@ -277,7 +279,9 @@ test("syncing appends with an older timestamp", async () => {
|
|
|
277
279
|
});
|
|
278
280
|
const otherClient = setupTestNode({});
|
|
279
281
|
|
|
280
|
-
const otherClientConnection = otherClient.connectToSyncServer(
|
|
282
|
+
const otherClientConnection = otherClient.connectToSyncServer({
|
|
283
|
+
ourName: "otherClient",
|
|
284
|
+
});
|
|
281
285
|
|
|
282
286
|
const coValue = client.node.createCoValue({
|
|
283
287
|
type: "colist",
|
|
@@ -309,7 +313,9 @@ test("syncing appends with an older timestamp", async () => {
|
|
|
309
313
|
|
|
310
314
|
list.append(6, undefined, "trusting");
|
|
311
315
|
|
|
312
|
-
otherClient.connectToSyncServer(
|
|
316
|
+
otherClient.connectToSyncServer({
|
|
317
|
+
ourName: "otherClient",
|
|
318
|
+
});
|
|
313
319
|
|
|
314
320
|
await waitFor(() => {
|
|
315
321
|
expect(list.toJSON()).toEqual([1, 2, 4, 6, 3, 5]);
|