cojson 0.18.18 → 0.18.20
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 +18 -0
- package/dist/GarbageCollector.d.ts +2 -1
- package/dist/GarbageCollector.d.ts.map +1 -1
- package/dist/GarbageCollector.js +3 -2
- package/dist/GarbageCollector.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +28 -25
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +128 -90
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/utils.d.ts +6 -0
- package/dist/coValueCore/utils.d.ts.map +1 -1
- package/dist/coValueCore/utils.js +53 -25
- package/dist/coValueCore/utils.js.map +1 -1
- package/dist/localNode.d.ts +5 -4
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +31 -37
- package/dist/localNode.js.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +56 -69
- package/dist/sync.js.map +1 -1
- package/dist/tests/GarbageCollector.test.js +14 -0
- package/dist/tests/GarbageCollector.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +1 -1
- package/dist/tests/coValueCore.dependencies.test.d.ts +2 -0
- package/dist/tests/coValueCore.dependencies.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.dependencies.test.js +55 -0
- package/dist/tests/coValueCore.dependencies.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +2 -2
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coValueCoreLoadingState.test.js +43 -62
- package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
- package/dist/tests/permissions.test.js +117 -117
- package/dist/tests/permissions.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +238 -9
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +7 -6
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +2 -1
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/GarbageCollector.ts +5 -2
- package/src/coValueCore/coValueCore.ts +172 -118
- package/src/coValueCore/utils.ts +85 -31
- package/src/localNode.ts +43 -48
- package/src/sync.ts +63 -89
- package/src/tests/GarbageCollector.test.ts +20 -0
- package/src/tests/SyncStateManager.test.ts +1 -1
- package/src/tests/coValueCore.dependencies.test.ts +90 -0
- package/src/tests/coValueCore.test.ts +2 -2
- package/src/tests/coValueCoreLoadingState.test.ts +50 -66
- package/src/tests/permissions.test.ts +120 -123
- package/src/tests/sync.load.test.ts +308 -9
- package/src/tests/sync.peerReconciliation.test.ts +7 -6
- package/src/tests/testUtils.ts +5 -3
package/package.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"type": "module",
|
|
31
31
|
"license": "MIT",
|
|
32
|
-
"version": "0.18.
|
|
32
|
+
"version": "0.18.20",
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@opentelemetry/sdk-metrics": "^2.0.0",
|
|
35
35
|
"libsql": "^0.5.13",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@scure/base": "1.2.1",
|
|
44
44
|
"neverthrow": "^7.0.1",
|
|
45
45
|
"unicode-segmenter": "^0.12.0",
|
|
46
|
-
"cojson-core-wasm": "0.18.
|
|
46
|
+
"cojson-core-wasm": "0.18.20"
|
|
47
47
|
},
|
|
48
48
|
"gitHead": "33c27053293b4801b968c61d5c4c989f93a67d13",
|
|
49
49
|
"scripts": {
|
package/src/GarbageCollector.ts
CHANGED
|
@@ -5,7 +5,10 @@ import { RawCoID } from "./ids.js";
|
|
|
5
5
|
export class GarbageCollector {
|
|
6
6
|
private readonly interval: ReturnType<typeof setInterval>;
|
|
7
7
|
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly coValues: Map<RawCoID, CoValueCore>,
|
|
10
|
+
private readonly garbageCollectGroups: boolean,
|
|
11
|
+
) {
|
|
9
12
|
this.interval = setInterval(() => {
|
|
10
13
|
this.collect();
|
|
11
14
|
}, GARBAGE_COLLECTOR_CONFIG.INTERVAL);
|
|
@@ -33,7 +36,7 @@ export class GarbageCollector {
|
|
|
33
36
|
const timeSinceLastAccessed = currentTime - verified.lastAccessed;
|
|
34
37
|
|
|
35
38
|
if (timeSinceLastAccessed > GARBAGE_COLLECTOR_CONFIG.MAX_AGE) {
|
|
36
|
-
coValue.unmount();
|
|
39
|
+
coValue.unmount(this.garbageCollectGroups);
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -20,10 +20,19 @@ import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
|
20
20
|
import { LocalNode, ResolveAccountAgentError } from "../localNode.js";
|
|
21
21
|
import { logger } from "../logger.js";
|
|
22
22
|
import { determineValidTransactions } from "../permissions.js";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
CoValueKnownState,
|
|
25
|
+
NewContentMessage,
|
|
26
|
+
PeerID,
|
|
27
|
+
emptyKnownState,
|
|
28
|
+
} from "../sync.js";
|
|
24
29
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
25
30
|
import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
26
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
getDependenciesFromContentMessage,
|
|
33
|
+
getDependenciesFromGroupRawTransactions,
|
|
34
|
+
getDependenciesFromHeader,
|
|
35
|
+
} from "./utils.js";
|
|
27
36
|
import { CoValueHeader, Transaction, VerifiedState } from "./verifiedState.js";
|
|
28
37
|
import { SessionMap } from "./SessionMap.js";
|
|
29
38
|
import {
|
|
@@ -125,7 +134,6 @@ export class CoValueCore {
|
|
|
125
134
|
private _cachedContent?: RawCoValue;
|
|
126
135
|
readonly listeners: Set<(core: CoValueCore, unsub: () => void) => void> =
|
|
127
136
|
new Set();
|
|
128
|
-
private _cachedDependentOn?: Set<RawCoID>;
|
|
129
137
|
private counter: UpDownCounter;
|
|
130
138
|
|
|
131
139
|
private constructor(
|
|
@@ -182,8 +190,12 @@ export class CoValueCore {
|
|
|
182
190
|
return "unavailable";
|
|
183
191
|
}
|
|
184
192
|
|
|
193
|
+
hasMissingDependencies() {
|
|
194
|
+
return this.missingDependencies.size > 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
185
197
|
isAvailable(): this is AvailableCoValueCore {
|
|
186
|
-
return this.hasVerifiedContent()
|
|
198
|
+
return this.hasVerifiedContent();
|
|
187
199
|
}
|
|
188
200
|
|
|
189
201
|
hasVerifiedContent(): this is AvailableCoValueCore {
|
|
@@ -251,9 +263,11 @@ export class CoValueCore {
|
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
265
|
|
|
254
|
-
unmount() {
|
|
255
|
-
|
|
256
|
-
|
|
266
|
+
unmount(garbageCollectGroups = false) {
|
|
267
|
+
if (
|
|
268
|
+
!garbageCollectGroups &&
|
|
269
|
+
this.verified?.header.ruleset.type === "group"
|
|
270
|
+
) {
|
|
257
271
|
return false;
|
|
258
272
|
}
|
|
259
273
|
|
|
@@ -280,10 +294,15 @@ export class CoValueCore {
|
|
|
280
294
|
this.scheduleNotifyUpdate();
|
|
281
295
|
}
|
|
282
296
|
|
|
297
|
+
markFoundInPeer(peerId: PeerID, previousState: string) {
|
|
298
|
+
this.peers.set(peerId, { type: "available" });
|
|
299
|
+
this.updateCounter(previousState);
|
|
300
|
+
this.scheduleNotifyUpdate();
|
|
301
|
+
}
|
|
302
|
+
|
|
283
303
|
missingDependencies = new Set<RawCoID>();
|
|
284
304
|
|
|
285
|
-
|
|
286
|
-
checkCircularDependencies(dependency: CoValueCore) {
|
|
305
|
+
isCircularMissingDependency(dependency: CoValueCore) {
|
|
287
306
|
const visited = new Set<RawCoID>();
|
|
288
307
|
const stack = [dependency];
|
|
289
308
|
|
|
@@ -291,14 +310,14 @@ export class CoValueCore {
|
|
|
291
310
|
const current = stack.pop();
|
|
292
311
|
|
|
293
312
|
if (!current) {
|
|
294
|
-
return
|
|
313
|
+
return false;
|
|
295
314
|
}
|
|
296
315
|
|
|
297
316
|
visited.add(current.id);
|
|
298
317
|
|
|
299
318
|
for (const dependency of current.missingDependencies) {
|
|
300
319
|
if (dependency === this.id) {
|
|
301
|
-
return
|
|
320
|
+
return true;
|
|
302
321
|
}
|
|
303
322
|
|
|
304
323
|
if (!visited.has(dependency)) {
|
|
@@ -307,43 +326,71 @@ export class CoValueCore {
|
|
|
307
326
|
}
|
|
308
327
|
}
|
|
309
328
|
|
|
310
|
-
return
|
|
329
|
+
return false;
|
|
311
330
|
}
|
|
312
331
|
|
|
313
|
-
|
|
314
|
-
|
|
332
|
+
markDependencyAvailable(dependency: RawCoID) {
|
|
333
|
+
this.missingDependencies.delete(dependency);
|
|
315
334
|
|
|
316
|
-
if (
|
|
317
|
-
this.
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (value.isAvailable()) {
|
|
321
|
-
this.missingDependencies.delete(dependency);
|
|
322
|
-
unsubscribe();
|
|
323
|
-
}
|
|
335
|
+
if (this.missingDependencies.size === 0) {
|
|
336
|
+
this.scheduleNotifyUpdate();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
324
339
|
|
|
325
|
-
|
|
326
|
-
|
|
340
|
+
newContentQueue: {
|
|
341
|
+
msg: NewContentMessage;
|
|
342
|
+
from: PeerState | "storage" | "import";
|
|
343
|
+
}[] = [];
|
|
344
|
+
/**
|
|
345
|
+
* Add a new content to the queue and handle it when the dependencies are available
|
|
346
|
+
*/
|
|
347
|
+
addNewContentToQueue(
|
|
348
|
+
msg: NewContentMessage,
|
|
349
|
+
from: PeerState | "storage" | "import",
|
|
350
|
+
) {
|
|
351
|
+
const alreadyEnqueued = this.newContentQueue.length > 0;
|
|
352
|
+
|
|
353
|
+
this.newContentQueue.push({ msg, from });
|
|
354
|
+
|
|
355
|
+
if (alreadyEnqueued) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this.subscribe((core, unsubscribe) => {
|
|
360
|
+
if (!core.hasMissingDependencies()) {
|
|
361
|
+
unsubscribe();
|
|
362
|
+
|
|
363
|
+
const enqueuedNewContent = this.newContentQueue;
|
|
364
|
+
this.newContentQueue = [];
|
|
365
|
+
|
|
366
|
+
for (const { msg, from } of enqueuedNewContent) {
|
|
367
|
+
this.node.syncManager.handleNewContent(msg, from);
|
|
327
368
|
}
|
|
328
|
-
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
329
372
|
|
|
330
|
-
|
|
373
|
+
addDependencyFromHeader(header: CoValueHeader) {
|
|
374
|
+
for (const dep of getDependenciesFromHeader(header)) {
|
|
375
|
+
this.addDependency(dep);
|
|
331
376
|
}
|
|
332
377
|
}
|
|
333
378
|
|
|
334
379
|
provideHeader(
|
|
335
380
|
header: CoValueHeader,
|
|
336
|
-
fromPeerId: PeerID,
|
|
337
381
|
streamingKnownState?: CoValueKnownState["sessions"],
|
|
382
|
+
skipVerify?: boolean,
|
|
338
383
|
) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const expectedId = idforHeader(header, this.node.crypto);
|
|
384
|
+
if (!skipVerify) {
|
|
385
|
+
const expectedId = idforHeader(header, this.node.crypto);
|
|
342
386
|
|
|
343
|
-
|
|
344
|
-
|
|
387
|
+
if (this.id !== expectedId) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
345
390
|
}
|
|
346
391
|
|
|
392
|
+
this.addDependencyFromHeader(header);
|
|
393
|
+
|
|
347
394
|
if (this._verified?.sessions.size) {
|
|
348
395
|
throw new Error(
|
|
349
396
|
"CoValueCore: provideHeader called on coValue with verified sessions present!",
|
|
@@ -357,26 +404,9 @@ export class CoValueCore {
|
|
|
357
404
|
streamingKnownState,
|
|
358
405
|
);
|
|
359
406
|
|
|
360
|
-
this.peers.set(fromPeerId, { type: "available" });
|
|
361
|
-
|
|
362
|
-
this.updateCounter(previousState);
|
|
363
|
-
this.scheduleNotifyUpdate();
|
|
364
|
-
|
|
365
407
|
return true;
|
|
366
408
|
}
|
|
367
409
|
|
|
368
|
-
internalMarkMagicallyAvailable(
|
|
369
|
-
verified: VerifiedState,
|
|
370
|
-
{ forceOverwrite = false }: { forceOverwrite?: boolean } = {},
|
|
371
|
-
) {
|
|
372
|
-
const previousState = this.loadingState;
|
|
373
|
-
this.internalShamefullyCloneVerifiedStateFrom(verified, {
|
|
374
|
-
forceOverwrite,
|
|
375
|
-
});
|
|
376
|
-
this.updateCounter(previousState);
|
|
377
|
-
this.scheduleNotifyUpdate();
|
|
378
|
-
}
|
|
379
|
-
|
|
380
410
|
markErrored(peerId: PeerID, error: TryAddTransactionsError) {
|
|
381
411
|
const previousState = this.loadingState;
|
|
382
412
|
this.peers.set(peerId, { type: "errored", error });
|
|
@@ -391,25 +421,6 @@ export class CoValueCore {
|
|
|
391
421
|
this.scheduleNotifyUpdate();
|
|
392
422
|
}
|
|
393
423
|
|
|
394
|
-
internalShamefullyCloneVerifiedStateFrom(
|
|
395
|
-
state: VerifiedState,
|
|
396
|
-
{ forceOverwrite = false }: { forceOverwrite?: boolean } = {},
|
|
397
|
-
) {
|
|
398
|
-
if (!forceOverwrite && this._verified?.sessions.size) {
|
|
399
|
-
throw new Error(
|
|
400
|
-
"CoValueCore: internalShamefullyCloneVerifiedStateFrom called on coValue with verified sessions present!",
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
this._verified = state.clone();
|
|
404
|
-
this.internalShamefullyResetCachedContent();
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
internalShamefullyResetCachedContent() {
|
|
408
|
-
this._cachedContent = undefined;
|
|
409
|
-
this._cachedDependentOn = undefined;
|
|
410
|
-
this.resetParsedTransactions();
|
|
411
|
-
}
|
|
412
|
-
|
|
413
424
|
groupInvalidationSubscription?: () => void;
|
|
414
425
|
|
|
415
426
|
subscribeToGroupInvalidation() {
|
|
@@ -430,7 +441,7 @@ export class CoValueCore {
|
|
|
430
441
|
if (entry.isAvailable()) {
|
|
431
442
|
this.groupInvalidationSubscription = entry.subscribe((_groupUpdate) => {
|
|
432
443
|
// When the group is updated, we need to reset the cached content because the transactions validity might have changed
|
|
433
|
-
this.
|
|
444
|
+
this.resetParsedTransactions();
|
|
434
445
|
this.scheduleNotifyUpdate();
|
|
435
446
|
}, false);
|
|
436
447
|
} else {
|
|
@@ -445,7 +456,7 @@ export class CoValueCore {
|
|
|
445
456
|
contentInClonedNodeWithDifferentAccount(account: ControlledAccountOrAgent) {
|
|
446
457
|
return this.node
|
|
447
458
|
.loadCoValueAsDifferentAgent(this.id, account.agentSecret, account.id)
|
|
448
|
-
.getCurrentContent();
|
|
459
|
+
.then((core) => core.getCurrentContent());
|
|
449
460
|
}
|
|
450
461
|
|
|
451
462
|
knownStateWithStreaming(): CoValueKnownState {
|
|
@@ -490,6 +501,14 @@ export class CoValueCore {
|
|
|
490
501
|
};
|
|
491
502
|
}
|
|
492
503
|
|
|
504
|
+
addDependenciesFromContentMessage(newContent: NewContentMessage) {
|
|
505
|
+
const dependencies = getDependenciesFromContentMessage(this, newContent);
|
|
506
|
+
|
|
507
|
+
for (const dependency of dependencies) {
|
|
508
|
+
this.addDependency(dependency);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
493
512
|
tryAddTransactions(
|
|
494
513
|
sessionID: SessionID,
|
|
495
514
|
newTransactions: Transaction[],
|
|
@@ -528,7 +547,7 @@ export class CoValueCore {
|
|
|
528
547
|
);
|
|
529
548
|
|
|
530
549
|
if (result.isOk()) {
|
|
531
|
-
this.
|
|
550
|
+
this.processNewTransactions();
|
|
532
551
|
this.scheduleNotifyUpdate();
|
|
533
552
|
}
|
|
534
553
|
|
|
@@ -536,18 +555,18 @@ export class CoValueCore {
|
|
|
536
555
|
});
|
|
537
556
|
}
|
|
538
557
|
|
|
539
|
-
private
|
|
540
|
-
if (
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
558
|
+
private processNewTransactions() {
|
|
559
|
+
if (this._cachedContent) {
|
|
560
|
+
// Does the cached content support incremental processing?
|
|
561
|
+
if (
|
|
562
|
+
"processNewTransactions" in this._cachedContent &&
|
|
563
|
+
typeof this._cachedContent.processNewTransactions === "function"
|
|
564
|
+
) {
|
|
565
|
+
this._cachedContent.processNewTransactions();
|
|
566
|
+
} else {
|
|
567
|
+
this._cachedContent = undefined;
|
|
568
|
+
}
|
|
548
569
|
}
|
|
549
|
-
|
|
550
|
-
this._cachedDependentOn = undefined;
|
|
551
570
|
}
|
|
552
571
|
|
|
553
572
|
#isNotificationScheduled = false;
|
|
@@ -574,8 +593,18 @@ export class CoValueCore {
|
|
|
574
593
|
}
|
|
575
594
|
}
|
|
576
595
|
|
|
596
|
+
#isNotifyUpdatePaused = false;
|
|
597
|
+
pauseNotifyUpdate() {
|
|
598
|
+
this.#isNotifyUpdatePaused = true;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
resumeNotifyUpdate() {
|
|
602
|
+
this.#isNotifyUpdatePaused = false;
|
|
603
|
+
this.notifyUpdate();
|
|
604
|
+
}
|
|
605
|
+
|
|
577
606
|
private notifyUpdate() {
|
|
578
|
-
if (this.listeners.size === 0) {
|
|
607
|
+
if (this.listeners.size === 0 || this.#isNotifyUpdatePaused) {
|
|
579
608
|
return;
|
|
580
609
|
}
|
|
581
610
|
|
|
@@ -664,15 +693,17 @@ export class CoValueCore {
|
|
|
664
693
|
|
|
665
694
|
const { transaction, signature } = result;
|
|
666
695
|
|
|
667
|
-
|
|
696
|
+
// Assign pre-parsed meta and changes to skip the parse/decrypt operation when loading
|
|
697
|
+
// this transaction in the current content
|
|
698
|
+
this.parsingCache.set(transaction, { changes, meta });
|
|
668
699
|
|
|
669
|
-
|
|
670
|
-
this.loadVerifiedTransactionsFromLogs({ transaction, changes, meta });
|
|
700
|
+
this.node.syncManager.recordTransactionsSize([transaction], "local");
|
|
671
701
|
|
|
672
702
|
const session = this.verified.sessions.get(sessionID);
|
|
673
703
|
const txIdx = session ? session.transactions.length - 1 : 0;
|
|
674
704
|
|
|
675
|
-
this.
|
|
705
|
+
this.processNewTransactions();
|
|
706
|
+
this.addDependenciesFromNewTransaction(transaction);
|
|
676
707
|
|
|
677
708
|
// force immediate notification because local updates may come from the UI
|
|
678
709
|
// where we need synchronous updates
|
|
@@ -688,6 +719,16 @@ export class CoValueCore {
|
|
|
688
719
|
return true;
|
|
689
720
|
}
|
|
690
721
|
|
|
722
|
+
addDependenciesFromNewTransaction(transaction: Transaction) {
|
|
723
|
+
if (this.verified?.header.ruleset.type === "group") {
|
|
724
|
+
for (const dependency of getDependenciesFromGroupRawTransactions([
|
|
725
|
+
transaction,
|
|
726
|
+
])) {
|
|
727
|
+
this.addDependency(dependency);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
691
732
|
getCurrentContent(options?: { ignorePrivateTransactions: true }): RawCoValue {
|
|
692
733
|
if (!this.verified) {
|
|
693
734
|
throw new Error(
|
|
@@ -721,6 +762,8 @@ export class CoValueCore {
|
|
|
721
762
|
|
|
722
763
|
// Reset the parsed transactions and branches, to validate them again from scratch when the group is updated
|
|
723
764
|
resetParsedTransactions() {
|
|
765
|
+
this._cachedContent = undefined;
|
|
766
|
+
|
|
724
767
|
this.branchStart = undefined;
|
|
725
768
|
this.mergeCommits = [];
|
|
726
769
|
|
|
@@ -738,23 +781,21 @@ export class CoValueCore {
|
|
|
738
781
|
VerifiedTransaction
|
|
739
782
|
> = {};
|
|
740
783
|
|
|
784
|
+
private parsingCache = new Map<
|
|
785
|
+
Transaction,
|
|
786
|
+
{ changes: JsonValue[]; meta: JsonObject | undefined }
|
|
787
|
+
>();
|
|
788
|
+
|
|
741
789
|
/**
|
|
742
790
|
* Loads the new transaction from the SessionMap into verifiedTransactions as a VerifiedTransaction.
|
|
743
791
|
*
|
|
744
792
|
* If the transaction is already loaded from the SessionMap in the past, it will not be loaded again.
|
|
745
793
|
*
|
|
746
794
|
* Used to have a fast way to iterate over the CoValue transactions, and track their validation/decoding state.
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
* If provided, the transaction will be preloaded with the given changes and meta.
|
|
750
|
-
*
|
|
751
|
-
* @internal
|
|
795
|
+
|
|
796
|
+
* @internal
|
|
752
797
|
* */
|
|
753
|
-
loadVerifiedTransactionsFromLogs(
|
|
754
|
-
transaction: Transaction;
|
|
755
|
-
changes: JsonValue[];
|
|
756
|
-
meta: JsonObject | undefined;
|
|
757
|
-
}) {
|
|
798
|
+
loadVerifiedTransactionsFromLogs() {
|
|
758
799
|
if (!this.verified) {
|
|
759
800
|
return;
|
|
760
801
|
}
|
|
@@ -780,14 +821,19 @@ export class CoValueCore {
|
|
|
780
821
|
txIndex,
|
|
781
822
|
};
|
|
782
823
|
|
|
824
|
+
const cache = this.parsingCache.get(tx);
|
|
825
|
+
if (cache) {
|
|
826
|
+
this.parsingCache.delete(tx);
|
|
827
|
+
}
|
|
828
|
+
|
|
783
829
|
const verifiedTransaction = {
|
|
784
830
|
author: accountOrAgentIDfromSessionID(sessionID),
|
|
785
831
|
txID,
|
|
786
832
|
madeAt: tx.madeAt,
|
|
787
833
|
isValidated: false,
|
|
788
834
|
isValid: false,
|
|
789
|
-
changes:
|
|
790
|
-
meta:
|
|
835
|
+
changes: cache?.changes,
|
|
836
|
+
meta: cache?.meta,
|
|
791
837
|
hasInvalidChanges: false,
|
|
792
838
|
hasInvalidMeta: false,
|
|
793
839
|
hasMetaBeenParsed: false,
|
|
@@ -978,6 +1024,32 @@ export class CoValueCore {
|
|
|
978
1024
|
return matchingTransactions;
|
|
979
1025
|
}
|
|
980
1026
|
|
|
1027
|
+
dependencies: Set<RawCoID> = new Set();
|
|
1028
|
+
private addDependency(dependency: RawCoID) {
|
|
1029
|
+
if (this.dependencies.has(dependency)) {
|
|
1030
|
+
return true;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
this.dependencies.add(dependency);
|
|
1034
|
+
|
|
1035
|
+
const dependencyCoValue = this.node.getCoValue(dependency);
|
|
1036
|
+
|
|
1037
|
+
if (
|
|
1038
|
+
!dependencyCoValue.isAvailable() &&
|
|
1039
|
+
!this.isCircularMissingDependency(dependencyCoValue)
|
|
1040
|
+
) {
|
|
1041
|
+
this.missingDependencies.add(dependency);
|
|
1042
|
+
|
|
1043
|
+
dependencyCoValue.subscribe((dependencyCoValue, unsubscribe) => {
|
|
1044
|
+
if (dependencyCoValue.isAvailable()) {
|
|
1045
|
+
unsubscribe();
|
|
1046
|
+
this.markDependencyAvailable(dependency);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
981
1053
|
createBranch(name: string, ownerId?: RawCoID) {
|
|
982
1054
|
return createBranch(this, name, ownerId);
|
|
983
1055
|
}
|
|
@@ -1144,25 +1216,7 @@ export class CoValueCore {
|
|
|
1144
1216
|
}
|
|
1145
1217
|
|
|
1146
1218
|
getDependedOnCoValues(): Set<RawCoID> {
|
|
1147
|
-
|
|
1148
|
-
return this._cachedDependentOn;
|
|
1149
|
-
} else {
|
|
1150
|
-
if (!this.verified) {
|
|
1151
|
-
return new Set();
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
const dependentOn = getDependedOnCoValuesFromRawData(
|
|
1155
|
-
this.id,
|
|
1156
|
-
this.verified.header,
|
|
1157
|
-
this.verified.sessions.keys(),
|
|
1158
|
-
Array.from(
|
|
1159
|
-
this.verified.sessions.values(),
|
|
1160
|
-
(session) => session.transactions,
|
|
1161
|
-
),
|
|
1162
|
-
);
|
|
1163
|
-
this._cachedDependentOn = dependentOn;
|
|
1164
|
-
return dependentOn;
|
|
1165
|
-
}
|
|
1219
|
+
return this.dependencies;
|
|
1166
1220
|
}
|
|
1167
1221
|
|
|
1168
1222
|
waitForSync(options?: { timeout?: number }) {
|
package/src/coValueCore/utils.ts
CHANGED
|
@@ -1,61 +1,115 @@
|
|
|
1
|
+
import { CoValueCore } from "../exports.js";
|
|
1
2
|
import { getGroupDependentKey } from "../ids.js";
|
|
2
3
|
import { RawCoID, SessionID } from "../ids.js";
|
|
3
4
|
import { Stringified, parseJSON } from "../jsonStringify.js";
|
|
4
5
|
import { JsonValue } from "../jsonValue.js";
|
|
6
|
+
import { NewContentMessage } from "../sync.js";
|
|
5
7
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
6
8
|
import { isAccountID } from "../typeUtils/isAccountID.js";
|
|
7
9
|
import { CoValueHeader, Transaction } from "./verifiedState.js";
|
|
8
10
|
|
|
9
|
-
export function
|
|
10
|
-
id: RawCoID,
|
|
11
|
+
export function getDependenciesFromHeader(
|
|
11
12
|
header: CoValueHeader,
|
|
12
|
-
|
|
13
|
-
transactions: Iterable<Iterable<Transaction>>,
|
|
13
|
+
deps = new Set<RawCoID>(),
|
|
14
14
|
): Set<RawCoID> {
|
|
15
|
-
|
|
15
|
+
if (header.ruleset.type === "ownedByGroup") {
|
|
16
|
+
deps.add(header.ruleset.group);
|
|
17
|
+
}
|
|
16
18
|
|
|
19
|
+
if (header.meta?.source) {
|
|
20
|
+
deps.add(header.meta.source as RawCoID);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return deps;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getDependenciesFromSessions(
|
|
27
|
+
sessions: Iterable<SessionID>,
|
|
28
|
+
deps: Set<RawCoID>,
|
|
29
|
+
): void {
|
|
17
30
|
for (const session of sessions) {
|
|
18
31
|
const accountId = accountOrAgentIDfromSessionID(session);
|
|
19
32
|
|
|
20
|
-
if (isAccountID(accountId)
|
|
33
|
+
if (isAccountID(accountId)) {
|
|
21
34
|
deps.add(accountId);
|
|
22
35
|
}
|
|
23
36
|
}
|
|
37
|
+
}
|
|
24
38
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
export function getDependenciesFromGroupRawTransactions(
|
|
40
|
+
transactions: Iterable<Transaction>,
|
|
41
|
+
deps = new Set<RawCoID>(),
|
|
42
|
+
): Set<RawCoID> {
|
|
43
|
+
for (const tx of transactions) {
|
|
44
|
+
if (tx.privacy !== "trusting") continue;
|
|
45
|
+
|
|
46
|
+
const changes = safeParseChanges(tx.changes);
|
|
47
|
+
for (const change of changes) {
|
|
48
|
+
if (
|
|
49
|
+
change &&
|
|
50
|
+
typeof change === "object" &&
|
|
51
|
+
"op" in change &&
|
|
52
|
+
change.op === "set" &&
|
|
53
|
+
"key" in change &&
|
|
54
|
+
change.key
|
|
55
|
+
) {
|
|
56
|
+
const key = getGroupDependentKey(change.key);
|
|
57
|
+
|
|
58
|
+
if (key) {
|
|
59
|
+
deps.add(key);
|
|
46
60
|
}
|
|
47
61
|
}
|
|
48
62
|
}
|
|
49
63
|
}
|
|
50
64
|
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
return deps;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getDependedOnCoValuesFromRawData(
|
|
69
|
+
id: RawCoID,
|
|
70
|
+
header: CoValueHeader,
|
|
71
|
+
sessions: Iterable<SessionID>,
|
|
72
|
+
transactions: Iterable<Iterable<Transaction>>,
|
|
73
|
+
): Set<RawCoID> {
|
|
74
|
+
const deps = new Set<RawCoID>();
|
|
75
|
+
|
|
76
|
+
getDependenciesFromHeader(header, deps);
|
|
77
|
+
getDependenciesFromSessions(sessions, deps);
|
|
78
|
+
|
|
79
|
+
if (header.ruleset.type === "group") {
|
|
80
|
+
for (const txs of transactions) {
|
|
81
|
+
getDependenciesFromGroupRawTransactions(txs, deps);
|
|
82
|
+
}
|
|
53
83
|
}
|
|
54
84
|
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
deps.delete(id);
|
|
86
|
+
|
|
87
|
+
return deps;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getDependenciesFromContentMessage(
|
|
91
|
+
coValue: CoValueCore,
|
|
92
|
+
contentMessage: NewContentMessage,
|
|
93
|
+
) {
|
|
94
|
+
const deps = new Set<RawCoID>();
|
|
95
|
+
|
|
96
|
+
if (contentMessage.header) {
|
|
97
|
+
getDependenciesFromHeader(contentMessage.header, deps);
|
|
57
98
|
}
|
|
58
99
|
|
|
100
|
+
const sessions = Object.keys(contentMessage.new) as SessionID[];
|
|
101
|
+
getDependenciesFromSessions(sessions, deps);
|
|
102
|
+
|
|
103
|
+
const header = coValue.verified?.header ?? contentMessage.header;
|
|
104
|
+
|
|
105
|
+
if (header?.ruleset.type === "group") {
|
|
106
|
+
for (const { newTransactions } of Object.values(contentMessage.new)) {
|
|
107
|
+
getDependenciesFromGroupRawTransactions(newTransactions, deps);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
deps.delete(coValue.id);
|
|
112
|
+
|
|
59
113
|
return deps;
|
|
60
114
|
}
|
|
61
115
|
|