@ibgib/core-gib 0.1.22 → 0.1.25
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 +25 -0
- package/dist/common/other/graph-helper.d.mts.map +1 -1
- package/dist/common/other/graph-helper.mjs +75 -1
- package/dist/common/other/graph-helper.mjs.map +1 -1
- package/dist/sync/graft-info/graft-info-helpers.mjs +2 -2
- package/dist/sync/graft-info/graft-info-helpers.mjs.map +1 -1
- package/dist/sync/sync-conflict.respec.mjs +8 -12
- package/dist/sync/sync-conflict.respec.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +14 -4
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +15 -3
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +22 -15
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +159 -90
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.respec.mjs +8 -9
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +8 -9
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +8 -9
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +8 -9
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +8 -9
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +6 -7
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts +2 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs +4 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +0 -15
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +30 -16
- 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 +47 -79
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +40 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +47 -14
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +188 -144
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +39 -3
- 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 +137 -31
- 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 +5 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +81 -77
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +608 -597
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +154 -26
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +87 -92
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +6 -2
- 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/package.json +1 -1
- package/src/common/other/graph-helper.mts +79 -1
- package/src/sync/graft-info/graft-info-helpers.mts +3 -3
- package/src/sync/sync-conflict.respec.mts +8 -14
- package/src/sync/sync-constants.mts +15 -4
- package/src/sync/sync-helpers.mts +173 -101
- package/src/sync/sync-innerspace-constants.respec.mts +8 -9
- package/src/sync/sync-innerspace-deep-updates.respec.mts +8 -9
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +8 -9
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +8 -9
- package/src/sync/sync-innerspace-partial-update.respec.mts +9 -12
- package/src/sync/sync-innerspace.respec.mts +6 -7
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mts +7 -0
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +0 -15
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +72 -96
- package/src/sync/sync-peer/sync-peer-types.mts +43 -2
- package/src/sync/sync-peer/sync-peer-v1.mts +215 -142
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +145 -37
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +5 -0
- package/src/sync/sync-saga-coordinator.mts +680 -714
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +160 -24
- package/src/sync/sync-types.mts +96 -105
- package/src/timeline/timeline-api.mts +17 -10
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { extractErrorMsg, getUUID, // so our timestamp in ticks as a string are uniform
|
|
2
|
-
pretty, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
2
|
+
pretty, delay, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
3
3
|
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
4
|
-
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
|
|
5
4
|
import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
|
|
6
5
|
import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space-helper.mjs";
|
|
7
6
|
import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
|
|
8
7
|
import { appendToTimeline, createTimeline } from "../timeline/timeline-api.mjs";
|
|
9
|
-
import { SyncMode, } from "./sync-types.mjs";
|
|
10
|
-
import { getSyncIb, isPastFrame } from "./sync-helpers.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import { getDependencyGraph } from "../common/other/graph-helper.mjs";
|
|
8
|
+
import { SyncConflictStrategy, SyncMode, SYNC_CONFLICT_STRATEGY_VALID_VALUES, } from "./sync-types.mjs";
|
|
9
|
+
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
10
|
+
import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
13
11
|
import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
14
12
|
import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
|
|
15
|
-
import {
|
|
13
|
+
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
|
|
14
|
+
import { createSyncSagaContext, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
16
15
|
import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
|
|
17
16
|
import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
|
|
18
17
|
import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
19
18
|
import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
|
|
20
|
-
import { SyncPeerInnerspace_V1 } from "./sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs";
|
|
21
19
|
// const logalot = GLOBAL_LOG_A_LOT || true;
|
|
22
20
|
const logalot = false;
|
|
23
21
|
const logalotControlDomain = true;
|
|
@@ -54,14 +52,13 @@ export class SyncSagaCoordinator {
|
|
|
54
52
|
* @param opts.domainIbGibs - The root ibgibs defining the scope of the sync.
|
|
55
53
|
* @param opts.useSessionIdentity - (Optional) Whether to create an ephemeral session identity. Default: true.
|
|
56
54
|
*/
|
|
57
|
-
async sync({ peer,
|
|
55
|
+
async sync({ peer, domainIbGibs, conflictStrategy = SyncConflictStrategy.abort, useSessionIdentity = true, metaspace, localSpace, }) {
|
|
58
56
|
const lc = `${this.lc}[${this.sync.name}]`;
|
|
59
57
|
if (logalot) {
|
|
60
58
|
console.log(`${lc} starting...`);
|
|
61
59
|
}
|
|
62
|
-
const localSpace = (_source || _localSpace);
|
|
63
60
|
if (!localSpace) {
|
|
64
|
-
throw new Error(`${lc} source (or localSpace) required (E:
|
|
61
|
+
throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`);
|
|
65
62
|
}
|
|
66
63
|
// 1. SETUP SAGA METADATA
|
|
67
64
|
const sagaId = await getUUID();
|
|
@@ -77,10 +74,6 @@ export class SyncSagaCoordinator {
|
|
|
77
74
|
resolveDone = resolve;
|
|
78
75
|
rejectDone = reject;
|
|
79
76
|
});
|
|
80
|
-
async function getTempSpaceName() {
|
|
81
|
-
const uuid = await getUUID();
|
|
82
|
-
return `tmp_sync_space_${uuid.substring(0, 8)}`;
|
|
83
|
-
}
|
|
84
77
|
// WORKING CONTEXT (Transactional)
|
|
85
78
|
const tempSpaceName = await getTempSpaceName();
|
|
86
79
|
const tempSpace = await metaspace.createNewLocalSpace({
|
|
@@ -102,33 +95,22 @@ export class SyncSagaCoordinator {
|
|
|
102
95
|
// Async execution wrapper
|
|
103
96
|
(async () => {
|
|
104
97
|
try {
|
|
105
|
-
//
|
|
98
|
+
// BOOTSTRAP IDENTITY (Session Keystone)
|
|
106
99
|
const sessionIdentity = useSessionIdentity
|
|
107
100
|
? await this.getSessionIdentity({ sagaId, metaspace, tempSpace })
|
|
108
101
|
: undefined;
|
|
109
102
|
// if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
|
|
110
|
-
//
|
|
111
|
-
const {
|
|
103
|
+
// CREATE INITIAL FRAME (Stage.init)
|
|
104
|
+
const { initFrame, initDomainGraph } = await this.createInitFrame({
|
|
112
105
|
sagaId,
|
|
113
106
|
sessionIdentity,
|
|
114
|
-
localSpace,
|
|
115
107
|
domainIbGibs,
|
|
116
|
-
|
|
117
|
-
metaspace,
|
|
118
|
-
conflictStrategy
|
|
108
|
+
conflictStrategy,
|
|
109
|
+
metaspace, localSpace, tempSpace,
|
|
119
110
|
});
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!peer.data) {
|
|
124
|
-
throw new Error(`(UNEXPECTED) peer.data falsy? (E: 8546a884c82ffb1999e95d9867da2826)`);
|
|
125
|
-
}
|
|
126
|
-
if (peer.data.classname === SyncPeerInnerspace_V1.name) {
|
|
127
|
-
// (peer as SyncPeerInnerspace_V1).senderTempSpace = tempSpace;
|
|
128
|
-
}
|
|
129
|
-
const syncedIbGibs = await this.executeSagaLoop({
|
|
130
|
-
initialFrame: initFrame,
|
|
131
|
-
srcGraph,
|
|
111
|
+
// KICK OFF THE PING-PONG SAGA LOOP (FSM)
|
|
112
|
+
await this.executeSagaLoop({
|
|
113
|
+
initFrame, initDomainGraph,
|
|
132
114
|
peer,
|
|
133
115
|
sessionIdentity,
|
|
134
116
|
updates$,
|
|
@@ -136,13 +118,6 @@ export class SyncSagaCoordinator {
|
|
|
136
118
|
tempSpace,
|
|
137
119
|
metaspace
|
|
138
120
|
});
|
|
139
|
-
// 5. MERGE RESULT (Optimistic Commit)
|
|
140
|
-
if (syncedIbGibs && syncedIbGibs.length > 0) {
|
|
141
|
-
if (logalot) {
|
|
142
|
-
console.log(`${lc} Merging ${syncedIbGibs.length} synced ibGibs to localSpace...`);
|
|
143
|
-
}
|
|
144
|
-
await putInSpace({ space: localSpace, ibGibs: syncedIbGibs });
|
|
145
|
-
}
|
|
146
121
|
resolveDone();
|
|
147
122
|
if (!updates$.complete) {
|
|
148
123
|
throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`);
|
|
@@ -205,139 +180,98 @@ export class SyncSagaCoordinator {
|
|
|
205
180
|
* **Execution Context**: **Sender (Local)**.
|
|
206
181
|
*
|
|
207
182
|
* This method manages the "Ping Pong" request-response cycle on the Sender.
|
|
208
|
-
* It sends frames via the Peer Witness and processes the responses using `
|
|
183
|
+
* It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
|
|
209
184
|
*
|
|
210
185
|
* **Data Transport Note**:
|
|
211
186
|
* Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
|
|
212
|
-
* When `
|
|
187
|
+
* When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
|
|
213
188
|
* the NEXT request context.
|
|
214
189
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
215
190
|
*/
|
|
216
|
-
async executeSagaLoop({
|
|
191
|
+
async executeSagaLoop({ initFrame, initDomainGraph, peer, sessionIdentity, updates$, localSpace, tempSpace, metaspace }) {
|
|
217
192
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
218
|
-
|
|
219
|
-
let currentFrame =
|
|
220
|
-
|
|
193
|
+
/** The current frame we just generated (e.g., Init or Delta Request) */
|
|
194
|
+
let currentFrame = initFrame;
|
|
195
|
+
/** The **domain** payload we need to transmit (data we are sending) */
|
|
221
196
|
let nextDomainIbGibs = [];
|
|
222
|
-
// Accumulator for all data we've successfully pulled from the remote
|
|
223
|
-
const allReceivedIbGibs = [];
|
|
224
197
|
while (currentFrame) {
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const payloadIbGibsDomain = [];
|
|
234
|
-
// A. Payload (Standard Deep Deps)
|
|
235
|
-
for (const nextDomainIbGib of nextDomainIbGibs) {
|
|
236
|
-
let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
|
|
237
|
-
if (!nextDomainIbGibGraph) {
|
|
238
|
-
nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
|
|
239
|
-
}
|
|
240
|
-
if (nextDomainIbGibGraph) {
|
|
241
|
-
payloadIbGibsDomain.push(...Object.values(nextDomainIbGibGraph));
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
if (logalot) {
|
|
248
|
-
console.log(`${lc} payloadIbGibsDomain count: ${payloadIbGibsDomain.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`);
|
|
249
|
-
}
|
|
250
|
-
// B. Frames (Shallow Sync Deps)
|
|
251
|
-
if (currentFrame) {
|
|
252
|
-
const depsCurrentFrame = await getSyncSagaDependencyGraphForThisFrameOnly({ ibGib: currentFrame, space: tempSpace });
|
|
253
|
-
if (depsCurrentFrame.length > 0) {
|
|
254
|
-
depsCurrentFrame.forEach(x => payloadIbGibsControl.push(x));
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
throw new Error(`(UNEXPECTED) couldn't get deps for currentFrame? currentFrame: ${JSON.stringify(currentFrame)} (E: 06344d07adc80d80b809211171444d26)`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// 2. Create Context (Envelope)
|
|
198
|
+
// first, prepare for context transmission...
|
|
199
|
+
/**
|
|
200
|
+
* this is used later in pollForDomainPayloads **iif** payloads are
|
|
201
|
+
* received **in the response**, i.e., on the return trip. So if we
|
|
202
|
+
* request addrs, the response should have these addrs and their
|
|
203
|
+
* dependencies. The ibgibs corresponding to these addrs will be
|
|
204
|
+
* streamed and placed into this map.
|
|
205
|
+
*/
|
|
261
206
|
const domainPayloadsMap = new Map();
|
|
207
|
+
// #region set up peer observable for any domainPayloadsMap
|
|
262
208
|
const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}));
|
|
293
|
-
}
|
|
294
|
-
// 2b. Request Context
|
|
209
|
+
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
210
|
+
next: async (ibgib) => {
|
|
211
|
+
if (logalot) {
|
|
212
|
+
console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`);
|
|
213
|
+
}
|
|
214
|
+
const addr = getIbGibAddr({ ibGib: ibgib });
|
|
215
|
+
if (logalotControlDomain) {
|
|
216
|
+
console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`);
|
|
217
|
+
}
|
|
218
|
+
domainPayloadsMap.set(addr, ibgib);
|
|
219
|
+
},
|
|
220
|
+
error: async (e) => {
|
|
221
|
+
if (isIbGib(e)) {
|
|
222
|
+
console.error(`${sublc} error fired. error: ${JSON.stringify(e.data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
console.dir(e);
|
|
226
|
+
console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
complete: async () => {
|
|
230
|
+
if (logalot) {
|
|
231
|
+
console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`);
|
|
232
|
+
}
|
|
233
|
+
await subscription.unsubscribe();
|
|
234
|
+
},
|
|
235
|
+
}));
|
|
236
|
+
// #endregion set up peer observable for any domainPayloadsMap
|
|
237
|
+
// ...create/compose the Request Context itself...
|
|
295
238
|
const requestCtx = await createSyncSagaContext({
|
|
296
239
|
sagaFrame: currentFrame,
|
|
297
240
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
298
|
-
|
|
241
|
+
/**
|
|
242
|
+
* init frame: empty
|
|
243
|
+
* ack frame: possible push offers
|
|
244
|
+
* delta frame: requested ibgibs or commit offer
|
|
245
|
+
* commit frame: empty
|
|
246
|
+
*/
|
|
247
|
+
payloadIbGibsDomain: nextDomainIbGibs,
|
|
248
|
+
localSpace,
|
|
299
249
|
});
|
|
300
|
-
// Log what we're sending
|
|
250
|
+
// #region Log what we're sending
|
|
301
251
|
if (logalotControlDomain) {
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
|
|
252
|
+
const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
253
|
+
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
|
|
305
254
|
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
306
255
|
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
307
|
-
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${controlAddrs.length}): ${controlAddrs.join(', ') || '(none)'}`);
|
|
308
256
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
309
257
|
}
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
// if (requestCtx) {
|
|
313
|
-
// const deps = await getSyncSagaDependencyGraphForThisFrameOnly({ ibGib: requestCtx, space: tempSpace });
|
|
314
|
-
// if (deps) { deps.forEach(x => payloadIbGibsControl.push(x)); }
|
|
315
|
-
// }
|
|
316
|
-
// 3. Identity (if exists)
|
|
317
|
-
// Identity might be deep? Keystone? Usually self-contained or shallow references.
|
|
318
|
-
if (sessionIdentity) {
|
|
319
|
-
payloadIbGibsControl.push(sessionIdentity);
|
|
320
|
-
}
|
|
321
|
-
// we only put the **CONTROL** payload ibgibs in localSpace. we
|
|
322
|
-
// don't put any domain ibgibs into this durable space until the
|
|
323
|
-
// final commit phase.
|
|
324
|
-
// The requestCtx envelope itself also goes to localSpace so transfer can find it.
|
|
325
|
-
await putInSpace({ space: localSpace, ibGib: requestCtx });
|
|
326
|
-
if (payloadIbGibsControl.length > 0) {
|
|
327
|
-
await putInSpace({ space: localSpace, ibGibs: payloadIbGibsControl });
|
|
328
|
-
}
|
|
329
|
-
// B. Transmit
|
|
258
|
+
// #endregion Log what we're sending
|
|
259
|
+
// update our saga listeners...
|
|
330
260
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
331
|
-
updates$.next(requestCtx); // spins off
|
|
261
|
+
updates$.next(requestCtx); // spins off for saga UI updates
|
|
262
|
+
// ...And send the context.
|
|
263
|
+
peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
|
|
332
264
|
const responseCtx = await peer.witness(requestCtx);
|
|
333
|
-
//
|
|
265
|
+
// the send returned, but a peer can return a falsy responseCtx, if
|
|
266
|
+
// we just sent a commit to them and they're done. If so, there will
|
|
267
|
+
// necessarily be no payload to wait to receive
|
|
334
268
|
if (!responseCtx) {
|
|
335
269
|
if (currentFrame) {
|
|
336
270
|
// Check for Commit (Peer silence expected)
|
|
337
271
|
const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
|
|
338
272
|
if (msg?.data?.stage === SyncStage.commit) {
|
|
339
273
|
if (logalot) {
|
|
340
|
-
console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete
|
|
274
|
+
console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`);
|
|
341
275
|
}
|
|
342
276
|
currentFrame = null;
|
|
343
277
|
break;
|
|
@@ -350,79 +284,83 @@ export class SyncSagaCoordinator {
|
|
|
350
284
|
throw new Error(`(UNEXPECTED) no response and currentFrame falsy? (E: 8d1085ea2f28cfc3f9c922649864a826)`);
|
|
351
285
|
}
|
|
352
286
|
}
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
287
|
+
// at this point, we did indeed receive a response to analyze. BUT!
|
|
288
|
+
// we have only necessarily received the response context ibgib.
|
|
289
|
+
// if there were payloads expected to be transferred, they may not
|
|
290
|
+
// be done yet.
|
|
356
291
|
if (!responseCtx.data) {
|
|
357
292
|
throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
|
|
358
293
|
}
|
|
359
|
-
updates$.next(responseCtx); // spins off
|
|
294
|
+
updates$.next(responseCtx); // spins off for saga UI updating
|
|
295
|
+
// validate context
|
|
296
|
+
await validateContextAndSagaFrame;
|
|
360
297
|
// Extract expected domain addresses from response context
|
|
361
298
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
|
|
362
|
-
//
|
|
363
|
-
// Poll for them if needed
|
|
299
|
+
// Poll for them if needed. see above jsdocs for domainPayloadsMap
|
|
364
300
|
if (responsePayloadAddrsDomain.length > 0) {
|
|
365
|
-
await this.pollForDomainPayloads({
|
|
301
|
+
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
366
302
|
expectedAddrs: responsePayloadAddrsDomain,
|
|
303
|
+
pollIntervalMs: 20, // relatively arbitrary right now
|
|
367
304
|
domainPayloadsMap,
|
|
368
305
|
tempSpace,
|
|
369
306
|
});
|
|
370
307
|
}
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new Error(`${lc} Peer response missing sagaFrame (E: 83a0)`);
|
|
308
|
+
// #region Log what we received back
|
|
309
|
+
if (!responseCtx.sagaFrame) {
|
|
310
|
+
throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`);
|
|
375
311
|
}
|
|
376
|
-
// Log what we received back
|
|
377
312
|
if (logalotControlDomain) {
|
|
378
313
|
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] || [];
|
|
379
|
-
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I:
|
|
314
|
+
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
|
|
380
315
|
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
381
|
-
console.log(`${lc}${lcControlDomain} Response Frame: ${
|
|
316
|
+
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
382
317
|
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
383
318
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
384
319
|
}
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
sagaIbGib: responseFrame,
|
|
397
|
-
srcGraph: {},
|
|
398
|
-
destSpace: localSpace,
|
|
399
|
-
tempSpace,
|
|
320
|
+
// #endregion Log what we received back
|
|
321
|
+
// at this point, we have received the context AND **all** of the
|
|
322
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
323
|
+
// iteration in the saga loop
|
|
324
|
+
// this is the part that drives the FSM forward, i.e., when we
|
|
325
|
+
// evolve the sync saga ibgib itself to the next frame if we aren't
|
|
326
|
+
// finished/errored out.
|
|
327
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
328
|
+
sagaContext: responseCtx,
|
|
329
|
+
mySpace: localSpace,
|
|
330
|
+
myTempSpace: tempSpace,
|
|
400
331
|
metaspace,
|
|
401
|
-
domainPayloadsMap,
|
|
402
|
-
expectedDomainAddrs: responsePayloadAddrsDomain,
|
|
403
332
|
});
|
|
404
|
-
if (!
|
|
333
|
+
if (!contextResult) {
|
|
405
334
|
if (logalot) {
|
|
406
|
-
console.log(`${lc} Handler returned null (Saga End)
|
|
335
|
+
console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`);
|
|
407
336
|
}
|
|
408
337
|
break;
|
|
409
338
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
339
|
+
// #region error conditions throw
|
|
340
|
+
if (contextResult.errorMsg) {
|
|
341
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
|
|
342
|
+
}
|
|
343
|
+
else if (!contextResult.nextFrameInfo) {
|
|
344
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
345
|
+
}
|
|
346
|
+
else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
347
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
348
|
+
}
|
|
349
|
+
// #endregion error conditions throw
|
|
350
|
+
// we have another frame to process and send out, with possibly
|
|
351
|
+
// payload domain ibgibs as well
|
|
352
|
+
const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
|
|
353
|
+
currentFrame = frame;
|
|
354
|
+
nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
|
|
355
|
+
// #region Log handler output for next iteration
|
|
415
356
|
if (logalotControlDomain) {
|
|
416
|
-
const handlerControlAddrs = (handleResult.payloadIbGibsControl || []).map(p => getIbGibAddr({ ibGib: p }));
|
|
417
357
|
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
418
|
-
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I:
|
|
358
|
+
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
|
|
419
359
|
console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
420
|
-
console.log(`${lc}${lcControlDomain} CONTROL for next (${handlerControlAddrs.length}): ${handlerControlAddrs.join(', ') || '(none)'} (saved via ensureSagaFrameInBothSpaces)`);
|
|
421
360
|
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
422
361
|
}
|
|
423
|
-
//
|
|
362
|
+
// #endregion Log handler output for next iteration
|
|
424
363
|
}
|
|
425
|
-
return allReceivedIbGibs;
|
|
426
364
|
}
|
|
427
365
|
/**
|
|
428
366
|
* Helper to get Knowledge Vector for specific domain ibGibs or TJPs.
|
|
@@ -481,7 +419,7 @@ export class SyncSagaCoordinator {
|
|
|
481
419
|
}
|
|
482
420
|
const res = await getLatestAddrs({ space, tjpAddrs: tjps });
|
|
483
421
|
if (!res.data || !res.data.latestAddrsMap) {
|
|
484
|
-
throw new Error(`${lc} Failed to get latest addrs. (E:
|
|
422
|
+
throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
|
|
485
423
|
}
|
|
486
424
|
// if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
|
|
487
425
|
return res.data.latestAddrsMap;
|
|
@@ -496,24 +434,27 @@ export class SyncSagaCoordinator {
|
|
|
496
434
|
}
|
|
497
435
|
}
|
|
498
436
|
}
|
|
499
|
-
async
|
|
500
|
-
const lc = `${this.lc}[${this.
|
|
501
|
-
|
|
437
|
+
async analyzeDomainIbGibs({ domainIbGibs, space, }) {
|
|
438
|
+
const lc = `${this.lc}[${this.analyzeDomainIbGibs.name}]`;
|
|
439
|
+
if (domainIbGibs.length === 0) {
|
|
440
|
+
throw new Error(`invalid domainIbGibs. expected at least one, but array is empty. (E: 820a719bcac5a16878a2af697113b826)`);
|
|
441
|
+
}
|
|
442
|
+
const fullGraph = await getDependencyGraph({
|
|
502
443
|
ibGibs: domainIbGibs,
|
|
503
444
|
live: true,
|
|
504
445
|
space,
|
|
505
446
|
});
|
|
506
|
-
const
|
|
447
|
+
const graphIsValid = fullGraph && Object.keys(fullGraph).length > 0;
|
|
507
448
|
if (logalot) {
|
|
508
|
-
console.log(`${lc} graph generated. nodes: ${
|
|
449
|
+
console.log(`${lc} graph generated. nodes: ${graphIsValid ? Object.keys(fullGraph).length : 0}`);
|
|
509
450
|
}
|
|
510
|
-
const srcIbGibs =
|
|
451
|
+
const srcIbGibs = graphIsValid ? Object.values(fullGraph) : [];
|
|
511
452
|
const { mapWithTjp_YesDna: srcMapWithTjp_YesDna, mapWithTjp_NoDna: srcMapWithTjp_NoDna, mapWithoutTjps: src_MapWithoutTjps } = splitPerTjpAndOrDna({ ibGibs: srcIbGibs });
|
|
512
|
-
const
|
|
513
|
-
const
|
|
514
|
-
const
|
|
515
|
-
const
|
|
516
|
-
return {
|
|
453
|
+
const stones = Object.values(src_MapWithoutTjps);
|
|
454
|
+
const withTimelines = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
|
|
455
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: withTimelines });
|
|
456
|
+
const topologicallySortedTjpAddrs = this.sortTimelinesTopologically(timelinesMap);
|
|
457
|
+
return { stones, timelinesMap, topologicallySortedTjpAddrs, fullGraph };
|
|
517
458
|
}
|
|
518
459
|
/**
|
|
519
460
|
* Creates the Initial Saga Frame (Init Stage).
|
|
@@ -524,57 +465,54 @@ export class SyncSagaCoordinator {
|
|
|
524
465
|
* Generates the first frame containing the Knowledge Vector of the Local Space.
|
|
525
466
|
* This is sent to the Receiver to begin Gap Analysis.
|
|
526
467
|
*/
|
|
527
|
-
async createInitFrame({ sagaId, sessionIdentity,
|
|
468
|
+
async createInitFrame({ sagaId, sessionIdentity, domainIbGibs, conflictStrategy, metaspace, localSpace, tempSpace, }) {
|
|
528
469
|
const lc = `${this.lc}[${this.createInitFrame.name}]`;
|
|
529
470
|
try {
|
|
530
471
|
if (logalot) {
|
|
531
472
|
console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`);
|
|
532
473
|
}
|
|
533
474
|
// Analyze Timelines & Stones
|
|
534
|
-
const analysis = await this.
|
|
535
|
-
//
|
|
536
|
-
|
|
537
|
-
|
|
475
|
+
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
476
|
+
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
477
|
+
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
478
|
+
// we need to store the fullGraph in our tempSpace for later...
|
|
479
|
+
await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
|
|
480
|
+
// populate our knowledge vector with tjp -> latest addr/tip in timeline
|
|
481
|
+
const knowledgeVector = {};
|
|
482
|
+
Object.keys(srcTimelinesMap).forEach(tjp => {
|
|
483
|
+
const timeline = srcTimelinesMap[tjp];
|
|
484
|
+
const tip = timeline.at(-1);
|
|
485
|
+
knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
|
|
486
|
+
});
|
|
538
487
|
const initData = {
|
|
539
488
|
sagaId,
|
|
540
489
|
stage: SyncStage.init,
|
|
541
|
-
knowledgeVector
|
|
490
|
+
knowledgeVector,
|
|
542
491
|
identity: sessionIdentity, // KeystoneIbGib is already public data
|
|
543
492
|
mode: SyncMode.sync,
|
|
544
493
|
stones: srcStones.map(s => getIbGibAddr({ ibGib: s })),
|
|
545
494
|
};
|
|
546
|
-
// Populate Knowledge Vector
|
|
547
|
-
Object.keys(srcTimelinesMap).forEach(tjp => {
|
|
548
|
-
const timeline = srcTimelinesMap[tjp];
|
|
549
|
-
const tip = timeline.at(-1);
|
|
550
|
-
initData.knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
|
|
551
|
-
});
|
|
552
495
|
if (logalot) {
|
|
553
496
|
console.log(`${lc} SyncStage.init: ${SyncStage.init}, SyncStage.commit: ${SyncStage.commit}`);
|
|
554
497
|
console.log(`${lc} initData.stage: ${initData.stage}`);
|
|
555
498
|
}
|
|
499
|
+
// create the stone and save/register it
|
|
556
500
|
const initStone = await this.createSyncMsgStone({
|
|
557
501
|
data: initData,
|
|
558
|
-
|
|
559
|
-
metaspace
|
|
502
|
+
localSpace,
|
|
503
|
+
metaspace,
|
|
560
504
|
});
|
|
561
505
|
// if (logalot) { console.log(`${lc} initStone: ${pretty(initStone)} (I: 06e532f8a408549069474e96bed44826)`); }
|
|
506
|
+
// create the initial sync ibgib frame, save/register it
|
|
562
507
|
const sagaFrame = await this.evolveSyncSagaIbGib({
|
|
563
508
|
msgStones: [initStone],
|
|
564
|
-
identity: sessionIdentity,
|
|
565
|
-
space: tempSpace,
|
|
566
|
-
metaspace,
|
|
567
509
|
conflictStrategy,
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
frame: sagaFrame,
|
|
572
|
-
destSpace: localSpace, // localSpace is the Sender's destSpace
|
|
573
|
-
tempSpace,
|
|
574
|
-
metaspace
|
|
510
|
+
sessionIdentity: sessionIdentity,
|
|
511
|
+
metaspace,
|
|
512
|
+
localSpace,
|
|
575
513
|
});
|
|
576
514
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
577
|
-
return { sagaFrame,
|
|
515
|
+
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
578
516
|
}
|
|
579
517
|
catch (error) {
|
|
580
518
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -589,13 +527,16 @@ export class SyncSagaCoordinator {
|
|
|
589
527
|
/**
|
|
590
528
|
* Helper to poll for streaming domain payloads and put them in the
|
|
591
529
|
* local {@link tempSpace}.
|
|
530
|
+
*
|
|
531
|
+
* @returns when all {@link expectedAddrs} are done being transmitted.
|
|
592
532
|
*/
|
|
593
|
-
async pollForDomainPayloads({ expectedAddrs, domainPayloadsMap, tempSpace, }) {
|
|
533
|
+
async pollForDomainPayloads({ expectedAddrs, pollIntervalMs, domainPayloadsMap, tempSpace, }) {
|
|
594
534
|
const lc = `${this.lc}[${this.pollForDomainPayloads.name}]`;
|
|
595
535
|
try {
|
|
596
536
|
if (logalot) {
|
|
597
537
|
console.log(`${lc} starting... (I: 26dce86bfca572939885798802d6e926)`);
|
|
598
538
|
}
|
|
539
|
+
let resultDomainPayloads = [];
|
|
599
540
|
let pending = [...expectedAddrs];
|
|
600
541
|
const start = Date.now();
|
|
601
542
|
/**
|
|
@@ -619,12 +560,17 @@ export class SyncSagaCoordinator {
|
|
|
619
560
|
}
|
|
620
561
|
if (found.length > 0) {
|
|
621
562
|
await putInSpace({ space: tempSpace, ibGibs: found });
|
|
563
|
+
found.forEach(x => resultDomainPayloads.push(x));
|
|
622
564
|
}
|
|
623
565
|
pending = stillPending;
|
|
624
566
|
if (pending.length > 0) {
|
|
625
|
-
await
|
|
567
|
+
await delay(pollIntervalMs);
|
|
626
568
|
}
|
|
627
569
|
}
|
|
570
|
+
if (expectedAddrs.length !== resultDomainPayloads.length) {
|
|
571
|
+
throw new Error(`(UNEXPECTED) expectedAddrs.length !== resultDomainPayloads.length? at this point, we expect all of the payload ibgibs to have been received. (E: 03749a7478c4b8b28bfc86951887a826)`);
|
|
572
|
+
}
|
|
573
|
+
return resultDomainPayloads;
|
|
628
574
|
}
|
|
629
575
|
catch (error) {
|
|
630
576
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -636,12 +582,33 @@ export class SyncSagaCoordinator {
|
|
|
636
582
|
}
|
|
637
583
|
}
|
|
638
584
|
}
|
|
639
|
-
|
|
640
|
-
|
|
585
|
+
/**
|
|
586
|
+
* This is the heart of the "ping pong" transaction, where we send a context
|
|
587
|
+
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
588
|
+
* a whole.
|
|
589
|
+
*
|
|
590
|
+
* This is called in two places:
|
|
591
|
+
*
|
|
592
|
+
* ## 1. Sender
|
|
593
|
+
*
|
|
594
|
+
* On the sender, this is called within the {@link executeSagaLoop} which
|
|
595
|
+
* initiates and drives the sync.
|
|
596
|
+
*
|
|
597
|
+
* ## 2. Receiver
|
|
598
|
+
*
|
|
599
|
+
* On the receiver, this is called directly by the receiving endpoint. That
|
|
600
|
+
* endpoint's job is basically to get these things collocated and prepared
|
|
601
|
+
* to make this call.
|
|
602
|
+
*
|
|
603
|
+
* This is a one-off on the receiver.
|
|
604
|
+
*/
|
|
605
|
+
async handleResponseSagaContext({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
606
|
+
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
641
607
|
try {
|
|
642
608
|
if (logalot) {
|
|
643
609
|
console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`);
|
|
644
610
|
}
|
|
611
|
+
const sagaIbGib = sagaContext.sagaFrame;
|
|
645
612
|
if (!sagaIbGib.data) {
|
|
646
613
|
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 71b938adf1d87c2527bfd4f86dfd0826)`);
|
|
647
614
|
}
|
|
@@ -649,26 +616,45 @@ export class SyncSagaCoordinator {
|
|
|
649
616
|
console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
|
|
650
617
|
}
|
|
651
618
|
// Get Stage from Stone (or Frame for Init fallback)
|
|
652
|
-
const { stage, messageData } = await this.getStageAndPayloadFromFrame({
|
|
619
|
+
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
|
|
653
620
|
if (logalot) {
|
|
654
621
|
console.log(`${lc} handling frame stage: ${stage}`);
|
|
655
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* don't like this name, need to refactor
|
|
625
|
+
*/
|
|
626
|
+
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
627
|
+
let nextFrameInfo;
|
|
656
628
|
switch (stage) {
|
|
657
629
|
case SyncStage.init:
|
|
658
|
-
|
|
630
|
+
nextFrameInfo = await this.handleInitFrame({
|
|
631
|
+
sagaIbGib,
|
|
632
|
+
messageData: messageData,
|
|
633
|
+
metaspace,
|
|
634
|
+
mySpace: mySpace,
|
|
635
|
+
myTempSpace: myTempSpace,
|
|
636
|
+
identity,
|
|
637
|
+
identitySecret
|
|
638
|
+
});
|
|
639
|
+
break;
|
|
659
640
|
case SyncStage.ack:
|
|
660
|
-
|
|
641
|
+
nextFrameInfo = await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
|
|
642
|
+
break;
|
|
661
643
|
case SyncStage.delta:
|
|
662
|
-
|
|
644
|
+
nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
645
|
+
break;
|
|
663
646
|
case SyncStage.commit:
|
|
664
|
-
|
|
647
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
648
|
+
break;
|
|
665
649
|
default:
|
|
666
650
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
667
651
|
}
|
|
652
|
+
return { errorMsg: undefined, nextFrameInfo, };
|
|
668
653
|
}
|
|
669
654
|
catch (error) {
|
|
670
|
-
|
|
671
|
-
|
|
655
|
+
const errorMsg = `${lc} ${extractErrorMsg(error)}`;
|
|
656
|
+
console.error(errorMsg);
|
|
657
|
+
return { errorMsg };
|
|
672
658
|
}
|
|
673
659
|
finally {
|
|
674
660
|
if (logalot) {
|
|
@@ -689,282 +675,332 @@ export class SyncSagaCoordinator {
|
|
|
689
675
|
* 3. Identifies what Receiver needs (`deltaReqAddrs`).
|
|
690
676
|
* 4. Returns an `Ack` frame containing these lists.
|
|
691
677
|
*/
|
|
692
|
-
async handleInitFrame({ sagaIbGib, messageData,
|
|
678
|
+
async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
|
|
679
|
+
// identitySecret,
|
|
680
|
+
}) {
|
|
693
681
|
const lc = `${this.lc}[${this.handleInitFrame.name}]`;
|
|
694
|
-
|
|
695
|
-
if (logalot) {
|
|
696
|
-
console.log(`${lc} starting...`);
|
|
697
|
-
}
|
|
698
|
-
// Extract Init Data
|
|
699
|
-
const initData = messageData; // Using renamed variable for clarity
|
|
700
|
-
if (initData.stage !== SyncStage.init) {
|
|
701
|
-
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
|
|
702
|
-
}
|
|
703
|
-
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
704
|
-
if (!initData || !initData.knowledgeVector) {
|
|
705
|
-
throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
|
|
706
|
-
}
|
|
707
|
-
// Determine Strategy from Saga Data (since V1 stores it in root)
|
|
708
|
-
const conflictStrategy = sagaIbGib.data.conflictStrategy || 'abort';
|
|
709
|
-
// 2. Gap Analysis
|
|
710
|
-
const conflicts = [];
|
|
711
|
-
const deltaReqAddrs = [];
|
|
712
|
-
const pushOfferAddrs = [];
|
|
713
|
-
// Stones Analysis (Constants / Non-TJPs)
|
|
714
|
-
const stones = initData.stones || [];
|
|
715
|
-
if (stones.length > 0) {
|
|
682
|
+
try {
|
|
716
683
|
if (logalot) {
|
|
717
|
-
console.log(`${lc}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
const
|
|
722
|
-
if (
|
|
684
|
+
console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`);
|
|
685
|
+
}
|
|
686
|
+
console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
|
|
687
|
+
// Extract Init Data
|
|
688
|
+
const initData = messageData; // Using renamed variable for clarity
|
|
689
|
+
if (initData.stage !== SyncStage.init) {
|
|
690
|
+
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
|
|
691
|
+
}
|
|
692
|
+
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
693
|
+
if (!initData || !initData.knowledgeVector) {
|
|
694
|
+
throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
|
|
695
|
+
}
|
|
696
|
+
// Determine Strategy from Saga Data (since V1 stores it in root)
|
|
697
|
+
const conflictStrategy = sagaIbGib.data.conflictStrategy || SyncConflictStrategy.abort;
|
|
698
|
+
// 2. Gap Analysis
|
|
699
|
+
const conflicts = [];
|
|
700
|
+
const deltaRequestAddrInfos = [];
|
|
701
|
+
const pushOfferInfos = [];
|
|
702
|
+
// First do the consant stones (Non-TJPs)
|
|
703
|
+
const stones = initData.stones || [];
|
|
704
|
+
if (stones.length > 0) {
|
|
723
705
|
if (logalot) {
|
|
724
|
-
console.log(`${lc} stones
|
|
706
|
+
console.log(`${lc} processing stones: ${stones.length}`);
|
|
707
|
+
}
|
|
708
|
+
// Check if we have these stones
|
|
709
|
+
const resStones = await getFromSpace({ space: mySpace, addrs: stones });
|
|
710
|
+
const rawResultIbGib = resStones.rawResultIbGib;
|
|
711
|
+
if (!rawResultIbGib.data) {
|
|
712
|
+
throw new Error(`(UNEXPECTED) rawResultIbGib.data falsy? (E: f3c40b492adc02c3f480b998fc7a5926)`);
|
|
725
713
|
}
|
|
726
|
-
addrsNotFound
|
|
727
|
-
|
|
728
|
-
|
|
714
|
+
const addrsNotFound = rawResultIbGib.data.addrsNotFound;
|
|
715
|
+
if (addrsNotFound && addrsNotFound.length > 0) {
|
|
716
|
+
if (logalot) {
|
|
717
|
+
console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`);
|
|
729
718
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
const remoteTjps = Object.keys(remoteKV);
|
|
738
|
-
console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
|
|
739
|
-
if (logalot) {
|
|
740
|
-
console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`);
|
|
741
|
-
}
|
|
742
|
-
// 1. Get Local Latest Addrs for all TJPs
|
|
743
|
-
let localKV = {};
|
|
744
|
-
if (remoteTjps.length > 0) {
|
|
745
|
-
// Batch get latest addrs for the TJPs
|
|
746
|
-
const resGetLatestAddrs = await getLatestAddrs({
|
|
747
|
-
space: destSpace,
|
|
748
|
-
tjpAddrs: remoteTjps,
|
|
749
|
-
});
|
|
750
|
-
if (!resGetLatestAddrs.data) {
|
|
751
|
-
throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`);
|
|
752
|
-
}
|
|
753
|
-
if (!resGetLatestAddrs.data.latestAddrsMap) {
|
|
754
|
-
throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`);
|
|
719
|
+
addrsNotFound.forEach(addr => {
|
|
720
|
+
if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
|
|
721
|
+
deltaRequestAddrInfos.push({ addr }); // no tjpAddr
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
}
|
|
755
725
|
}
|
|
756
|
-
|
|
757
|
-
|
|
726
|
+
/**
|
|
727
|
+
* "remote" is sender in this case
|
|
728
|
+
*/
|
|
729
|
+
const remoteKV = initData.knowledgeVector;
|
|
758
730
|
if (logalot) {
|
|
759
|
-
console.log(`${lc}
|
|
731
|
+
console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`);
|
|
760
732
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (isRemoteInPast) {
|
|
786
|
-
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
|
|
787
|
-
pushOfferAddrs.push(localAddr);
|
|
733
|
+
const remoteTjps = Object.keys(remoteKV);
|
|
734
|
+
console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
|
|
735
|
+
if (logalot) {
|
|
736
|
+
console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`);
|
|
737
|
+
}
|
|
738
|
+
// 1. Get Local Latest Addrs for all TJPs
|
|
739
|
+
let localLatestAddrsMap = {};
|
|
740
|
+
if (remoteTjps.length > 0) {
|
|
741
|
+
// Batch get latest addrs for the TJPs
|
|
742
|
+
const resGetLatestAddrs = await getLatestAddrs({
|
|
743
|
+
space: mySpace, // executing on receiver, so this is receiver's space
|
|
744
|
+
tjpAddrs: remoteTjps,
|
|
745
|
+
});
|
|
746
|
+
if (!resGetLatestAddrs.data) {
|
|
747
|
+
throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`);
|
|
748
|
+
}
|
|
749
|
+
if (!resGetLatestAddrs.data.latestAddrsMap) {
|
|
750
|
+
throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`);
|
|
751
|
+
}
|
|
752
|
+
localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
|
|
753
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
|
|
754
|
+
if (logalot) {
|
|
755
|
+
console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`);
|
|
756
|
+
}
|
|
788
757
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
758
|
+
// 2. Gap Analysis
|
|
759
|
+
for (const tjp of remoteTjps) {
|
|
760
|
+
const remoteAddr = remoteKV[tjp];
|
|
761
|
+
const localAddr = localLatestAddrsMap[tjp];
|
|
762
|
+
if (!localAddr) {
|
|
763
|
+
// We (Receiver) don't have this timeline at all. Request it.
|
|
764
|
+
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
765
|
+
deltaRequestAddrInfos.push({
|
|
766
|
+
addr: remoteAddr,
|
|
767
|
+
tjpAddr: tjp,
|
|
768
|
+
// we don't have this timeline at all
|
|
769
|
+
// latestAddrAlreadyHave: undefined
|
|
770
|
+
});
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
// we do have this timeline...
|
|
774
|
+
if (localAddr === remoteAddr) {
|
|
775
|
+
// ...already synced
|
|
776
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
780
|
+
// we have this timeline but it's not synced...
|
|
781
|
+
// We're executing on receiver. Check if Remote (Sender) is in
|
|
782
|
+
// our past, and if so, we are ahead and need to push the delta
|
|
783
|
+
// to remote
|
|
784
|
+
const remoteIsInPast = await isPastFrame({
|
|
785
|
+
olderAddr: remoteAddr,
|
|
786
|
+
newerAddr: localAddr,
|
|
787
|
+
space: mySpace,
|
|
797
788
|
});
|
|
798
|
-
if (
|
|
799
|
-
//
|
|
800
|
-
|
|
801
|
-
|
|
789
|
+
if (remoteIsInPast) {
|
|
790
|
+
// we're ahead, so push the delta of what the sender doesn't
|
|
791
|
+
// have (we have full knowledge)
|
|
792
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
|
|
793
|
+
const deltaGraph = await getDeltaDependencyGraph({
|
|
794
|
+
ibGibAddr: localAddr,
|
|
795
|
+
live: false, // always live: false right?
|
|
796
|
+
latestCommonFrameAddr: remoteAddr,
|
|
797
|
+
space: mySpace,
|
|
798
|
+
});
|
|
799
|
+
pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
|
|
802
800
|
}
|
|
803
801
|
else {
|
|
804
|
-
//
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
802
|
+
// Remote tip is not in our past. So, either Remote is
|
|
803
|
+
// ahead (we're in THEIR past, i.e., Fast-Backward) OR
|
|
804
|
+
// Diverged/conflict (we've both made edits).
|
|
805
|
+
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
806
|
+
/**
|
|
807
|
+
* we could first check for existence of remoteAddr in
|
|
808
|
+
* mySpace, but this is quick and easy. If it throws, we
|
|
809
|
+
* don't have it so it can't be in the past.
|
|
810
|
+
*/
|
|
811
|
+
let localIsInPast = false;
|
|
812
|
+
try {
|
|
813
|
+
// we could
|
|
814
|
+
localIsInPast = await isPastFrame({
|
|
815
|
+
olderAddr: localAddr,
|
|
816
|
+
newerAddr: remoteAddr,
|
|
817
|
+
space: mySpace,
|
|
818
818
|
});
|
|
819
819
|
}
|
|
820
|
-
|
|
821
|
-
//
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
825
|
-
// But for now, get full past.
|
|
826
|
-
// Optimization: localKV might not have full history.
|
|
827
|
-
// We need to inspect the 'past' of the local tip.
|
|
828
|
-
// We need the ACTUAL object to get the past.
|
|
829
|
-
// We have localAddr.
|
|
830
|
-
const resLocalTip = await getFromSpace({ space: destSpace, addr: localAddr });
|
|
831
|
-
const localTip = resLocalTip.ibGibs?.[0];
|
|
832
|
-
if (!localTip) {
|
|
833
|
-
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`);
|
|
820
|
+
catch (error) {
|
|
821
|
+
// couldn't get one of them, so localIsInPast remains false.
|
|
822
|
+
if (logalot) {
|
|
823
|
+
console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`);
|
|
834
824
|
}
|
|
835
|
-
|
|
836
|
-
|
|
825
|
+
}
|
|
826
|
+
if (localIsInPast) {
|
|
827
|
+
// Fast-Forward: We update to remote's tip.
|
|
828
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
829
|
+
deltaRequestAddrInfos.push({
|
|
830
|
+
addr: remoteAddr,
|
|
837
831
|
tjpAddr: tjp,
|
|
838
|
-
|
|
839
|
-
remoteAddr,
|
|
840
|
-
timelineAddrs,
|
|
841
|
-
reason: 'divergence',
|
|
842
|
-
terminal: false
|
|
832
|
+
latestAddrAlreadyHave: localAddr,
|
|
843
833
|
});
|
|
844
834
|
}
|
|
845
835
|
else {
|
|
846
|
-
|
|
836
|
+
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
837
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
|
|
838
|
+
if (conflictStrategy === 'abort') {
|
|
839
|
+
// Abort Strategy: We will treat this as terminal.
|
|
840
|
+
// But for Unified Ack, we just mark it terminal in the list?
|
|
841
|
+
// Or do we actually throw/abort the saga?
|
|
842
|
+
// Current logic (below) aborts the saga if ANY conflict is terminal/abort.
|
|
843
|
+
conflicts.push({
|
|
844
|
+
tjpAddr: tjp,
|
|
845
|
+
localAddr: localAddr,
|
|
846
|
+
remoteAddr,
|
|
847
|
+
timelineAddrs: [], // Not needed for abort
|
|
848
|
+
reason: 'divergence',
|
|
849
|
+
terminal: true
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
else if (conflictStrategy === 'optimistic') {
|
|
853
|
+
// Optimistic: We want resolve this.
|
|
854
|
+
// We need to send our history to the Sender so they can Merge.
|
|
855
|
+
// Fetch Full History for Local Timeline
|
|
856
|
+
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
857
|
+
// But for now, get full past.
|
|
858
|
+
// Optimization: localKV might not have full history.
|
|
859
|
+
// We need to inspect the 'past' of the local tip.
|
|
860
|
+
// We need the ACTUAL object to get the past.
|
|
861
|
+
// We have localAddr.
|
|
862
|
+
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
863
|
+
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
864
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
865
|
+
}
|
|
866
|
+
const localTip = resLocalTip.ibGibs[0];
|
|
867
|
+
if (!localTip) {
|
|
868
|
+
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`);
|
|
869
|
+
}
|
|
870
|
+
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
871
|
+
conflicts.push({
|
|
872
|
+
tjpAddr: tjp,
|
|
873
|
+
localAddr,
|
|
874
|
+
remoteAddr,
|
|
875
|
+
timelineAddrs,
|
|
876
|
+
reason: 'divergence',
|
|
877
|
+
terminal: false
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
|
|
882
|
+
}
|
|
847
883
|
}
|
|
848
884
|
}
|
|
849
885
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
886
|
+
// Check if we should ABORT (if any conflict is terminal)
|
|
887
|
+
const hasTerminalConflicts = conflicts.some(c => c.terminal);
|
|
888
|
+
if (hasTerminalConflicts) {
|
|
889
|
+
// Abort Strategy: Kill the saga.
|
|
890
|
+
if (logalot) {
|
|
891
|
+
console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`);
|
|
892
|
+
}
|
|
893
|
+
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
894
|
+
}
|
|
895
|
+
// 3. Create Ack Frame
|
|
896
|
+
if (!sagaIbGib.data) {
|
|
897
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`);
|
|
898
|
+
}
|
|
899
|
+
const sagaId = sagaIbGib.data.uuid;
|
|
900
|
+
// Create Payload Stone
|
|
901
|
+
const ackData = {
|
|
902
|
+
sagaId,
|
|
903
|
+
stage: SyncStage.ack,
|
|
904
|
+
deltaRequestAddrInfos,
|
|
905
|
+
pushOfferInfos,
|
|
906
|
+
};
|
|
907
|
+
if (conflicts?.length > 0) {
|
|
908
|
+
ackData.conflicts = conflicts;
|
|
909
|
+
}
|
|
910
|
+
const ackStone = await this.createSyncMsgStone({
|
|
911
|
+
data: ackData,
|
|
912
|
+
localSpace: myTempSpace,
|
|
913
|
+
metaspace,
|
|
914
|
+
});
|
|
855
915
|
if (logalot) {
|
|
856
|
-
console.
|
|
857
|
-
}
|
|
858
|
-
// We reuse the ConflictData structure for terminal aborts?
|
|
859
|
-
// Or do we send an Ack with terminal conflicts?
|
|
860
|
-
// Original design had explicit Conflict Frame for Abort.
|
|
861
|
-
// Let's stick to that for purely terminal cases to be safe/explicit?
|
|
862
|
-
// Or Unified: Just send Ack with terminal=true conflicts. Sender sees them and aborts.
|
|
863
|
-
// Decision: Unified Ack for everything is cleaner protocol.
|
|
864
|
-
// But wait, the original code below creates a Conflict Stone.
|
|
865
|
-
// Let's preserve the explicit 'Conflict' frame for total aborts if that's easier,
|
|
866
|
-
// OR fully switch to Ack.
|
|
867
|
-
// Protocol states: Init -> Ack. If Ack contains terminal errors, Sender can Commit(Fail).
|
|
868
|
-
// Let's use Ack with conflicts.
|
|
869
|
-
}
|
|
870
|
-
// 2. Add Push Offers (Missing in Local)
|
|
871
|
-
// Check if we have them. If not, ask for them.
|
|
872
|
-
for (const addr of pushOfferAddrs) {
|
|
873
|
-
const hasIt = await getFromSpace({ addr, space: destSpace });
|
|
874
|
-
if (!hasIt.success || !hasIt.ibGibs || hasIt.ibGibs.length === 0) {
|
|
875
|
-
// If we don't have it, we put it in `deltaReqAddrs` of the Ack.
|
|
876
|
-
deltaReqAddrs.push(addr);
|
|
916
|
+
console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`);
|
|
877
917
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
918
|
+
// Evolve Saga
|
|
919
|
+
const ackFrame = await this.evolveSyncSagaIbGib({
|
|
920
|
+
prevSagaIbGib: sagaIbGib,
|
|
921
|
+
msgStones: [ackStone],
|
|
922
|
+
sessionIdentity: identity,
|
|
923
|
+
localSpace: mySpace,
|
|
924
|
+
metaspace,
|
|
925
|
+
});
|
|
926
|
+
// if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
|
|
927
|
+
/**
|
|
928
|
+
* we want to push ibgibs to the remote/sender if we have push
|
|
929
|
+
* offers. an ack frame's payloads, if any, are those push offers
|
|
930
|
+
*/
|
|
931
|
+
// let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
|
|
932
|
+
let payloadIbGibsDomain;
|
|
933
|
+
if (pushOfferInfos.length > 0) {
|
|
934
|
+
const searchSecondSpaceAddrs = [];
|
|
935
|
+
payloadIbGibsDomain = [];
|
|
936
|
+
const domainAddrs = pushOfferInfos.flatMap(x => x.addrs);
|
|
937
|
+
const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
|
|
938
|
+
const resGetRawData = resGet.rawResultIbGib?.data;
|
|
939
|
+
if (!resGetRawData) {
|
|
940
|
+
throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`);
|
|
941
|
+
}
|
|
942
|
+
const addrsNotFound = resGetRawData.addrsNotFound ?? [];
|
|
943
|
+
const addrsErrored = resGetRawData.addrsErrored ?? [];
|
|
944
|
+
if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
|
|
945
|
+
// found all of them
|
|
946
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain.push(x); });
|
|
947
|
+
}
|
|
948
|
+
else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
|
|
949
|
+
// found some of them
|
|
950
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain.push(x); });
|
|
951
|
+
const foundPlusNotFoundCount = payloadIbGibsDomain.length +
|
|
952
|
+
addrsNotFound.length +
|
|
953
|
+
addrsErrored.length;
|
|
954
|
+
if (foundPlusNotFoundCount === domainAddrs.length) {
|
|
955
|
+
// we can still conceivably get the remaining addrs
|
|
956
|
+
addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
957
|
+
addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
958
|
+
}
|
|
959
|
+
else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
|
|
960
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
|
|
896
964
|
}
|
|
897
965
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
// Only process if not already covered by TJP logic above
|
|
903
|
-
// We can't really know if it's covered easily without resolving.
|
|
904
|
-
// But if we don't have it (requesting), we won't find it here anyway.
|
|
905
|
-
// If we DO have it (push offer), we might find it.
|
|
906
|
-
const res = await getFromSpace({ addr, space: destSpace });
|
|
907
|
-
if (res.success && res.ibGibs?.[0]) {
|
|
908
|
-
const ibGib = res.ibGibs[0];
|
|
909
|
-
const tjpAddr = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib });
|
|
910
|
-
if (!knowledgeVector[tjpAddr]) {
|
|
911
|
-
const past = ibGib.rel8ns?.past || [];
|
|
912
|
-
knowledgeVector[tjpAddr] = [getIbGibAddr({ ibGib }), ...past];
|
|
966
|
+
else {
|
|
967
|
+
// found none of them(?)
|
|
968
|
+
console.warn(`${lc} we were expecting all domainAddrs in mySpace (${mySpace.ib}) but we couldn't get all of them. addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. Trying temp space (${myTempSpace.ib}). (W: f61908e264f8dddd2e3fb9084463d826)`);
|
|
969
|
+
domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
913
970
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
971
|
+
if (searchSecondSpaceAddrs.length > 0) {
|
|
972
|
+
if (logalot) {
|
|
973
|
+
console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`);
|
|
974
|
+
}
|
|
975
|
+
const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
|
|
976
|
+
if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
|
|
977
|
+
// got them all the second try
|
|
978
|
+
resGet2.ibGibs.forEach(x => payloadIbGibsDomain.push(x));
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
resGet2.ibGibs?.forEach(x => payloadIbGibsDomain.push(x));
|
|
982
|
+
const addrsFailed = domainAddrs.filter(x => !payloadIbGibsDomain.map(pid => getIbGibAddr({ ibGib: pid })).includes(x));
|
|
983
|
+
throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
// we have now populated payloadIbGibsDomain
|
|
987
|
+
}
|
|
988
|
+
throw new Error(`not implemented (E: ed3f98abb0988c5ae8038bb8d741fb26)`);
|
|
989
|
+
// return {
|
|
990
|
+
// frame: ackFrame,
|
|
991
|
+
// // conflictInfos,
|
|
992
|
+
// payloadIbGibsDomain,
|
|
993
|
+
// };
|
|
918
994
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
// 1. Sender says "I have X"
|
|
923
|
-
// 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
|
|
924
|
-
// Problem: Receiver doesn't know X is related to Y without X.
|
|
925
|
-
// SOLUTION:
|
|
926
|
-
// The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
|
|
927
|
-
// `SyncSagaMessageInitData_V1` extends `SyncInitData`.
|
|
928
|
-
// `SyncInitData` has `knowledgeVector`.
|
|
929
|
-
// If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
|
|
930
|
-
// Let's implement Sender populating `Init.knowledgeVector`.
|
|
931
|
-
// But `SyncSagaCoordinator.startSaga` creates Init.
|
|
932
|
-
// 3. Create Ack Frame
|
|
933
|
-
const sagaId = sagaIbGib.data.uuid;
|
|
934
|
-
// Create Payload Stone
|
|
935
|
-
const ackData = {
|
|
936
|
-
sagaId,
|
|
937
|
-
stage: SyncStage.ack,
|
|
938
|
-
deltaReqAddrs,
|
|
939
|
-
pushOfferAddrs,
|
|
940
|
-
knowledgeVector,
|
|
941
|
-
conflicts: conflicts.length > 0 ? conflicts : undefined, // Include conflicts if any detected
|
|
942
|
-
};
|
|
943
|
-
const ackStone = await this.createSyncMsgStone({
|
|
944
|
-
data: ackData,
|
|
945
|
-
space: tempSpace,
|
|
946
|
-
metaspace,
|
|
947
|
-
});
|
|
948
|
-
if (logalot) {
|
|
949
|
-
console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`);
|
|
995
|
+
catch (error) {
|
|
996
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
997
|
+
throw error;
|
|
950
998
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
identity,
|
|
956
|
-
space: tempSpace,
|
|
957
|
-
metaspace,
|
|
958
|
-
});
|
|
959
|
-
// IMMEDIATELY persist to both spaces for audit trail (before any errors can occur)
|
|
960
|
-
await this.ensureSagaFrameInBothSpaces({ frame: ackFrame, destSpace, tempSpace, metaspace });
|
|
961
|
-
// if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
|
|
962
|
-
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
963
|
-
const payloadIbGibsControl = [ackFrame, ackStone];
|
|
964
|
-
if (identity) {
|
|
965
|
-
payloadIbGibsControl.push(identity);
|
|
999
|
+
finally {
|
|
1000
|
+
if (logalot) {
|
|
1001
|
+
console.log(`${lc} complete.`);
|
|
1002
|
+
}
|
|
966
1003
|
}
|
|
967
|
-
return { frame: ackFrame, payloadIbGibsControl };
|
|
968
1004
|
}
|
|
969
1005
|
/**
|
|
970
1006
|
* Handles the `Ack` frame.
|
|
@@ -984,7 +1020,7 @@ export class SyncSagaCoordinator {
|
|
|
984
1020
|
if (logalot) {
|
|
985
1021
|
console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
|
|
986
1022
|
}
|
|
987
|
-
const { messageData, } = await this.getStageAndPayloadFromFrame({
|
|
1023
|
+
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
988
1024
|
const ackData = messageData;
|
|
989
1025
|
if (!ackData) {
|
|
990
1026
|
throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
|
|
@@ -1006,7 +1042,7 @@ export class SyncSagaCoordinator {
|
|
|
1006
1042
|
console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
|
|
1007
1043
|
// Terminal failure. Sender should probably Commit(Fail) or just Abort.
|
|
1008
1044
|
// For now, throw to trigger abort.
|
|
1009
|
-
throw new Error(`${lc} Peer reported terminal conflicts. (E:
|
|
1045
|
+
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1010
1046
|
}
|
|
1011
1047
|
const optimisticConflicts = conflicts.filter(c => !c.terminal);
|
|
1012
1048
|
const mergeDeltaReqs = []; // Additional requests for merging
|
|
@@ -1028,7 +1064,7 @@ export class SyncSagaCoordinator {
|
|
|
1028
1064
|
// Or can we send a 'Delta Request' frame?
|
|
1029
1065
|
// Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
|
|
1030
1066
|
// If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
|
|
1031
|
-
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1067
|
+
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1032
1068
|
// AND we piggyback our own requests?
|
|
1033
1069
|
// OR: We treat the Conflict Resolution as a sub-saga or side-effect?
|
|
1034
1070
|
// SIMPLIFICATION for V1:
|
|
@@ -1052,7 +1088,7 @@ export class SyncSagaCoordinator {
|
|
|
1052
1088
|
const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
|
|
1053
1089
|
const senderTipIbGib = resSenderTip.ibGibs?.[0];
|
|
1054
1090
|
if (!senderTipIbGib) {
|
|
1055
|
-
throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E:
|
|
1091
|
+
throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`);
|
|
1056
1092
|
}
|
|
1057
1093
|
// Basic Diff: Find what Receiver has that we don't.
|
|
1058
1094
|
// Actually, we need to traverse OUR past to find commonality.
|
|
@@ -1129,46 +1165,46 @@ export class SyncSagaCoordinator {
|
|
|
1129
1165
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1130
1166
|
}
|
|
1131
1167
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1132
|
-
const deltaReqAddrs = ackData.
|
|
1133
|
-
const pushOfferAddrs = ackData.
|
|
1168
|
+
const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
|
|
1169
|
+
const pushOfferAddrs = ackData.pushOfferInfos || [];
|
|
1134
1170
|
// 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
|
|
1135
1171
|
const pullReqAddrs = [];
|
|
1136
1172
|
for (const addr of pushOfferAddrs) {
|
|
1137
|
-
const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1138
|
-
if (!existing) {
|
|
1139
|
-
|
|
1140
|
-
}
|
|
1173
|
+
// const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1174
|
+
// if (!existing) {
|
|
1175
|
+
// pullReqAddrs.push(addr);
|
|
1176
|
+
// }
|
|
1141
1177
|
}
|
|
1142
1178
|
// 2. Process Delta Requests (Push Payload)
|
|
1143
1179
|
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1180
|
+
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1144
1181
|
const skipAddrs = new Set();
|
|
1145
1182
|
if (ackData.knowledgeVector) {
|
|
1146
1183
|
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1147
|
-
addrs.forEach(a => skipAddrs.add(a));
|
|
1184
|
+
// addrs.forEach(a => skipAddrs.add(a));
|
|
1148
1185
|
});
|
|
1149
1186
|
}
|
|
1150
1187
|
const payloadIbGibs = [];
|
|
1151
1188
|
// Gather all tips to sync first
|
|
1152
1189
|
const tipsToSync = [];
|
|
1153
1190
|
for (const addr of deltaReqAddrs) {
|
|
1154
|
-
let ibGib = srcGraph[addr];
|
|
1155
|
-
if (!ibGib) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
}
|
|
1161
|
-
if (ibGib) {
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
}
|
|
1191
|
+
// let ibGib = srcGraph[addr];
|
|
1192
|
+
// if (!ibGib) {
|
|
1193
|
+
// const res = await getFromSpace({ addr, space: destSpace });
|
|
1194
|
+
// if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1195
|
+
// ibGib = res.ibGibs[0];
|
|
1196
|
+
// }
|
|
1197
|
+
// }
|
|
1198
|
+
// if (ibGib) {
|
|
1199
|
+
// tipsToSync.push(ibGib);
|
|
1200
|
+
// } else {
|
|
1201
|
+
// throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
|
|
1202
|
+
// }
|
|
1167
1203
|
}
|
|
1168
1204
|
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1169
1205
|
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1170
1206
|
// `getDependencyGraph` takes a single ibGib.
|
|
1171
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1207
|
+
// We can optimize by doing it for each tip and unioning the result?
|
|
1172
1208
|
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1173
1209
|
// We will loop.
|
|
1174
1210
|
const allDepsSet = new Set();
|
|
@@ -1221,18 +1257,16 @@ export class SyncSagaCoordinator {
|
|
|
1221
1257
|
}
|
|
1222
1258
|
const deltaStone = await this.createSyncMsgStone({
|
|
1223
1259
|
data: deltaData,
|
|
1224
|
-
|
|
1260
|
+
localSpace: tempSpace,
|
|
1225
1261
|
metaspace,
|
|
1226
1262
|
});
|
|
1227
1263
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1228
1264
|
prevSagaIbGib: sagaIbGib,
|
|
1229
1265
|
msgStones: [deltaStone],
|
|
1230
|
-
identity,
|
|
1231
|
-
|
|
1266
|
+
sessionIdentity: identity,
|
|
1267
|
+
localSpace: tempSpace,
|
|
1232
1268
|
metaspace,
|
|
1233
1269
|
});
|
|
1234
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1235
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1236
1270
|
if (logalot) {
|
|
1237
1271
|
console.log(`${lc} Delta Frame created. Rel8ns: ${JSON.stringify(deltaFrame.rel8ns)}`);
|
|
1238
1272
|
}
|
|
@@ -1241,7 +1275,9 @@ export class SyncSagaCoordinator {
|
|
|
1241
1275
|
if (identity) {
|
|
1242
1276
|
payloadIbGibsControl.push(identity);
|
|
1243
1277
|
}
|
|
1244
|
-
return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1278
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1279
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1280
|
+
throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
|
|
1245
1281
|
}
|
|
1246
1282
|
catch (error) {
|
|
1247
1283
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1268,7 +1304,7 @@ export class SyncSagaCoordinator {
|
|
|
1268
1304
|
if (logalot) {
|
|
1269
1305
|
console.log(`${lc} starting...`);
|
|
1270
1306
|
}
|
|
1271
|
-
const { messageData } = await this.getStageAndPayloadFromFrame({
|
|
1307
|
+
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
1272
1308
|
const deltaData = messageData;
|
|
1273
1309
|
if (!deltaData) {
|
|
1274
1310
|
throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
|
|
@@ -1277,7 +1313,7 @@ export class SyncSagaCoordinator {
|
|
|
1277
1313
|
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1278
1314
|
}
|
|
1279
1315
|
if (logalot) {
|
|
1280
|
-
console.log(`${lc} deltaData: ${pretty(deltaData)} (I:
|
|
1316
|
+
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1281
1317
|
}
|
|
1282
1318
|
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1283
1319
|
const payloadAddrs = deltaData.payloadAddrs || [];
|
|
@@ -1286,8 +1322,8 @@ export class SyncSagaCoordinator {
|
|
|
1286
1322
|
// 1. Process Received Payload (Ingest)
|
|
1287
1323
|
const receivedPayloadIbGibs = [];
|
|
1288
1324
|
if (payloadAddrs.length > 0) {
|
|
1289
|
-
// We use `payloadAddrs` as the manifest.
|
|
1290
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1325
|
+
// We use `payloadAddrs` as the manifest.
|
|
1326
|
+
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1291
1327
|
// assuming the "Transport" layer put them there implicitly?
|
|
1292
1328
|
// OR, if we are local-only, we just get them.
|
|
1293
1329
|
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
@@ -1312,7 +1348,7 @@ export class SyncSagaCoordinator {
|
|
|
1312
1348
|
// Get the requested ibGib
|
|
1313
1349
|
let ibGib = srcGraph[addr];
|
|
1314
1350
|
if (!ibGib) {
|
|
1315
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1351
|
+
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1316
1352
|
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1317
1353
|
ibGib = res.ibGibs[0];
|
|
1318
1354
|
}
|
|
@@ -1398,7 +1434,7 @@ export class SyncSagaCoordinator {
|
|
|
1398
1434
|
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1399
1435
|
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1400
1436
|
// We have the tip!
|
|
1401
|
-
// Do we have the full history?
|
|
1437
|
+
// Do we have the full history?
|
|
1402
1438
|
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1403
1439
|
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1404
1440
|
// Perform Merge!
|
|
@@ -1442,9 +1478,9 @@ export class SyncSagaCoordinator {
|
|
|
1442
1478
|
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1443
1479
|
requests: hasMyRequests ? myRequests : undefined,
|
|
1444
1480
|
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1445
|
-
// Wait. If we send data, we are NOT committing yet.
|
|
1481
|
+
// Wait. If we send data, we are NOT committing yet.
|
|
1446
1482
|
// We are sending data. The OTHER side must ingest it.
|
|
1447
|
-
// So proposeCommit = true?
|
|
1483
|
+
// So proposeCommit = true?
|
|
1448
1484
|
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1449
1485
|
// Yes.
|
|
1450
1486
|
};
|
|
@@ -1454,24 +1490,24 @@ export class SyncSagaCoordinator {
|
|
|
1454
1490
|
responseDeltaData.proposeCommit = true;
|
|
1455
1491
|
const deltaStone = await this.createSyncMsgStone({
|
|
1456
1492
|
data: responseDeltaData,
|
|
1457
|
-
|
|
1493
|
+
localSpace: tempSpace,
|
|
1458
1494
|
metaspace
|
|
1459
1495
|
});
|
|
1460
1496
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1461
1497
|
prevSagaIbGib: sagaIbGib,
|
|
1462
1498
|
msgStones: [deltaStone],
|
|
1463
|
-
identity,
|
|
1464
|
-
|
|
1499
|
+
sessionIdentity: identity,
|
|
1500
|
+
localSpace: tempSpace,
|
|
1465
1501
|
metaspace
|
|
1466
1502
|
});
|
|
1467
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1468
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1469
1503
|
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1470
1504
|
const payloadIbGibsControl = [deltaFrame, deltaStone];
|
|
1471
1505
|
if (identity) {
|
|
1472
1506
|
payloadIbGibsControl.push(identity);
|
|
1473
1507
|
}
|
|
1474
|
-
return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1508
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1509
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1510
|
+
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1475
1511
|
}
|
|
1476
1512
|
else {
|
|
1477
1513
|
// We have nothing to send.
|
|
@@ -1484,24 +1520,24 @@ export class SyncSagaCoordinator {
|
|
|
1484
1520
|
};
|
|
1485
1521
|
const commitStone = await this.createSyncMsgStone({
|
|
1486
1522
|
data: commitData,
|
|
1487
|
-
|
|
1523
|
+
localSpace: tempSpace,
|
|
1488
1524
|
metaspace
|
|
1489
1525
|
});
|
|
1490
1526
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1491
1527
|
prevSagaIbGib: sagaIbGib,
|
|
1492
1528
|
msgStones: [commitStone],
|
|
1493
|
-
identity,
|
|
1494
|
-
|
|
1529
|
+
sessionIdentity: identity,
|
|
1530
|
+
localSpace: tempSpace,
|
|
1495
1531
|
metaspace
|
|
1496
1532
|
});
|
|
1497
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1498
|
-
await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
|
|
1499
1533
|
// Build control payloads for commit
|
|
1500
1534
|
const commitCtrlPayloads = [commitFrame, commitStone];
|
|
1501
1535
|
if (identity) {
|
|
1502
1536
|
commitCtrlPayloads.push(identity);
|
|
1503
1537
|
}
|
|
1504
|
-
return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1538
|
+
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1539
|
+
// return { frame: commitFrame, };
|
|
1540
|
+
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1505
1541
|
}
|
|
1506
1542
|
else {
|
|
1507
1543
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1515,18 +1551,16 @@ export class SyncSagaCoordinator {
|
|
|
1515
1551
|
};
|
|
1516
1552
|
const deltaStone = await this.createSyncMsgStone({
|
|
1517
1553
|
data: responseDeltaData,
|
|
1518
|
-
|
|
1554
|
+
localSpace: tempSpace,
|
|
1519
1555
|
metaspace
|
|
1520
1556
|
});
|
|
1521
1557
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1522
1558
|
prevSagaIbGib: sagaIbGib,
|
|
1523
1559
|
msgStones: [deltaStone],
|
|
1524
|
-
identity,
|
|
1525
|
-
|
|
1560
|
+
sessionIdentity: identity,
|
|
1561
|
+
localSpace: tempSpace,
|
|
1526
1562
|
metaspace
|
|
1527
1563
|
});
|
|
1528
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1529
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1530
1564
|
// Check if PEER proposed commit
|
|
1531
1565
|
if (deltaData.proposeCommit) {
|
|
1532
1566
|
if (logalot) {
|
|
@@ -1541,31 +1575,33 @@ export class SyncSagaCoordinator {
|
|
|
1541
1575
|
};
|
|
1542
1576
|
const commitStone = await this.createSyncMsgStone({
|
|
1543
1577
|
data: commitData,
|
|
1544
|
-
|
|
1578
|
+
localSpace: tempSpace,
|
|
1545
1579
|
metaspace
|
|
1546
1580
|
});
|
|
1547
1581
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1548
1582
|
prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
|
|
1549
1583
|
msgStones: [commitStone],
|
|
1550
|
-
identity,
|
|
1551
|
-
|
|
1584
|
+
sessionIdentity: identity,
|
|
1585
|
+
localSpace: tempSpace,
|
|
1552
1586
|
metaspace
|
|
1553
1587
|
});
|
|
1554
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1555
|
-
await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
|
|
1556
1588
|
// Build control payloads for commit
|
|
1557
1589
|
const commitCtrlPayloads2 = [commitFrame, commitStone];
|
|
1558
1590
|
if (identity) {
|
|
1559
1591
|
commitCtrlPayloads2.push(identity);
|
|
1560
1592
|
}
|
|
1561
|
-
return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1593
|
+
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1594
|
+
// return { frame: commitFrame, };
|
|
1595
|
+
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1562
1596
|
}
|
|
1563
1597
|
// Build control payloads for delta propose
|
|
1564
1598
|
const deltaCtrlPayloads = [deltaFrame, deltaStone];
|
|
1565
1599
|
if (identity) {
|
|
1566
1600
|
deltaCtrlPayloads.push(identity);
|
|
1567
1601
|
}
|
|
1568
|
-
return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1602
|
+
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1603
|
+
// return { frame: deltaFrame, };
|
|
1604
|
+
throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
|
|
1569
1605
|
}
|
|
1570
1606
|
}
|
|
1571
1607
|
}
|
|
@@ -1585,10 +1621,11 @@ export class SyncSagaCoordinator {
|
|
|
1585
1621
|
if (logalot) {
|
|
1586
1622
|
console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
|
|
1587
1623
|
}
|
|
1588
|
-
return
|
|
1624
|
+
// return { responseWasNull: true };
|
|
1625
|
+
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1589
1626
|
}
|
|
1590
1627
|
// #endregion Handlers
|
|
1591
|
-
async createSyncMsgStone({ data,
|
|
1628
|
+
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|
|
1592
1629
|
const lc = `${this.lc}[${this.createSyncMsgStone.name}]`;
|
|
1593
1630
|
try {
|
|
1594
1631
|
if (logalot) {
|
|
@@ -1604,7 +1641,7 @@ export class SyncSagaCoordinator {
|
|
|
1604
1641
|
if (logalot) {
|
|
1605
1642
|
console.log(`${lc} Created stone: ${getIbGibAddr({ ibGib: stone })}`);
|
|
1606
1643
|
}
|
|
1607
|
-
await
|
|
1644
|
+
await metaspace.put({ ibGib: stone, space: localSpace });
|
|
1608
1645
|
await metaspace.registerNewIbGib({ ibGib: stone });
|
|
1609
1646
|
return stone;
|
|
1610
1647
|
}
|
|
@@ -1618,41 +1655,10 @@ export class SyncSagaCoordinator {
|
|
|
1618
1655
|
}
|
|
1619
1656
|
}
|
|
1620
1657
|
}
|
|
1621
|
-
/**
|
|
1622
|
-
* Ensures saga frame and its msg stone(s) are in BOTH spaces for audit trail.
|
|
1623
|
-
* Control ibgibs (saga frames, msg stones, identity) must be in both destSpace and tempSpace.
|
|
1624
|
-
*/
|
|
1625
|
-
async ensureSagaFrameInBothSpaces({ frame, destSpace, tempSpace, metaspace, }) {
|
|
1626
|
-
// Frame itself (already in tempSpace from creation, need in destSpace for audit)
|
|
1627
|
-
await putInSpace({ space: destSpace, ibGib: frame });
|
|
1628
|
-
await metaspace.registerNewIbGib({ ibGib: frame });
|
|
1629
|
-
// Msg stone(s) (already in tempSpace, need in destSpace for audit)
|
|
1630
|
-
const msgStoneAddrs = frame.rel8ns?.[SYNC_MSG_REL8N_NAME];
|
|
1631
|
-
if (msgStoneAddrs && msgStoneAddrs.length > 0) {
|
|
1632
|
-
const resMsgStones = await getFromSpace({ space: tempSpace, addrs: msgStoneAddrs });
|
|
1633
|
-
if (resMsgStones.ibGibs) {
|
|
1634
|
-
for (const msgStone of resMsgStones.ibGibs) {
|
|
1635
|
-
await putInSpace({ space: destSpace, ibGib: msgStone });
|
|
1636
|
-
await metaspace.registerNewIbGib({ ibGib: msgStone });
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
// Identity (if present, already in tempSpace, need in destSpace)
|
|
1641
|
-
const identityAddrs = frame.rel8ns?.identity;
|
|
1642
|
-
if (identityAddrs && identityAddrs.length > 0) {
|
|
1643
|
-
const resIdentity = await getFromSpace({ space: tempSpace, addrs: identityAddrs });
|
|
1644
|
-
if (resIdentity.ibGibs) {
|
|
1645
|
-
for (const identity of resIdentity.ibGibs) {
|
|
1646
|
-
await putInSpace({ space: destSpace, ibGib: identity });
|
|
1647
|
-
await metaspace.registerNewIbGib({ ibGib: identity });
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
1658
|
/**
|
|
1653
1659
|
* Evolves the saga timeline with a new frame.
|
|
1654
1660
|
*/
|
|
1655
|
-
async evolveSyncSagaIbGib({ prevSagaIbGib, msgStones,
|
|
1661
|
+
async evolveSyncSagaIbGib({ prevSagaIbGib, conflictStrategy, msgStones, sessionIdentity, localSpace, metaspace, }) {
|
|
1656
1662
|
const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
|
|
1657
1663
|
try {
|
|
1658
1664
|
// Validation
|
|
@@ -1675,7 +1681,7 @@ export class SyncSagaCoordinator {
|
|
|
1675
1681
|
throw new Error(`${lc} Mismatched stage in stones. Expected ${stage}, got ${d.stage} (E: d12c6571b0882f762921b60880c3f826)`);
|
|
1676
1682
|
}
|
|
1677
1683
|
}
|
|
1678
|
-
const identityAddr =
|
|
1684
|
+
const identityAddr = sessionIdentity ? getIbGibAddr({ ibGib: sessionIdentity }) : undefined;
|
|
1679
1685
|
if (prevSagaIbGib) {
|
|
1680
1686
|
/**
|
|
1681
1687
|
* rel8ns always include the new msg stone(s)
|
|
@@ -1687,10 +1693,10 @@ export class SyncSagaCoordinator {
|
|
|
1687
1693
|
}
|
|
1688
1694
|
];
|
|
1689
1695
|
// if we're authenticating/signing, we'll have identity
|
|
1690
|
-
if (
|
|
1691
|
-
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [
|
|
1696
|
+
if (sessionIdentity) {
|
|
1697
|
+
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1692
1698
|
}
|
|
1693
|
-
// remove the existing sync msg stones' addrs
|
|
1699
|
+
// remove the existing sync msg stones' addrs
|
|
1694
1700
|
if (!prevSagaIbGib.rel8ns) {
|
|
1695
1701
|
throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`);
|
|
1696
1702
|
}
|
|
@@ -1709,7 +1715,7 @@ export class SyncSagaCoordinator {
|
|
|
1709
1715
|
rel8nInfos,
|
|
1710
1716
|
rel8nRemovalInfos,
|
|
1711
1717
|
metaspace,
|
|
1712
|
-
space,
|
|
1718
|
+
space: localSpace,
|
|
1713
1719
|
noDna: true, // Explicitly no DNA for sync frames
|
|
1714
1720
|
});
|
|
1715
1721
|
const newFrame = resAppend;
|
|
@@ -1717,31 +1723,36 @@ export class SyncSagaCoordinator {
|
|
|
1717
1723
|
}
|
|
1718
1724
|
else {
|
|
1719
1725
|
// Create New Timeline (Root Frame)
|
|
1726
|
+
// data
|
|
1720
1727
|
const data = {
|
|
1721
1728
|
uuid: sagaId,
|
|
1722
|
-
// stage, // Removed from V1
|
|
1723
|
-
payload: undefined, // Data in stone
|
|
1724
1729
|
n: 0,
|
|
1725
1730
|
isTjp: true,
|
|
1726
1731
|
conflictStrategy,
|
|
1727
1732
|
};
|
|
1733
|
+
// ib
|
|
1728
1734
|
const ib = await getSyncIb({ data });
|
|
1735
|
+
// rel8ns
|
|
1729
1736
|
const stoneAddrs = msgStones.map(s => getIbGibAddr({ ibGib: s }));
|
|
1730
|
-
const rel8ns = {
|
|
1731
|
-
[SYNC_MSG_REL8N_NAME]: stoneAddrs,
|
|
1732
|
-
};
|
|
1737
|
+
const rel8ns = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
|
|
1733
1738
|
if (identityAddr) {
|
|
1734
1739
|
rel8ns.identity = [identityAddr];
|
|
1735
1740
|
}
|
|
1736
1741
|
const resNew = await createTimeline({
|
|
1737
|
-
space,
|
|
1742
|
+
space: localSpace,
|
|
1738
1743
|
metaspace,
|
|
1739
1744
|
ib,
|
|
1740
1745
|
data,
|
|
1741
1746
|
rel8ns,
|
|
1742
1747
|
parentIb: SYNC_ATOM, // "sync"
|
|
1748
|
+
/**
|
|
1749
|
+
* this will squash the result into a single frame
|
|
1750
|
+
*/
|
|
1743
1751
|
noDna: true,
|
|
1744
1752
|
});
|
|
1753
|
+
if (!!resNew.intermediateIbGibs) {
|
|
1754
|
+
throw new Error(`(UNEXPECTED) resNew.intermediateIbGibs? we were assuming we were creating a single frame. (E: 456818147235d991ccb4d10d0bbe4826)`);
|
|
1755
|
+
}
|
|
1745
1756
|
return resNew.newIbGib;
|
|
1746
1757
|
}
|
|
1747
1758
|
}
|
|
@@ -1750,16 +1761,16 @@ export class SyncSagaCoordinator {
|
|
|
1750
1761
|
throw error;
|
|
1751
1762
|
}
|
|
1752
1763
|
}
|
|
1753
|
-
async getStageAndPayloadFromFrame({
|
|
1764
|
+
async getStageAndPayloadFromFrame({ sagaFrame, space }) {
|
|
1754
1765
|
const lc = `${this.lc}[${this.getStageAndPayloadFromFrame.name}]`;
|
|
1755
1766
|
try {
|
|
1756
1767
|
if (logalot) {
|
|
1757
1768
|
console.log(`${lc} starting... (I: fddc287bd4d55253dc50e519fd352226)`);
|
|
1758
1769
|
}
|
|
1759
|
-
if (!
|
|
1760
|
-
throw new Error(`(UNEXPECTED)
|
|
1770
|
+
if (!sagaFrame.rel8ns) {
|
|
1771
|
+
throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`);
|
|
1761
1772
|
}
|
|
1762
|
-
const stoneAddrs =
|
|
1773
|
+
const stoneAddrs = sagaFrame.rel8ns?.[SYNC_MSG_REL8N_NAME];
|
|
1763
1774
|
if (stoneAddrs && stoneAddrs.length > 0) {
|
|
1764
1775
|
const stoneAddr = stoneAddrs[0];
|
|
1765
1776
|
const res = await getFromSpace({ addr: stoneAddr, space });
|
|
@@ -1778,7 +1789,7 @@ export class SyncSagaCoordinator {
|
|
|
1778
1789
|
}
|
|
1779
1790
|
}
|
|
1780
1791
|
else {
|
|
1781
|
-
throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame?
|
|
1792
|
+
throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? sagaFrame addr: ${getIbGibAddr(sagaFrame)} (E: 43eae8579e289d016741b5526052f226)`);
|
|
1782
1793
|
}
|
|
1783
1794
|
}
|
|
1784
1795
|
catch (error) {
|