@ibgib/core-gib 0.1.28 → 0.1.30
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/.vscode/launch.json +17 -1
- package/dist/common/meta-stone/meta-stone-helper.d.mts.map +1 -1
- package/dist/common/meta-stone/meta-stone-helper.mjs +30 -12
- package/dist/common/meta-stone/meta-stone-helper.mjs.map +1 -1
- package/dist/common/meta-stone/meta-stone-types.d.mts +5 -2
- package/dist/common/meta-stone/meta-stone-types.d.mts.map +1 -1
- package/dist/sync/strategies/conflict-optimistic.d.mts +16 -0
- package/dist/sync/strategies/conflict-optimistic.d.mts.map +1 -1
- package/dist/sync/strategies/conflict-optimistic.mjs +28 -1
- package/dist/sync/strategies/conflict-optimistic.mjs.map +1 -1
- package/dist/sync/sync-conflict.respec.mjs +5 -5
- package/dist/sync/sync-conflict.respec.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +4 -4
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +12 -8
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.respec.mjs +36 -36
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +3 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +62 -9
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +45 -16
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +0 -9
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +25 -43
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +2 -2
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +15 -10
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +304 -163
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +3 -3
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +12 -18
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +15 -21
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/timeline/timeline-api.d.mts +12 -0
- package/dist/timeline/timeline-api.d.mts.map +1 -1
- package/dist/timeline/timeline-api.mjs +26 -0
- package/dist/timeline/timeline-api.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.d.mts +19 -0
- package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs +189 -30
- package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +9 -0
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/space-helper.d.mts.map +1 -1
- package/dist/witness/space/space-helper.mjs +2 -1
- package/dist/witness/space/space-helper.mjs.map +1 -1
- package/package.json +2 -1
- package/src/common/meta-stone/meta-stone-helper.mts +25 -11
- package/src/common/meta-stone/meta-stone-types.mts +5 -2
- package/src/sync/README.md +4 -4
- package/src/sync/docs/architecture.md +6 -6
- package/src/sync/strategies/conflict-optimistic.mts +41 -4
- package/src/sync/sync-conflict.respec.mts +5 -5
- package/src/sync/sync-helpers.mts +13 -9
- package/src/sync/sync-innerspace-constants.respec.mts +39 -39
- package/src/sync/sync-innerspace-deep-updates.respec.mts +5 -5
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +73 -9
- package/src/sync/sync-innerspace.respec.mts +17 -16
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +23 -60
- package/src/sync/sync-peer/sync-peer-v1.mts +2 -2
- package/src/sync/sync-saga-coordinator.mts +231 -173
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +3 -3
- package/src/sync/sync-types.mts +19 -26
- package/src/timeline/timeline-api.mts +51 -11
- package/src/witness/space/inner-space/inner-space-v1.mts +191 -29
- package/src/witness/space/inner-space/inner-space-v1.respec.mts +13 -0
- package/src/witness/space/space-helper.mts +3 -2
- package/test_output.log +0 -0
- package/tmp.md +170 -62
|
@@ -10,8 +10,8 @@ Synchronization is not a stateless request/response. It is a **Saga**: a durable
|
|
|
10
10
|
* **Symmetry**: Both peers (Alice and Bob) run the exact same `SyncSagaCoordinator`. There is no "Client" or "Server" code, only "Initiator" and "Reactor".
|
|
11
11
|
|
|
12
12
|
### 1.2 Smart Diff (Partial Updates)
|
|
13
|
-
To optimize bandwidth, the protocol exchanges "Knowledge
|
|
14
|
-
* **Knowledge
|
|
13
|
+
To optimize bandwidth, the protocol exchanges "Knowledge Maps" to calculate the precise difference between peers.
|
|
14
|
+
* **Knowledge Map**: A summary of what a peer knows (Timelines, Tips, and Ancestors).
|
|
15
15
|
* **Space-Side Dependency Graph**: The `Space.getDependencyGraph` capability allows the "Network" layer to request *only* the new nodes in a graph, skipping everything the receiver already has.
|
|
16
16
|
|
|
17
17
|
## 2. Protocol Workflow (The State Machine)
|
|
@@ -22,12 +22,12 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
|
|
|
22
22
|
|
|
23
23
|
1. **Start (Initiator)**:
|
|
24
24
|
* Initiator (Alice) calls `startSaga`.
|
|
25
|
-
* Generates `Init` frame containing her `
|
|
25
|
+
* Generates `Init` frame containing her `KnowledgeMap` and `Mode` (Push/Pull/Sync).
|
|
26
26
|
* Sends `Init` to Bob.
|
|
27
27
|
|
|
28
28
|
2. **Gap Analysis (`handleInitFrame`)**:
|
|
29
29
|
* Reactor (Bob) receives `Init`.
|
|
30
|
-
* Bob compares Alice's `
|
|
30
|
+
* Bob compares Alice's `KnowledgeMap` against his own local space.
|
|
31
31
|
* **Identifies Gaps**:
|
|
32
32
|
* **Delta Request**: "Alice has `X`, I don't. Send me `X`."
|
|
33
33
|
* **Push Offer**: "I have `Y` (newer than Alice's). Do you want `Y`?"
|
|
@@ -36,7 +36,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
|
|
|
36
36
|
3. **Payload Generation (`handleAckFrame`)**:
|
|
37
37
|
* Alice receives `Ack`.
|
|
38
38
|
* **Process Requests**: Iterates `deltaReqAddrs` (what Bob wants).
|
|
39
|
-
* **Smart Diff**: Uses Bob's `
|
|
39
|
+
* **Smart Diff**: Uses Bob's `KnowledgeMap` (sent in Ack) to exclude all shared history.
|
|
40
40
|
* **Payload**: Generates a bundle of new ibGibs.
|
|
41
41
|
* Alice sends `Delta` frame containing the payload.
|
|
42
42
|
|
|
@@ -57,7 +57,7 @@ All frames are `SyncIbGib`s carrying specific data payloads.
|
|
|
57
57
|
|
|
58
58
|
* `sagaId`: Unique UUID for the session.
|
|
59
59
|
* `stage`: `init` | `ack` | `delta` | `commit` | `conflict`.
|
|
60
|
-
* `
|
|
60
|
+
* `knowledgeMap`: `{ [tjpAddr]: [tipAddr, ...ancestors] }`.
|
|
61
61
|
|
|
62
62
|
### 3.2 Constants vs Timelines
|
|
63
63
|
The protocol supports two types of data:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module conflict-optimistic
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Implements the Optimistic Conflict Resolution Strategy.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Logic:
|
|
7
7
|
* 1. findLCA: Traverses `past` to find common ancestor.
|
|
8
8
|
* 2. mergeDivergentTimelines: Replays DNA to create a merged tip.
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
import { IbGib_V1, IbGibRel8ns_V1, IbGibData_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
12
12
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
13
|
+
|
|
13
14
|
import { IbGibSpaceAny } from '../../witness/space/space-base-v1.mjs';
|
|
14
15
|
import { getFromSpace, putInSpace } from '../../witness/space/space-helper.mjs';
|
|
15
16
|
import { mut8Timeline } from '../../timeline/timeline-api.mjs';
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
const logalot = GLOBAL_LOG_A_LOT || true;
|
|
18
19
|
|
|
19
20
|
export interface LCAResult {
|
|
20
21
|
lcaAddr: string;
|
|
@@ -22,9 +23,42 @@ export interface LCAResult {
|
|
|
22
23
|
branchB: IbGib_V1[]; // Path from LCA to Tip B (exclusive of LCA)
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
export interface TimelinesAnalysis {
|
|
27
|
+
latestCommonAddr: IbGibAddr;
|
|
28
|
+
uniqueBranchA
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* gets a quick analysis comparing/contrasting two timeline addrs.
|
|
33
|
+
*
|
|
34
|
+
* This does not do in-depth analysis that would involve retrieving ibgibs
|
|
35
|
+
from
|
|
36
|
+
* any spaces.
|
|
37
|
+
*/
|
|
38
|
+
export function analyzeTimelinesShallow({
|
|
39
|
+
a,
|
|
40
|
+
b,
|
|
41
|
+
}: {
|
|
42
|
+
a: IbGibAddr[],
|
|
43
|
+
b: IbGibAddr[],
|
|
44
|
+
}
|
|
45
|
+
): IbGibAddr {
|
|
46
|
+
const lc = `[${analyzeTimelinesShallow.name}]`;
|
|
47
|
+
try {
|
|
48
|
+
if (logalot) { console.log(`${lc} starting... (I: 1f65b883a2089948842c927c6a08c826)`); }
|
|
49
|
+
|
|
50
|
+
throw new Error(`not implemented (E: eab7ac95806ff9c60dafa22eb59cb926)`);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
53
|
+
throw error;
|
|
54
|
+
} finally {
|
|
55
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
25
59
|
/**
|
|
26
60
|
* Finds the Last Common Ancestor (LCA) between two divergent timelines.
|
|
27
|
-
*
|
|
61
|
+
*
|
|
28
62
|
* Note: simplistic implementation assuming simple linear divergence.
|
|
29
63
|
* Complex DAGs might require more robust graph traversal.
|
|
30
64
|
*/
|
|
@@ -114,6 +148,9 @@ export async function findLCA({
|
|
|
114
148
|
}
|
|
115
149
|
|
|
116
150
|
import { graftTimelines, mergeTextLCS } from '../graft-info/graft-info-helpers.mjs';
|
|
151
|
+
import { IbGibAddr } from '@ibgib/ts-gib';
|
|
152
|
+
import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
|
|
153
|
+
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
117
154
|
|
|
118
155
|
/**
|
|
119
156
|
* Optimistically grafts two divergent timelines by inspecting DNA and replaying transforms.
|
|
@@ -166,7 +166,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
166
166
|
// This ensures the conflict precondition exists.
|
|
167
167
|
await ifWe(sir, 'verify receiver KV pre-sync', async () => {
|
|
168
168
|
try {
|
|
169
|
-
const destKV = await receiverCoordinator.
|
|
169
|
+
const destKV = await receiverCoordinator.getKnowledgeMap({
|
|
170
170
|
space: destSpace,
|
|
171
171
|
metaspace,
|
|
172
172
|
domainIbGibs: [v1_Dest]
|
|
@@ -186,7 +186,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
186
186
|
|
|
187
187
|
} catch (error) {
|
|
188
188
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
189
|
-
iReckon(sir, true).asTo('
|
|
189
|
+
iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
|
|
@@ -251,7 +251,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
251
251
|
|
|
252
252
|
try {
|
|
253
253
|
// Get the KV for the Source Space
|
|
254
|
-
const sourceKV = await senderCoordinator.
|
|
254
|
+
const sourceKV = await senderCoordinator.getKnowledgeMap({
|
|
255
255
|
space: sourceSpace,
|
|
256
256
|
metaspace,
|
|
257
257
|
domainIbGibs: [testRoot] // We want to know the tip of this timeline
|
|
@@ -260,7 +260,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
260
260
|
getTjpAddr({ ibGib: testRoot, defaultIfNone: 'incomingAddr' }) ??
|
|
261
261
|
getIbGibAddr({ ibGib: testRoot });
|
|
262
262
|
|
|
263
|
-
if (logalot) { console.log(`${lc}
|
|
263
|
+
if (logalot) { console.log(`${lc} getKnowledgeMap returned. sourceKV: ${pretty(sourceKV)} (I: e8780cda37c8b2a46eeb85786874e926)`); }
|
|
264
264
|
|
|
265
265
|
const sourceTipAddr = sourceKV[tjpAddr];
|
|
266
266
|
if (!sourceTipAddr) {
|
|
@@ -319,7 +319,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
319
319
|
}
|
|
320
320
|
} catch (error) {
|
|
321
321
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
322
|
-
iReckon(sir, true).asTo('
|
|
322
|
+
iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
});
|
|
@@ -7,7 +7,7 @@ import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
|
7
7
|
|
|
8
8
|
import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
|
|
9
9
|
import { SYNC_ATOM, SYNC_MSG_REL8N_NAME } from "./sync-constants.mjs";
|
|
10
|
-
import { isValidSyncConflictStrategy, SYNC_CONFLICT_STRATEGY_VALID_VALUES, SyncData_V1,
|
|
10
|
+
import { isValidSyncConflictStrategy, SYNC_CONFLICT_STRATEGY_VALID_VALUES, SyncData_V1, SyncSagaFrameOrigin, SyncIb_V1, SyncIbGib_V1, SyncSagaFrameDependencyGraph } from "./sync-types.mjs";
|
|
11
11
|
import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
|
|
12
12
|
import { getFromSpace, putInSpace } from "../witness/space/space-helper.mjs";
|
|
13
13
|
import { IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns } from "../witness/space/space-types.mjs";
|
|
@@ -380,10 +380,14 @@ export async function putInSpace_dnasThenNonDnas({
|
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
// dnas are the simplest and we store those first...
|
|
383
|
-
|
|
383
|
+
if (payload_Dnas.length > 0) {
|
|
384
|
+
await putInSpace({ ibGibs: payload_Dnas, isDna: true, space, });
|
|
385
|
+
}
|
|
384
386
|
|
|
385
387
|
// then put all non-dnas ("regular" ibgibs) in the space
|
|
386
|
-
|
|
388
|
+
if (payload_NonDnas.length > 0) {
|
|
389
|
+
await putInSpace({ ibGibs: payload_NonDnas, isDna: false, space, });
|
|
390
|
+
}
|
|
387
391
|
|
|
388
392
|
return { payload_Dnas, payload_NonDnas };
|
|
389
393
|
} catch (error) {
|
|
@@ -395,14 +399,14 @@ export async function putInSpace_dnasThenNonDnas({
|
|
|
395
399
|
}
|
|
396
400
|
|
|
397
401
|
/**
|
|
398
|
-
* @see {@link
|
|
402
|
+
* @see {@link SyncSagaFrameOrigin}
|
|
399
403
|
*/
|
|
400
|
-
export function
|
|
404
|
+
export function getSyncSagaFrameOrigin({
|
|
401
405
|
sagaFrame,
|
|
402
406
|
}: {
|
|
403
407
|
sagaFrame: SyncIbGib_V1,
|
|
404
|
-
}):
|
|
405
|
-
const lc = `[${
|
|
408
|
+
}): SyncSagaFrameOrigin {
|
|
409
|
+
const lc = `[${getSyncSagaFrameOrigin.name}]`;
|
|
406
410
|
try {
|
|
407
411
|
if (logalot) { console.log(`${lc} starting... (I: 93521332d0dd2f5e88398b7db95e8126)`); }
|
|
408
412
|
if (!sagaFrame) { throw new Error(`(UNEXPECTED) sagaFrame falsy? (E: 01fb28be6668d88ea49bb298c739c926)`); }
|
|
@@ -414,9 +418,9 @@ export function getExecutionContext({
|
|
|
414
418
|
// odd => receiver
|
|
415
419
|
|
|
416
420
|
if (modulo === 0) {
|
|
417
|
-
return
|
|
421
|
+
return SyncSagaFrameOrigin.sender;
|
|
418
422
|
} else if (modulo === 1) {
|
|
419
|
-
return
|
|
423
|
+
return SyncSagaFrameOrigin.receiver;
|
|
420
424
|
} else {
|
|
421
425
|
throw new Error(`(UNEXPECTED) modulo is neither 0 nor 1? my logic sucks! This was way too defensive on my part... (E: 559488a2eef8d17b57079ca8886ee826)`);
|
|
422
426
|
}
|
|
@@ -55,8 +55,8 @@ await respecfully(sir, `Sync Constants (No TJP)`, async () => {
|
|
|
55
55
|
// 2. Create Constant (C1) in Source
|
|
56
56
|
// Factory_V1.constant returns Promise<IbGib_V1>, not { newIbGib: ... }
|
|
57
57
|
const c1 = await Factory_V1.constant({
|
|
58
|
-
ib: 'constant_c1',
|
|
59
58
|
parentPrimitiveIb: 'root',
|
|
59
|
+
ib: 'constant_c1',
|
|
60
60
|
data: { some: 'data' },
|
|
61
61
|
});
|
|
62
62
|
const addrC1 = getIbGibAddr({ ibGib: c1 });
|
|
@@ -81,7 +81,7 @@ await respecfully(sir, `Sync Constants (No TJP)`, async () => {
|
|
|
81
81
|
|
|
82
82
|
// 4. Sync C1 (First Pass)
|
|
83
83
|
let resSync = await senderCoordinator.sync({
|
|
84
|
-
peer
|
|
84
|
+
peer,
|
|
85
85
|
localSpace: sourceSpace,
|
|
86
86
|
metaspace: metaspace,
|
|
87
87
|
domainIbGibs: [c1],
|
|
@@ -94,43 +94,43 @@ await respecfully(sir, `Sync Constants (No TJP)`, async () => {
|
|
|
94
94
|
const getC1Dest = await getFromSpace({ space: destSpace, addr: addrC1 });
|
|
95
95
|
iReckon(sir, getC1Dest.success && !!getC1Dest.ibGibs![0]).asTo('Dest has C1 after sync').isGonnaBeTrue();
|
|
96
96
|
|
|
97
|
-
// 5. Sync C1 (Second Pass - Idempotency / Smart Diff)
|
|
98
|
-
// Should NOT send payload again.
|
|
99
|
-
resSync = await senderCoordinator.sync({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
await resSync.done;
|
|
107
|
-
iReckon(sir, true).asTo('Sync execution 2 (idempotent) completes').isGonnaBeTrue();
|
|
108
|
-
|
|
109
|
-
// 6. Create "Constant with Dependency" (C2 -> C1)
|
|
110
|
-
const c2 = await Factory_V1.constant({
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
const addrC2 = getIbGibAddr({ ibGib: c2 });
|
|
118
|
-
await putInSpace({ space: sourceSpace, ibGibs: [c2] });
|
|
119
|
-
|
|
120
|
-
// Sync C2
|
|
121
|
-
resSync = await senderCoordinator.sync({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
await resSync.done;
|
|
129
|
-
iReckon(sir, true).asTo('Sync C2 completes').isGonnaBeTrue();
|
|
130
|
-
|
|
131
|
-
// Verify C2 in Dest
|
|
132
|
-
const getC2Dest = await getFromSpace({ space: destSpace, addr: addrC2 });
|
|
133
|
-
iReckon(sir, getC2Dest.success).asTo('Dest has C2').isGonnaBeTrue();
|
|
97
|
+
// // 5. Sync C1 (Second Pass - Idempotency / Smart Diff)
|
|
98
|
+
// // Should NOT send payload again.
|
|
99
|
+
// resSync = await senderCoordinator.sync({
|
|
100
|
+
// peer: peer,
|
|
101
|
+
// localSpace: sourceSpace,
|
|
102
|
+
// metaspace: metaspace,
|
|
103
|
+
// domainIbGibs: [c1],
|
|
104
|
+
// useSessionIdentity: false,
|
|
105
|
+
// });
|
|
106
|
+
// await resSync.done;
|
|
107
|
+
// iReckon(sir, true).asTo('Sync execution 2 (idempotent) completes').isGonnaBeTrue();
|
|
108
|
+
|
|
109
|
+
// // 6. Create "Constant with Dependency" (C2 -> C1)
|
|
110
|
+
// const c2 = await Factory_V1.constant({
|
|
111
|
+
// ib: 'constant_c2',
|
|
112
|
+
// parentPrimitiveIb: 'root',
|
|
113
|
+
// rel8ns: {
|
|
114
|
+
// link: [addrC1], // C2 links to C1
|
|
115
|
+
// },
|
|
116
|
+
// });
|
|
117
|
+
// const addrC2 = getIbGibAddr({ ibGib: c2 });
|
|
118
|
+
// await putInSpace({ space: sourceSpace, ibGibs: [c2] });
|
|
119
|
+
|
|
120
|
+
// // Sync C2
|
|
121
|
+
// resSync = await senderCoordinator.sync({
|
|
122
|
+
// peer: peer,
|
|
123
|
+
// localSpace: sourceSpace,
|
|
124
|
+
// metaspace: metaspace,
|
|
125
|
+
// domainIbGibs: [c2],
|
|
126
|
+
// useSessionIdentity: false,
|
|
127
|
+
// });
|
|
128
|
+
// await resSync.done;
|
|
129
|
+
// iReckon(sir, true).asTo('Sync C2 completes').isGonnaBeTrue();
|
|
130
|
+
|
|
131
|
+
// // Verify C2 in Dest
|
|
132
|
+
// const getC2Dest = await getFromSpace({ space: destSpace, addr: addrC2 });
|
|
133
|
+
// iReckon(sir, getC2Dest.success).asTo('Dest has C2').isGonnaBeTrue();
|
|
134
134
|
|
|
135
135
|
});
|
|
136
136
|
});
|
|
@@ -11,20 +11,20 @@ import {
|
|
|
11
11
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
12
12
|
import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
13
13
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
14
|
-
import { getDependencyGraph } from '../common/other/graph-helper.mjs';
|
|
15
14
|
|
|
15
|
+
import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
|
|
16
|
+
import { getDependencyGraph } from '../common/other/graph-helper.mjs';
|
|
16
17
|
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
|
|
17
|
-
import {
|
|
18
|
+
import { getFromSpace } from '../witness/space/space-helper.mjs';
|
|
18
19
|
import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
|
|
19
20
|
import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
|
|
20
21
|
import { createTimelineRootHelper, getTestKeystoneServiceHelper } from '../agent-helpers.mjs';
|
|
21
22
|
import { mut8Timeline } from '../timeline/timeline-api.mjs';
|
|
22
23
|
import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
|
|
23
|
-
import { toDto } from '../common/other/ibgib-helper.mjs';
|
|
24
24
|
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
|
|
25
25
|
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
|
|
26
26
|
|
|
27
|
-
const logalot = true;
|
|
27
|
+
const logalot = GLOBAL_LOG_A_LOT || true;
|
|
28
28
|
const lc = `[sync-innerspace-deep-updates.respec]`;
|
|
29
29
|
|
|
30
30
|
await respecfully(sir, `Sync InnerSpaces (Deep Updates)`, async () => {
|
|
@@ -121,8 +121,8 @@ await respecfully(sir, `Sync InnerSpaces (Deep Updates)`, async () => {
|
|
|
121
121
|
localSpace: sourceSpace,
|
|
122
122
|
metaspace: metaspace,
|
|
123
123
|
domainIbGibs: [commentV2], // Sync the TIP
|
|
124
|
+
useSessionIdentity: false,
|
|
124
125
|
});
|
|
125
|
-
|
|
126
126
|
await resSync.done;
|
|
127
127
|
|
|
128
128
|
// 5. Verify Dest
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
12
12
|
import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
13
13
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
14
|
-
import {
|
|
14
|
+
import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
|
|
15
15
|
|
|
16
16
|
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
|
|
17
|
-
import { putInSpace, getFromSpace } from '../witness/space/space-helper.mjs';
|
|
17
|
+
import { putInSpace, getFromSpace, registerNewIbGib } from '../witness/space/space-helper.mjs';
|
|
18
18
|
import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
|
|
19
19
|
import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
|
|
20
20
|
import { createTimelineRootHelper, getTestKeystoneServiceHelper } from '../agent-helpers.mjs';
|
|
@@ -23,6 +23,8 @@ import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-
|
|
|
23
23
|
import { toDto } from '../common/other/ibgib-helper.mjs';
|
|
24
24
|
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
|
|
25
25
|
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
|
|
26
|
+
import { GetIbGibResult } from '../common/other/other-types.mjs';
|
|
27
|
+
import { IbGibSpaceAny } from '../witness/space/space-base-v1.mjs';
|
|
26
28
|
|
|
27
29
|
const logalot = true;
|
|
28
30
|
const lc = `[sync-innerspace-dest-ahead.respec]`;
|
|
@@ -73,22 +75,43 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
73
75
|
// Root -> V1 (Shared) -> V2 (Dest has New)
|
|
74
76
|
// Source only has V1.
|
|
75
77
|
|
|
76
|
-
const
|
|
78
|
+
const v0 = await createTimelineRootHelper<TestData>({
|
|
77
79
|
ib: 'timeline_root_ff',
|
|
78
80
|
data: { type: 'root', label: 'Root' },
|
|
79
81
|
space: sourceSpace,
|
|
80
82
|
});
|
|
83
|
+
const addrV0 = getIbGibAddr({ ibGib: v0 });
|
|
84
|
+
console.log(pretty(v0));
|
|
85
|
+
|
|
86
|
+
console.log('0000000000000')
|
|
87
|
+
console.log('0000000000000')
|
|
88
|
+
console.log('0000000000000')
|
|
89
|
+
console.log('0000000000000')
|
|
90
|
+
console.log('0000000000000')
|
|
81
91
|
|
|
82
92
|
// V1 (Both have it, but we create in source and copy to dest)
|
|
83
93
|
const v1 = await mut8Timeline<TestData>({
|
|
84
|
-
timeline:
|
|
94
|
+
timeline: v0,
|
|
85
95
|
mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1' } },
|
|
86
96
|
metaspace,
|
|
87
97
|
space: sourceSpace,
|
|
88
98
|
});
|
|
99
|
+
const addrV1 = getIbGibAddr({ ibGib: v1 });
|
|
100
|
+
console.log(pretty(v1));
|
|
101
|
+
|
|
89
102
|
|
|
90
103
|
// Transfer Root & V1 to Dest
|
|
91
|
-
await putInSpace({ space: destSpace, ibGibs: [
|
|
104
|
+
await putInSpace({ space: destSpace, ibGibs: [v0, v1] }); // Naive seeding
|
|
105
|
+
await registerNewIbGib({ space: destSpace, ibGib: v0 });
|
|
106
|
+
await registerNewIbGib({ space: destSpace, ibGib: v1 });
|
|
107
|
+
|
|
108
|
+
console.log('1111111111111111111111111111111')
|
|
109
|
+
console.log('1111111111111111111111111111111')
|
|
110
|
+
console.log('1111111111111111111111111111111')
|
|
111
|
+
console.log('1111111111111111111111111111111')
|
|
112
|
+
console.log('1111111111111111111111111111111')
|
|
113
|
+
console.log('1111111111111111111111111111111')
|
|
114
|
+
console.log('1111111111111111111111111111111')
|
|
92
115
|
|
|
93
116
|
// V2 (Created in Dest ONLY)
|
|
94
117
|
const v2 = await mut8Timeline<TestData>({
|
|
@@ -98,12 +121,31 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
98
121
|
space: destSpace, // Mutate in Dest
|
|
99
122
|
});
|
|
100
123
|
const addrV2 = getIbGibAddr({ ibGib: v2 });
|
|
124
|
+
console.log(pretty(v2));
|
|
125
|
+
|
|
126
|
+
const fnAddrExistsInSpace = async (addr: IbGibAddr, space: IbGibSpaceAny) => {
|
|
127
|
+
const resGet = await getFromSpace({ addr, space });
|
|
128
|
+
return resGet.success && resGet.ibGibs && resGet.ibGibs.length === 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log('22222222222')
|
|
132
|
+
console.log('22222222222')
|
|
133
|
+
console.log('22222222222')
|
|
134
|
+
console.log('22222222222')
|
|
135
|
+
console.log('22222222222')
|
|
136
|
+
console.log('22222222222')
|
|
137
|
+
console.log('22222222222')
|
|
138
|
+
|
|
101
139
|
|
|
102
140
|
await ifWeMight(sir, 'verify setup', async () => {
|
|
103
141
|
// Ensure V2 is ONLY in Dest (it is, per `space: destSpace`)
|
|
104
142
|
// Ensure Source does NOT have V2
|
|
105
|
-
|
|
106
|
-
iReckon(sir,
|
|
143
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, sourceSpace)).asTo('source has V0').isGonnaBeTrue();
|
|
144
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, sourceSpace)).asTo('source has V1').isGonnaBeTrue();
|
|
145
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, sourceSpace)).asTo('source has V2').isGonnaBeFalse();
|
|
146
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, destSpace)).asTo('dest has V0').isGonnaBeTrue();
|
|
147
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, destSpace)).asTo('dest has V1').isGonnaBeTrue();
|
|
148
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, destSpace)).asTo('dest has V2').isGonnaBeTrue();
|
|
107
149
|
});
|
|
108
150
|
|
|
109
151
|
// 3. Setup Sync
|
|
@@ -119,6 +161,14 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
119
161
|
receiverCoordinator,
|
|
120
162
|
receiverMetaspace: metaspace,
|
|
121
163
|
});
|
|
164
|
+
console.log('333333333')
|
|
165
|
+
console.log('333333333')
|
|
166
|
+
console.log('333333333')
|
|
167
|
+
console.log('333333333')
|
|
168
|
+
console.log('333333333')
|
|
169
|
+
console.log('333333333')
|
|
170
|
+
console.log('333333333')
|
|
171
|
+
|
|
122
172
|
|
|
123
173
|
// 4. Run Sync (Source Pushes V1)
|
|
124
174
|
console.log(`${lc} Running Sync...`);
|
|
@@ -132,13 +182,27 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
132
182
|
|
|
133
183
|
await resSync.done;
|
|
134
184
|
|
|
135
|
-
|
|
185
|
+
console.log('444444444')
|
|
186
|
+
console.log('444444444')
|
|
187
|
+
console.log('444444444')
|
|
188
|
+
console.log('444444444')
|
|
189
|
+
console.log('444444444')
|
|
190
|
+
console.log('444444444')
|
|
191
|
+
console.log('444444444')
|
|
192
|
+
|
|
193
|
+
// 5. Verify Sync (v2 should be in both source and dest now)
|
|
136
194
|
console.log(`${lc} Verifying Destination...`);
|
|
137
195
|
|
|
138
196
|
await ifWeMight(sir, `verify dest stays ahead`, async () => {
|
|
139
197
|
// Verify Tip (V2)
|
|
140
198
|
const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
|
|
141
|
-
|
|
199
|
+
|
|
200
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, sourceSpace)).asTo('source has V0').isGonnaBeTrue();
|
|
201
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, sourceSpace)).asTo('source has V1').isGonnaBeTrue();
|
|
202
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, sourceSpace)).asTo('source has V2').isGonnaBeTrue();
|
|
203
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, destSpace)).asTo('dest has V0').isGonnaBeTrue();
|
|
204
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, destSpace)).asTo('dest has V1').isGonnaBeTrue();
|
|
205
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, destSpace)).asTo('dest has V2').isGonnaBeTrue();
|
|
142
206
|
|
|
143
207
|
// Verify V2 points to V1
|
|
144
208
|
const v2IbGib = getV2.ibGibs![0];
|
|
@@ -13,6 +13,7 @@ const maam = `[${import.meta.url}]`, sir = maam;
|
|
|
13
13
|
import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
14
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
15
15
|
|
|
16
|
+
import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
|
|
16
17
|
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
|
|
17
18
|
import { getFromSpace } from '../witness/space/space-helper.mjs';
|
|
18
19
|
import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
|
|
@@ -25,7 +26,7 @@ import { fnObs } from '../common/pubsub/observer/observer-helper.mjs';
|
|
|
25
26
|
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
|
|
26
27
|
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
|
|
27
28
|
|
|
28
|
-
const logalot =
|
|
29
|
+
const logalot = GLOBAL_LOG_A_LOT;
|
|
29
30
|
const lc = `[sync-innerspace.respec]`;
|
|
30
31
|
|
|
31
32
|
await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
@@ -47,7 +48,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
47
48
|
const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
|
|
48
49
|
if (!defaultLocalUserSpace) { throw new Error(`(UNEXPECTED) defaultLocalUserSpace falsy? (E: 56f019430b29b45147e0caa4d5931826)`); }
|
|
49
50
|
await defaultLocalUserSpace.initialized;
|
|
50
|
-
console.log(`defaultLocalUserSpace: ${pretty(toDto({ ibGib: defaultLocalUserSpace }))}`);
|
|
51
|
+
if (logalot) { console.log(`defaultLocalUserSpace: ${pretty(toDto({ ibGib: defaultLocalUserSpace }))}`); }
|
|
51
52
|
|
|
52
53
|
sourceSpace = new InnerSpace_V1({
|
|
53
54
|
...DEFAULT_INNER_SPACE_DATA_V1,
|
|
@@ -56,7 +57,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
56
57
|
description: 'source test space',
|
|
57
58
|
});
|
|
58
59
|
await sourceSpace.initialized;
|
|
59
|
-
console.log(`sourceSpace: ${pretty(toDto({ ibGib: sourceSpace }))}`);
|
|
60
|
+
if (logalot) { console.log(`sourceSpace: ${pretty(toDto({ ibGib: sourceSpace }))}`); }
|
|
60
61
|
// await (sourceSpace as any).initialize(); // DO NOT AWAIT THIS. THIS IS AUTOMATICALLY AWAITED IN PLUMBING
|
|
61
62
|
|
|
62
63
|
destSpace = new InnerSpace_V1({
|
|
@@ -66,11 +67,11 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
66
67
|
description: 'dest test space',
|
|
67
68
|
});
|
|
68
69
|
await destSpace.initialized;
|
|
69
|
-
console.log(`destSpace: ${pretty(toDto({ ibGib: destSpace }))}`);
|
|
70
|
+
if (logalot) { console.log(`destSpace: ${pretty(toDto({ ibGib: destSpace }))}`); }
|
|
70
71
|
// await (destSpace as any).initialized; // DO NOT AWAIT THIS. THIS IS AUTOMATICALLY AWAITED IN PLUMBING
|
|
71
72
|
|
|
72
73
|
// 2. Seed Source Data
|
|
73
|
-
console.log(`${lc} Creating data in Source...`);
|
|
74
|
+
if (logalot) { console.log(`${lc} Creating data in Source...`); }
|
|
74
75
|
const root = await createTimelineRootHelper({
|
|
75
76
|
ib: 'timeline_root',
|
|
76
77
|
// data: { type: 'root', n: 0 }, // DO NOT TRY TO MANUALLY SET n
|
|
@@ -90,11 +91,11 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
90
91
|
const childAddr = getIbGibAddr({ ibGib: child });
|
|
91
92
|
|
|
92
93
|
// 3. Setup Coordinators & Peer
|
|
93
|
-
console.log(`${lc} Setting up Coordinators...`);
|
|
94
|
+
if (logalot) { console.log(`${lc} Setting up Coordinators...`); }
|
|
94
95
|
const mockKeystone = await getTestKeystoneServiceHelper();
|
|
95
96
|
// const identity = await (mockKeystone as any).getIdentity(); // SKIP IDENTITY FOR INITIAL TESTING SIMPLICITY
|
|
96
97
|
|
|
97
|
-
// Sender Coordinator
|
|
98
|
+
// Sender Coordinator
|
|
98
99
|
const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
|
|
99
100
|
|
|
100
101
|
// Receiver Coordinator
|
|
@@ -114,7 +115,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
// 4. Run Sync
|
|
117
|
-
console.log(`${lc} Running Sync...`);
|
|
118
|
+
if (logalot) { console.log(`${lc} Running Sync...`); }
|
|
118
119
|
const resSync = await senderCoordinator.sync({
|
|
119
120
|
peer: peer,
|
|
120
121
|
localSpace: sourceSpace,
|
|
@@ -123,7 +124,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
123
124
|
useSessionIdentity: false,
|
|
124
125
|
}); // Type cast if needed until we align optional params
|
|
125
126
|
|
|
126
|
-
console.log(`${lc} resSync: ${pretty(resSync)}`);
|
|
127
|
+
if (logalot) { console.log(`${lc} resSync: ${pretty(resSync)}`); }
|
|
127
128
|
|
|
128
129
|
const sublc = `${lc}[updates$]`;
|
|
129
130
|
/**
|
|
@@ -132,24 +133,24 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
132
133
|
*/
|
|
133
134
|
const subscription = await resSync.updates$.subscribe(fnObs({
|
|
134
135
|
next: async (ctxIbGib) => {
|
|
135
|
-
console.log(`${sublc} next fired`);
|
|
136
|
+
if (logalot) { console.log(`${sublc} next fired`); }
|
|
136
137
|
},
|
|
137
138
|
error: async (e) => {
|
|
138
|
-
console.log(`${sublc} error fired`);
|
|
139
|
+
if (logalot) { console.log(`${sublc} error fired`); }
|
|
139
140
|
},
|
|
140
141
|
complete: async () => {
|
|
141
|
-
console.log(`${sublc} complete fired`);
|
|
142
|
+
if (logalot) { console.log(`${sublc} complete fired`); }
|
|
142
143
|
},
|
|
143
144
|
}));
|
|
144
145
|
|
|
145
|
-
console.log(`${lc} awaiting resSync.done`)
|
|
146
|
+
if (logalot) { console.log(`${lc} awaiting resSync.done`) }
|
|
146
147
|
await resSync.done;
|
|
147
148
|
|
|
148
149
|
|
|
149
150
|
// todo: add iReckon assertions about resSync
|
|
150
151
|
|
|
151
152
|
// 5. Verify Dest
|
|
152
|
-
console.log(`${lc} Verifying Destination...`);
|
|
153
|
+
if (logalot) { console.log(`${lc} Verifying Destination...`); }
|
|
153
154
|
try {
|
|
154
155
|
const getChildInDest = await getFromSpace({ space: destSpace, addr: childAddr });
|
|
155
156
|
|
|
@@ -159,7 +160,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
159
160
|
|
|
160
161
|
await ifWe(sir, `verify getChildInDest.ibGibs`, async () => {
|
|
161
162
|
const firstChild = getChildInDest.ibGibs?.[0];
|
|
162
|
-
console.log(`${lc} firstChild: ${pretty(firstChild)}`);
|
|
163
|
+
if (logalot) { console.log(`${lc} firstChild: ${pretty(firstChild)}`); }
|
|
163
164
|
iReckon(sir, firstChild?.data?.n).asTo('Child content matches').isGonnaBe(2);
|
|
164
165
|
});
|
|
165
166
|
|
|
@@ -169,7 +170,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
169
170
|
iReckon(sir, true).asTo(`error: ${extractErrorMsg(error)}`).isGonnaBeFalse();
|
|
170
171
|
});
|
|
171
172
|
} finally {
|
|
172
|
-
console.log(`${lc} Test complete.`);
|
|
173
|
+
if (logalot) { console.log(`${lc} Test complete.`); }
|
|
173
174
|
}
|
|
174
175
|
});
|
|
175
176
|
|