@ibgib/core-gib 0.1.21 → 0.1.23
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/dist/common/other/graph-helper.d.mts +8 -0
- package/dist/common/other/graph-helper.d.mts.map +1 -1
- package/dist/common/other/graph-helper.mjs +31 -1
- package/dist/common/other/graph-helper.mjs.map +1 -1
- package/dist/sync/sync-conflict.respec.mjs +31 -28
- package/dist/sync/sync-conflict.respec.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +21 -11
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +18 -6
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +24 -15
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +163 -92
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.respec.mjs +12 -7
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +12 -7
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +15 -10
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +13 -7
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +15 -7
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +13 -7
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-constants.d.mts +2 -0
- package/dist/sync/sync-peer/sync-peer-constants.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-constants.mjs +2 -0
- package/dist/sync/sync-peer/sync-peer-constants.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-helpers.d.mts +2 -0
- package/dist/sync/sync-peer/sync-peer-helpers.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-helpers.mjs +2 -0
- package/dist/sync/sync-peer/sync-peer-helpers.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts +8 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs +8 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.d.mts +18 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.mjs +54 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +65 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mjs +5 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +44 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +183 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts +28 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +51 -9
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +244 -26
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +26 -10
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +119 -30
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +31 -22
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-types.mjs +1 -9
- package/dist/sync/sync-saga-context/sync-saga-context-types.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +81 -87
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +627 -571
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +105 -22
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +56 -76
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +5 -0
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/timeline/timeline-api.d.mts.map +1 -1
- package/dist/timeline/timeline-api.mjs +15 -8
- package/dist/timeline/timeline-api.mjs.map +1 -1
- package/dist/witness/light-witness-base-v1.d.mts.map +1 -1
- package/dist/witness/light-witness-base-v1.mjs +2 -0
- package/dist/witness/light-witness-base-v1.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
- package/package.json +1 -1
- package/src/common/other/graph-helper.mts +26 -1
- package/src/sync/README.md +31 -22
- package/src/sync/sync-conflict.respec.mts +31 -26
- package/src/sync/sync-constants.mts +19 -9
- package/src/sync/sync-helpers.mts +173 -97
- package/src/sync/sync-innerspace-constants.respec.mts +12 -7
- package/src/sync/sync-innerspace-deep-updates.respec.mts +12 -7
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +14 -9
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +13 -7
- package/src/sync/sync-innerspace-partial-update.respec.mts +15 -7
- package/src/sync/sync-innerspace.respec.mts +13 -7
- package/src/sync/sync-peer/sync-peer-constants.mts +0 -0
- package/src/sync/sync-peer/sync-peer-helpers.mts +0 -0
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mts +8 -0
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.mts +72 -0
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +72 -0
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +193 -0
- package/src/sync/sync-peer/sync-peer-types.mts +30 -1
- package/src/sync/sync-peer/sync-peer-v1.mts +229 -30
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +140 -43
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +34 -30
- package/src/sync/sync-saga-coordinator.mts +678 -660
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +106 -22
- package/src/sync/sync-types.mts +59 -87
- package/src/timeline/timeline-api.mts +17 -10
- package/src/witness/light-witness-base-v1.mts +2 -1
- package/src/witness/space/inner-space/inner-space-v1.mts +1 -1
- package/test_output.log +0 -0
- package/tmp.md +62 -44
- package/dist/sync/sync-local-spaces.respec.d.mts +0 -2
- package/dist/sync/sync-local-spaces.respec.d.mts.map +0 -1
- package/dist/sync/sync-local-spaces.respec.mjs +0 -159
- package/dist/sync/sync-local-spaces.respec.mjs.map +0 -1
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.d.mts +0 -42
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.d.mts.map +0 -1
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.mjs +0 -194
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.mjs.map +0 -1
- package/dist/sync/sync-saga-coordinator.respec.d.mts +0 -2
- package/dist/sync/sync-saga-coordinator.respec.d.mts.map +0 -1
- package/dist/sync/sync-saga-coordinator.respec.mjs +0 -40
- package/dist/sync/sync-saga-coordinator.respec.mjs.map +0 -1
- package/src/sync/sync-local-spaces.respec.mts +0 -200
- package/src/sync/sync-peer/sync-peer-innerspace-v1.mts +0 -240
- package/src/sync/sync-saga-coordinator.respec.mts +0 -52
|
@@ -4,13 +4,14 @@ import {
|
|
|
4
4
|
getTimestamp, // so our timestamp strings are uniform
|
|
5
5
|
getTimestampInTicks, // so our timestamp in ticks as a string are uniform
|
|
6
6
|
pretty,
|
|
7
|
-
clone
|
|
7
|
+
clone,
|
|
8
|
+
unique,
|
|
9
|
+
delay,
|
|
8
10
|
} from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
9
11
|
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
10
|
-
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp } from "../common/other/ibgib-helper.mjs";
|
|
11
12
|
import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
|
|
12
|
-
import { IbGib_V1,
|
|
13
|
-
import {
|
|
13
|
+
import { IbGib_V1, } from "@ibgib/ts-gib/dist/V1/types.mjs";
|
|
14
|
+
import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
|
|
14
15
|
|
|
15
16
|
import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
|
|
16
17
|
import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
|
|
@@ -18,35 +19,42 @@ import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space
|
|
|
18
19
|
import { KeystoneIbGib_V1 } from "../keystone/keystone-types.mjs";
|
|
19
20
|
import { KeystoneService_V1 } from "../keystone/keystone-service-v1.mjs";
|
|
20
21
|
import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
|
|
21
|
-
import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME } from "./sync-constants.mjs";
|
|
22
|
-
import { appendToTimeline, createTimeline, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
|
|
22
|
+
import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
|
|
23
|
+
import { appendToTimeline, createTimeline, getHistory, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
|
|
23
24
|
import {
|
|
24
|
-
SyncData_V1, SyncIbGib_V1,
|
|
25
|
-
|
|
26
|
-
SyncOptions,
|
|
25
|
+
SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
|
|
26
|
+
SyncRel8ns_V1, DomainIbGibAnalysisInfo, HandleSagaFrameResult,
|
|
27
27
|
} from "./sync-types.mjs";
|
|
28
|
-
import { getSyncIb, isPastFrame } from "./sync-helpers.mjs";
|
|
29
|
-
import {
|
|
30
|
-
import { getDependencyGraph } from "../common/other/graph-helper.mjs";
|
|
28
|
+
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
29
|
+
import { getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
31
30
|
import {
|
|
32
31
|
SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
|
|
33
32
|
SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
|
|
34
|
-
SyncSagaMessageCommitData_V1
|
|
33
|
+
SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
|
|
35
34
|
} from "./sync-saga-message/sync-saga-message-types.mjs";
|
|
36
35
|
import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
37
36
|
import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
|
|
38
37
|
import { SyncSagaInfo } from "./sync-types.mjs";
|
|
38
|
+
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
|
|
39
39
|
import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
|
|
40
|
-
import { SyncSagaContextIbGib_V1,
|
|
40
|
+
import { SyncSagaContextIbGib_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
|
|
41
41
|
import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
42
|
-
import { newupSubject } from "../common/pubsub/subject/subject-helper.mjs";
|
|
42
|
+
import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
|
|
43
43
|
import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
|
|
44
|
-
|
|
45
44
|
import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
|
|
46
45
|
import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
46
|
+
import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
|
|
47
|
+
import { ErrorIbGib_V1 } from "../common/error/error-types.mjs";
|
|
48
|
+
import {
|
|
49
|
+
IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns
|
|
50
|
+
} from "../witness/space/space-types.mjs";
|
|
51
|
+
import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
|
|
47
52
|
|
|
48
53
|
|
|
49
|
-
const logalot = GLOBAL_LOG_A_LOT || true;
|
|
54
|
+
// const logalot = GLOBAL_LOG_A_LOT || true;
|
|
55
|
+
const logalot = false;
|
|
56
|
+
const logalotControlDomain = true;
|
|
57
|
+
const lcControlDomain = '[ControlDomain]';
|
|
50
58
|
|
|
51
59
|
/**
|
|
52
60
|
* Orchestrates the synchronization process between two spaces (Source and Destination).
|
|
@@ -85,18 +93,16 @@ export class SyncSagaCoordinator {
|
|
|
85
93
|
*/
|
|
86
94
|
async sync({
|
|
87
95
|
peer,
|
|
88
|
-
localSpace: _localSpace,
|
|
89
|
-
source: _source,
|
|
90
|
-
metaspace,
|
|
91
96
|
domainIbGibs,
|
|
92
|
-
conflictStrategy =
|
|
97
|
+
conflictStrategy = SyncConflictStrategy.abort,
|
|
93
98
|
useSessionIdentity = true,
|
|
99
|
+
metaspace,
|
|
100
|
+
localSpace,
|
|
94
101
|
}: SyncOptions): Promise<SyncSagaInfo> {
|
|
95
102
|
const lc = `${this.lc}[${this.sync.name}]`;
|
|
96
103
|
if (logalot) { console.log(`${lc} starting...`); }
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
if (!localSpace) { throw new Error(`${lc} source (or localSpace) required (E: 8a9b0c1d)`); }
|
|
105
|
+
if (!localSpace) { throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`); }
|
|
100
106
|
|
|
101
107
|
// 1. SETUP SAGA METADATA
|
|
102
108
|
const sagaId = await getUUID();
|
|
@@ -112,11 +118,6 @@ export class SyncSagaCoordinator {
|
|
|
112
118
|
rejectDone = reject;
|
|
113
119
|
});
|
|
114
120
|
|
|
115
|
-
async function getTempSpaceName(): Promise<string> {
|
|
116
|
-
const uuid = await getUUID();
|
|
117
|
-
return `tmp_sync_space_${uuid.substring(0, 8)}`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
121
|
// WORKING CONTEXT (Transactional)
|
|
121
122
|
const tempSpaceName = await getTempSpaceName();
|
|
122
123
|
const tempSpace = await metaspace.createNewLocalSpace({
|
|
@@ -146,33 +147,25 @@ export class SyncSagaCoordinator {
|
|
|
146
147
|
// if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
|
|
147
148
|
|
|
148
149
|
// 3. CREATE INITIAL FRAME (Stage.init)
|
|
149
|
-
const { sagaFrame: initFrame,
|
|
150
|
+
const { sagaFrame: initFrame, initialDomainGraph } = await this.createInitFrame({
|
|
150
151
|
sagaId,
|
|
151
152
|
sessionIdentity,
|
|
152
|
-
localSpace,
|
|
153
153
|
domainIbGibs,
|
|
154
|
-
|
|
155
|
-
metaspace,
|
|
156
|
-
conflictStrategy
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// 4. EXECUTE SAGA LOOP (FSM)
|
|
160
|
-
const syncedIbGibs = await this.executeSagaLoop({
|
|
161
|
-
initialFrame: initFrame,
|
|
162
|
-
srcGraph,
|
|
163
|
-
peer,
|
|
164
|
-
sessionIdentity,
|
|
165
|
-
updates$,
|
|
166
|
-
localSpace,
|
|
167
|
-
tempSpace,
|
|
168
|
-
metaspace
|
|
154
|
+
conflictStrategy,
|
|
155
|
+
metaspace, localSpace, tempSpace,
|
|
169
156
|
});
|
|
170
157
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
158
|
+
// 4. KICK OFF THE PING-PONG SAGA LOOP (FSM)
|
|
159
|
+
// commented to compile
|
|
160
|
+
// await this.executeSagaLoop({
|
|
161
|
+
// initialFrame: initFrame,
|
|
162
|
+
// peer,
|
|
163
|
+
// sessionIdentity,
|
|
164
|
+
// updates$,
|
|
165
|
+
// localSpace,
|
|
166
|
+
// tempSpace,
|
|
167
|
+
// metaspace
|
|
168
|
+
// });
|
|
176
169
|
|
|
177
170
|
resolveDone();
|
|
178
171
|
if (!updates$.complete) { throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`); }
|
|
@@ -238,17 +231,17 @@ export class SyncSagaCoordinator {
|
|
|
238
231
|
* **Execution Context**: **Sender (Local)**.
|
|
239
232
|
*
|
|
240
233
|
* This method manages the "Ping Pong" request-response cycle on the Sender.
|
|
241
|
-
* It sends frames via the Peer Witness and processes the responses using `
|
|
234
|
+
* It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
|
|
242
235
|
*
|
|
243
236
|
* **Data Transport Note**:
|
|
244
237
|
* Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
|
|
245
|
-
* When `
|
|
238
|
+
* When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
|
|
246
239
|
* the NEXT request context.
|
|
247
240
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
248
241
|
*/
|
|
249
242
|
protected async executeSagaLoop({
|
|
250
243
|
initialFrame,
|
|
251
|
-
|
|
244
|
+
initialDomainGraph,
|
|
252
245
|
peer,
|
|
253
246
|
sessionIdentity,
|
|
254
247
|
updates$,
|
|
@@ -257,161 +250,190 @@ export class SyncSagaCoordinator {
|
|
|
257
250
|
metaspace
|
|
258
251
|
}: {
|
|
259
252
|
initialFrame: SyncIbGib_V1,
|
|
260
|
-
|
|
253
|
+
initialDomainGraph: FlatIbGibGraph,
|
|
261
254
|
peer: SyncPeerWitness,
|
|
262
255
|
sessionIdentity?: KeystoneIbGib_V1,
|
|
263
|
-
// updates$: Subject_V1<SyncSagaContextIbGib_V1>,
|
|
264
256
|
updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
|
|
257
|
+
metaspace: MetaspaceService
|
|
265
258
|
localSpace: IbGibSpaceAny,
|
|
266
259
|
tempSpace: IbGibSpaceAny,
|
|
267
|
-
|
|
268
|
-
}): Promise<IbGib_V1[]> {
|
|
260
|
+
}): Promise<void> {
|
|
269
261
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
270
262
|
|
|
271
263
|
// The current frame we just generated (e.g., Init or Delta Request)
|
|
272
264
|
let currentFrame: SyncIbGib_V1 | null = initialFrame;
|
|
273
265
|
// The payload we need to attach to the message (data we are sending)
|
|
274
|
-
let
|
|
275
|
-
|
|
276
|
-
|
|
266
|
+
let nextDomainIbGibs: IbGib_V1[] = [];
|
|
267
|
+
|
|
268
|
+
// First, inject local/tempSpace into peer so it can pull as
|
|
269
|
+
// needed...code smell?
|
|
270
|
+
peer.senderSpace = localSpace;
|
|
271
|
+
peer.senderTempSpace = tempSpace;
|
|
277
272
|
|
|
278
273
|
while (currentFrame) {
|
|
279
274
|
// A. Create Context (Request)
|
|
280
275
|
// 1. Calculate Full Dependency Graph (including Ancestors/DNA)
|
|
281
|
-
// We must do this BEFORE creating the Context so we can list them
|
|
282
|
-
|
|
276
|
+
// We must do this BEFORE creating the Context so we can list them
|
|
277
|
+
// all in payloadAddrsDomain and payloadAddrsControl.
|
|
278
|
+
const nextDomainIbGibsAndDeps: IbGib_V1[] = [];
|
|
283
279
|
|
|
284
280
|
// A. Payload (Standard Deep Deps)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
281
|
+
// TODO: THIS IS EXTREMELY INEFFICIENT. adjust this algorithm to
|
|
282
|
+
// only do the dependencies that the other end doesn't need (diff
|
|
283
|
+
// between tip and LCA). This can be done using the information in
|
|
284
|
+
// the ack's conflicts array.
|
|
285
|
+
for (const nextDomainIbGib of nextDomainIbGibs) {
|
|
286
|
+
let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
|
|
287
|
+
if (!nextDomainIbGibGraph) {
|
|
288
|
+
nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
|
|
289
289
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
allDeps.push(...Object.values(graph));
|
|
290
|
+
if (nextDomainIbGibGraph) {
|
|
291
|
+
nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
|
|
293
292
|
} else {
|
|
294
|
-
|
|
293
|
+
throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
|
|
295
294
|
}
|
|
296
295
|
}
|
|
297
|
-
if (logalot) {
|
|
298
|
-
// console.log(`${lc} allDeps count: ${allDeps.length}`);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// B. Frames (Shallow Sync Deps)
|
|
302
|
-
if (currentFrame) {
|
|
303
|
-
const deps = await getSyncSagaDependencyGraph({ ibGib: currentFrame, space: tempSpace });
|
|
304
|
-
if (deps) allDeps.push(...deps);
|
|
305
|
-
}
|
|
296
|
+
if (logalot) { console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`); }
|
|
306
297
|
|
|
307
298
|
// 2. Create Context (Envelope)
|
|
308
|
-
//
|
|
309
|
-
const
|
|
310
|
-
|
|
299
|
+
// set up subscription for response's payload ibgibs (if any)
|
|
300
|
+
const domainPayloadsMap = new Map<string, IbGib_V1>();
|
|
301
|
+
const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
|
|
302
|
+
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
303
|
+
next: async (ibgib: IbGib_V1) => {
|
|
304
|
+
if (logalot) { console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`); }
|
|
305
|
+
const addr = getIbGibAddr({ ibGib: ibgib });
|
|
306
|
+
if (logalotControlDomain) { console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`); }
|
|
307
|
+
domainPayloadsMap.set(addr, ibgib);
|
|
308
|
+
},
|
|
309
|
+
error: async (e: string | Error | ErrorIbGib_V1) => {
|
|
310
|
+
if (isIbGib(e)) {
|
|
311
|
+
console.error(`${sublc} error fired. error: ${JSON.stringify((e as IbGib_V1).data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
|
|
312
|
+
} else {
|
|
313
|
+
console.dir(e);
|
|
314
|
+
console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
complete: async () => {
|
|
318
|
+
if (logalot) { console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`); }
|
|
319
|
+
await subscription.unsubscribe();
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
|
|
323
|
+
// Create the Request Context...
|
|
311
324
|
const requestCtx = await createSyncSagaContext({
|
|
312
|
-
cmd: SyncSagaContextCmd.process,
|
|
313
325
|
sagaFrame: currentFrame,
|
|
314
326
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
315
|
-
payloadAddrs: payloadAddrs.length > 0 ? payloadAddrs : undefined,
|
|
316
|
-
});
|
|
317
327
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// Identity might be deep? Keystone? Usually self-contained or shallow references.
|
|
326
|
-
if (sessionIdentity) {
|
|
327
|
-
allDeps.push(sessionIdentity);
|
|
328
|
-
}
|
|
328
|
+
/**
|
|
329
|
+
* init frame: empty
|
|
330
|
+
*
|
|
331
|
+
*/
|
|
332
|
+
payloadIbGibsDomain: nextDomainIbGibsAndDeps,
|
|
333
|
+
localSpace,
|
|
334
|
+
});
|
|
329
335
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
});
|
|
336
|
+
// Log what we're sending
|
|
337
|
+
if (logalotControlDomain) {
|
|
338
|
+
const domainAddrs = nextDomainIbGibsAndDeps.map(p => getIbGibAddr({ ibGib: p }));
|
|
339
|
+
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
|
|
340
|
+
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
341
|
+
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
342
|
+
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
335
343
|
}
|
|
336
344
|
|
|
337
|
-
//
|
|
345
|
+
// update our saga listeners...
|
|
338
346
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
339
|
-
updates$.next(requestCtx);
|
|
347
|
+
updates$.next(requestCtx); // spins off
|
|
348
|
+
|
|
349
|
+
// ...And send the context
|
|
340
350
|
const responseCtx = await peer.witness(requestCtx);
|
|
341
351
|
|
|
342
352
|
// C. Handle Response
|
|
343
353
|
if (!responseCtx) {
|
|
344
|
-
// Check if we just sent a Commit frame. If so, peer's silence is success/expected.
|
|
345
354
|
if (currentFrame) {
|
|
355
|
+
// Check for Commit (Peer silence expected)
|
|
346
356
|
const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
|
|
347
|
-
if (logalot) { console.log(`${lc} Checking currentFrame stage: ${msg?.data?.stage} (Expected: ${SyncStage.commit})`); }
|
|
348
357
|
if (msg?.data?.stage === SyncStage.commit) {
|
|
349
358
|
if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete.`); }
|
|
350
359
|
currentFrame = null;
|
|
351
360
|
break;
|
|
361
|
+
} else {
|
|
362
|
+
throw new Error(`(UNEXPECTED) responseCtx falsy and currentFrame truthy, but we're not in the commit stage? This may be expected ultimately, but atow I am not seeing this as being expected. (E: cc34498962bd370deeff351fac939f26)`);
|
|
352
363
|
}
|
|
364
|
+
} else {
|
|
365
|
+
throw new Error(`(UNEXPECTED) no response and currentFrame falsy? (E: 8d1085ea2f28cfc3f9c922649864a826)`);
|
|
353
366
|
}
|
|
354
|
-
|
|
355
|
-
throw new Error(`responseCtx falsy. Peer returned no response context (E: c099d8073b48d85e881f917835158f26)`);
|
|
356
|
-
// console.warn(`${lc} Peer returned no response context. Ending loop.`);
|
|
357
|
-
// currentFrame = null;
|
|
358
|
-
// break;
|
|
359
367
|
}
|
|
360
368
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
369
|
+
// ---------------------------------------------------------------------
|
|
370
|
+
// 2d. HANDLE RESPONSE
|
|
371
|
+
// ---------------------------------------------------------------------
|
|
372
|
+
// at this point, we have received the response context ibgib, but
|
|
373
|
+
// if there were payloads expected to be transferred, they may not
|
|
374
|
+
// be done yet.
|
|
375
|
+
if (!responseCtx.data) { throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`); }
|
|
376
|
+
updates$.next(responseCtx); // spins off -- don't remove this comment!
|
|
377
|
+
|
|
378
|
+
// Extract expected domain addresses from response context
|
|
379
|
+
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] as string[] || [];
|
|
380
|
+
|
|
381
|
+
// Poll for them if needed
|
|
382
|
+
if (responsePayloadAddrsDomain.length > 0) {
|
|
383
|
+
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
384
|
+
expectedAddrs: responsePayloadAddrsDomain,
|
|
385
|
+
pollIntervalMs: 20, // relatively arbitrary right now
|
|
386
|
+
domainPayloadsMap,
|
|
387
|
+
tempSpace,
|
|
388
|
+
});
|
|
372
389
|
}
|
|
373
390
|
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
391
|
+
// at this point, we have received the context AND **all** of the
|
|
392
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
393
|
+
// iteration in the saga loop
|
|
394
|
+
|
|
395
|
+
// Log what we received back
|
|
396
|
+
if (!responseCtx.sagaFrame) { throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`); }
|
|
397
|
+
if (logalotControlDomain) {
|
|
398
|
+
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] as string[] || [];
|
|
399
|
+
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: c4d5e6f7a8b9c0d1)`);
|
|
400
|
+
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
401
|
+
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
402
|
+
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
403
|
+
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
379
404
|
}
|
|
380
|
-
const remoteFrame = resRemoteFrame.ibGibs?.[0];
|
|
381
405
|
|
|
382
|
-
|
|
383
|
-
//
|
|
406
|
+
// Handle Response Frame
|
|
407
|
+
// interface HandleSagaFrameResult {
|
|
408
|
+
// frame: SyncIbGib_V1;
|
|
409
|
+
// payloadIbGibsDomain?: IbGib_V1[];
|
|
410
|
+
// conflictInfos?: SyncSagaConflictInfo;
|
|
411
|
+
// }
|
|
412
|
+
const handleResult = await this.handleSagaContext({
|
|
413
|
+
sagaContext: responseCtx,
|
|
414
|
+
mySpace: localSpace,
|
|
415
|
+
myTempSpace: tempSpace,
|
|
416
|
+
metaspace,
|
|
417
|
+
});
|
|
384
418
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (remoteDeps && remoteDeps.length > 0) {
|
|
389
|
-
await putInSpace({ space: tempSpace, ibGibs: remoteDeps });
|
|
419
|
+
if (!handleResult) {
|
|
420
|
+
if (logalot) { console.log(`${lc} Handler returned null (Saga End).`); }
|
|
421
|
+
break;
|
|
390
422
|
}
|
|
391
423
|
|
|
424
|
+
currentFrame = handleResult.frame;
|
|
392
425
|
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
// i.e., We Sent Init -> Got Ack. This calls handleAckFrame.
|
|
396
|
-
// i.e., We Sent Delta -> Got Delta. This calls handleDeltaFrame.
|
|
397
|
-
const result = await this.handleSagaFrame({
|
|
398
|
-
sagaIbGib: remoteFrame as SyncIbGib_V1,
|
|
399
|
-
srcGraph,
|
|
400
|
-
destSpace: localSpace, // Query existing data from localSpace (Source)
|
|
401
|
-
tempSpace: tempSpace, // Transaction space for saga frames
|
|
402
|
-
identity: sessionIdentity,
|
|
403
|
-
metaspace
|
|
404
|
-
});
|
|
426
|
+
// Collect next DOMAIN payloads for the NEXT request
|
|
427
|
+
nextDomainIbGibs = [...(handleResult.payloadIbGibsDomain || [])];
|
|
405
428
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
429
|
+
// Log handler output for next iteration
|
|
430
|
+
if (logalotControlDomain) {
|
|
431
|
+
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
432
|
+
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: d5e6f7a8b9c0d1e2)`);
|
|
433
|
+
console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
434
|
+
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
411
435
|
}
|
|
412
436
|
}
|
|
413
|
-
|
|
414
|
-
return allReceivedIbGibs;
|
|
415
437
|
}
|
|
416
438
|
|
|
417
439
|
/**
|
|
@@ -432,7 +454,7 @@ export class SyncSagaCoordinator {
|
|
|
432
454
|
const lc = `${this.lc}[${this.getKnowledgeVector.name}]`;
|
|
433
455
|
try {
|
|
434
456
|
if (logalot) { console.log(`${lc} starting... (I: e184f8a7818666febfbbd2d841ed3826)`); }
|
|
435
|
-
console.dir(space)
|
|
457
|
+
// console.dir(space);
|
|
436
458
|
|
|
437
459
|
if (!(domainIbGibs && domainIbGibs.length > 0) &&
|
|
438
460
|
!(tjpAddrs && tjpAddrs.length > 0)
|
|
@@ -451,19 +473,19 @@ export class SyncSagaCoordinator {
|
|
|
451
473
|
tjps = tjpAddrs;
|
|
452
474
|
} else if (domainIbGibs && domainIbGibs.length > 0) {
|
|
453
475
|
// Extract TJPs from domain Ibgibs
|
|
454
|
-
if (
|
|
476
|
+
// if (false) { console.log(`${lc} domainIbGibs (${domainIbGibs.length}) provided. (I: a378995a0658af1f086ac1f297486c26)`); }
|
|
455
477
|
|
|
456
478
|
const { mapWithTjp_YesDna, mapWithTjp_NoDna } =
|
|
457
479
|
splitPerTjpAndOrDna({ ibGibs: domainIbGibs });
|
|
458
|
-
if (
|
|
459
|
-
if (
|
|
480
|
+
if (false) { console.log(`${lc}[TEST DEBUG] mapWithTjp_YesDna: ${JSON.stringify(mapWithTjp_YesDna)} (I: 287e22897148298e185712c8d50cfb26)`); }
|
|
481
|
+
if (false) { console.log(`${lc}[TEST DEBUG] mapWithTjp_NoDna: ${JSON.stringify(mapWithTjp_NoDna)} (I: 1bdc62656294aed0f9df334647dc7326)`); }
|
|
460
482
|
|
|
461
483
|
const allWithTjp = [...Object.values(mapWithTjp_YesDna), ...Object.values(mapWithTjp_NoDna)];
|
|
462
484
|
const timelineMap = getTimelinesGroupedByTjp({ ibGibs: allWithTjp });
|
|
463
|
-
if (
|
|
485
|
+
if (false) { console.log(`${lc}[TEST DEBUG] timelineMap: ${JSON.stringify(timelineMap)} (I: 2cc04898e5f85179fb1ac7f827abc426)`); }
|
|
464
486
|
|
|
465
487
|
tjps = Object.keys(timelineMap);
|
|
466
|
-
if (
|
|
488
|
+
if (false) { console.log(`${lc}[TEST DEBUG] tjps: ${tjps} (I: 3dd548667cbd967c68e57c88dc570826)`); }
|
|
467
489
|
} else {
|
|
468
490
|
// No info provided. Return empty? Or throw?
|
|
469
491
|
// User test context implied "everything", but implementation requires scope.
|
|
@@ -473,14 +495,14 @@ export class SyncSagaCoordinator {
|
|
|
473
495
|
|
|
474
496
|
if (tjps.length === 0) { return {}; }
|
|
475
497
|
|
|
476
|
-
if (
|
|
498
|
+
if (false) { console.log(`${lc} getting latest addrs for tjps: ${tjps} (I: d4e7080b8ba8187c583b82fd91ac0626)`); }
|
|
477
499
|
|
|
478
500
|
const res = await getLatestAddrs({ space, tjpAddrs: tjps });
|
|
479
501
|
if (!res.data || !res.data.latestAddrsMap) {
|
|
480
502
|
throw new Error(`${lc} Failed to get latest addrs. (E: 7a8b9c0d)`);
|
|
481
503
|
}
|
|
482
504
|
|
|
483
|
-
if (
|
|
505
|
+
// if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
|
|
484
506
|
|
|
485
507
|
return res.data.latestAddrsMap;
|
|
486
508
|
} catch (error) {
|
|
@@ -491,42 +513,41 @@ export class SyncSagaCoordinator {
|
|
|
491
513
|
}
|
|
492
514
|
}
|
|
493
515
|
|
|
494
|
-
protected async
|
|
516
|
+
protected async analyzeDomainIbGibs({
|
|
495
517
|
domainIbGibs,
|
|
496
518
|
space,
|
|
497
519
|
}: {
|
|
498
520
|
domainIbGibs: IbGib_V1[],
|
|
499
521
|
space: IbGibSpaceAny,
|
|
500
|
-
}): Promise<{
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
const srcGraph = await getDependencyGraph({
|
|
522
|
+
}): Promise<DomainIbGibAnalysisInfo> {
|
|
523
|
+
const lc = `${this.lc}[${this.analyzeDomainIbGibs.name}]`;
|
|
524
|
+
if (domainIbGibs.length === 0) {
|
|
525
|
+
throw new Error(`invalid domainIbGibs. expected at least one, but array is empty. (E: 820a719bcac5a16878a2af697113b826)`);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const fullGraph = await getDependencyGraph({
|
|
508
529
|
ibGibs: domainIbGibs,
|
|
509
530
|
live: true,
|
|
510
531
|
space,
|
|
511
532
|
});
|
|
512
533
|
|
|
513
|
-
const
|
|
514
|
-
if (logalot) { console.log(`${lc} graph generated. nodes: ${
|
|
534
|
+
const graphIsValid = fullGraph && Object.keys(fullGraph).length > 0;
|
|
535
|
+
if (logalot) { console.log(`${lc} graph generated. nodes: ${graphIsValid ? Object.keys(fullGraph).length : 0}`); }
|
|
515
536
|
|
|
516
|
-
const srcIbGibs =
|
|
537
|
+
const srcIbGibs = graphIsValid ? Object.values(fullGraph) : [];
|
|
517
538
|
const {
|
|
518
539
|
mapWithTjp_YesDna: srcMapWithTjp_YesDna,
|
|
519
540
|
mapWithTjp_NoDna: srcMapWithTjp_NoDna,
|
|
520
541
|
mapWithoutTjps: src_MapWithoutTjps
|
|
521
542
|
} = splitPerTjpAndOrDna({ ibGibs: srcIbGibs });
|
|
522
543
|
|
|
523
|
-
const
|
|
524
|
-
const
|
|
544
|
+
const stones = Object.values(src_MapWithoutTjps);
|
|
545
|
+
const withTimelines = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
|
|
525
546
|
|
|
526
|
-
const
|
|
527
|
-
const
|
|
547
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: withTimelines });
|
|
548
|
+
const topologicallySortedTjpAddrs = this.sortTimelinesTopologically(timelinesMap);
|
|
528
549
|
|
|
529
|
-
return {
|
|
550
|
+
return { stones, timelinesMap, topologicallySortedTjpAddrs, fullGraph };
|
|
530
551
|
}
|
|
531
552
|
|
|
532
553
|
/**
|
|
@@ -541,78 +562,142 @@ export class SyncSagaCoordinator {
|
|
|
541
562
|
protected async createInitFrame({
|
|
542
563
|
sagaId,
|
|
543
564
|
sessionIdentity,
|
|
544
|
-
localSpace,
|
|
545
565
|
domainIbGibs,
|
|
546
|
-
tempSpace,
|
|
547
|
-
metaspace,
|
|
548
566
|
conflictStrategy,
|
|
567
|
+
metaspace,
|
|
568
|
+
localSpace,
|
|
569
|
+
tempSpace,
|
|
549
570
|
}: {
|
|
550
571
|
sagaId: string,
|
|
551
572
|
sessionIdentity?: KeystoneIbGib_V1,
|
|
552
|
-
localSpace: IbGibSpaceAny,
|
|
553
573
|
domainIbGibs: IbGib_V1[],
|
|
554
|
-
tempSpace: IbGibSpaceAny,
|
|
555
|
-
metaspace: MetaspaceService,
|
|
556
574
|
conflictStrategy: SyncConflictStrategy,
|
|
557
|
-
|
|
575
|
+
metaspace: MetaspaceService,
|
|
576
|
+
localSpace: IbGibSpaceAny,
|
|
577
|
+
tempSpace: IbGibSpaceAny,
|
|
578
|
+
}): Promise<{ sagaFrame: SyncIbGib_V1, initialDomainGraph: { [addr: string]: IbGib_V1 } }> {
|
|
558
579
|
const lc = `${this.lc}[${this.createInitFrame.name}]`;
|
|
559
580
|
try {
|
|
560
581
|
if (logalot) { console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`); }
|
|
561
582
|
|
|
562
583
|
// Analyze Timelines & Stones
|
|
563
|
-
const analysis = await this.
|
|
564
|
-
// this is a lot, so uncomment this only if you want even more logging specific to this analysis
|
|
584
|
+
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
565
585
|
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
566
|
-
const { srcTimelinesMap,
|
|
586
|
+
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
567
587
|
|
|
588
|
+
// we need to store the fullGraph in our tempSpace for later...
|
|
589
|
+
await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
|
|
590
|
+
|
|
591
|
+
// populate our knowledge vector with tjp -> latest addr/tip in timeline
|
|
592
|
+
const knowledgeVector: { [tjp: string]: IbGibAddr } = {};
|
|
593
|
+
Object.keys(srcTimelinesMap).forEach(tjp => {
|
|
594
|
+
const timeline = srcTimelinesMap[tjp];
|
|
595
|
+
const tip = timeline.at(-1)!;
|
|
596
|
+
knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
|
|
597
|
+
});
|
|
568
598
|
const initData: SyncSagaMessageInitData_V1 = {
|
|
569
599
|
sagaId,
|
|
570
600
|
stage: SyncStage.init,
|
|
571
|
-
knowledgeVector
|
|
601
|
+
knowledgeVector,
|
|
572
602
|
identity: sessionIdentity, // KeystoneIbGib is already public data
|
|
573
603
|
mode: SyncMode.sync,
|
|
574
604
|
stones: srcStones.map(s => getIbGibAddr({ ibGib: s })),
|
|
575
605
|
};
|
|
576
|
-
|
|
577
|
-
// Populate Knowledge Vector
|
|
578
|
-
Object.keys(srcTimelinesMap).forEach(tjp => {
|
|
579
|
-
const timeline = srcTimelinesMap[tjp];
|
|
580
|
-
const tip = timeline.at(-1)!;
|
|
581
|
-
initData.knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
|
|
582
|
-
});
|
|
583
|
-
|
|
584
606
|
if (logalot) {
|
|
585
607
|
console.log(`${lc} SyncStage.init: ${SyncStage.init}, SyncStage.commit: ${SyncStage.commit}`);
|
|
586
608
|
console.log(`${lc} initData.stage: ${initData.stage}`);
|
|
587
609
|
}
|
|
588
610
|
|
|
611
|
+
// create the stone and save/register it
|
|
589
612
|
const initStone = await this.createSyncMsgStone({
|
|
590
613
|
data: initData,
|
|
591
|
-
|
|
592
|
-
metaspace
|
|
614
|
+
localSpace,
|
|
615
|
+
metaspace,
|
|
593
616
|
});
|
|
594
617
|
// if (logalot) { console.log(`${lc} initStone: ${pretty(initStone)} (I: 06e532f8a408549069474e96bed44826)`); }
|
|
595
618
|
|
|
619
|
+
// create the initial sync ibgib frame, save/register it
|
|
596
620
|
const sagaFrame = await this.evolveSyncSagaIbGib({
|
|
597
621
|
msgStones: [initStone],
|
|
598
|
-
identity: sessionIdentity,
|
|
599
|
-
space: tempSpace,
|
|
600
|
-
metaspace,
|
|
601
622
|
conflictStrategy,
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
await this.ensureSagaFrameInBothSpaces({
|
|
606
|
-
frame: sagaFrame,
|
|
607
|
-
destSpace: localSpace, // localSpace is the Sender's destSpace
|
|
608
|
-
tempSpace,
|
|
609
|
-
metaspace
|
|
623
|
+
sessionIdentity: sessionIdentity,
|
|
624
|
+
metaspace,
|
|
625
|
+
localSpace,
|
|
610
626
|
});
|
|
611
627
|
|
|
612
628
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
613
629
|
|
|
614
|
-
return { sagaFrame,
|
|
630
|
+
return { sagaFrame, initialDomainGraph: fullGraph };
|
|
631
|
+
} catch (error) {
|
|
632
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
633
|
+
throw error;
|
|
634
|
+
} finally {
|
|
635
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
636
|
+
}
|
|
637
|
+
}
|
|
615
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Helper to poll for streaming domain payloads and put them in the
|
|
641
|
+
* local {@link tempSpace}.
|
|
642
|
+
*
|
|
643
|
+
* @returns when all {@link expectedAddrs} are done being transmitted.
|
|
644
|
+
*/
|
|
645
|
+
protected async pollForDomainPayloads({
|
|
646
|
+
expectedAddrs,
|
|
647
|
+
pollIntervalMs,
|
|
648
|
+
domainPayloadsMap,
|
|
649
|
+
tempSpace,
|
|
650
|
+
}: {
|
|
651
|
+
expectedAddrs: string[],
|
|
652
|
+
pollIntervalMs: number,
|
|
653
|
+
domainPayloadsMap: Map<string, IbGib_V1>,
|
|
654
|
+
tempSpace: IbGibSpaceAny,
|
|
655
|
+
}): Promise<IbGib_V1[]> {
|
|
656
|
+
const lc = `${this.lc}[${this.pollForDomainPayloads.name}]`;
|
|
657
|
+
try {
|
|
658
|
+
if (logalot) { console.log(`${lc} starting... (I: 26dce86bfca572939885798802d6e926)`); }
|
|
659
|
+
|
|
660
|
+
let resultDomainPayloads: IbGib_V1[] = [];
|
|
661
|
+
|
|
662
|
+
let pending = [...expectedAddrs];
|
|
663
|
+
const start = Date.now();
|
|
664
|
+
/**
|
|
665
|
+
* This needs
|
|
666
|
+
*/
|
|
667
|
+
const timeoutMs = 5 * 60 * 1000; // 5 minutes...arbitrary at this point. This needs to be pulled out and improved eesh.
|
|
668
|
+
|
|
669
|
+
while (pending.length > 0) {
|
|
670
|
+
if (Date.now() - start > timeoutMs) {
|
|
671
|
+
throw new Error(`Timeout waiting for payloads: ${pending.join(', ')} (E: 46e1683c9578095261aaf798bd5e1826)`);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const stillPending: string[] = [];
|
|
675
|
+
const found: IbGib_V1[] = [];
|
|
676
|
+
|
|
677
|
+
for (const addr of pending) {
|
|
678
|
+
if (domainPayloadsMap.has(addr)) {
|
|
679
|
+
found.push(domainPayloadsMap.get(addr)!);
|
|
680
|
+
domainPayloadsMap.delete(addr);
|
|
681
|
+
} else {
|
|
682
|
+
stillPending.push(addr);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (found.length > 0) {
|
|
687
|
+
await putInSpace({ space: tempSpace, ibGibs: found });
|
|
688
|
+
found.forEach(x => resultDomainPayloads.push(x));
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
pending = stillPending;
|
|
692
|
+
|
|
693
|
+
if (pending.length > 0) { await delay(pollIntervalMs); }
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (expectedAddrs.length !== resultDomainPayloads.length) {
|
|
697
|
+
throw new Error(`(UNEXPECTED) expectedAddrs.length !== resultDomainPayloads.length? at this point, we expect all of the payload ibgibs to have been received. (E: 03749a7478c4b8b28bfc86951887a826)`);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return resultDomainPayloads;
|
|
616
701
|
} catch (error) {
|
|
617
702
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
618
703
|
throw error;
|
|
@@ -622,59 +707,84 @@ export class SyncSagaCoordinator {
|
|
|
622
707
|
}
|
|
623
708
|
|
|
624
709
|
/**
|
|
625
|
-
*
|
|
710
|
+
* This is the heart of the "ping pong" transaction, where we send a context
|
|
711
|
+
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
712
|
+
* a whole.
|
|
626
713
|
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
714
|
+
* This is called in two places:
|
|
715
|
+
*
|
|
716
|
+
* ## 1. Sender
|
|
717
|
+
*
|
|
718
|
+
* On the sender, this is called within the {@link executeSagaLoop} which
|
|
719
|
+
* initiates and drives the sync.
|
|
720
|
+
*
|
|
721
|
+
* ## 2. Receiver
|
|
629
722
|
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
723
|
+
* On the receiver, this is called directly by the receiving endpoint. That
|
|
724
|
+
* endpoint's job is basically to get these things collocated and prepared
|
|
725
|
+
* to make this call.
|
|
632
726
|
*
|
|
633
|
-
*
|
|
634
|
-
* * If running on **Sender**: Handles `Ack` (via `handleAckFrame`).
|
|
635
|
-
* * If running on **Either**: Handles `Delta` (via `handleDeltaFrame`) or `Commit`.
|
|
727
|
+
* This is a one-off on the receiver.
|
|
636
728
|
*/
|
|
637
|
-
async
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
tempSpace,
|
|
729
|
+
public async handleSagaContext({
|
|
730
|
+
sagaContext,
|
|
731
|
+
mySpace,
|
|
732
|
+
myTempSpace,
|
|
642
733
|
identity,
|
|
643
734
|
identitySecret,
|
|
644
735
|
metaspace,
|
|
645
736
|
}: {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
737
|
+
sagaContext: SyncSagaContextIbGib_V1,
|
|
738
|
+
/**
|
|
739
|
+
* Local space relative to the execution context's POV
|
|
740
|
+
*/
|
|
741
|
+
mySpace: IbGibSpaceAny,
|
|
742
|
+
/**
|
|
743
|
+
* Local temp space relative to the execution context's POV
|
|
744
|
+
*/
|
|
745
|
+
myTempSpace: IbGibSpaceAny,
|
|
650
746
|
identity?: KeystoneIbGib_V1,
|
|
651
747
|
identitySecret?: string,
|
|
652
748
|
metaspace: MetaspaceService,
|
|
653
|
-
}): Promise<
|
|
654
|
-
const lc = `${this.lc}[${this.
|
|
749
|
+
}): Promise<HandleSagaFrameResult | null> {
|
|
750
|
+
const lc = `${this.lc}[${this.handleSagaContext.name}]`;
|
|
655
751
|
try {
|
|
656
752
|
if (logalot) { console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`); }
|
|
657
753
|
|
|
754
|
+
const sagaIbGib = sagaContext.sagaFrame;
|
|
658
755
|
if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 71b938adf1d87c2527bfd4f86dfd0826)`); }
|
|
659
756
|
if (logalot) { console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`); }
|
|
660
757
|
|
|
661
758
|
// Get Stage from Stone (or Frame for Init fallback)
|
|
662
|
-
const { stage, messageData } = await this.getStageAndPayloadFromFrame({
|
|
759
|
+
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
|
|
663
760
|
|
|
664
761
|
if (logalot) { console.log(`${lc} handling frame stage: ${stage}`); }
|
|
665
762
|
|
|
763
|
+
/**
|
|
764
|
+
* don't like this name, need to refactor
|
|
765
|
+
*/
|
|
766
|
+
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
767
|
+
|
|
666
768
|
switch (stage) {
|
|
667
769
|
case SyncStage.init:
|
|
668
|
-
return await this.handleInitFrame({
|
|
770
|
+
return await this.handleInitFrame({
|
|
771
|
+
sagaIbGib,
|
|
772
|
+
messageData: messageData as SyncSagaMessageInitData_V1,
|
|
773
|
+
metaspace,
|
|
774
|
+
mySpace: mySpace,
|
|
775
|
+
myTempSpace: myTempSpace,
|
|
776
|
+
identity,
|
|
777
|
+
identitySecret
|
|
778
|
+
});
|
|
669
779
|
|
|
670
780
|
case SyncStage.ack:
|
|
671
|
-
return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity });
|
|
781
|
+
return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
|
|
672
782
|
|
|
673
783
|
case SyncStage.delta:
|
|
674
|
-
return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity, });
|
|
784
|
+
return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
675
785
|
|
|
676
786
|
case SyncStage.commit:
|
|
677
|
-
return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace, tempSpace, identity, });
|
|
787
|
+
return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
678
788
|
|
|
679
789
|
default:
|
|
680
790
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
@@ -704,305 +814,297 @@ export class SyncSagaCoordinator {
|
|
|
704
814
|
protected async handleInitFrame({
|
|
705
815
|
sagaIbGib,
|
|
706
816
|
messageData,
|
|
707
|
-
|
|
708
|
-
|
|
817
|
+
mySpace,
|
|
818
|
+
myTempSpace,
|
|
709
819
|
metaspace,
|
|
710
820
|
identity,
|
|
711
|
-
identitySecret,
|
|
821
|
+
// identitySecret,
|
|
712
822
|
}: {
|
|
713
823
|
sagaIbGib: SyncIbGib_V1,
|
|
714
|
-
messageData:
|
|
715
|
-
|
|
716
|
-
|
|
824
|
+
messageData: SyncSagaMessageInitData_V1,
|
|
825
|
+
/**
|
|
826
|
+
* Local space relative to the execution context's POV
|
|
827
|
+
*/
|
|
828
|
+
mySpace: IbGibSpaceAny,
|
|
829
|
+
/**
|
|
830
|
+
* Local temp space relative to the execution context's POV.
|
|
831
|
+
*
|
|
832
|
+
* NOTE: Since this always executes on the receiver's end, this should
|
|
833
|
+
* be the receiver's temp space.
|
|
834
|
+
*/
|
|
835
|
+
myTempSpace: IbGibSpaceAny,
|
|
717
836
|
metaspace: MetaspaceService,
|
|
718
837
|
identity?: KeystoneIbGib_V1,
|
|
719
838
|
identitySecret?: string,
|
|
720
|
-
}): Promise<
|
|
839
|
+
}): Promise<HandleSagaFrameResult | null> {
|
|
721
840
|
const lc = `${this.lc}[${this.handleInitFrame.name}]`;
|
|
722
|
-
|
|
723
|
-
|
|
841
|
+
try {
|
|
842
|
+
if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
|
|
724
843
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
if (initData.stage !== SyncStage.init) {
|
|
728
|
-
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
|
|
729
|
-
}
|
|
730
|
-
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
731
|
-
if (!initData || !initData.knowledgeVector) {
|
|
732
|
-
throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
|
|
733
|
-
}
|
|
844
|
+
console.log(`${lc} [TEST DEBUG] Received destSpace: ${mySpace.data?.name || mySpace.ib} (uuid: ${mySpace.data?.uuid || '[no uuid]'})`);
|
|
845
|
+
if (logalot) { console.log(`${lc} starting...`); }
|
|
734
846
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
// Stones Analysis (Constants / Non-TJPs)
|
|
745
|
-
const stones = initData.stones || [];
|
|
746
|
-
if (stones.length > 0) {
|
|
747
|
-
if (logalot) { console.log(`${lc} processing stones: ${stones.length}`); }
|
|
748
|
-
// Check if we have these stones
|
|
749
|
-
const resStones = await getFromSpace({ space: destSpace, addrs: stones });
|
|
750
|
-
const addrsNotFound = resStones.rawResultIbGib?.data?.addrsNotFound;
|
|
751
|
-
if (addrsNotFound && addrsNotFound.length > 0) {
|
|
752
|
-
if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
|
|
753
|
-
addrsNotFound.forEach(addr => {
|
|
754
|
-
if (!deltaReqAddrs.includes(addr)) {
|
|
755
|
-
deltaReqAddrs.push(addr);
|
|
756
|
-
}
|
|
757
|
-
});
|
|
847
|
+
// Extract Init Data
|
|
848
|
+
const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
|
|
849
|
+
if (initData.stage !== SyncStage.init) {
|
|
850
|
+
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
|
|
851
|
+
}
|
|
852
|
+
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
853
|
+
if (!initData || !initData.knowledgeVector) {
|
|
854
|
+
throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
|
|
758
855
|
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
const remoteKV = initData.knowledgeVector;
|
|
762
|
-
if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
|
|
763
|
-
const remoteTjps = Object.keys(remoteKV);
|
|
764
|
-
console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
|
|
765
|
-
if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
|
|
766
|
-
|
|
767
|
-
// 1. Get Local Latest Addrs for all TJPs
|
|
768
|
-
let localKV: { [tjp: string]: string | null } = {};
|
|
769
|
-
if (remoteTjps.length > 0) {
|
|
770
|
-
// Batch get latest addrs for the TJPs
|
|
771
|
-
const resGetLatestAddrs = await getLatestAddrs({
|
|
772
|
-
space: destSpace,
|
|
773
|
-
tjpAddrs: remoteTjps,
|
|
774
|
-
});
|
|
775
|
-
if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
|
|
776
|
-
if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
|
|
777
|
-
localKV = resGetLatestAddrs.data.latestAddrsMap;
|
|
778
|
-
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
|
|
779
|
-
if (logalot) { console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`); }
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// 2. Gap Analysis
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
for (const tjp of remoteTjps) {
|
|
786
|
-
const remoteAddr = remoteKV[tjp];
|
|
787
|
-
const localAddr = localKV[tjp];
|
|
788
856
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
857
|
+
// Determine Strategy from Saga Data (since V1 stores it in root)
|
|
858
|
+
const conflictStrategy = sagaIbGib.data!.conflictStrategy || SyncConflictStrategy.abort;
|
|
859
|
+
|
|
860
|
+
// 2. Gap Analysis
|
|
861
|
+
const conflicts: SyncSagaConflictInfo[] = [];
|
|
862
|
+
const deltaReqAddrs: string[] = [];
|
|
863
|
+
const pushOfferAddrs: string[] = [];
|
|
864
|
+
|
|
865
|
+
// First do the consant stones (Non-TJPs)
|
|
866
|
+
const stones = initData.stones || [];
|
|
867
|
+
if (stones.length > 0) {
|
|
868
|
+
if (logalot) { console.log(`${lc} processing stones: ${stones.length}`); }
|
|
869
|
+
// Check if we have these stones
|
|
870
|
+
const resStones = await getFromSpace({ space: mySpace, addrs: stones });
|
|
871
|
+
const rawResultIbGib = resStones.rawResultIbGib as IbGibSpaceResultIbGib<IbGib_V1, IbGibSpaceResultData, IbGibSpaceResultRel8ns>;
|
|
872
|
+
if (!rawResultIbGib.data) { throw new Error(`(UNEXPECTED) rawResultIbGib.data falsy? (E: f3c40b492adc02c3f480b998fc7a5926)`); }
|
|
873
|
+
const addrsNotFound = rawResultIbGib.data.addrsNotFound;
|
|
874
|
+
if (addrsNotFound && addrsNotFound.length > 0) {
|
|
875
|
+
if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
|
|
876
|
+
addrsNotFound.forEach(addr => {
|
|
877
|
+
if (!deltaReqAddrs.includes(addr)) { deltaReqAddrs.push(addr); }
|
|
878
|
+
});
|
|
879
|
+
}
|
|
794
880
|
}
|
|
795
881
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
882
|
+
/**
|
|
883
|
+
* "remote" local to receiver's context is the sender
|
|
884
|
+
*/
|
|
885
|
+
const remoteKV = initData.knowledgeVector;
|
|
886
|
+
if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
|
|
887
|
+
const remoteTjps = Object.keys(remoteKV);
|
|
888
|
+
console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
|
|
889
|
+
if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
|
|
890
|
+
|
|
891
|
+
// 1. Get Local Latest Addrs for all TJPs
|
|
892
|
+
let localKV: { [tjp: string]: string | null } = {};
|
|
893
|
+
if (remoteTjps.length > 0) {
|
|
894
|
+
// Batch get latest addrs for the TJPs
|
|
895
|
+
const resGetLatestAddrs = await getLatestAddrs({
|
|
896
|
+
space: mySpace, // executing on receiver, so this is receiver's space
|
|
897
|
+
tjpAddrs: remoteTjps,
|
|
898
|
+
});
|
|
899
|
+
if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
|
|
900
|
+
if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
|
|
901
|
+
localKV = resGetLatestAddrs.data.latestAddrsMap;
|
|
902
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
|
|
903
|
+
if (logalot) { console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`); }
|
|
800
904
|
}
|
|
801
|
-
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
802
|
-
|
|
803
|
-
// Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
|
|
804
|
-
// (Sender has older version, Receiver has newer) -> Receiver Offers Push
|
|
805
|
-
const isRemoteInPast = await isPastFrame({
|
|
806
|
-
olderAddr: remoteAddr,
|
|
807
|
-
newerAddr: localAddr,
|
|
808
|
-
space: destSpace,
|
|
809
|
-
});
|
|
810
905
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
// Remote is not in our past.
|
|
816
|
-
// Either Remote is ahead (Fast-Backward) OR Diverged.
|
|
906
|
+
// 2. Gap Analysis
|
|
907
|
+
for (const tjp of remoteTjps) {
|
|
908
|
+
const remoteAddr = remoteKV[tjp];
|
|
909
|
+
const localAddr = localKV[tjp];
|
|
817
910
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
911
|
+
if (!localAddr) {
|
|
912
|
+
// We (Receiver) don't have this timeline at all. Request it.
|
|
913
|
+
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
914
|
+
deltaReqAddrs.push(remoteAddr);
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (localAddr === remoteAddr) {
|
|
919
|
+
// Synced
|
|
920
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
924
|
+
|
|
925
|
+
// Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
|
|
926
|
+
// (Sender has older version, Receiver has newer) -> Receiver Offers Push
|
|
927
|
+
const isRemoteInPast = await isPastFrame({
|
|
928
|
+
olderAddr: remoteAddr,
|
|
929
|
+
newerAddr: localAddr,
|
|
930
|
+
space: mySpace,
|
|
823
931
|
});
|
|
824
932
|
|
|
825
|
-
if (
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
deltaReqAddrs.push(remoteAddr);
|
|
933
|
+
if (isRemoteInPast) {
|
|
934
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
|
|
935
|
+
pushOfferAddrs.push(localAddr);
|
|
829
936
|
} else {
|
|
830
|
-
//
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
if (
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
tjpAddr: tjp,
|
|
840
|
-
localAddr: localAddr!,
|
|
841
|
-
remoteAddr,
|
|
842
|
-
timelineAddrs: [], // Not needed for abort
|
|
843
|
-
reason: 'divergence',
|
|
844
|
-
terminal: true
|
|
845
|
-
});
|
|
846
|
-
} else if (conflictStrategy === 'optimistic') {
|
|
847
|
-
// Optimistic: We want to resolving this.
|
|
848
|
-
// We need to send our history to the Sender so they can Merge.
|
|
849
|
-
|
|
850
|
-
// Fetch Full History for Local Timeline
|
|
851
|
-
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
852
|
-
// But for now, get full past.
|
|
853
|
-
// Optimization: localKV might not have full history.
|
|
854
|
-
// We need to inspect the 'past' of the local tip.
|
|
855
|
-
|
|
856
|
-
// We need the ACTUAL object to get the past.
|
|
857
|
-
// We have localAddr.
|
|
858
|
-
const resLocalTip = await getFromSpace({ space: destSpace, addr: localAddr! });
|
|
859
|
-
const localTip = resLocalTip.ibGibs?.[0];
|
|
860
|
-
if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`); }
|
|
861
|
-
|
|
862
|
-
const timelineAddrs = [localAddr!, ...(localTip.rel8ns?.past || [])];
|
|
863
|
-
|
|
864
|
-
conflicts.push({
|
|
865
|
-
tjpAddr: tjp,
|
|
866
|
-
localAddr: localAddr!,
|
|
867
|
-
remoteAddr,
|
|
868
|
-
timelineAddrs,
|
|
869
|
-
reason: 'divergence',
|
|
870
|
-
terminal: false
|
|
871
|
-
});
|
|
937
|
+
// Remote is not in our past.
|
|
938
|
+
// Either Remote is ahead (Fast-Backward) OR Diverged.
|
|
939
|
+
|
|
940
|
+
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
941
|
+
const isLocalInPast = await isPastFrame({
|
|
942
|
+
olderAddr: localAddr,
|
|
943
|
+
newerAddr: remoteAddr,
|
|
944
|
+
space: mySpace,
|
|
945
|
+
});
|
|
872
946
|
|
|
947
|
+
if (isLocalInPast) {
|
|
948
|
+
// Fast-Forward: We update to remote's tip.
|
|
949
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
950
|
+
deltaReqAddrs.push(remoteAddr);
|
|
873
951
|
} else {
|
|
874
|
-
|
|
952
|
+
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
953
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
|
|
954
|
+
|
|
955
|
+
if (conflictStrategy === 'abort') {
|
|
956
|
+
// Abort Strategy: We will treat this as terminal.
|
|
957
|
+
// But for Unified Ack, we just mark it terminal in the list?
|
|
958
|
+
// Or do we actually throw/abort the saga?
|
|
959
|
+
// Current logic (below) aborts the saga if ANY conflict is terminal/abort.
|
|
960
|
+
conflicts.push({
|
|
961
|
+
tjpAddr: tjp,
|
|
962
|
+
localAddr: localAddr,
|
|
963
|
+
remoteAddr,
|
|
964
|
+
timelineAddrs: [], // Not needed for abort
|
|
965
|
+
reason: 'divergence',
|
|
966
|
+
terminal: true
|
|
967
|
+
});
|
|
968
|
+
} else if (conflictStrategy === 'optimistic') {
|
|
969
|
+
// Optimistic: We want to resolving this.
|
|
970
|
+
// We need to send our history to the Sender so they can Merge.
|
|
971
|
+
|
|
972
|
+
// Fetch Full History for Local Timeline
|
|
973
|
+
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
974
|
+
// But for now, get full past.
|
|
975
|
+
// Optimization: localKV might not have full history.
|
|
976
|
+
// We need to inspect the 'past' of the local tip.
|
|
977
|
+
|
|
978
|
+
// We need the ACTUAL object to get the past.
|
|
979
|
+
// We have localAddr.
|
|
980
|
+
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
981
|
+
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
982
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: ff06ff849fa8e8dba32ce09807411226)`);
|
|
983
|
+
}
|
|
984
|
+
const localTip = resLocalTip.ibGibs[0];
|
|
985
|
+
if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`); }
|
|
986
|
+
|
|
987
|
+
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
988
|
+
|
|
989
|
+
conflicts.push({
|
|
990
|
+
tjpAddr: tjp,
|
|
991
|
+
localAddr,
|
|
992
|
+
remoteAddr,
|
|
993
|
+
timelineAddrs,
|
|
994
|
+
reason: 'divergence',
|
|
995
|
+
terminal: false
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
} else {
|
|
999
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
|
|
1000
|
+
}
|
|
875
1001
|
}
|
|
876
1002
|
}
|
|
877
1003
|
}
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Check if we should ABORT (if any conflict is terminal)
|
|
881
|
-
const hasTerminalConflicts = conflicts.some(c => c.terminal);
|
|
882
1004
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
if (logalot) { console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`); }
|
|
1005
|
+
// Check if we should ABORT (if any conflict is terminal)
|
|
1006
|
+
const hasTerminalConflicts = conflicts.some(c => c.terminal);
|
|
886
1007
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
// Let's stick to that for purely terminal cases to be safe/explicit?
|
|
891
|
-
// Or Unified: Just send Ack with terminal=true conflicts. Sender sees them and aborts.
|
|
1008
|
+
if (hasTerminalConflicts) {
|
|
1009
|
+
// Abort Strategy: Kill the saga.
|
|
1010
|
+
if (logalot) { console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`); }
|
|
892
1011
|
|
|
893
|
-
|
|
894
|
-
// But wait, the original code below creates a Conflict Stone.
|
|
895
|
-
// Let's preserve the explicit 'Conflict' frame for total aborts if that's easier,
|
|
896
|
-
// OR fully switch to Ack.
|
|
897
|
-
// Protocol states: Init -> Ack. If Ack contains terminal errors, Sender can Commit(Fail).
|
|
898
|
-
|
|
899
|
-
// Let's use Ack with conflicts.
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// 2. Add Push Offers (Missing in Local)
|
|
903
|
-
// Check if we have them. If not, ask for them.
|
|
904
|
-
for (const addr of pushOfferAddrs) {
|
|
905
|
-
const hasIt = await getFromSpace({ addr, space: destSpace });
|
|
906
|
-
if (!hasIt.success || !hasIt.ibGibs || hasIt.ibGibs.length === 0) {
|
|
907
|
-
// If we don't have it, we put it in `deltaReqAddrs` of the Ack.
|
|
908
|
-
deltaReqAddrs.push(addr);
|
|
1012
|
+
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
909
1013
|
}
|
|
910
|
-
}
|
|
911
1014
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
const
|
|
925
|
-
if (
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
const
|
|
930
|
-
knowledgeVector[realTjp]
|
|
1015
|
+
// 3. Build Knowledge Vector (Full History for known timelines)
|
|
1016
|
+
// [NEW] Smart Diff
|
|
1017
|
+
// We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
|
|
1018
|
+
// Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
|
|
1022
|
+
*/
|
|
1023
|
+
const knowledgeVector: { [groupKey: string]: string[] } = {};
|
|
1024
|
+
|
|
1025
|
+
// [Smart Diff] Populate knowledge from timelines identified by Sender
|
|
1026
|
+
for (const tjp of remoteTjps) {
|
|
1027
|
+
const localAddr = localKV[tjp];
|
|
1028
|
+
if (localAddr) {
|
|
1029
|
+
const res = await getFromSpace({ addr: localAddr, space: mySpace });
|
|
1030
|
+
if (res.success && res.ibGibs?.[0]) {
|
|
1031
|
+
const ibGib = res.ibGibs[0];
|
|
1032
|
+
const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
|
|
1033
|
+
if (!knowledgeVector[realTjp]) {
|
|
1034
|
+
const past = ibGib.rel8ns?.past || [];
|
|
1035
|
+
knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
|
|
1036
|
+
}
|
|
931
1037
|
}
|
|
932
1038
|
}
|
|
933
1039
|
}
|
|
934
|
-
}
|
|
935
1040
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
//
|
|
939
|
-
//
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1041
|
+
// Also populate from `knowledgeVector` in Init if we want bidirectional?
|
|
1042
|
+
// No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
|
|
1043
|
+
// Let's assume standard flow:
|
|
1044
|
+
// 1. Sender says "I have X"
|
|
1045
|
+
// 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
|
|
1046
|
+
// Problem: Receiver doesn't know X is related to Y without X.
|
|
1047
|
+
|
|
1048
|
+
// SOLUTION:
|
|
1049
|
+
// The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
|
|
1050
|
+
// `SyncSagaMessageInitData_V1` extends `SyncInitData`.
|
|
1051
|
+
// `SyncInitData` has `knowledgeVector`.
|
|
1052
|
+
// If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
|
|
1053
|
+
|
|
1054
|
+
// Let's implement Sender populating `Init.knowledgeVector`.
|
|
1055
|
+
// But `SyncSagaCoordinator.startSaga` creates Init.
|
|
1056
|
+
|
|
1057
|
+
// 3. Create Ack Frame
|
|
1058
|
+
const sagaId = sagaIbGib.data!.uuid;
|
|
1059
|
+
// Create Payload Stone
|
|
1060
|
+
const ackData: SyncSagaMessageAckData_V1 = {
|
|
1061
|
+
sagaId,
|
|
1062
|
+
stage: SyncStage.ack,
|
|
1063
|
+
deltaReqAddrs,
|
|
1064
|
+
pushOfferAddrs,
|
|
1065
|
+
knowledgeVector,
|
|
1066
|
+
};
|
|
1067
|
+
if (conflicts?.length > 0) { ackData.conflicts = conflicts; }
|
|
955
1068
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
// SOLUTION:
|
|
964
|
-
// The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
|
|
965
|
-
// `SyncSagaMessageInitData_V1` extends `SyncInitData`.
|
|
966
|
-
// `SyncInitData` has `knowledgeVector`.
|
|
967
|
-
// If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
|
|
968
|
-
|
|
969
|
-
// Let's implement Sender populating `Init.knowledgeVector`.
|
|
970
|
-
// But `SyncSagaCoordinator.startSaga` creates Init.
|
|
971
|
-
|
|
972
|
-
// 3. Create Ack Frame
|
|
973
|
-
const sagaId = sagaIbGib.data!.uuid;
|
|
974
|
-
// Create Payload Stone
|
|
975
|
-
const ackData: SyncSagaMessageAckData_V1 = {
|
|
976
|
-
sagaId,
|
|
977
|
-
stage: SyncStage.ack,
|
|
978
|
-
deltaReqAddrs,
|
|
979
|
-
pushOfferAddrs,
|
|
980
|
-
knowledgeVector,
|
|
981
|
-
conflicts: conflicts.length > 0 ? conflicts : undefined, // Include conflicts if any detected
|
|
982
|
-
};
|
|
1069
|
+
const ackStone = await this.createSyncMsgStone({
|
|
1070
|
+
data: ackData,
|
|
1071
|
+
localSpace: myTempSpace,
|
|
1072
|
+
metaspace,
|
|
1073
|
+
});
|
|
1074
|
+
if (logalot) { console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`); }
|
|
983
1075
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1076
|
+
// Evolve Saga
|
|
1077
|
+
const ackFrame = await this.evolveSyncSagaIbGib({
|
|
1078
|
+
prevSagaIbGib: sagaIbGib,
|
|
1079
|
+
msgStones: [ackStone],
|
|
1080
|
+
sessionIdentity: identity,
|
|
1081
|
+
localSpace: mySpace,
|
|
1082
|
+
metaspace,
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
// if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* we want to push ibgibs to the remote/sender if we have push
|
|
1089
|
+
* offers. an ack frame's payloads, if any, are those push offers
|
|
1090
|
+
*/
|
|
1091
|
+
// let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
|
|
999
1092
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1093
|
+
if (pushOfferAddrs.length > 0) {
|
|
1094
|
+
}
|
|
1002
1095
|
|
|
1003
|
-
|
|
1096
|
+
return {
|
|
1097
|
+
frame: ackFrame,
|
|
1098
|
+
// conflictInfos,
|
|
1099
|
+
// payloadIbGibsDomain,
|
|
1100
|
+
};
|
|
1004
1101
|
|
|
1005
|
-
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1104
|
+
throw error;
|
|
1105
|
+
} finally {
|
|
1106
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
1107
|
+
}
|
|
1006
1108
|
}
|
|
1007
1109
|
|
|
1008
1110
|
/**
|
|
@@ -1031,12 +1133,12 @@ export class SyncSagaCoordinator {
|
|
|
1031
1133
|
tempSpace: IbGibSpaceAny,
|
|
1032
1134
|
metaspace: MetaspaceService,
|
|
1033
1135
|
identity?: KeystoneIbGib_V1,
|
|
1034
|
-
}): Promise<
|
|
1136
|
+
}): Promise<HandleSagaFrameResult | null> {
|
|
1035
1137
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
1036
1138
|
try {
|
|
1037
1139
|
if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
|
|
1038
1140
|
|
|
1039
|
-
const { messageData, } = await this.getStageAndPayloadFromFrame({
|
|
1141
|
+
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
1040
1142
|
const ackData = messageData as SyncSagaMessageAckData_V1;
|
|
1041
1143
|
|
|
1042
1144
|
if (!ackData) {
|
|
@@ -1130,62 +1232,7 @@ export class SyncSagaCoordinator {
|
|
|
1130
1232
|
// PULL these frames from Peer into Local Space
|
|
1131
1233
|
// (Validation: We trust peer for now / verification happens on put)
|
|
1132
1234
|
for (const addr of receiverOnlyAddrs) {
|
|
1133
|
-
|
|
1134
|
-
// The Coordinator 'peer' passed in 'sync()' might be needed here?
|
|
1135
|
-
// Wait, `handleAckFrame` doesn't have reference to `peer`?
|
|
1136
|
-
// It only has `space`, `metaspace`.
|
|
1137
|
-
// The `peer` is held by the `executeSagaLoop`.
|
|
1138
|
-
|
|
1139
|
-
// PROBLEM: `handleAckFrame` is pure logic on the Space/Data?
|
|
1140
|
-
// No, it's a method on Coordinator.
|
|
1141
|
-
// But `executeSagaLoop` calls it.
|
|
1142
|
-
// We might need to return "Requirements" to the loop?
|
|
1143
|
-
|
|
1144
|
-
// Checking return type: `{ frame: SyncIbGib_V1, payloadIbGibs?: ... }`
|
|
1145
|
-
// It returns the NEXT frame (Delta).
|
|
1146
|
-
|
|
1147
|
-
// If we need to fetch data, we are blocked.
|
|
1148
|
-
// We can't easily "Pull" here without the Peer reference.
|
|
1149
|
-
|
|
1150
|
-
// OPTION A: Pass `peer` to `handleAckFrame`.
|
|
1151
|
-
// OPTION B: Return a strict list of "MissingDeps" and let Loop handle it.
|
|
1152
|
-
|
|
1153
|
-
// Let's assume we can resolve this by adding `peer` to signature or using `metaspace` if it's a peer-witness?
|
|
1154
|
-
// No, Peer is ephemeral connection.
|
|
1155
|
-
|
|
1156
|
-
// Let's add `peer` to `handleAckFrame` signature?
|
|
1157
|
-
// It breaks the pattern of just handling frame + space.
|
|
1158
|
-
|
|
1159
|
-
// ALTERNATIVE: Use the `Delta` frame to request data?
|
|
1160
|
-
// `SyncSagaMessageDeltaData` has `requests?: string[]`.
|
|
1161
|
-
// Sender sends Delta Frame.
|
|
1162
|
-
// Does Receiver handle Delta Requests?
|
|
1163
|
-
// `handleDeltaFrame` (Receiver) -> checks `requests`.
|
|
1164
|
-
// YES.
|
|
1165
|
-
|
|
1166
|
-
// So Sender puts `receiverOnlyAddrs` into `deltaFrame.requests`.
|
|
1167
|
-
// Receiver sees them, fetches them, and includes them in the Response (Commit?).
|
|
1168
|
-
// Wait, Init->Ack->Delta->Commit.
|
|
1169
|
-
// If Receiver sends data in Commit, that's "too late" for Sender to Merge in THIS saga round?
|
|
1170
|
-
// Unless Commit is not the end?
|
|
1171
|
-
|
|
1172
|
-
// Or we do a "Delta 2" loop?
|
|
1173
|
-
|
|
1174
|
-
// "Iterative Resolution Loop" from plan.
|
|
1175
|
-
// If we request data in Delta, Receiver sends it in Commit (or Delta-Response).
|
|
1176
|
-
// Sender gets Commit. Sees data. Merges.
|
|
1177
|
-
// Then Sender needs to Send the MERGE result.
|
|
1178
|
-
// Needs another Push/Delta.
|
|
1179
|
-
|
|
1180
|
-
// REFINED FLOW:
|
|
1181
|
-
// 1. Sender sends Delta Frame with `requests: [receiverOnlyAddrs]`.
|
|
1182
|
-
// 2. Receiver responds (Commit? or Ack 2?) with Payload (Divergent Frames).
|
|
1183
|
-
// 3. Sender handles response -> Merges.
|
|
1184
|
-
// 4. Sender sends Commit (containing Merge Frame).
|
|
1185
|
-
|
|
1186
|
-
// Issue: Current state machine is Init->Ack->Delta->Commit.
|
|
1187
|
-
// We need to keep Saga open.
|
|
1188
|
-
// If Sender sends Delta with requests, does it transition to Commit?
|
|
1235
|
+
console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
|
|
1189
1236
|
}
|
|
1190
1237
|
|
|
1191
1238
|
// Compute DELTA dependencies for each receiver-only frame
|
|
@@ -1354,24 +1401,26 @@ export class SyncSagaCoordinator {
|
|
|
1354
1401
|
|
|
1355
1402
|
const deltaStone = await this.createSyncMsgStone({
|
|
1356
1403
|
data: deltaData,
|
|
1357
|
-
|
|
1404
|
+
localSpace: tempSpace,
|
|
1358
1405
|
metaspace,
|
|
1359
1406
|
});
|
|
1360
1407
|
|
|
1361
1408
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1362
1409
|
prevSagaIbGib: sagaIbGib,
|
|
1363
1410
|
msgStones: [deltaStone],
|
|
1364
|
-
identity,
|
|
1365
|
-
|
|
1411
|
+
sessionIdentity: identity,
|
|
1412
|
+
localSpace: tempSpace,
|
|
1366
1413
|
metaspace,
|
|
1367
1414
|
});
|
|
1368
1415
|
|
|
1369
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1370
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1371
|
-
|
|
1372
1416
|
if (logalot) { console.log(`${lc} Delta Frame created. Rel8ns: ${JSON.stringify(deltaFrame.rel8ns)}`); }
|
|
1373
1417
|
|
|
1374
|
-
|
|
1418
|
+
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1419
|
+
const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
|
|
1420
|
+
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1421
|
+
|
|
1422
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1423
|
+
return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1375
1424
|
} catch (error) {
|
|
1376
1425
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1377
1426
|
throw error;
|
|
@@ -1404,11 +1453,11 @@ export class SyncSagaCoordinator {
|
|
|
1404
1453
|
tempSpace: IbGibSpaceAny,
|
|
1405
1454
|
metaspace: MetaspaceService,
|
|
1406
1455
|
identity?: KeystoneIbGib_V1,
|
|
1407
|
-
}): Promise<
|
|
1456
|
+
}): Promise<HandleSagaFrameResult | null> {
|
|
1408
1457
|
const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
|
|
1409
1458
|
if (logalot) { console.log(`${lc} starting...`); }
|
|
1410
1459
|
|
|
1411
|
-
const { messageData } = await this.getStageAndPayloadFromFrame({
|
|
1460
|
+
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
1412
1461
|
const deltaData = messageData as SyncSagaMessageDeltaData_V1;
|
|
1413
1462
|
|
|
1414
1463
|
if (!deltaData) {
|
|
@@ -1614,22 +1663,24 @@ export class SyncSagaCoordinator {
|
|
|
1614
1663
|
|
|
1615
1664
|
const deltaStone = await this.createSyncMsgStone({
|
|
1616
1665
|
data: responseDeltaData,
|
|
1617
|
-
|
|
1666
|
+
localSpace: tempSpace,
|
|
1618
1667
|
metaspace
|
|
1619
1668
|
});
|
|
1620
1669
|
|
|
1621
1670
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1622
1671
|
prevSagaIbGib: sagaIbGib,
|
|
1623
1672
|
msgStones: [deltaStone],
|
|
1624
|
-
identity,
|
|
1625
|
-
|
|
1673
|
+
sessionIdentity: identity,
|
|
1674
|
+
localSpace: tempSpace,
|
|
1626
1675
|
metaspace
|
|
1627
1676
|
});
|
|
1628
1677
|
|
|
1629
|
-
//
|
|
1630
|
-
|
|
1678
|
+
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1679
|
+
const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
|
|
1680
|
+
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1631
1681
|
|
|
1632
|
-
return { frame: deltaFrame,
|
|
1682
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1683
|
+
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1633
1684
|
|
|
1634
1685
|
} else {
|
|
1635
1686
|
// We have nothing to send.
|
|
@@ -1644,22 +1695,24 @@ export class SyncSagaCoordinator {
|
|
|
1644
1695
|
|
|
1645
1696
|
const commitStone = await this.createSyncMsgStone({
|
|
1646
1697
|
data: commitData,
|
|
1647
|
-
|
|
1698
|
+
localSpace: tempSpace,
|
|
1648
1699
|
metaspace
|
|
1649
1700
|
});
|
|
1650
1701
|
|
|
1651
1702
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1652
1703
|
prevSagaIbGib: sagaIbGib,
|
|
1653
1704
|
msgStones: [commitStone],
|
|
1654
|
-
identity,
|
|
1655
|
-
|
|
1705
|
+
sessionIdentity: identity,
|
|
1706
|
+
localSpace: tempSpace,
|
|
1656
1707
|
metaspace
|
|
1657
1708
|
});
|
|
1658
1709
|
|
|
1659
|
-
//
|
|
1660
|
-
|
|
1710
|
+
// Build control payloads for commit
|
|
1711
|
+
const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
|
|
1712
|
+
if (identity) { commitCtrlPayloads.push(identity); }
|
|
1661
1713
|
|
|
1662
|
-
return { frame: commitFrame,
|
|
1714
|
+
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1715
|
+
return { frame: commitFrame, };
|
|
1663
1716
|
|
|
1664
1717
|
} else {
|
|
1665
1718
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1674,21 +1727,18 @@ export class SyncSagaCoordinator {
|
|
|
1674
1727
|
|
|
1675
1728
|
const deltaStone = await this.createSyncMsgStone({
|
|
1676
1729
|
data: responseDeltaData,
|
|
1677
|
-
|
|
1730
|
+
localSpace: tempSpace,
|
|
1678
1731
|
metaspace
|
|
1679
1732
|
});
|
|
1680
1733
|
|
|
1681
1734
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1682
1735
|
prevSagaIbGib: sagaIbGib,
|
|
1683
1736
|
msgStones: [deltaStone],
|
|
1684
|
-
identity,
|
|
1685
|
-
|
|
1737
|
+
sessionIdentity: identity,
|
|
1738
|
+
localSpace: tempSpace,
|
|
1686
1739
|
metaspace
|
|
1687
1740
|
});
|
|
1688
1741
|
|
|
1689
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1690
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1691
|
-
|
|
1692
1742
|
// Check if PEER proposed commit
|
|
1693
1743
|
if (deltaData.proposeCommit) {
|
|
1694
1744
|
if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
|
|
@@ -1703,25 +1753,32 @@ export class SyncSagaCoordinator {
|
|
|
1703
1753
|
|
|
1704
1754
|
const commitStone = await this.createSyncMsgStone({
|
|
1705
1755
|
data: commitData,
|
|
1706
|
-
|
|
1756
|
+
localSpace: tempSpace,
|
|
1707
1757
|
metaspace
|
|
1708
1758
|
});
|
|
1709
1759
|
|
|
1710
1760
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1711
1761
|
prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
|
|
1712
1762
|
msgStones: [commitStone],
|
|
1713
|
-
identity,
|
|
1714
|
-
|
|
1763
|
+
sessionIdentity: identity,
|
|
1764
|
+
localSpace: tempSpace,
|
|
1715
1765
|
metaspace
|
|
1716
1766
|
});
|
|
1717
1767
|
|
|
1718
|
-
//
|
|
1719
|
-
|
|
1768
|
+
// Build control payloads for commit
|
|
1769
|
+
const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
|
|
1770
|
+
if (identity) { commitCtrlPayloads2.push(identity); }
|
|
1720
1771
|
|
|
1721
|
-
return { frame: commitFrame,
|
|
1772
|
+
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1773
|
+
return { frame: commitFrame, };
|
|
1722
1774
|
}
|
|
1723
1775
|
|
|
1724
|
-
|
|
1776
|
+
// Build control payloads for delta propose
|
|
1777
|
+
const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
|
|
1778
|
+
if (identity) { deltaCtrlPayloads.push(identity); }
|
|
1779
|
+
|
|
1780
|
+
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1781
|
+
return { frame: deltaFrame, };
|
|
1725
1782
|
}
|
|
1726
1783
|
}
|
|
1727
1784
|
}
|
|
@@ -1739,7 +1796,7 @@ export class SyncSagaCoordinator {
|
|
|
1739
1796
|
tempSpace: IbGibSpaceAny,
|
|
1740
1797
|
metaspace: MetaspaceService,
|
|
1741
1798
|
identity?: KeystoneIbGib_V1,
|
|
1742
|
-
}): Promise<
|
|
1799
|
+
}): Promise<HandleSagaFrameResult | null> {
|
|
1743
1800
|
const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
|
|
1744
1801
|
if (logalot) { console.log(`${lc} Commit received.`); }
|
|
1745
1802
|
|
|
@@ -1761,11 +1818,11 @@ export class SyncSagaCoordinator {
|
|
|
1761
1818
|
|
|
1762
1819
|
protected async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
|
|
1763
1820
|
data,
|
|
1764
|
-
|
|
1821
|
+
localSpace,
|
|
1765
1822
|
metaspace,
|
|
1766
1823
|
}: {
|
|
1767
1824
|
data: TStoneData,
|
|
1768
|
-
|
|
1825
|
+
localSpace: IbGibSpaceAny,
|
|
1769
1826
|
metaspace: MetaspaceService,
|
|
1770
1827
|
}): Promise<IbGib_V1<TStoneData>> {
|
|
1771
1828
|
const lc = `${this.lc}[${this.createSyncMsgStone.name}]`;
|
|
@@ -1780,7 +1837,7 @@ export class SyncSagaCoordinator {
|
|
|
1780
1837
|
uuid: true, // we want the stone to have its own uniqueness
|
|
1781
1838
|
});
|
|
1782
1839
|
if (logalot) { console.log(`${lc} Created stone: ${getIbGibAddr({ ibGib: stone })}`); }
|
|
1783
|
-
await
|
|
1840
|
+
await metaspace.put({ ibGib: stone, space: localSpace });
|
|
1784
1841
|
await metaspace.registerNewIbGib({ ibGib: stone });
|
|
1785
1842
|
return stone as IbGib_V1<TStoneData>;
|
|
1786
1843
|
} catch (error) {
|
|
@@ -1792,67 +1849,23 @@ export class SyncSagaCoordinator {
|
|
|
1792
1849
|
}
|
|
1793
1850
|
|
|
1794
1851
|
|
|
1795
|
-
/**
|
|
1796
|
-
* Ensures saga frame and its msg stone(s) are in BOTH spaces for audit trail.
|
|
1797
|
-
* Control ibgibs (saga frames, msg stones, identity) must be in both destSpace and tempSpace.
|
|
1798
|
-
*/
|
|
1799
|
-
protected async ensureSagaFrameInBothSpaces({
|
|
1800
|
-
frame,
|
|
1801
|
-
destSpace,
|
|
1802
|
-
tempSpace,
|
|
1803
|
-
metaspace,
|
|
1804
|
-
}: {
|
|
1805
|
-
frame: SyncIbGib_V1,
|
|
1806
|
-
destSpace: IbGibSpaceAny,
|
|
1807
|
-
tempSpace: IbGibSpaceAny,
|
|
1808
|
-
metaspace: MetaspaceService,
|
|
1809
|
-
}): Promise<void> {
|
|
1810
|
-
// Frame itself (already in tempSpace from creation, need in destSpace for audit)
|
|
1811
|
-
await putInSpace({ space: destSpace, ibGib: frame });
|
|
1812
|
-
await metaspace.registerNewIbGib({ ibGib: frame });
|
|
1813
|
-
|
|
1814
|
-
// Msg stone(s) (already in tempSpace, need in destSpace for audit)
|
|
1815
|
-
const msgStoneAddrs = frame.rel8ns?.[SYNC_MSG_REL8N_NAME];
|
|
1816
|
-
if (msgStoneAddrs && msgStoneAddrs.length > 0) {
|
|
1817
|
-
const resMsgStones = await getFromSpace({ space: tempSpace, addrs: msgStoneAddrs });
|
|
1818
|
-
if (resMsgStones.ibGibs) {
|
|
1819
|
-
for (const msgStone of resMsgStones.ibGibs) {
|
|
1820
|
-
await putInSpace({ space: destSpace, ibGib: msgStone });
|
|
1821
|
-
await metaspace.registerNewIbGib({ ibGib: msgStone });
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
// Identity (if present, already in tempSpace, need in destSpace)
|
|
1827
|
-
const identityAddrs = frame.rel8ns?.identity;
|
|
1828
|
-
if (identityAddrs && identityAddrs.length > 0) {
|
|
1829
|
-
const resIdentity = await getFromSpace({ space: tempSpace, addrs: identityAddrs });
|
|
1830
|
-
if (resIdentity.ibGibs) {
|
|
1831
|
-
for (const identity of resIdentity.ibGibs) {
|
|
1832
|
-
await putInSpace({ space: destSpace, ibGib: identity });
|
|
1833
|
-
await metaspace.registerNewIbGib({ ibGib: identity });
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
1852
|
/**
|
|
1840
1853
|
* Evolves the saga timeline with a new frame.
|
|
1841
1854
|
*/
|
|
1842
1855
|
protected async evolveSyncSagaIbGib({
|
|
1843
1856
|
prevSagaIbGib,
|
|
1857
|
+
conflictStrategy,
|
|
1844
1858
|
msgStones,
|
|
1845
|
-
|
|
1846
|
-
|
|
1859
|
+
sessionIdentity,
|
|
1860
|
+
localSpace,
|
|
1847
1861
|
metaspace,
|
|
1848
|
-
conflictStrategy,
|
|
1849
1862
|
}: {
|
|
1850
1863
|
prevSagaIbGib?: SyncIbGib_V1,
|
|
1864
|
+
conflictStrategy?: SyncConflictStrategy,
|
|
1851
1865
|
msgStones: IbGib_V1[],
|
|
1852
|
-
|
|
1853
|
-
space: IbGibSpaceAny,
|
|
1866
|
+
localSpace: IbGibSpaceAny,
|
|
1854
1867
|
metaspace: MetaspaceService,
|
|
1855
|
-
|
|
1868
|
+
sessionIdentity?: KeystoneIbGib_V1,
|
|
1856
1869
|
}): Promise<SyncIbGib_V1> {
|
|
1857
1870
|
const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
|
|
1858
1871
|
try {
|
|
@@ -1878,10 +1891,9 @@ export class SyncSagaCoordinator {
|
|
|
1878
1891
|
}
|
|
1879
1892
|
}
|
|
1880
1893
|
|
|
1881
|
-
const identityAddr =
|
|
1894
|
+
const identityAddr = sessionIdentity ? getIbGibAddr({ ibGib: sessionIdentity }) : undefined;
|
|
1882
1895
|
|
|
1883
1896
|
if (prevSagaIbGib) {
|
|
1884
|
-
|
|
1885
1897
|
/**
|
|
1886
1898
|
* rel8ns always include the new msg stone(s)
|
|
1887
1899
|
*/
|
|
@@ -1893,8 +1905,8 @@ export class SyncSagaCoordinator {
|
|
|
1893
1905
|
];
|
|
1894
1906
|
|
|
1895
1907
|
// if we're authenticating/signing, we'll have identity
|
|
1896
|
-
if (
|
|
1897
|
-
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [
|
|
1908
|
+
if (sessionIdentity) {
|
|
1909
|
+
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1898
1910
|
}
|
|
1899
1911
|
|
|
1900
1912
|
// remove the existing sync msg stones' addrs
|
|
@@ -1915,7 +1927,7 @@ export class SyncSagaCoordinator {
|
|
|
1915
1927
|
rel8nInfos,
|
|
1916
1928
|
rel8nRemovalInfos,
|
|
1917
1929
|
metaspace,
|
|
1918
|
-
space,
|
|
1930
|
+
space: localSpace,
|
|
1919
1931
|
noDna: true, // Explicitly no DNA for sync frames
|
|
1920
1932
|
});
|
|
1921
1933
|
|
|
@@ -1923,33 +1935,33 @@ export class SyncSagaCoordinator {
|
|
|
1923
1935
|
return newFrame;
|
|
1924
1936
|
} else {
|
|
1925
1937
|
// Create New Timeline (Root Frame)
|
|
1938
|
+
// data
|
|
1926
1939
|
const data: SyncData_V1 = {
|
|
1927
1940
|
uuid: sagaId,
|
|
1928
|
-
// stage, // Removed from V1
|
|
1929
|
-
payload: undefined, // Data in stone
|
|
1930
1941
|
n: 0,
|
|
1931
1942
|
isTjp: true,
|
|
1932
1943
|
conflictStrategy,
|
|
1933
1944
|
};
|
|
1945
|
+
// ib
|
|
1934
1946
|
const ib = await getSyncIb({ data });
|
|
1935
|
-
|
|
1947
|
+
// rel8ns
|
|
1936
1948
|
const stoneAddrs = msgStones.map(s => getIbGibAddr({ ibGib: s }));
|
|
1937
|
-
const rel8ns:
|
|
1938
|
-
|
|
1939
|
-
};
|
|
1940
|
-
if (identityAddr) {
|
|
1941
|
-
rel8ns.identity = [identityAddr];
|
|
1942
|
-
}
|
|
1949
|
+
const rel8ns: SyncRel8ns_V1 = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
|
|
1950
|
+
if (identityAddr) { rel8ns.identity = [identityAddr]; }
|
|
1943
1951
|
|
|
1944
1952
|
const resNew = await createTimeline({
|
|
1945
|
-
space,
|
|
1953
|
+
space: localSpace,
|
|
1946
1954
|
metaspace,
|
|
1947
1955
|
ib,
|
|
1948
1956
|
data,
|
|
1949
1957
|
rel8ns,
|
|
1950
1958
|
parentIb: SYNC_ATOM, // "sync"
|
|
1959
|
+
/**
|
|
1960
|
+
* this will squash the result into a single frame
|
|
1961
|
+
*/
|
|
1951
1962
|
noDna: true,
|
|
1952
1963
|
});
|
|
1964
|
+
if (!!resNew.intermediateIbGibs) { throw new Error(`(UNEXPECTED) resNew.intermediateIbGibs? we were assuming we were creating a single frame. (E: 456818147235d991ccb4d10d0bbe4826)`); }
|
|
1953
1965
|
|
|
1954
1966
|
return resNew.newIbGib as SyncIbGib_V1;
|
|
1955
1967
|
}
|
|
@@ -1960,14 +1972,20 @@ export class SyncSagaCoordinator {
|
|
|
1960
1972
|
}
|
|
1961
1973
|
}
|
|
1962
1974
|
|
|
1963
|
-
protected async getStageAndPayloadFromFrame({
|
|
1975
|
+
protected async getStageAndPayloadFromFrame({
|
|
1976
|
+
sagaFrame,
|
|
1977
|
+
space
|
|
1978
|
+
}: {
|
|
1979
|
+
sagaFrame: IbGib_V1,
|
|
1980
|
+
space: IbGibSpaceAny
|
|
1981
|
+
}): Promise<{ stage: SyncStage, messageData: unknown }> {
|
|
1964
1982
|
const lc = `${this.lc}[${this.getStageAndPayloadFromFrame.name}]`;
|
|
1965
1983
|
try {
|
|
1966
1984
|
if (logalot) { console.log(`${lc} starting... (I: fddc287bd4d55253dc50e519fd352226)`); }
|
|
1967
1985
|
|
|
1968
|
-
if (!
|
|
1986
|
+
if (!sagaFrame.rel8ns) { throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`); }
|
|
1969
1987
|
|
|
1970
|
-
const stoneAddrs =
|
|
1988
|
+
const stoneAddrs = sagaFrame.rel8ns?.[SYNC_MSG_REL8N_NAME];
|
|
1971
1989
|
if (stoneAddrs && stoneAddrs.length > 0) {
|
|
1972
1990
|
const stoneAddr = stoneAddrs[0];
|
|
1973
1991
|
const res = await getFromSpace({ addr: stoneAddr, space });
|
|
@@ -1980,7 +1998,7 @@ export class SyncSagaCoordinator {
|
|
|
1980
1998
|
throw new Error(`couldn't get stoneAddr (${stoneAddr}) from space (${space.ib}) (E: f3c826e756f8a5c358beb88aa4a65e26)`);
|
|
1981
1999
|
}
|
|
1982
2000
|
} else {
|
|
1983
|
-
throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame?
|
|
2001
|
+
throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? sagaFrame addr: ${getIbGibAddr(sagaFrame)} (E: 43eae8579e289d016741b5526052f226)`);
|
|
1984
2002
|
}
|
|
1985
2003
|
} catch (error) {
|
|
1986
2004
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|