@ibgib/core-gib 0.1.22 → 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 +12 -11
- 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 +11 -10
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +11 -10
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +11 -10
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +12 -10
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +14 -10
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +12 -10
- package/dist/sync/sync-innerspace.respec.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 +15 -14
- 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 +9 -55
- 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 +16 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +39 -5
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +141 -4
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +22 -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 +104 -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 +74 -70
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +475 -527
- 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 -88
- 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/package.json +1 -1
- package/src/common/other/graph-helper.mts +26 -1
- package/src/sync/sync-conflict.respec.mts +12 -11
- package/src/sync/sync-constants.mts +15 -4
- package/src/sync/sync-helpers.mts +167 -95
- package/src/sync/sync-innerspace-constants.respec.mts +11 -10
- package/src/sync/sync-innerspace-deep-updates.respec.mts +11 -10
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +11 -10
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +12 -10
- package/src/sync/sync-innerspace-partial-update.respec.mts +14 -12
- package/src/sync/sync-innerspace.respec.mts +12 -10
- 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 +16 -65
- package/src/sync/sync-peer/sync-peer-types.mts +17 -0
- package/src/sync/sync-peer/sync-peer-v1.mts +141 -8
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +116 -37
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +5 -0
- package/src/sync/sync-saga-coordinator.mts +525 -617
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +106 -22
- package/src/sync/sync-types.mts +59 -101
- 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, } from "./sync-types.mjs";
|
|
9
|
+
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
10
|
+
import { 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";
|
|
13
|
+
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
|
|
15
14
|
import { createSyncSagaContext } 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({
|
|
@@ -108,41 +101,24 @@ export class SyncSagaCoordinator {
|
|
|
108
101
|
: undefined;
|
|
109
102
|
// if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
|
|
110
103
|
// 3. CREATE INITIAL FRAME (Stage.init)
|
|
111
|
-
const { sagaFrame: initFrame,
|
|
104
|
+
const { sagaFrame: initFrame, initialDomainGraph } = await this.createInitFrame({
|
|
112
105
|
sagaId,
|
|
113
106
|
sessionIdentity,
|
|
114
|
-
localSpace,
|
|
115
107
|
domainIbGibs,
|
|
116
|
-
|
|
117
|
-
metaspace,
|
|
118
|
-
conflictStrategy
|
|
119
|
-
});
|
|
120
|
-
// 4. EXECUTE SAGA LOOP (FSM)
|
|
121
|
-
// Inject tempSpace into peer so it can pull control payloads to the right place
|
|
122
|
-
// if ('opts' in peer && peer.opts) {
|
|
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,
|
|
132
|
-
peer,
|
|
133
|
-
sessionIdentity,
|
|
134
|
-
updates$,
|
|
135
|
-
localSpace,
|
|
136
|
-
tempSpace,
|
|
137
|
-
metaspace
|
|
108
|
+
conflictStrategy,
|
|
109
|
+
metaspace, localSpace, tempSpace,
|
|
138
110
|
});
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
111
|
+
// 4. KICK OFF THE PING-PONG SAGA LOOP (FSM)
|
|
112
|
+
// commented to compile
|
|
113
|
+
// await this.executeSagaLoop({
|
|
114
|
+
// initialFrame: initFrame,
|
|
115
|
+
// peer,
|
|
116
|
+
// sessionIdentity,
|
|
117
|
+
// updates$,
|
|
118
|
+
// localSpace,
|
|
119
|
+
// tempSpace,
|
|
120
|
+
// metaspace
|
|
121
|
+
// });
|
|
146
122
|
resolveDone();
|
|
147
123
|
if (!updates$.complete) {
|
|
148
124
|
throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`);
|
|
@@ -205,130 +181,104 @@ export class SyncSagaCoordinator {
|
|
|
205
181
|
* **Execution Context**: **Sender (Local)**.
|
|
206
182
|
*
|
|
207
183
|
* 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 `
|
|
184
|
+
* It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
|
|
209
185
|
*
|
|
210
186
|
* **Data Transport Note**:
|
|
211
187
|
* Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
|
|
212
|
-
* When `
|
|
188
|
+
* When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
|
|
213
189
|
* the NEXT request context.
|
|
214
190
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
215
191
|
*/
|
|
216
|
-
async executeSagaLoop({ initialFrame,
|
|
192
|
+
async executeSagaLoop({ initialFrame, initialDomainGraph, peer, sessionIdentity, updates$, localSpace, tempSpace, metaspace }) {
|
|
217
193
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
218
194
|
// The current frame we just generated (e.g., Init or Delta Request)
|
|
219
195
|
let currentFrame = initialFrame;
|
|
220
196
|
// The payload we need to attach to the message (data we are sending)
|
|
221
197
|
let nextDomainIbGibs = [];
|
|
222
|
-
//
|
|
223
|
-
|
|
198
|
+
// First, inject local/tempSpace into peer so it can pull as
|
|
199
|
+
// needed...code smell?
|
|
200
|
+
peer.senderSpace = localSpace;
|
|
201
|
+
peer.senderTempSpace = tempSpace;
|
|
224
202
|
while (currentFrame) {
|
|
225
203
|
// A. Create Context (Request)
|
|
226
204
|
// 1. Calculate Full Dependency Graph (including Ancestors/DNA)
|
|
227
|
-
// TODO: adjust this algorithm to only do the dependencies that the other end doesn't need (diff between tip and LCA)
|
|
228
205
|
// We must do this BEFORE creating the Context so we can list them
|
|
229
206
|
// all in payloadAddrsDomain and payloadAddrsControl.
|
|
230
|
-
|
|
231
|
-
// const depsControlIbGibs: IbGib_V1[] = [];
|
|
232
|
-
const payloadIbGibsControl = [];
|
|
233
|
-
const payloadIbGibsDomain = [];
|
|
207
|
+
const nextDomainIbGibsAndDeps = [];
|
|
234
208
|
// A. Payload (Standard Deep Deps)
|
|
209
|
+
// TODO: THIS IS EXTREMELY INEFFICIENT. adjust this algorithm to
|
|
210
|
+
// only do the dependencies that the other end doesn't need (diff
|
|
211
|
+
// between tip and LCA). This can be done using the information in
|
|
212
|
+
// the ack's conflicts array.
|
|
235
213
|
for (const nextDomainIbGib of nextDomainIbGibs) {
|
|
236
214
|
let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
|
|
237
215
|
if (!nextDomainIbGibGraph) {
|
|
238
216
|
nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
|
|
239
217
|
}
|
|
240
218
|
if (nextDomainIbGibGraph) {
|
|
241
|
-
|
|
219
|
+
nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
|
|
242
220
|
}
|
|
243
221
|
else {
|
|
244
222
|
throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
|
|
245
223
|
}
|
|
246
224
|
}
|
|
247
225
|
if (logalot) {
|
|
248
|
-
console.log(`${lc} payloadIbGibsDomain count: ${
|
|
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
|
-
}
|
|
226
|
+
console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`);
|
|
259
227
|
}
|
|
260
228
|
// 2. Create Context (Envelope)
|
|
229
|
+
// set up subscription for response's payload ibgibs (if any)
|
|
261
230
|
const domainPayloadsMap = new Map();
|
|
262
231
|
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
|
|
232
|
+
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
233
|
+
next: async (ibgib) => {
|
|
234
|
+
if (logalot) {
|
|
235
|
+
console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`);
|
|
236
|
+
}
|
|
237
|
+
const addr = getIbGibAddr({ ibGib: ibgib });
|
|
238
|
+
if (logalotControlDomain) {
|
|
239
|
+
console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`);
|
|
240
|
+
}
|
|
241
|
+
domainPayloadsMap.set(addr, ibgib);
|
|
242
|
+
},
|
|
243
|
+
error: async (e) => {
|
|
244
|
+
if (isIbGib(e)) {
|
|
245
|
+
console.error(`${sublc} error fired. error: ${JSON.stringify(e.data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
console.dir(e);
|
|
249
|
+
console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
complete: async () => {
|
|
253
|
+
if (logalot) {
|
|
254
|
+
console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`);
|
|
255
|
+
}
|
|
256
|
+
await subscription.unsubscribe();
|
|
257
|
+
},
|
|
258
|
+
}));
|
|
259
|
+
// Create the Request Context...
|
|
295
260
|
const requestCtx = await createSyncSagaContext({
|
|
296
261
|
sagaFrame: currentFrame,
|
|
297
262
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
298
|
-
|
|
263
|
+
/**
|
|
264
|
+
* init frame: empty
|
|
265
|
+
*
|
|
266
|
+
*/
|
|
267
|
+
payloadIbGibsDomain: nextDomainIbGibsAndDeps,
|
|
268
|
+
localSpace,
|
|
299
269
|
});
|
|
300
270
|
// Log what we're sending
|
|
301
271
|
if (logalotControlDomain) {
|
|
302
|
-
const
|
|
303
|
-
const domainAddrs = payloadIbGibsDomain.map(p => getIbGibAddr({ ibGib: p }));
|
|
272
|
+
const domainAddrs = nextDomainIbGibsAndDeps.map(p => getIbGibAddr({ ibGib: p }));
|
|
304
273
|
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
|
|
305
274
|
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
306
275
|
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
307
|
-
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${controlAddrs.length}): ${controlAddrs.join(', ') || '(none)'}`);
|
|
308
276
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
309
277
|
}
|
|
310
|
-
//
|
|
311
|
-
// do we need to add requestCtx to the payload ibgibs?
|
|
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
|
|
278
|
+
// update our saga listeners...
|
|
330
279
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
331
|
-
updates$.next(requestCtx); // spins off
|
|
280
|
+
updates$.next(requestCtx); // spins off
|
|
281
|
+
// ...And send the context
|
|
332
282
|
const responseCtx = await peer.witness(requestCtx);
|
|
333
283
|
// C. Handle Response
|
|
334
284
|
if (!responseCtx) {
|
|
@@ -353,53 +303,50 @@ export class SyncSagaCoordinator {
|
|
|
353
303
|
// ---------------------------------------------------------------------
|
|
354
304
|
// 2d. HANDLE RESPONSE
|
|
355
305
|
// ---------------------------------------------------------------------
|
|
306
|
+
// at this point, we have received the response context ibgib, but
|
|
307
|
+
// if there were payloads expected to be transferred, they may not
|
|
308
|
+
// be done yet.
|
|
356
309
|
if (!responseCtx.data) {
|
|
357
310
|
throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
|
|
358
311
|
}
|
|
359
312
|
updates$.next(responseCtx); // spins off -- don't remove this comment!
|
|
360
313
|
// Extract expected domain addresses from response context
|
|
361
314
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
|
|
362
|
-
// TODO: check if we are validating the responseCtx in sync peer. I'm thinking that we must, because we can't start the domain ibgibs until we know the response's control ibgibs are valid.
|
|
363
315
|
// Poll for them if needed
|
|
364
316
|
if (responsePayloadAddrsDomain.length > 0) {
|
|
365
|
-
await this.pollForDomainPayloads({
|
|
317
|
+
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
366
318
|
expectedAddrs: responsePayloadAddrsDomain,
|
|
319
|
+
pollIntervalMs: 20, // relatively arbitrary right now
|
|
367
320
|
domainPayloadsMap,
|
|
368
321
|
tempSpace,
|
|
369
322
|
});
|
|
370
323
|
}
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new Error(`${lc} Peer response missing sagaFrame (E: 83a0)`);
|
|
375
|
-
}
|
|
324
|
+
// at this point, we have received the context AND **all** of the
|
|
325
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
326
|
+
// iteration in the saga loop
|
|
376
327
|
// Log what we received back
|
|
328
|
+
if (!responseCtx.sagaFrame) {
|
|
329
|
+
throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`);
|
|
330
|
+
}
|
|
377
331
|
if (logalotControlDomain) {
|
|
378
332
|
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] || [];
|
|
379
333
|
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: c4d5e6f7a8b9c0d1)`);
|
|
380
334
|
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
381
|
-
console.log(`${lc}${lcControlDomain} Response Frame: ${
|
|
335
|
+
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
382
336
|
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
383
337
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
384
338
|
}
|
|
385
|
-
// Get response frame from localSpace (SyncPeer puts it there)
|
|
386
|
-
let resResponseFrame = await getFromSpace({ space: localSpace, addr: responseFrameAddr });
|
|
387
|
-
if (!resResponseFrame.success || !resResponseFrame.ibGibs?.length) {
|
|
388
|
-
// Fallback to tempSpace
|
|
389
|
-
resResponseFrame = await getFromSpace({ space: tempSpace, addr: responseFrameAddr });
|
|
390
|
-
}
|
|
391
|
-
const responseFrame = resResponseFrame.ibGibs?.[0];
|
|
392
|
-
if (!responseFrame)
|
|
393
|
-
throw new Error(`${lc} Response frame not found (E: 7c2a)`);
|
|
394
339
|
// Handle Response Frame
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
340
|
+
// interface HandleSagaFrameResult {
|
|
341
|
+
// frame: SyncIbGib_V1;
|
|
342
|
+
// payloadIbGibsDomain?: IbGib_V1[];
|
|
343
|
+
// conflictInfos?: SyncSagaConflictInfo;
|
|
344
|
+
// }
|
|
345
|
+
const handleResult = await this.handleSagaContext({
|
|
346
|
+
sagaContext: responseCtx,
|
|
347
|
+
mySpace: localSpace,
|
|
348
|
+
myTempSpace: tempSpace,
|
|
400
349
|
metaspace,
|
|
401
|
-
domainPayloadsMap,
|
|
402
|
-
expectedDomainAddrs: responsePayloadAddrsDomain,
|
|
403
350
|
});
|
|
404
351
|
if (!handleResult) {
|
|
405
352
|
if (logalot) {
|
|
@@ -409,20 +356,15 @@ export class SyncSagaCoordinator {
|
|
|
409
356
|
}
|
|
410
357
|
currentFrame = handleResult.frame;
|
|
411
358
|
// Collect next DOMAIN payloads for the NEXT request
|
|
412
|
-
// Control payloads are handled separately by ensureSagaFrameInBothSpaces
|
|
413
359
|
nextDomainIbGibs = [...(handleResult.payloadIbGibsDomain || [])];
|
|
414
360
|
// Log handler output for next iteration
|
|
415
361
|
if (logalotControlDomain) {
|
|
416
|
-
const handlerControlAddrs = (handleResult.payloadIbGibsControl || []).map(p => getIbGibAddr({ ibGib: p }));
|
|
417
362
|
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
418
363
|
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: d5e6f7a8b9c0d1e2)`);
|
|
419
364
|
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
365
|
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
422
366
|
}
|
|
423
|
-
// Note: currentFrame is automatically added to dependencies at start of next loop via B. Frames logic.
|
|
424
367
|
}
|
|
425
|
-
return allReceivedIbGibs;
|
|
426
368
|
}
|
|
427
369
|
/**
|
|
428
370
|
* Helper to get Knowledge Vector for specific domain ibGibs or TJPs.
|
|
@@ -496,24 +438,27 @@ export class SyncSagaCoordinator {
|
|
|
496
438
|
}
|
|
497
439
|
}
|
|
498
440
|
}
|
|
499
|
-
async
|
|
500
|
-
const lc = `${this.lc}[${this.
|
|
501
|
-
|
|
441
|
+
async analyzeDomainIbGibs({ domainIbGibs, space, }) {
|
|
442
|
+
const lc = `${this.lc}[${this.analyzeDomainIbGibs.name}]`;
|
|
443
|
+
if (domainIbGibs.length === 0) {
|
|
444
|
+
throw new Error(`invalid domainIbGibs. expected at least one, but array is empty. (E: 820a719bcac5a16878a2af697113b826)`);
|
|
445
|
+
}
|
|
446
|
+
const fullGraph = await getDependencyGraph({
|
|
502
447
|
ibGibs: domainIbGibs,
|
|
503
448
|
live: true,
|
|
504
449
|
space,
|
|
505
450
|
});
|
|
506
|
-
const
|
|
451
|
+
const graphIsValid = fullGraph && Object.keys(fullGraph).length > 0;
|
|
507
452
|
if (logalot) {
|
|
508
|
-
console.log(`${lc} graph generated. nodes: ${
|
|
453
|
+
console.log(`${lc} graph generated. nodes: ${graphIsValid ? Object.keys(fullGraph).length : 0}`);
|
|
509
454
|
}
|
|
510
|
-
const srcIbGibs =
|
|
455
|
+
const srcIbGibs = graphIsValid ? Object.values(fullGraph) : [];
|
|
511
456
|
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 {
|
|
457
|
+
const stones = Object.values(src_MapWithoutTjps);
|
|
458
|
+
const withTimelines = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
|
|
459
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: withTimelines });
|
|
460
|
+
const topologicallySortedTjpAddrs = this.sortTimelinesTopologically(timelinesMap);
|
|
461
|
+
return { stones, timelinesMap, topologicallySortedTjpAddrs, fullGraph };
|
|
517
462
|
}
|
|
518
463
|
/**
|
|
519
464
|
* Creates the Initial Saga Frame (Init Stage).
|
|
@@ -524,57 +469,54 @@ export class SyncSagaCoordinator {
|
|
|
524
469
|
* Generates the first frame containing the Knowledge Vector of the Local Space.
|
|
525
470
|
* This is sent to the Receiver to begin Gap Analysis.
|
|
526
471
|
*/
|
|
527
|
-
async createInitFrame({ sagaId, sessionIdentity,
|
|
472
|
+
async createInitFrame({ sagaId, sessionIdentity, domainIbGibs, conflictStrategy, metaspace, localSpace, tempSpace, }) {
|
|
528
473
|
const lc = `${this.lc}[${this.createInitFrame.name}]`;
|
|
529
474
|
try {
|
|
530
475
|
if (logalot) {
|
|
531
476
|
console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`);
|
|
532
477
|
}
|
|
533
478
|
// Analyze Timelines & Stones
|
|
534
|
-
const analysis = await this.
|
|
535
|
-
// this is a lot, so uncomment this only if you want even more logging specific to this analysis
|
|
479
|
+
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
536
480
|
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
537
|
-
const { srcTimelinesMap,
|
|
481
|
+
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
482
|
+
// we need to store the fullGraph in our tempSpace for later...
|
|
483
|
+
await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
|
|
484
|
+
// populate our knowledge vector with tjp -> latest addr/tip in timeline
|
|
485
|
+
const knowledgeVector = {};
|
|
486
|
+
Object.keys(srcTimelinesMap).forEach(tjp => {
|
|
487
|
+
const timeline = srcTimelinesMap[tjp];
|
|
488
|
+
const tip = timeline.at(-1);
|
|
489
|
+
knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
|
|
490
|
+
});
|
|
538
491
|
const initData = {
|
|
539
492
|
sagaId,
|
|
540
493
|
stage: SyncStage.init,
|
|
541
|
-
knowledgeVector
|
|
494
|
+
knowledgeVector,
|
|
542
495
|
identity: sessionIdentity, // KeystoneIbGib is already public data
|
|
543
496
|
mode: SyncMode.sync,
|
|
544
497
|
stones: srcStones.map(s => getIbGibAddr({ ibGib: s })),
|
|
545
498
|
};
|
|
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
499
|
if (logalot) {
|
|
553
500
|
console.log(`${lc} SyncStage.init: ${SyncStage.init}, SyncStage.commit: ${SyncStage.commit}`);
|
|
554
501
|
console.log(`${lc} initData.stage: ${initData.stage}`);
|
|
555
502
|
}
|
|
503
|
+
// create the stone and save/register it
|
|
556
504
|
const initStone = await this.createSyncMsgStone({
|
|
557
505
|
data: initData,
|
|
558
|
-
|
|
559
|
-
metaspace
|
|
506
|
+
localSpace,
|
|
507
|
+
metaspace,
|
|
560
508
|
});
|
|
561
509
|
// if (logalot) { console.log(`${lc} initStone: ${pretty(initStone)} (I: 06e532f8a408549069474e96bed44826)`); }
|
|
510
|
+
// create the initial sync ibgib frame, save/register it
|
|
562
511
|
const sagaFrame = await this.evolveSyncSagaIbGib({
|
|
563
512
|
msgStones: [initStone],
|
|
564
|
-
identity: sessionIdentity,
|
|
565
|
-
space: tempSpace,
|
|
566
|
-
metaspace,
|
|
567
513
|
conflictStrategy,
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
frame: sagaFrame,
|
|
572
|
-
destSpace: localSpace, // localSpace is the Sender's destSpace
|
|
573
|
-
tempSpace,
|
|
574
|
-
metaspace
|
|
514
|
+
sessionIdentity: sessionIdentity,
|
|
515
|
+
metaspace,
|
|
516
|
+
localSpace,
|
|
575
517
|
});
|
|
576
518
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
577
|
-
return { sagaFrame,
|
|
519
|
+
return { sagaFrame, initialDomainGraph: fullGraph };
|
|
578
520
|
}
|
|
579
521
|
catch (error) {
|
|
580
522
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -589,13 +531,16 @@ export class SyncSagaCoordinator {
|
|
|
589
531
|
/**
|
|
590
532
|
* Helper to poll for streaming domain payloads and put them in the
|
|
591
533
|
* local {@link tempSpace}.
|
|
534
|
+
*
|
|
535
|
+
* @returns when all {@link expectedAddrs} are done being transmitted.
|
|
592
536
|
*/
|
|
593
|
-
async pollForDomainPayloads({ expectedAddrs, domainPayloadsMap, tempSpace, }) {
|
|
537
|
+
async pollForDomainPayloads({ expectedAddrs, pollIntervalMs, domainPayloadsMap, tempSpace, }) {
|
|
594
538
|
const lc = `${this.lc}[${this.pollForDomainPayloads.name}]`;
|
|
595
539
|
try {
|
|
596
540
|
if (logalot) {
|
|
597
541
|
console.log(`${lc} starting... (I: 26dce86bfca572939885798802d6e926)`);
|
|
598
542
|
}
|
|
543
|
+
let resultDomainPayloads = [];
|
|
599
544
|
let pending = [...expectedAddrs];
|
|
600
545
|
const start = Date.now();
|
|
601
546
|
/**
|
|
@@ -619,12 +564,17 @@ export class SyncSagaCoordinator {
|
|
|
619
564
|
}
|
|
620
565
|
if (found.length > 0) {
|
|
621
566
|
await putInSpace({ space: tempSpace, ibGibs: found });
|
|
567
|
+
found.forEach(x => resultDomainPayloads.push(x));
|
|
622
568
|
}
|
|
623
569
|
pending = stillPending;
|
|
624
570
|
if (pending.length > 0) {
|
|
625
|
-
await
|
|
571
|
+
await delay(pollIntervalMs);
|
|
626
572
|
}
|
|
627
573
|
}
|
|
574
|
+
if (expectedAddrs.length !== resultDomainPayloads.length) {
|
|
575
|
+
throw new Error(`(UNEXPECTED) expectedAddrs.length !== resultDomainPayloads.length? at this point, we expect all of the payload ibgibs to have been received. (E: 03749a7478c4b8b28bfc86951887a826)`);
|
|
576
|
+
}
|
|
577
|
+
return resultDomainPayloads;
|
|
628
578
|
}
|
|
629
579
|
catch (error) {
|
|
630
580
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -636,12 +586,33 @@ export class SyncSagaCoordinator {
|
|
|
636
586
|
}
|
|
637
587
|
}
|
|
638
588
|
}
|
|
639
|
-
|
|
640
|
-
|
|
589
|
+
/**
|
|
590
|
+
* This is the heart of the "ping pong" transaction, where we send a context
|
|
591
|
+
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
592
|
+
* a whole.
|
|
593
|
+
*
|
|
594
|
+
* This is called in two places:
|
|
595
|
+
*
|
|
596
|
+
* ## 1. Sender
|
|
597
|
+
*
|
|
598
|
+
* On the sender, this is called within the {@link executeSagaLoop} which
|
|
599
|
+
* initiates and drives the sync.
|
|
600
|
+
*
|
|
601
|
+
* ## 2. Receiver
|
|
602
|
+
*
|
|
603
|
+
* On the receiver, this is called directly by the receiving endpoint. That
|
|
604
|
+
* endpoint's job is basically to get these things collocated and prepared
|
|
605
|
+
* to make this call.
|
|
606
|
+
*
|
|
607
|
+
* This is a one-off on the receiver.
|
|
608
|
+
*/
|
|
609
|
+
async handleSagaContext({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
610
|
+
const lc = `${this.lc}[${this.handleSagaContext.name}]`;
|
|
641
611
|
try {
|
|
642
612
|
if (logalot) {
|
|
643
613
|
console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`);
|
|
644
614
|
}
|
|
615
|
+
const sagaIbGib = sagaContext.sagaFrame;
|
|
645
616
|
if (!sagaIbGib.data) {
|
|
646
617
|
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 71b938adf1d87c2527bfd4f86dfd0826)`);
|
|
647
618
|
}
|
|
@@ -649,19 +620,31 @@ export class SyncSagaCoordinator {
|
|
|
649
620
|
console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
|
|
650
621
|
}
|
|
651
622
|
// Get Stage from Stone (or Frame for Init fallback)
|
|
652
|
-
const { stage, messageData } = await this.getStageAndPayloadFromFrame({
|
|
623
|
+
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
|
|
653
624
|
if (logalot) {
|
|
654
625
|
console.log(`${lc} handling frame stage: ${stage}`);
|
|
655
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* don't like this name, need to refactor
|
|
629
|
+
*/
|
|
630
|
+
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
656
631
|
switch (stage) {
|
|
657
632
|
case SyncStage.init:
|
|
658
|
-
return await this.handleInitFrame({
|
|
633
|
+
return await this.handleInitFrame({
|
|
634
|
+
sagaIbGib,
|
|
635
|
+
messageData: messageData,
|
|
636
|
+
metaspace,
|
|
637
|
+
mySpace: mySpace,
|
|
638
|
+
myTempSpace: myTempSpace,
|
|
639
|
+
identity,
|
|
640
|
+
identitySecret
|
|
641
|
+
});
|
|
659
642
|
case SyncStage.ack:
|
|
660
|
-
return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity });
|
|
643
|
+
return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
|
|
661
644
|
case SyncStage.delta:
|
|
662
|
-
return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity, });
|
|
645
|
+
return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
663
646
|
case SyncStage.commit:
|
|
664
|
-
return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace, tempSpace, identity, });
|
|
647
|
+
return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
665
648
|
default:
|
|
666
649
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
667
650
|
}
|
|
@@ -689,282 +672,278 @@ export class SyncSagaCoordinator {
|
|
|
689
672
|
* 3. Identifies what Receiver needs (`deltaReqAddrs`).
|
|
690
673
|
* 4. Returns an `Ack` frame containing these lists.
|
|
691
674
|
*/
|
|
692
|
-
async handleInitFrame({ sagaIbGib, messageData,
|
|
675
|
+
async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
|
|
676
|
+
// identitySecret,
|
|
677
|
+
}) {
|
|
693
678
|
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) {
|
|
679
|
+
try {
|
|
716
680
|
if (logalot) {
|
|
717
|
-
console.log(`${lc}
|
|
681
|
+
console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`);
|
|
718
682
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
683
|
+
console.log(`${lc} [TEST DEBUG] Received destSpace: ${mySpace.data?.name || mySpace.ib} (uuid: ${mySpace.data?.uuid || '[no uuid]'})`);
|
|
684
|
+
if (logalot) {
|
|
685
|
+
console.log(`${lc} starting...`);
|
|
686
|
+
}
|
|
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: 8a2b3c4d5e6f7g8h)`);
|
|
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 deltaReqAddrs = [];
|
|
701
|
+
const pushOfferAddrs = [];
|
|
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}`);
|
|
725
707
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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)`);
|
|
713
|
+
}
|
|
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 (!deltaReqAddrs.includes(addr)) {
|
|
721
|
+
deltaReqAddrs.push(addr);
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
}
|
|
755
725
|
}
|
|
756
|
-
|
|
757
|
-
|
|
726
|
+
/**
|
|
727
|
+
* "remote" local to receiver's context is the sender
|
|
728
|
+
*/
|
|
729
|
+
const remoteKV = initData.knowledgeVector;
|
|
758
730
|
if (logalot) {
|
|
759
|
-
console.log(`${lc}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// 2. Gap Analysis
|
|
763
|
-
for (const tjp of remoteTjps) {
|
|
764
|
-
const remoteAddr = remoteKV[tjp];
|
|
765
|
-
const localAddr = localKV[tjp];
|
|
766
|
-
if (!localAddr) {
|
|
767
|
-
// We (Receiver) don't have this timeline. Request it.
|
|
768
|
-
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
769
|
-
deltaReqAddrs.push(remoteAddr);
|
|
770
|
-
continue;
|
|
771
|
-
}
|
|
772
|
-
if (localAddr === remoteAddr) {
|
|
773
|
-
// Synced
|
|
774
|
-
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
775
|
-
continue;
|
|
776
|
-
}
|
|
777
|
-
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
778
|
-
// Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
|
|
779
|
-
// (Sender has older version, Receiver has newer) -> Receiver Offers Push
|
|
780
|
-
const isRemoteInPast = await isPastFrame({
|
|
781
|
-
olderAddr: remoteAddr,
|
|
782
|
-
newerAddr: localAddr,
|
|
783
|
-
space: destSpace,
|
|
784
|
-
});
|
|
785
|
-
if (isRemoteInPast) {
|
|
786
|
-
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
|
|
787
|
-
pushOfferAddrs.push(localAddr);
|
|
731
|
+
console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`);
|
|
788
732
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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 localKV = {};
|
|
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,
|
|
797
745
|
});
|
|
798
|
-
if (
|
|
799
|
-
|
|
800
|
-
|
|
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
|
+
localKV = resGetLatestAddrs.data.latestAddrsMap;
|
|
753
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
|
|
754
|
+
if (logalot) {
|
|
755
|
+
console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
// 2. Gap Analysis
|
|
759
|
+
for (const tjp of remoteTjps) {
|
|
760
|
+
const remoteAddr = remoteKV[tjp];
|
|
761
|
+
const localAddr = localKV[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}`);
|
|
801
765
|
deltaReqAddrs.push(remoteAddr);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (localAddr === remoteAddr) {
|
|
769
|
+
// Synced
|
|
770
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
774
|
+
// Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
|
|
775
|
+
// (Sender has older version, Receiver has newer) -> Receiver Offers Push
|
|
776
|
+
const isRemoteInPast = await isPastFrame({
|
|
777
|
+
olderAddr: remoteAddr,
|
|
778
|
+
newerAddr: localAddr,
|
|
779
|
+
space: mySpace,
|
|
780
|
+
});
|
|
781
|
+
if (isRemoteInPast) {
|
|
782
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
|
|
783
|
+
pushOfferAddrs.push(localAddr);
|
|
802
784
|
}
|
|
803
785
|
else {
|
|
804
|
-
//
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
reason: 'divergence',
|
|
817
|
-
terminal: true
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
else if (conflictStrategy === 'optimistic') {
|
|
821
|
-
// Optimistic: We want to resolving this.
|
|
822
|
-
// We need to send our history to the Sender so they can Merge.
|
|
823
|
-
// Fetch Full History for Local Timeline
|
|
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)`);
|
|
834
|
-
}
|
|
835
|
-
const timelineAddrs = [localAddr, ...(localTip.rel8ns?.past || [])];
|
|
836
|
-
conflicts.push({
|
|
837
|
-
tjpAddr: tjp,
|
|
838
|
-
localAddr: localAddr,
|
|
839
|
-
remoteAddr,
|
|
840
|
-
timelineAddrs,
|
|
841
|
-
reason: 'divergence',
|
|
842
|
-
terminal: false
|
|
843
|
-
});
|
|
786
|
+
// Remote is not in our past.
|
|
787
|
+
// Either Remote is ahead (Fast-Backward) OR Diverged.
|
|
788
|
+
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
789
|
+
const isLocalInPast = await isPastFrame({
|
|
790
|
+
olderAddr: localAddr,
|
|
791
|
+
newerAddr: remoteAddr,
|
|
792
|
+
space: mySpace,
|
|
793
|
+
});
|
|
794
|
+
if (isLocalInPast) {
|
|
795
|
+
// Fast-Forward: We update to remote's tip.
|
|
796
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
797
|
+
deltaReqAddrs.push(remoteAddr);
|
|
844
798
|
}
|
|
845
799
|
else {
|
|
846
|
-
|
|
800
|
+
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
801
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
|
|
802
|
+
if (conflictStrategy === 'abort') {
|
|
803
|
+
// Abort Strategy: We will treat this as terminal.
|
|
804
|
+
// But for Unified Ack, we just mark it terminal in the list?
|
|
805
|
+
// Or do we actually throw/abort the saga?
|
|
806
|
+
// Current logic (below) aborts the saga if ANY conflict is terminal/abort.
|
|
807
|
+
conflicts.push({
|
|
808
|
+
tjpAddr: tjp,
|
|
809
|
+
localAddr: localAddr,
|
|
810
|
+
remoteAddr,
|
|
811
|
+
timelineAddrs: [], // Not needed for abort
|
|
812
|
+
reason: 'divergence',
|
|
813
|
+
terminal: true
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
else if (conflictStrategy === 'optimistic') {
|
|
817
|
+
// Optimistic: We want to resolving this.
|
|
818
|
+
// We need to send our history to the Sender so they can Merge.
|
|
819
|
+
// Fetch Full History for Local Timeline
|
|
820
|
+
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
821
|
+
// But for now, get full past.
|
|
822
|
+
// Optimization: localKV might not have full history.
|
|
823
|
+
// We need to inspect the 'past' of the local tip.
|
|
824
|
+
// We need the ACTUAL object to get the past.
|
|
825
|
+
// We have localAddr.
|
|
826
|
+
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
827
|
+
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
828
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: ff06ff849fa8e8dba32ce09807411226)`);
|
|
829
|
+
}
|
|
830
|
+
const localTip = resLocalTip.ibGibs[0];
|
|
831
|
+
if (!localTip) {
|
|
832
|
+
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`);
|
|
833
|
+
}
|
|
834
|
+
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
835
|
+
conflicts.push({
|
|
836
|
+
tjpAddr: tjp,
|
|
837
|
+
localAddr,
|
|
838
|
+
remoteAddr,
|
|
839
|
+
timelineAddrs,
|
|
840
|
+
reason: 'divergence',
|
|
841
|
+
terminal: false
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
|
|
846
|
+
}
|
|
847
847
|
}
|
|
848
848
|
}
|
|
849
849
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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);
|
|
850
|
+
// Check if we should ABORT (if any conflict is terminal)
|
|
851
|
+
const hasTerminalConflicts = conflicts.some(c => c.terminal);
|
|
852
|
+
if (hasTerminalConflicts) {
|
|
853
|
+
// Abort Strategy: Kill the saga.
|
|
854
|
+
if (logalot) {
|
|
855
|
+
console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`);
|
|
856
|
+
}
|
|
857
|
+
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
877
858
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
859
|
+
// 3. Build Knowledge Vector (Full History for known timelines)
|
|
860
|
+
// [NEW] Smart Diff
|
|
861
|
+
// We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
|
|
862
|
+
// Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
|
|
863
|
+
/**
|
|
864
|
+
* we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
|
|
865
|
+
*/
|
|
866
|
+
const knowledgeVector = {};
|
|
867
|
+
// [Smart Diff] Populate knowledge from timelines identified by Sender
|
|
868
|
+
for (const tjp of remoteTjps) {
|
|
869
|
+
const localAddr = localKV[tjp];
|
|
870
|
+
if (localAddr) {
|
|
871
|
+
const res = await getFromSpace({ addr: localAddr, space: mySpace });
|
|
872
|
+
if (res.success && res.ibGibs?.[0]) {
|
|
873
|
+
const ibGib = res.ibGibs[0];
|
|
874
|
+
const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
|
|
875
|
+
if (!knowledgeVector[realTjp]) {
|
|
876
|
+
const past = ibGib.rel8ns?.past || [];
|
|
877
|
+
knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
|
|
878
|
+
}
|
|
896
879
|
}
|
|
897
880
|
}
|
|
898
881
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
//
|
|
903
|
-
//
|
|
904
|
-
//
|
|
905
|
-
//
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
882
|
+
// Also populate from `knowledgeVector` in Init if we want bidirectional?
|
|
883
|
+
// No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
|
|
884
|
+
// Let's assume standard flow:
|
|
885
|
+
// 1. Sender says "I have X"
|
|
886
|
+
// 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
|
|
887
|
+
// Problem: Receiver doesn't know X is related to Y without X.
|
|
888
|
+
// SOLUTION:
|
|
889
|
+
// The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
|
|
890
|
+
// `SyncSagaMessageInitData_V1` extends `SyncInitData`.
|
|
891
|
+
// `SyncInitData` has `knowledgeVector`.
|
|
892
|
+
// If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
|
|
893
|
+
// Let's implement Sender populating `Init.knowledgeVector`.
|
|
894
|
+
// But `SyncSagaCoordinator.startSaga` creates Init.
|
|
895
|
+
// 3. Create Ack Frame
|
|
896
|
+
const sagaId = sagaIbGib.data.uuid;
|
|
897
|
+
// Create Payload Stone
|
|
898
|
+
const ackData = {
|
|
899
|
+
sagaId,
|
|
900
|
+
stage: SyncStage.ack,
|
|
901
|
+
deltaReqAddrs,
|
|
902
|
+
pushOfferAddrs,
|
|
903
|
+
knowledgeVector,
|
|
904
|
+
};
|
|
905
|
+
if (conflicts?.length > 0) {
|
|
906
|
+
ackData.conflicts = conflicts;
|
|
914
907
|
}
|
|
915
|
-
|
|
916
|
-
|
|
908
|
+
const ackStone = await this.createSyncMsgStone({
|
|
909
|
+
data: ackData,
|
|
910
|
+
localSpace: myTempSpace,
|
|
911
|
+
metaspace,
|
|
912
|
+
});
|
|
913
|
+
if (logalot) {
|
|
914
|
+
console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`);
|
|
917
915
|
}
|
|
916
|
+
// Evolve Saga
|
|
917
|
+
const ackFrame = await this.evolveSyncSagaIbGib({
|
|
918
|
+
prevSagaIbGib: sagaIbGib,
|
|
919
|
+
msgStones: [ackStone],
|
|
920
|
+
sessionIdentity: identity,
|
|
921
|
+
localSpace: mySpace,
|
|
922
|
+
metaspace,
|
|
923
|
+
});
|
|
924
|
+
// if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
|
|
925
|
+
/**
|
|
926
|
+
* we want to push ibgibs to the remote/sender if we have push
|
|
927
|
+
* offers. an ack frame's payloads, if any, are those push offers
|
|
928
|
+
*/
|
|
929
|
+
// let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
|
|
930
|
+
if (pushOfferAddrs.length > 0) {
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
frame: ackFrame,
|
|
934
|
+
// conflictInfos,
|
|
935
|
+
// payloadIbGibsDomain,
|
|
936
|
+
};
|
|
918
937
|
}
|
|
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)`);
|
|
938
|
+
catch (error) {
|
|
939
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
940
|
+
throw error;
|
|
950
941
|
}
|
|
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);
|
|
942
|
+
finally {
|
|
943
|
+
if (logalot) {
|
|
944
|
+
console.log(`${lc} complete.`);
|
|
945
|
+
}
|
|
966
946
|
}
|
|
967
|
-
return { frame: ackFrame, payloadIbGibsControl };
|
|
968
947
|
}
|
|
969
948
|
/**
|
|
970
949
|
* Handles the `Ack` frame.
|
|
@@ -984,7 +963,7 @@ export class SyncSagaCoordinator {
|
|
|
984
963
|
if (logalot) {
|
|
985
964
|
console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
|
|
986
965
|
}
|
|
987
|
-
const { messageData, } = await this.getStageAndPayloadFromFrame({
|
|
966
|
+
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
988
967
|
const ackData = messageData;
|
|
989
968
|
if (!ackData) {
|
|
990
969
|
throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
|
|
@@ -1221,18 +1200,16 @@ export class SyncSagaCoordinator {
|
|
|
1221
1200
|
}
|
|
1222
1201
|
const deltaStone = await this.createSyncMsgStone({
|
|
1223
1202
|
data: deltaData,
|
|
1224
|
-
|
|
1203
|
+
localSpace: tempSpace,
|
|
1225
1204
|
metaspace,
|
|
1226
1205
|
});
|
|
1227
1206
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1228
1207
|
prevSagaIbGib: sagaIbGib,
|
|
1229
1208
|
msgStones: [deltaStone],
|
|
1230
|
-
identity,
|
|
1231
|
-
|
|
1209
|
+
sessionIdentity: identity,
|
|
1210
|
+
localSpace: tempSpace,
|
|
1232
1211
|
metaspace,
|
|
1233
1212
|
});
|
|
1234
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1235
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1236
1213
|
if (logalot) {
|
|
1237
1214
|
console.log(`${lc} Delta Frame created. Rel8ns: ${JSON.stringify(deltaFrame.rel8ns)}`);
|
|
1238
1215
|
}
|
|
@@ -1241,7 +1218,8 @@ export class SyncSagaCoordinator {
|
|
|
1241
1218
|
if (identity) {
|
|
1242
1219
|
payloadIbGibsControl.push(identity);
|
|
1243
1220
|
}
|
|
1244
|
-
return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1221
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1222
|
+
return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1245
1223
|
}
|
|
1246
1224
|
catch (error) {
|
|
1247
1225
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1268,7 +1246,7 @@ export class SyncSagaCoordinator {
|
|
|
1268
1246
|
if (logalot) {
|
|
1269
1247
|
console.log(`${lc} starting...`);
|
|
1270
1248
|
}
|
|
1271
|
-
const { messageData } = await this.getStageAndPayloadFromFrame({
|
|
1249
|
+
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
1272
1250
|
const deltaData = messageData;
|
|
1273
1251
|
if (!deltaData) {
|
|
1274
1252
|
throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
|
|
@@ -1454,24 +1432,23 @@ export class SyncSagaCoordinator {
|
|
|
1454
1432
|
responseDeltaData.proposeCommit = true;
|
|
1455
1433
|
const deltaStone = await this.createSyncMsgStone({
|
|
1456
1434
|
data: responseDeltaData,
|
|
1457
|
-
|
|
1435
|
+
localSpace: tempSpace,
|
|
1458
1436
|
metaspace
|
|
1459
1437
|
});
|
|
1460
1438
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1461
1439
|
prevSagaIbGib: sagaIbGib,
|
|
1462
1440
|
msgStones: [deltaStone],
|
|
1463
|
-
identity,
|
|
1464
|
-
|
|
1441
|
+
sessionIdentity: identity,
|
|
1442
|
+
localSpace: tempSpace,
|
|
1465
1443
|
metaspace
|
|
1466
1444
|
});
|
|
1467
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1468
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1469
1445
|
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1470
1446
|
const payloadIbGibsControl = [deltaFrame, deltaStone];
|
|
1471
1447
|
if (identity) {
|
|
1472
1448
|
payloadIbGibsControl.push(identity);
|
|
1473
1449
|
}
|
|
1474
|
-
return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1450
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1451
|
+
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1475
1452
|
}
|
|
1476
1453
|
else {
|
|
1477
1454
|
// We have nothing to send.
|
|
@@ -1484,24 +1461,23 @@ export class SyncSagaCoordinator {
|
|
|
1484
1461
|
};
|
|
1485
1462
|
const commitStone = await this.createSyncMsgStone({
|
|
1486
1463
|
data: commitData,
|
|
1487
|
-
|
|
1464
|
+
localSpace: tempSpace,
|
|
1488
1465
|
metaspace
|
|
1489
1466
|
});
|
|
1490
1467
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1491
1468
|
prevSagaIbGib: sagaIbGib,
|
|
1492
1469
|
msgStones: [commitStone],
|
|
1493
|
-
identity,
|
|
1494
|
-
|
|
1470
|
+
sessionIdentity: identity,
|
|
1471
|
+
localSpace: tempSpace,
|
|
1495
1472
|
metaspace
|
|
1496
1473
|
});
|
|
1497
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1498
|
-
await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
|
|
1499
1474
|
// Build control payloads for commit
|
|
1500
1475
|
const commitCtrlPayloads = [commitFrame, commitStone];
|
|
1501
1476
|
if (identity) {
|
|
1502
1477
|
commitCtrlPayloads.push(identity);
|
|
1503
1478
|
}
|
|
1504
|
-
return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1479
|
+
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1480
|
+
return { frame: commitFrame, };
|
|
1505
1481
|
}
|
|
1506
1482
|
else {
|
|
1507
1483
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1515,18 +1491,16 @@ export class SyncSagaCoordinator {
|
|
|
1515
1491
|
};
|
|
1516
1492
|
const deltaStone = await this.createSyncMsgStone({
|
|
1517
1493
|
data: responseDeltaData,
|
|
1518
|
-
|
|
1494
|
+
localSpace: tempSpace,
|
|
1519
1495
|
metaspace
|
|
1520
1496
|
});
|
|
1521
1497
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1522
1498
|
prevSagaIbGib: sagaIbGib,
|
|
1523
1499
|
msgStones: [deltaStone],
|
|
1524
|
-
identity,
|
|
1525
|
-
|
|
1500
|
+
sessionIdentity: identity,
|
|
1501
|
+
localSpace: tempSpace,
|
|
1526
1502
|
metaspace
|
|
1527
1503
|
});
|
|
1528
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1529
|
-
await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
|
|
1530
1504
|
// Check if PEER proposed commit
|
|
1531
1505
|
if (deltaData.proposeCommit) {
|
|
1532
1506
|
if (logalot) {
|
|
@@ -1541,31 +1515,31 @@ export class SyncSagaCoordinator {
|
|
|
1541
1515
|
};
|
|
1542
1516
|
const commitStone = await this.createSyncMsgStone({
|
|
1543
1517
|
data: commitData,
|
|
1544
|
-
|
|
1518
|
+
localSpace: tempSpace,
|
|
1545
1519
|
metaspace
|
|
1546
1520
|
});
|
|
1547
1521
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1548
1522
|
prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
|
|
1549
1523
|
msgStones: [commitStone],
|
|
1550
|
-
identity,
|
|
1551
|
-
|
|
1524
|
+
sessionIdentity: identity,
|
|
1525
|
+
localSpace: tempSpace,
|
|
1552
1526
|
metaspace
|
|
1553
1527
|
});
|
|
1554
|
-
// IMMEDIATELY persist to both spaces for audit trail
|
|
1555
|
-
await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
|
|
1556
1528
|
// Build control payloads for commit
|
|
1557
1529
|
const commitCtrlPayloads2 = [commitFrame, commitStone];
|
|
1558
1530
|
if (identity) {
|
|
1559
1531
|
commitCtrlPayloads2.push(identity);
|
|
1560
1532
|
}
|
|
1561
|
-
return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1533
|
+
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1534
|
+
return { frame: commitFrame, };
|
|
1562
1535
|
}
|
|
1563
1536
|
// Build control payloads for delta propose
|
|
1564
1537
|
const deltaCtrlPayloads = [deltaFrame, deltaStone];
|
|
1565
1538
|
if (identity) {
|
|
1566
1539
|
deltaCtrlPayloads.push(identity);
|
|
1567
1540
|
}
|
|
1568
|
-
return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1541
|
+
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1542
|
+
return { frame: deltaFrame, };
|
|
1569
1543
|
}
|
|
1570
1544
|
}
|
|
1571
1545
|
}
|
|
@@ -1588,7 +1562,7 @@ export class SyncSagaCoordinator {
|
|
|
1588
1562
|
return null;
|
|
1589
1563
|
}
|
|
1590
1564
|
// #endregion Handlers
|
|
1591
|
-
async createSyncMsgStone({ data,
|
|
1565
|
+
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|
|
1592
1566
|
const lc = `${this.lc}[${this.createSyncMsgStone.name}]`;
|
|
1593
1567
|
try {
|
|
1594
1568
|
if (logalot) {
|
|
@@ -1604,7 +1578,7 @@ export class SyncSagaCoordinator {
|
|
|
1604
1578
|
if (logalot) {
|
|
1605
1579
|
console.log(`${lc} Created stone: ${getIbGibAddr({ ibGib: stone })}`);
|
|
1606
1580
|
}
|
|
1607
|
-
await
|
|
1581
|
+
await metaspace.put({ ibGib: stone, space: localSpace });
|
|
1608
1582
|
await metaspace.registerNewIbGib({ ibGib: stone });
|
|
1609
1583
|
return stone;
|
|
1610
1584
|
}
|
|
@@ -1618,41 +1592,10 @@ export class SyncSagaCoordinator {
|
|
|
1618
1592
|
}
|
|
1619
1593
|
}
|
|
1620
1594
|
}
|
|
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
1595
|
/**
|
|
1653
1596
|
* Evolves the saga timeline with a new frame.
|
|
1654
1597
|
*/
|
|
1655
|
-
async evolveSyncSagaIbGib({ prevSagaIbGib, msgStones,
|
|
1598
|
+
async evolveSyncSagaIbGib({ prevSagaIbGib, conflictStrategy, msgStones, sessionIdentity, localSpace, metaspace, }) {
|
|
1656
1599
|
const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
|
|
1657
1600
|
try {
|
|
1658
1601
|
// Validation
|
|
@@ -1675,7 +1618,7 @@ export class SyncSagaCoordinator {
|
|
|
1675
1618
|
throw new Error(`${lc} Mismatched stage in stones. Expected ${stage}, got ${d.stage} (E: d12c6571b0882f762921b60880c3f826)`);
|
|
1676
1619
|
}
|
|
1677
1620
|
}
|
|
1678
|
-
const identityAddr =
|
|
1621
|
+
const identityAddr = sessionIdentity ? getIbGibAddr({ ibGib: sessionIdentity }) : undefined;
|
|
1679
1622
|
if (prevSagaIbGib) {
|
|
1680
1623
|
/**
|
|
1681
1624
|
* rel8ns always include the new msg stone(s)
|
|
@@ -1687,8 +1630,8 @@ export class SyncSagaCoordinator {
|
|
|
1687
1630
|
}
|
|
1688
1631
|
];
|
|
1689
1632
|
// if we're authenticating/signing, we'll have identity
|
|
1690
|
-
if (
|
|
1691
|
-
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [
|
|
1633
|
+
if (sessionIdentity) {
|
|
1634
|
+
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1692
1635
|
}
|
|
1693
1636
|
// remove the existing sync msg stones' addrs
|
|
1694
1637
|
if (!prevSagaIbGib.rel8ns) {
|
|
@@ -1709,7 +1652,7 @@ export class SyncSagaCoordinator {
|
|
|
1709
1652
|
rel8nInfos,
|
|
1710
1653
|
rel8nRemovalInfos,
|
|
1711
1654
|
metaspace,
|
|
1712
|
-
space,
|
|
1655
|
+
space: localSpace,
|
|
1713
1656
|
noDna: true, // Explicitly no DNA for sync frames
|
|
1714
1657
|
});
|
|
1715
1658
|
const newFrame = resAppend;
|
|
@@ -1717,31 +1660,36 @@ export class SyncSagaCoordinator {
|
|
|
1717
1660
|
}
|
|
1718
1661
|
else {
|
|
1719
1662
|
// Create New Timeline (Root Frame)
|
|
1663
|
+
// data
|
|
1720
1664
|
const data = {
|
|
1721
1665
|
uuid: sagaId,
|
|
1722
|
-
// stage, // Removed from V1
|
|
1723
|
-
payload: undefined, // Data in stone
|
|
1724
1666
|
n: 0,
|
|
1725
1667
|
isTjp: true,
|
|
1726
1668
|
conflictStrategy,
|
|
1727
1669
|
};
|
|
1670
|
+
// ib
|
|
1728
1671
|
const ib = await getSyncIb({ data });
|
|
1672
|
+
// rel8ns
|
|
1729
1673
|
const stoneAddrs = msgStones.map(s => getIbGibAddr({ ibGib: s }));
|
|
1730
|
-
const rel8ns = {
|
|
1731
|
-
[SYNC_MSG_REL8N_NAME]: stoneAddrs,
|
|
1732
|
-
};
|
|
1674
|
+
const rel8ns = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
|
|
1733
1675
|
if (identityAddr) {
|
|
1734
1676
|
rel8ns.identity = [identityAddr];
|
|
1735
1677
|
}
|
|
1736
1678
|
const resNew = await createTimeline({
|
|
1737
|
-
space,
|
|
1679
|
+
space: localSpace,
|
|
1738
1680
|
metaspace,
|
|
1739
1681
|
ib,
|
|
1740
1682
|
data,
|
|
1741
1683
|
rel8ns,
|
|
1742
1684
|
parentIb: SYNC_ATOM, // "sync"
|
|
1685
|
+
/**
|
|
1686
|
+
* this will squash the result into a single frame
|
|
1687
|
+
*/
|
|
1743
1688
|
noDna: true,
|
|
1744
1689
|
});
|
|
1690
|
+
if (!!resNew.intermediateIbGibs) {
|
|
1691
|
+
throw new Error(`(UNEXPECTED) resNew.intermediateIbGibs? we were assuming we were creating a single frame. (E: 456818147235d991ccb4d10d0bbe4826)`);
|
|
1692
|
+
}
|
|
1745
1693
|
return resNew.newIbGib;
|
|
1746
1694
|
}
|
|
1747
1695
|
}
|
|
@@ -1750,16 +1698,16 @@ export class SyncSagaCoordinator {
|
|
|
1750
1698
|
throw error;
|
|
1751
1699
|
}
|
|
1752
1700
|
}
|
|
1753
|
-
async getStageAndPayloadFromFrame({
|
|
1701
|
+
async getStageAndPayloadFromFrame({ sagaFrame, space }) {
|
|
1754
1702
|
const lc = `${this.lc}[${this.getStageAndPayloadFromFrame.name}]`;
|
|
1755
1703
|
try {
|
|
1756
1704
|
if (logalot) {
|
|
1757
1705
|
console.log(`${lc} starting... (I: fddc287bd4d55253dc50e519fd352226)`);
|
|
1758
1706
|
}
|
|
1759
|
-
if (!
|
|
1760
|
-
throw new Error(`(UNEXPECTED)
|
|
1707
|
+
if (!sagaFrame.rel8ns) {
|
|
1708
|
+
throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`);
|
|
1761
1709
|
}
|
|
1762
|
-
const stoneAddrs =
|
|
1710
|
+
const stoneAddrs = sagaFrame.rel8ns?.[SYNC_MSG_REL8N_NAME];
|
|
1763
1711
|
if (stoneAddrs && stoneAddrs.length > 0) {
|
|
1764
1712
|
const stoneAddr = stoneAddrs[0];
|
|
1765
1713
|
const res = await getFromSpace({ addr: stoneAddr, space });
|
|
@@ -1778,7 +1726,7 @@ export class SyncSagaCoordinator {
|
|
|
1778
1726
|
}
|
|
1779
1727
|
}
|
|
1780
1728
|
else {
|
|
1781
|
-
throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame?
|
|
1729
|
+
throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? sagaFrame addr: ${getIbGibAddr(sagaFrame)} (E: 43eae8579e289d016741b5526052f226)`);
|
|
1782
1730
|
}
|
|
1783
1731
|
}
|
|
1784
1732
|
catch (error) {
|