cojson 0.15.8 → 0.15.10
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 +12 -0
- package/dist/IncomingMessagesQueue.d.ts +27 -0
- package/dist/IncomingMessagesQueue.d.ts.map +1 -0
- package/dist/IncomingMessagesQueue.js +114 -0
- package/dist/IncomingMessagesQueue.js.map +1 -0
- package/dist/PeerState.d.ts +2 -10
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +9 -90
- package/dist/PeerState.js.map +1 -1
- package/dist/PriorityBasedMessageQueue.d.ts +2 -1
- package/dist/PriorityBasedMessageQueue.d.ts.map +1 -1
- package/dist/PriorityBasedMessageQueue.js +9 -6
- package/dist/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/SyncStateManager.d.ts +1 -0
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +1 -1
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValue.d.ts +1 -1
- package/dist/coValueCore/coValueCore.d.ts +9 -17
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +75 -50
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +10 -3
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +73 -14
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -3
- package/dist/coValues/coStream.d.ts +2 -2
- package/dist/coValues/group.d.ts +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +2 -4
- package/dist/coValues/group.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +2 -1
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/exports.d.ts +18 -7
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +11 -8
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts +8 -2
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +19 -12
- package/dist/localNode.js.map +1 -1
- package/dist/storage/StoreQueue.d.ts +15 -0
- package/dist/storage/StoreQueue.d.ts.map +1 -0
- package/dist/storage/StoreQueue.js +35 -0
- package/dist/storage/StoreQueue.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/knownState.d.ts +18 -0
- package/dist/storage/knownState.d.ts.map +1 -0
- package/dist/storage/knownState.js +63 -0
- package/dist/storage/knownState.js.map +1 -0
- package/dist/storage/sqlite/client.d.ts +37 -0
- package/dist/storage/sqlite/client.d.ts.map +1 -0
- package/dist/storage/sqlite/client.js +89 -0
- package/dist/storage/sqlite/client.js.map +1 -0
- package/dist/storage/sqlite/index.d.ts +5 -0
- package/dist/storage/sqlite/index.d.ts.map +1 -0
- package/dist/storage/sqlite/index.js +13 -0
- package/dist/storage/sqlite/index.js.map +1 -0
- package/dist/storage/sqlite/sqliteMigrations.d.ts +3 -0
- package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -0
- package/dist/storage/sqlite/sqliteMigrations.js +44 -0
- package/dist/storage/sqlite/sqliteMigrations.js.map +1 -0
- package/dist/storage/sqlite/types.d.ts +8 -0
- package/dist/storage/sqlite/types.d.ts.map +1 -0
- package/dist/storage/sqlite/types.js +2 -0
- package/dist/storage/sqlite/types.js.map +1 -0
- package/dist/storage/sqliteAsync/client.d.ts +37 -0
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -0
- package/dist/storage/sqliteAsync/client.js +88 -0
- package/dist/storage/sqliteAsync/client.js.map +1 -0
- package/dist/storage/sqliteAsync/index.d.ts +6 -0
- package/dist/storage/sqliteAsync/index.d.ts.map +1 -0
- package/dist/storage/sqliteAsync/index.js +15 -0
- package/dist/storage/sqliteAsync/index.js.map +1 -0
- package/dist/storage/sqliteAsync/types.d.ts +9 -0
- package/dist/storage/sqliteAsync/types.d.ts.map +1 -0
- package/dist/storage/sqliteAsync/types.js +2 -0
- package/dist/storage/sqliteAsync/types.js.map +1 -0
- package/dist/storage/storageAsync.d.ts +22 -0
- package/dist/storage/storageAsync.d.ts.map +1 -0
- package/dist/storage/storageAsync.js +214 -0
- package/dist/storage/storageAsync.js.map +1 -0
- package/dist/storage/storageSync.d.ts +21 -0
- package/dist/storage/storageSync.d.ts.map +1 -0
- package/dist/storage/storageSync.js +206 -0
- package/dist/storage/storageSync.js.map +1 -0
- package/dist/storage/syncUtils.d.ts +13 -0
- package/dist/storage/syncUtils.d.ts.map +1 -0
- package/dist/storage/syncUtils.js +25 -0
- package/dist/storage/syncUtils.js.map +1 -0
- package/dist/storage/types.d.ts +82 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/streamUtils.d.ts +13 -9
- package/dist/streamUtils.d.ts.map +1 -1
- package/dist/streamUtils.js +46 -13
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.d.ts +22 -14
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +143 -125
- package/dist/sync.js.map +1 -1
- package/dist/tests/IncomingMessagesQueue.test.d.ts +2 -0
- package/dist/tests/IncomingMessagesQueue.test.d.ts.map +1 -0
- package/dist/tests/IncomingMessagesQueue.test.js +437 -0
- package/dist/tests/IncomingMessagesQueue.test.js.map +1 -0
- package/dist/tests/PeerState.test.js +6 -94
- package/dist/tests/PeerState.test.js.map +1 -1
- package/dist/tests/PriorityBasedMessageQueue.test.js +14 -14
- package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -1
- package/dist/tests/StoreQueue.test.d.ts +2 -0
- package/dist/tests/StoreQueue.test.d.ts.map +1 -0
- package/dist/tests/StoreQueue.test.js +208 -0
- package/dist/tests/StoreQueue.test.js.map +1 -0
- package/dist/tests/SyncStateManager.test.js +3 -1
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/account.test.js +9 -9
- package/dist/tests/account.test.js.map +1 -1
- package/dist/tests/coStream.test.js +1 -1
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +208 -1
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coValueCoreLoadingState.test.js +2 -2
- package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
- package/dist/tests/group.addMember.test.js.map +1 -1
- package/dist/tests/group.removeMember.test.js +1 -1
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.js +1 -1
- package/dist/tests/messagesTestUtils.js.map +1 -1
- package/dist/tests/sync.auth.test.js +23 -15
- package/dist/tests/sync.auth.test.js.map +1 -1
- package/dist/tests/sync.invite.test.js +10 -16
- package/dist/tests/sync.invite.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +52 -50
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +173 -56
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +42 -32
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +162 -62
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.d.ts +2 -0
- package/dist/tests/sync.storageAsync.test.d.ts.map +1 -0
- package/dist/tests/sync.storageAsync.test.js +361 -0
- package/dist/tests/sync.storageAsync.test.js.map +1 -0
- package/dist/tests/sync.test.js +16 -21
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +28 -25
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testStorage.d.ts +12 -0
- package/dist/tests/testStorage.d.ts.map +1 -0
- package/dist/tests/testStorage.js +151 -0
- package/dist/tests/testStorage.js.map +1 -0
- package/dist/tests/testUtils.d.ts +20 -15
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +79 -45
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/IncomingMessagesQueue.ts +142 -0
- package/src/PeerState.ts +11 -110
- package/src/PriorityBasedMessageQueue.ts +13 -5
- package/src/SyncStateManager.ts +1 -1
- package/src/coValueCore/coValueCore.ts +100 -66
- package/src/coValueCore/verifiedState.ts +91 -21
- package/src/coValues/group.ts +2 -4
- package/src/config.ts +26 -0
- package/src/crypto/WasmCrypto.ts +3 -1
- package/src/exports.ts +20 -27
- package/src/localNode.ts +27 -12
- package/src/storage/StoreQueue.ts +56 -0
- package/src/storage/index.ts +5 -0
- package/src/storage/knownState.ts +88 -0
- package/src/storage/sqlite/client.ts +180 -0
- package/src/storage/sqlite/index.ts +19 -0
- package/src/storage/sqlite/sqliteMigrations.ts +44 -0
- package/src/storage/sqlite/types.ts +7 -0
- package/src/storage/sqliteAsync/client.ts +179 -0
- package/src/storage/sqliteAsync/index.ts +25 -0
- package/src/storage/sqliteAsync/types.ts +8 -0
- package/src/storage/storageAsync.ts +367 -0
- package/src/storage/storageSync.ts +343 -0
- package/src/storage/syncUtils.ts +50 -0
- package/src/storage/types.ts +162 -0
- package/src/streamUtils.ts +61 -19
- package/src/sync.ts +191 -160
- package/src/tests/IncomingMessagesQueue.test.ts +626 -0
- package/src/tests/PeerState.test.ts +6 -118
- package/src/tests/PriorityBasedMessageQueue.test.ts +18 -14
- package/src/tests/StoreQueue.test.ts +283 -0
- package/src/tests/SyncStateManager.test.ts +4 -1
- package/src/tests/account.test.ts +11 -12
- package/src/tests/coStream.test.ts +1 -3
- package/src/tests/coValueCore.test.ts +270 -1
- package/src/tests/coValueCoreLoadingState.test.ts +2 -2
- package/src/tests/group.addMember.test.ts +1 -0
- package/src/tests/group.removeMember.test.ts +2 -8
- package/src/tests/messagesTestUtils.ts +2 -2
- package/src/tests/sync.auth.test.ts +24 -14
- package/src/tests/sync.invite.test.ts +11 -17
- package/src/tests/sync.load.test.ts +53 -49
- package/src/tests/sync.mesh.test.ts +198 -56
- package/src/tests/sync.peerReconciliation.test.ts +44 -34
- package/src/tests/sync.storage.test.ts +231 -64
- package/src/tests/sync.storageAsync.test.ts +486 -0
- package/src/tests/sync.test.ts +17 -23
- package/src/tests/sync.upload.test.ts +29 -24
- package/src/tests/testStorage.ts +216 -0
- package/src/tests/testUtils.ts +89 -54
|
@@ -4,6 +4,7 @@ import { PeerState } from "../PeerState.js";
|
|
|
4
4
|
import { RawCoValue } from "../coValue.js";
|
|
5
5
|
import { ControlledAccountOrAgent, RawAccountID } from "../coValues/account.js";
|
|
6
6
|
import { RawGroup } from "../coValues/group.js";
|
|
7
|
+
import { CO_VALUE_LOADING_CONFIG, MAX_RECOMMENDED_TX_SIZE } from "../config.js";
|
|
7
8
|
import { coreToCoValue } from "../coreToCoValue.js";
|
|
8
9
|
import {
|
|
9
10
|
CryptoProvider,
|
|
@@ -19,8 +20,6 @@ import {
|
|
|
19
20
|
RawCoID,
|
|
20
21
|
SessionID,
|
|
21
22
|
TransactionID,
|
|
22
|
-
getGroupDependentKey,
|
|
23
|
-
getGroupDependentKeyList,
|
|
24
23
|
getParentGroupId,
|
|
25
24
|
isParentGroupReference,
|
|
26
25
|
} from "../ids.js";
|
|
@@ -39,15 +38,6 @@ import { isAccountID } from "../typeUtils/isAccountID.js";
|
|
|
39
38
|
import { getDependedOnCoValuesFromRawData } from "./utils.js";
|
|
40
39
|
import { CoValueHeader, Transaction, VerifiedState } from "./verifiedState.js";
|
|
41
40
|
|
|
42
|
-
/**
|
|
43
|
-
In order to not block other concurrently syncing CoValues we introduce a maximum size of transactions,
|
|
44
|
-
since they are the smallest unit of progress that can be synced within a CoValue.
|
|
45
|
-
This is particularly important for storing binary data in CoValues, since they are likely to be at least on the order of megabytes.
|
|
46
|
-
This also means that we want to keep signatures roughly after each MAX_RECOMMENDED_TX size chunk,
|
|
47
|
-
to be able to verify partially loaded CoValues or CoValues that are still being created (like a video live stream).
|
|
48
|
-
**/
|
|
49
|
-
export const MAX_RECOMMENDED_TX_SIZE = 100 * 1024;
|
|
50
|
-
|
|
51
41
|
export function idforHeader(
|
|
52
42
|
header: CoValueHeader,
|
|
53
43
|
crypto: CryptoProvider,
|
|
@@ -66,12 +56,6 @@ const readKeyCache = new WeakMap<CoValueCore, { [id: KeyID]: KeySecret }>();
|
|
|
66
56
|
|
|
67
57
|
export type AvailableCoValueCore = CoValueCore & { verified: VerifiedState };
|
|
68
58
|
|
|
69
|
-
export const CO_VALUE_LOADING_CONFIG = {
|
|
70
|
-
MAX_RETRIES: 1,
|
|
71
|
-
TIMEOUT: 30_000,
|
|
72
|
-
RETRY_DELAY: 3000,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
59
|
export class CoValueCore {
|
|
76
60
|
// context
|
|
77
61
|
readonly node: LocalNode;
|
|
@@ -174,7 +158,11 @@ export class CoValueCore {
|
|
|
174
158
|
}
|
|
175
159
|
|
|
176
160
|
isAvailable(): this is AvailableCoValueCore {
|
|
177
|
-
return
|
|
161
|
+
return this.hasVerifiedContent() && this.missingDependencies.size === 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
hasVerifiedContent(): this is AvailableCoValueCore {
|
|
165
|
+
return !!this.verified;
|
|
178
166
|
}
|
|
179
167
|
|
|
180
168
|
isErroredInPeer(peerId: PeerID) {
|
|
@@ -232,12 +220,41 @@ export class CoValueCore {
|
|
|
232
220
|
}
|
|
233
221
|
|
|
234
222
|
missingDependencies = new Set<RawCoID>();
|
|
223
|
+
|
|
224
|
+
// Checks if the current CoValueCore is already a missing dependency of the given CoValueCore
|
|
225
|
+
checkCircularDependencies(dependency: CoValueCore) {
|
|
226
|
+
const visited = new Set<RawCoID>();
|
|
227
|
+
const stack = [dependency];
|
|
228
|
+
|
|
229
|
+
while (stack.length > 0) {
|
|
230
|
+
const current = stack.pop();
|
|
231
|
+
|
|
232
|
+
if (!current) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
visited.add(current.id);
|
|
237
|
+
|
|
238
|
+
for (const dependency of current.missingDependencies) {
|
|
239
|
+
if (dependency === this.id) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!visited.has(dependency)) {
|
|
244
|
+
stack.push(this.node.getCoValue(dependency));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
235
252
|
markMissingDependency(dependency: RawCoID) {
|
|
236
253
|
const value = this.node.getCoValue(dependency);
|
|
237
254
|
|
|
238
255
|
if (value.isAvailable()) {
|
|
239
256
|
this.missingDependencies.delete(dependency);
|
|
240
|
-
} else {
|
|
257
|
+
} else if (this.checkCircularDependencies(value)) {
|
|
241
258
|
const unsubscribe = value.subscribe(() => {
|
|
242
259
|
if (value.isAvailable()) {
|
|
243
260
|
this.missingDependencies.delete(dependency);
|
|
@@ -253,7 +270,11 @@ export class CoValueCore {
|
|
|
253
270
|
}
|
|
254
271
|
}
|
|
255
272
|
|
|
256
|
-
provideHeader(
|
|
273
|
+
provideHeader(
|
|
274
|
+
header: CoValueHeader,
|
|
275
|
+
fromPeerId: PeerID,
|
|
276
|
+
streamingKnownState?: CoValueKnownState["sessions"],
|
|
277
|
+
) {
|
|
257
278
|
const previousState = this.loadingState;
|
|
258
279
|
|
|
259
280
|
if (this._verified?.sessions.size) {
|
|
@@ -266,9 +287,11 @@ export class CoValueCore {
|
|
|
266
287
|
this.node.crypto,
|
|
267
288
|
header,
|
|
268
289
|
new Map(),
|
|
290
|
+
streamingKnownState,
|
|
269
291
|
);
|
|
270
292
|
|
|
271
293
|
this.peers.set(fromPeerId, { type: "available" });
|
|
294
|
+
|
|
272
295
|
this.updateCounter(previousState);
|
|
273
296
|
this.notifyUpdate("immediate");
|
|
274
297
|
}
|
|
@@ -292,7 +315,7 @@ export class CoValueCore {
|
|
|
292
315
|
this.notifyUpdate("immediate");
|
|
293
316
|
}
|
|
294
317
|
|
|
295
|
-
|
|
318
|
+
markPending(peerId: PeerID) {
|
|
296
319
|
const previousState = this.loadingState;
|
|
297
320
|
this.peers.set(peerId, { type: "pending" });
|
|
298
321
|
this.updateCounter(previousState);
|
|
@@ -355,6 +378,14 @@ export class CoValueCore {
|
|
|
355
378
|
.getCurrentContent();
|
|
356
379
|
}
|
|
357
380
|
|
|
381
|
+
knownStateWithStreaming(): CoValueKnownState {
|
|
382
|
+
if (this.isAvailable()) {
|
|
383
|
+
return this.verified.knownStateWithStreaming();
|
|
384
|
+
} else {
|
|
385
|
+
return emptyKnownState(this.id);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
358
389
|
knownState(): CoValueKnownState {
|
|
359
390
|
if (this.isAvailable()) {
|
|
360
391
|
return this.verified.knownState();
|
|
@@ -980,65 +1011,68 @@ export class CoValueCore {
|
|
|
980
1011
|
return this.node.syncManager.waitForSync(this.id, options?.timeout);
|
|
981
1012
|
}
|
|
982
1013
|
|
|
983
|
-
|
|
984
|
-
|
|
1014
|
+
load(peers: PeerState[]) {
|
|
1015
|
+
this.loadFromStorage((found) => {
|
|
1016
|
+
// When found the load is triggered by handleNewContent
|
|
1017
|
+
if (!found) {
|
|
1018
|
+
this.loadFromPeers(peers);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
loadFromStorage(done?: (found: boolean) => void) {
|
|
1024
|
+
const node = this.node;
|
|
1025
|
+
|
|
1026
|
+
if (!node.storage) {
|
|
1027
|
+
done?.(false);
|
|
985
1028
|
return;
|
|
986
1029
|
}
|
|
987
1030
|
|
|
988
|
-
const
|
|
989
|
-
storage: [] as PeerState[],
|
|
990
|
-
server: [] as PeerState[],
|
|
991
|
-
};
|
|
1031
|
+
const currentState = this.peers.get("storage");
|
|
992
1032
|
|
|
993
|
-
|
|
994
|
-
|
|
1033
|
+
if (currentState && currentState.type !== "unknown") {
|
|
1034
|
+
done?.(currentState.type === "available");
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
995
1037
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
) {
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1038
|
+
this.markPending("storage");
|
|
1039
|
+
node.storage.load(
|
|
1040
|
+
this.id,
|
|
1041
|
+
(data) => {
|
|
1042
|
+
node.syncManager.handleNewContent(data, "storage");
|
|
1043
|
+
},
|
|
1044
|
+
(found) => {
|
|
1045
|
+
if (!found) {
|
|
1046
|
+
this.markNotFoundInPeer("storage");
|
|
1047
|
+
}
|
|
1002
1048
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1049
|
+
done?.(found);
|
|
1050
|
+
},
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1006
1053
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
}
|
|
1054
|
+
loadFromPeers(peers: PeerState[]) {
|
|
1055
|
+
if (peers.length === 0) {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1012
1058
|
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1059
|
+
const peersToActuallyLoadFrom = [] as PeerState[];
|
|
1015
1060
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
peersToActuallyLoadFrom.storage.push(peer);
|
|
1019
|
-
} else {
|
|
1020
|
-
peersToActuallyLoadFrom.server.push(peer);
|
|
1021
|
-
}
|
|
1061
|
+
for (const peer of peers) {
|
|
1062
|
+
const currentState = this.peers.get(peer.id)?.type;
|
|
1022
1063
|
|
|
1064
|
+
if (
|
|
1065
|
+
!currentState ||
|
|
1066
|
+
currentState === "unknown" ||
|
|
1067
|
+
currentState === "unavailable"
|
|
1068
|
+
) {
|
|
1069
|
+
peersToActuallyLoadFrom.push(peer);
|
|
1023
1070
|
this.markPending(peer.id);
|
|
1024
1071
|
}
|
|
1025
1072
|
}
|
|
1026
1073
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
await Promise.all(
|
|
1030
|
-
peersToActuallyLoadFrom.storage.map((peer) =>
|
|
1031
|
-
this.internalLoadFromPeer(peer),
|
|
1032
|
-
),
|
|
1033
|
-
);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
if (peersToActuallyLoadFrom.server.length > 0) {
|
|
1037
|
-
await Promise.all(
|
|
1038
|
-
peersToActuallyLoadFrom.server.map((peer) =>
|
|
1039
|
-
this.internalLoadFromPeer(peer),
|
|
1040
|
-
),
|
|
1041
|
-
);
|
|
1074
|
+
for (const peer of peersToActuallyLoadFrom) {
|
|
1075
|
+
this.internalLoadFromPeer(peer);
|
|
1042
1076
|
}
|
|
1043
1077
|
}
|
|
1044
1078
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Result, err, ok } from "neverthrow";
|
|
2
2
|
import { AnyRawCoValue } from "../coValue.js";
|
|
3
|
+
import { MAX_RECOMMENDED_TX_SIZE } from "../config.js";
|
|
3
4
|
import {
|
|
4
5
|
CryptoProvider,
|
|
5
6
|
Encrypted,
|
|
@@ -15,11 +16,7 @@ import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
|
15
16
|
import { PermissionsDef as RulesetDef } from "../permissions.js";
|
|
16
17
|
import { getPriorityFromHeader } from "../priority.js";
|
|
17
18
|
import { CoValueKnownState, NewContentMessage } from "../sync.js";
|
|
18
|
-
import {
|
|
19
|
-
InvalidHashError,
|
|
20
|
-
InvalidSignatureError,
|
|
21
|
-
MAX_RECOMMENDED_TX_SIZE,
|
|
22
|
-
} from "./coValueCore.js";
|
|
19
|
+
import { InvalidHashError, InvalidSignatureError } from "./coValueCore.js";
|
|
23
20
|
import { TryAddTransactionsError } from "./coValueCore.js";
|
|
24
21
|
|
|
25
22
|
export type CoValueHeader = {
|
|
@@ -50,8 +47,7 @@ export type Transaction = PrivateTransaction | TrustingTransaction;
|
|
|
50
47
|
|
|
51
48
|
type SessionLog = {
|
|
52
49
|
readonly transactions: Transaction[];
|
|
53
|
-
|
|
54
|
-
streamingHash: StreamingHash;
|
|
50
|
+
streamingHash?: StreamingHash;
|
|
55
51
|
readonly signatureAfter: { [txIdx: number]: Signature | undefined };
|
|
56
52
|
lastSignature: Signature;
|
|
57
53
|
};
|
|
@@ -65,17 +61,22 @@ export class VerifiedState {
|
|
|
65
61
|
readonly sessions: ValidatedSessions;
|
|
66
62
|
private _cachedKnownState?: CoValueKnownState;
|
|
67
63
|
private _cachedNewContentSinceEmpty: NewContentMessage[] | undefined;
|
|
64
|
+
private streamingKnownState?: CoValueKnownState["sessions"];
|
|
68
65
|
|
|
69
66
|
constructor(
|
|
70
67
|
id: RawCoID,
|
|
71
68
|
crypto: CryptoProvider,
|
|
72
69
|
header: CoValueHeader,
|
|
73
70
|
sessions: ValidatedSessions,
|
|
71
|
+
streamingKnownState?: CoValueKnownState["sessions"],
|
|
74
72
|
) {
|
|
75
73
|
this.id = id;
|
|
76
74
|
this.crypto = crypto;
|
|
77
75
|
this.header = header;
|
|
78
76
|
this.sessions = sessions;
|
|
77
|
+
this.streamingKnownState = streamingKnownState
|
|
78
|
+
? { ...streamingKnownState }
|
|
79
|
+
: undefined;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
clone(): VerifiedState {
|
|
@@ -84,13 +85,18 @@ export class VerifiedState {
|
|
|
84
85
|
for (let [sessionID, sessionLog] of this.sessions) {
|
|
85
86
|
clonedSessions.set(sessionID, {
|
|
86
87
|
lastSignature: sessionLog.lastSignature,
|
|
87
|
-
|
|
88
|
-
streamingHash: sessionLog.streamingHash.clone(),
|
|
88
|
+
streamingHash: sessionLog.streamingHash?.clone(),
|
|
89
89
|
signatureAfter: { ...sessionLog.signatureAfter },
|
|
90
90
|
transactions: sessionLog.transactions.slice(),
|
|
91
91
|
} satisfies SessionLog);
|
|
92
92
|
}
|
|
93
|
-
return new VerifiedState(
|
|
93
|
+
return new VerifiedState(
|
|
94
|
+
this.id,
|
|
95
|
+
this.crypto,
|
|
96
|
+
this.header,
|
|
97
|
+
clonedSessions,
|
|
98
|
+
this.streamingKnownState,
|
|
99
|
+
);
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
tryAddTransactions(
|
|
@@ -102,12 +108,11 @@ export class VerifiedState {
|
|
|
102
108
|
skipVerify: boolean = false,
|
|
103
109
|
givenNewStreamingHash?: StreamingHash,
|
|
104
110
|
): Result<true, TryAddTransactionsError> {
|
|
105
|
-
if (skipVerify === true
|
|
111
|
+
if (skipVerify === true) {
|
|
106
112
|
this.doAddTransactions(
|
|
107
113
|
sessionID,
|
|
108
114
|
newTransactions,
|
|
109
115
|
newSignature,
|
|
110
|
-
givenExpectedNewHash,
|
|
111
116
|
givenNewStreamingHash,
|
|
112
117
|
);
|
|
113
118
|
} else {
|
|
@@ -139,7 +144,6 @@ export class VerifiedState {
|
|
|
139
144
|
sessionID,
|
|
140
145
|
newTransactions,
|
|
141
146
|
newSignature,
|
|
142
|
-
expectedNewHash,
|
|
143
147
|
newStreamingHash,
|
|
144
148
|
);
|
|
145
149
|
}
|
|
@@ -151,16 +155,16 @@ export class VerifiedState {
|
|
|
151
155
|
sessionID: SessionID,
|
|
152
156
|
newTransactions: Transaction[],
|
|
153
157
|
newSignature: Signature,
|
|
154
|
-
|
|
155
|
-
newStreamingHash: StreamingHash,
|
|
158
|
+
newStreamingHash?: StreamingHash,
|
|
156
159
|
) {
|
|
157
|
-
const
|
|
160
|
+
const sessionLog = this.sessions.get(sessionID);
|
|
161
|
+
const transactions = sessionLog?.transactions ?? [];
|
|
158
162
|
|
|
159
163
|
for (const tx of newTransactions) {
|
|
160
164
|
transactions.push(tx);
|
|
161
165
|
}
|
|
162
166
|
|
|
163
|
-
const signatureAfter =
|
|
167
|
+
const signatureAfter = sessionLog?.signatureAfter ?? {};
|
|
164
168
|
|
|
165
169
|
const lastInbetweenSignatureIdx = Object.keys(signatureAfter).reduce(
|
|
166
170
|
(max, idx) => (parseInt(idx) > max ? parseInt(idx) : max),
|
|
@@ -184,7 +188,6 @@ export class VerifiedState {
|
|
|
184
188
|
|
|
185
189
|
this.sessions.set(sessionID, {
|
|
186
190
|
transactions,
|
|
187
|
-
lastHash: expectedNewHash,
|
|
188
191
|
streamingHash: newStreamingHash,
|
|
189
192
|
lastSignature: newSignature,
|
|
190
193
|
signatureAfter: signatureAfter,
|
|
@@ -198,9 +201,27 @@ export class VerifiedState {
|
|
|
198
201
|
sessionID: SessionID,
|
|
199
202
|
newTransactions: Transaction[],
|
|
200
203
|
): { expectedNewHash: Hash; newStreamingHash: StreamingHash } {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
const sessionLog = this.sessions.get(sessionID);
|
|
205
|
+
|
|
206
|
+
if (!sessionLog?.streamingHash) {
|
|
207
|
+
const streamingHash = new StreamingHash(this.crypto);
|
|
208
|
+
const oldTransactions = sessionLog?.transactions ?? [];
|
|
209
|
+
|
|
210
|
+
for (const transaction of oldTransactions) {
|
|
211
|
+
streamingHash.update(transaction);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const transaction of newTransactions) {
|
|
215
|
+
streamingHash.update(transaction);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
expectedNewHash: streamingHash.digest(),
|
|
220
|
+
newStreamingHash: streamingHash,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const streamingHash = sessionLog.streamingHash.clone();
|
|
204
225
|
|
|
205
226
|
for (const transaction of newTransactions) {
|
|
206
227
|
streamingHash.update(transaction);
|
|
@@ -285,6 +306,11 @@ export class VerifiedState {
|
|
|
285
306
|
}
|
|
286
307
|
|
|
287
308
|
if (pieceSize >= MAX_RECOMMENDED_TX_SIZE) {
|
|
309
|
+
if (!currentPiece.expectContentUntil && pieces.length === 1) {
|
|
310
|
+
currentPiece.expectContentUntil =
|
|
311
|
+
this.knownStateWithStreaming().sessions;
|
|
312
|
+
}
|
|
313
|
+
|
|
288
314
|
currentPiece = {
|
|
289
315
|
action: "content",
|
|
290
316
|
id: this.id,
|
|
@@ -336,6 +362,50 @@ export class VerifiedState {
|
|
|
336
362
|
return piecesWithContent;
|
|
337
363
|
}
|
|
338
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Returns the known state considering the known state of the streaming source
|
|
367
|
+
*
|
|
368
|
+
* Used to correctly manage the content & subscriptions during the content streaming process
|
|
369
|
+
*/
|
|
370
|
+
knownStateWithStreaming(): CoValueKnownState {
|
|
371
|
+
const knownState = this.knownState();
|
|
372
|
+
|
|
373
|
+
if (this.streamingKnownState) {
|
|
374
|
+
const newSessions: CoValueKnownState["sessions"] = {};
|
|
375
|
+
const entries = Object.entries(this.streamingKnownState);
|
|
376
|
+
|
|
377
|
+
for (const [sessionID, txs] of entries) {
|
|
378
|
+
newSessions[sessionID as SessionID] = txs;
|
|
379
|
+
if ((knownState.sessions[sessionID as SessionID] ?? 0) < txs) {
|
|
380
|
+
newSessions[sessionID as SessionID] = txs;
|
|
381
|
+
} else {
|
|
382
|
+
newSessions[sessionID as SessionID] = txs;
|
|
383
|
+
delete this.streamingKnownState[sessionID as SessionID];
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (Object.keys(this.streamingKnownState).length === 0) {
|
|
388
|
+
this.streamingKnownState = undefined;
|
|
389
|
+
return knownState;
|
|
390
|
+
} else {
|
|
391
|
+
return {
|
|
392
|
+
id: knownState.id,
|
|
393
|
+
header: knownState.header,
|
|
394
|
+
sessions: newSessions,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return knownState;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
isStreaming(): boolean {
|
|
403
|
+
// Call knownStateWithStreaming to delete the streamingKnownState when it matches the current knownState
|
|
404
|
+
this.knownStateWithStreaming();
|
|
405
|
+
|
|
406
|
+
return this.streamingKnownState !== undefined;
|
|
407
|
+
}
|
|
408
|
+
|
|
339
409
|
knownState(): CoValueKnownState {
|
|
340
410
|
if (this._cachedKnownState) {
|
|
341
411
|
return this._cachedKnownState;
|
package/src/coValues/group.ts
CHANGED
|
@@ -193,7 +193,7 @@ export class RawGroup<
|
|
|
193
193
|
|
|
194
194
|
loadAllChildGroups() {
|
|
195
195
|
const requests: Promise<unknown>[] = [];
|
|
196
|
-
const peers = this.core.node.syncManager.
|
|
196
|
+
const peers = this.core.node.syncManager.getServerPeers();
|
|
197
197
|
|
|
198
198
|
for (const key of this.keys()) {
|
|
199
199
|
if (!isChildGroupReference(key)) {
|
|
@@ -207,9 +207,7 @@ export class RawGroup<
|
|
|
207
207
|
child.loadingState === "unknown" ||
|
|
208
208
|
child.loadingState === "unavailable"
|
|
209
209
|
) {
|
|
210
|
-
child.
|
|
211
|
-
logger.error(`Failed to load child group ${id}`);
|
|
212
|
-
});
|
|
210
|
+
child.load(peers);
|
|
213
211
|
}
|
|
214
212
|
|
|
215
213
|
requests.push(
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
In order to not block other concurrently syncing CoValues we introduce a maximum size of transactions,
|
|
3
|
+
since they are the smallest unit of progress that can be synced within a CoValue.
|
|
4
|
+
This is particularly important for storing binary data in CoValues, since they are likely to be at least on the order of megabytes.
|
|
5
|
+
This also means that we want to keep signatures roughly after each MAX_RECOMMENDED_TX size chunk,
|
|
6
|
+
to be able to verify partially loaded CoValues or CoValues that are still being created (like a video live stream).
|
|
7
|
+
**/
|
|
8
|
+
export const MAX_RECOMMENDED_TX_SIZE = 100 * 1024;
|
|
9
|
+
|
|
10
|
+
export const CO_VALUE_LOADING_CONFIG = {
|
|
11
|
+
MAX_RETRIES: 1,
|
|
12
|
+
TIMEOUT: 30_000,
|
|
13
|
+
RETRY_DELAY: 3000,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function setCoValueLoadingRetryDelay(delay: number) {
|
|
17
|
+
CO_VALUE_LOADING_CONFIG.RETRY_DELAY = delay;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const SYNC_SCHEDULER_CONFIG = {
|
|
21
|
+
INCOMING_MESSAGES_TIME_BUDGET: 50,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function setIncomingMessagesTimeBudget(budget: number) {
|
|
25
|
+
SYNC_SCHEDULER_CONFIG.INCOMING_MESSAGES_TIME_BUDGET = budget;
|
|
26
|
+
}
|
package/src/crypto/WasmCrypto.ts
CHANGED
|
@@ -108,11 +108,13 @@ export class WasmCrypto extends CryptoProvider<Blake3State> {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
verify(signature: Signature, message: JsonValue, id: SignerID): boolean {
|
|
111
|
-
|
|
111
|
+
const result = verify(
|
|
112
112
|
textEncoder.encode(signature),
|
|
113
113
|
textEncoder.encode(stableStringify(message)),
|
|
114
114
|
textEncoder.encode(id),
|
|
115
115
|
);
|
|
116
|
+
|
|
117
|
+
return result;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
newX25519StaticSecret(): Uint8Array {
|
package/src/exports.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { base64URLtoBytes, bytesToBase64url } from "./base64url.js";
|
|
2
2
|
import { type RawCoValue } from "./coValue.js";
|
|
3
|
-
import {
|
|
4
|
-
CO_VALUE_LOADING_CONFIG,
|
|
5
|
-
CoValueCore,
|
|
6
|
-
MAX_RECOMMENDED_TX_SIZE,
|
|
7
|
-
idforHeader,
|
|
8
|
-
} from "./coValueCore/coValueCore.js";
|
|
3
|
+
import { CoValueCore, idforHeader } from "./coValueCore/coValueCore.js";
|
|
9
4
|
import { CoValueUniqueness } from "./coValueCore/verifiedState.js";
|
|
10
5
|
import {
|
|
11
6
|
ControlledAccount,
|
|
@@ -42,7 +37,7 @@ import {
|
|
|
42
37
|
import { Stringified, parseJSON, stableStringify } from "./jsonStringify.js";
|
|
43
38
|
import { LocalNode } from "./localNode.js";
|
|
44
39
|
import type { AccountRole, Role } from "./permissions.js";
|
|
45
|
-
import {
|
|
40
|
+
import { ConnectedPeerChannel, connectedPeers } from "./streamUtils.js";
|
|
46
41
|
import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
|
|
47
42
|
import { expectGroup } from "./typeUtils/expectGroup.js";
|
|
48
43
|
import { isAccountID } from "./typeUtils/isAccountID.js";
|
|
@@ -63,24 +58,21 @@ import type { AgentID, RawCoID, SessionID } from "./ids.js";
|
|
|
63
58
|
import type { JsonObject, JsonValue } from "./jsonValue.js";
|
|
64
59
|
import type * as Media from "./media.js";
|
|
65
60
|
import { disablePermissionErrors } from "./permissions.js";
|
|
66
|
-
import type {
|
|
67
|
-
|
|
68
|
-
OutgoingSyncQueue,
|
|
69
|
-
Peer,
|
|
70
|
-
SyncMessage,
|
|
71
|
-
} from "./sync.js";
|
|
72
|
-
import {
|
|
73
|
-
DisconnectedError,
|
|
74
|
-
PingTimeoutError,
|
|
75
|
-
SyncManager,
|
|
76
|
-
emptyKnownState,
|
|
77
|
-
} from "./sync.js";
|
|
61
|
+
import type { Peer, SyncMessage } from "./sync.js";
|
|
62
|
+
import { DisconnectedError, SyncManager, emptyKnownState } from "./sync.js";
|
|
78
63
|
|
|
79
64
|
type Value = JsonValue | AnyRawCoValue;
|
|
80
65
|
|
|
66
|
+
export { PriorityBasedMessageQueue } from "./PriorityBasedMessageQueue.js";
|
|
81
67
|
import { getDependedOnCoValuesFromRawData } from "./coValueCore/utils.js";
|
|
68
|
+
import {
|
|
69
|
+
CO_VALUE_LOADING_CONFIG,
|
|
70
|
+
MAX_RECOMMENDED_TX_SIZE,
|
|
71
|
+
setCoValueLoadingRetryDelay,
|
|
72
|
+
setIncomingMessagesTimeBudget,
|
|
73
|
+
} from "./config.js";
|
|
82
74
|
import { LogLevel, logger } from "./logger.js";
|
|
83
|
-
import { getPriorityFromHeader } from "./priority.js";
|
|
75
|
+
import { CO_VALUE_PRIORITY, getPriorityFromHeader } from "./priority.js";
|
|
84
76
|
|
|
85
77
|
/** @hidden */
|
|
86
78
|
export const cojsonInternals = {
|
|
@@ -100,16 +92,16 @@ export const cojsonInternals = {
|
|
|
100
92
|
accountHeaderForInitialAgentSecret,
|
|
101
93
|
idforHeader,
|
|
102
94
|
StreamingHash,
|
|
103
|
-
Channel,
|
|
104
95
|
getPriorityFromHeader,
|
|
105
96
|
getGroupDependentKeyList,
|
|
106
97
|
getGroupDependentKey,
|
|
107
98
|
disablePermissionErrors,
|
|
108
99
|
SyncManager,
|
|
109
100
|
CO_VALUE_LOADING_CONFIG,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
CO_VALUE_PRIORITY,
|
|
102
|
+
setIncomingMessagesTimeBudget,
|
|
103
|
+
setCoValueLoadingRetryDelay,
|
|
104
|
+
ConnectedPeerChannel,
|
|
113
105
|
textEncoder,
|
|
114
106
|
textDecoder,
|
|
115
107
|
};
|
|
@@ -161,10 +153,7 @@ export {
|
|
|
161
153
|
|
|
162
154
|
export type {
|
|
163
155
|
Value,
|
|
164
|
-
IncomingSyncStream,
|
|
165
|
-
OutgoingSyncQueue,
|
|
166
156
|
DisconnectedError,
|
|
167
|
-
PingTimeoutError,
|
|
168
157
|
CoValueUniqueness,
|
|
169
158
|
Stringified,
|
|
170
159
|
CoStreamItem,
|
|
@@ -174,6 +163,8 @@ export type {
|
|
|
174
163
|
AccountRole,
|
|
175
164
|
};
|
|
176
165
|
|
|
166
|
+
export * from "./storage/index.js";
|
|
167
|
+
|
|
177
168
|
// biome-ignore format: off
|
|
178
169
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
179
170
|
export namespace CojsonInternalTypes {
|
|
@@ -196,5 +187,7 @@ export namespace CojsonInternalTypes {
|
|
|
196
187
|
export type SignerID = import("./crypto/crypto.js").SignerID;
|
|
197
188
|
export type SignerSecret = import("./crypto/crypto.js").SignerSecret;
|
|
198
189
|
export type JsonObject = import("./jsonValue.js").JsonObject;
|
|
190
|
+
export type OutgoingPeerChannel = import("./sync.js").OutgoingPeerChannel;
|
|
191
|
+
export type IncomingPeerChannel = import("./sync.js").IncomingPeerChannel;
|
|
199
192
|
}
|
|
200
193
|
// biome-ignore format: on
|