@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.
Files changed (78) hide show
  1. package/dist/common/other/graph-helper.d.mts +8 -0
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +31 -1
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/sync/sync-conflict.respec.mjs +12 -11
  6. package/dist/sync/sync-conflict.respec.mjs.map +1 -1
  7. package/dist/sync/sync-constants.d.mts +14 -4
  8. package/dist/sync/sync-constants.d.mts.map +1 -1
  9. package/dist/sync/sync-constants.mjs +15 -3
  10. package/dist/sync/sync-constants.mjs.map +1 -1
  11. package/dist/sync/sync-helpers.d.mts +22 -15
  12. package/dist/sync/sync-helpers.d.mts.map +1 -1
  13. package/dist/sync/sync-helpers.mjs +159 -90
  14. package/dist/sync/sync-helpers.mjs.map +1 -1
  15. package/dist/sync/sync-innerspace-constants.respec.mjs +11 -10
  16. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  17. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +11 -10
  18. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  19. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +11 -10
  20. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  21. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +12 -10
  22. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  23. package/dist/sync/sync-innerspace-partial-update.respec.mjs +14 -10
  24. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  25. package/dist/sync/sync-innerspace.respec.mjs +12 -10
  26. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  27. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +0 -15
  28. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts.map +1 -1
  29. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +15 -14
  30. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  31. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +9 -55
  32. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  33. package/dist/sync/sync-peer/sync-peer-types.d.mts +16 -0
  34. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  35. package/dist/sync/sync-peer/sync-peer-v1.d.mts +39 -5
  36. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  37. package/dist/sync/sync-peer/sync-peer-v1.mjs +141 -4
  38. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  39. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +22 -3
  40. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  41. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +104 -31
  42. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  43. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +5 -0
  44. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  45. package/dist/sync/sync-saga-coordinator.d.mts +74 -70
  46. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  47. package/dist/sync/sync-saga-coordinator.mjs +475 -527
  48. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  49. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +105 -22
  50. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  51. package/dist/sync/sync-types.d.mts +56 -88
  52. package/dist/sync/sync-types.d.mts.map +1 -1
  53. package/dist/sync/sync-types.mjs +5 -0
  54. package/dist/sync/sync-types.mjs.map +1 -1
  55. package/dist/timeline/timeline-api.d.mts.map +1 -1
  56. package/dist/timeline/timeline-api.mjs +15 -8
  57. package/dist/timeline/timeline-api.mjs.map +1 -1
  58. package/package.json +1 -1
  59. package/src/common/other/graph-helper.mts +26 -1
  60. package/src/sync/sync-conflict.respec.mts +12 -11
  61. package/src/sync/sync-constants.mts +15 -4
  62. package/src/sync/sync-helpers.mts +167 -95
  63. package/src/sync/sync-innerspace-constants.respec.mts +11 -10
  64. package/src/sync/sync-innerspace-deep-updates.respec.mts +11 -10
  65. package/src/sync/sync-innerspace-dest-ahead.respec.mts +11 -10
  66. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +12 -10
  67. package/src/sync/sync-innerspace-partial-update.respec.mts +14 -12
  68. package/src/sync/sync-innerspace.respec.mts +12 -10
  69. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +0 -15
  70. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +16 -65
  71. package/src/sync/sync-peer/sync-peer-types.mts +17 -0
  72. package/src/sync/sync-peer/sync-peer-v1.mts +141 -8
  73. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +116 -37
  74. package/src/sync/sync-saga-context/sync-saga-context-types.mts +5 -0
  75. package/src/sync/sync-saga-coordinator.mts +525 -617
  76. package/src/sync/sync-saga-message/sync-saga-message-types.mts +106 -22
  77. package/src/sync/sync-types.mts +59 -101
  78. package/src/timeline/timeline-api.mts +17 -10
@@ -6,12 +6,12 @@ import {
6
6
  pretty,
7
7
  clone,
8
8
  unique,
9
+ delay,
9
10
  } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
10
11
  import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
11
- import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
12
12
  import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
13
- import { IbGib_V1, IbGibRel8ns_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
14
- import { isPrimitive } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
13
+ import { IbGib_V1, } from "@ibgib/ts-gib/dist/V1/types.mjs";
14
+ import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
15
15
 
16
16
  import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
17
17
  import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
@@ -20,35 +20,35 @@ import { KeystoneIbGib_V1 } from "../keystone/keystone-types.mjs";
20
20
  import { KeystoneService_V1 } from "../keystone/keystone-service-v1.mjs";
21
21
  import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
22
22
  import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
23
- import { appendToTimeline, createTimeline, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
23
+ import { appendToTimeline, createTimeline, getHistory, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
24
24
  import {
25
- SyncData_V1, SyncIbGib_V1, SyncInitData, SyncConflictStrategy,
26
- SyncMode,
27
- SyncOptions,
25
+ SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
26
+ SyncRel8ns_V1, DomainIbGibAnalysisInfo, HandleSagaFrameResult,
28
27
  } from "./sync-types.mjs";
29
- import { getSyncIb, isPastFrame } from "./sync-helpers.mjs";
30
- import { getSyncSagaDependencyGraph as getSyncSagaDependencyGraphForThisFrameOnly } from "./sync-helpers.mjs";
31
- import { getDependencyGraph } from "../common/other/graph-helper.mjs";
28
+ import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
29
+ import { getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
32
30
  import {
33
31
  SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
34
32
  SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
35
- SyncSagaMessageCommitData_V1
33
+ SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
36
34
  } from "./sync-saga-message/sync-saga-message-types.mjs";
37
35
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
38
36
  import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
39
37
  import { SyncSagaInfo } from "./sync-types.mjs";
38
+ import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
40
39
  import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
41
40
  import { SyncSagaContextIbGib_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
42
41
  import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
43
42
  import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
44
43
  import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
45
-
46
44
  import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
47
45
  import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
48
46
  import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
49
- import { SubscriptionWitness } from "../common/pubsub/subscription/subscription-types.mjs";
50
47
  import { ErrorIbGib_V1 } from "../common/error/error-types.mjs";
51
- import { SyncPeerInnerspace_V1 } from "./sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs";
48
+ import {
49
+ IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns
50
+ } from "../witness/space/space-types.mjs";
51
+ import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
52
52
 
53
53
 
54
54
  // const logalot = GLOBAL_LOG_A_LOT || true;
@@ -56,16 +56,6 @@ const logalot = false;
56
56
  const logalotControlDomain = true;
57
57
  const lcControlDomain = '[ControlDomain]';
58
58
 
59
- /**
60
- * Result of handling a saga frame.
61
- * Separates control and domain payloads.
62
- */
63
- export interface HandleSagaFrameResult {
64
- frame: SyncIbGib_V1;
65
- payloadIbGibsControl?: IbGib_V1[];
66
- payloadIbGibsDomain?: IbGib_V1[];
67
- }
68
-
69
59
  /**
70
60
  * Orchestrates the synchronization process between two spaces (Source and Destination).
71
61
  *
@@ -103,18 +93,16 @@ export class SyncSagaCoordinator {
103
93
  */
104
94
  async sync({
105
95
  peer,
106
- localSpace: _localSpace,
107
- source: _source,
108
- metaspace,
109
96
  domainIbGibs,
110
- conflictStrategy = 'abort',
97
+ conflictStrategy = SyncConflictStrategy.abort,
111
98
  useSessionIdentity = true,
99
+ metaspace,
100
+ localSpace,
112
101
  }: SyncOptions): Promise<SyncSagaInfo> {
113
102
  const lc = `${this.lc}[${this.sync.name}]`;
114
103
  if (logalot) { console.log(`${lc} starting...`); }
115
104
 
116
- const localSpace = (_source || _localSpace)!;
117
- if (!localSpace) { throw new Error(`${lc} source (or localSpace) required (E: 8a9b0c1d)`); }
105
+ if (!localSpace) { throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`); }
118
106
 
119
107
  // 1. SETUP SAGA METADATA
120
108
  const sagaId = await getUUID();
@@ -130,11 +118,6 @@ export class SyncSagaCoordinator {
130
118
  rejectDone = reject;
131
119
  });
132
120
 
133
- async function getTempSpaceName(): Promise<string> {
134
- const uuid = await getUUID();
135
- return `tmp_sync_space_${uuid.substring(0, 8)}`;
136
- }
137
-
138
121
  // WORKING CONTEXT (Transactional)
139
122
  const tempSpaceName = await getTempSpaceName();
140
123
  const tempSpace = await metaspace.createNewLocalSpace({
@@ -164,40 +147,25 @@ export class SyncSagaCoordinator {
164
147
  // if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
165
148
 
166
149
  // 3. CREATE INITIAL FRAME (Stage.init)
167
- const { sagaFrame: initFrame, srcGraph } = await this.createInitFrame({
150
+ const { sagaFrame: initFrame, initialDomainGraph } = await this.createInitFrame({
168
151
  sagaId,
169
152
  sessionIdentity,
170
- localSpace,
171
153
  domainIbGibs,
172
- tempSpace,
173
- metaspace,
174
- conflictStrategy
175
- });
176
-
177
- // 4. EXECUTE SAGA LOOP (FSM)
178
- // Inject tempSpace into peer so it can pull control payloads to the right place
179
- // if ('opts' in peer && peer.opts) {
180
- if (!peer.data) { throw new Error(`(UNEXPECTED) peer.data falsy? (E: 8546a884c82ffb1999e95d9867da2826)`); }
181
- if (peer.data.classname === SyncPeerInnerspace_V1.name) {
182
- // (peer as SyncPeerInnerspace_V1).senderTempSpace = tempSpace;
183
- }
184
-
185
- const syncedIbGibs = await this.executeSagaLoop({
186
- initialFrame: initFrame,
187
- srcGraph,
188
- peer,
189
- sessionIdentity,
190
- updates$,
191
- localSpace,
192
- tempSpace,
193
- metaspace
154
+ conflictStrategy,
155
+ metaspace, localSpace, tempSpace,
194
156
  });
195
157
 
196
- // 5. MERGE RESULT (Optimistic Commit)
197
- if (syncedIbGibs && syncedIbGibs.length > 0) {
198
- if (logalot) { console.log(`${lc} Merging ${syncedIbGibs.length} synced ibGibs to localSpace...`); }
199
- await putInSpace({ space: localSpace, ibGibs: syncedIbGibs });
200
- }
158
+ // 4. KICK OFF THE PING-PONG SAGA LOOP (FSM)
159
+ // commented to compile
160
+ // await this.executeSagaLoop({
161
+ // initialFrame: initFrame,
162
+ // peer,
163
+ // sessionIdentity,
164
+ // updates$,
165
+ // localSpace,
166
+ // tempSpace,
167
+ // metaspace
168
+ // });
201
169
 
202
170
  resolveDone();
203
171
  if (!updates$.complete) { throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`); }
@@ -263,17 +231,17 @@ export class SyncSagaCoordinator {
263
231
  * **Execution Context**: **Sender (Local)**.
264
232
  *
265
233
  * This method manages the "Ping Pong" request-response cycle on the Sender.
266
- * It sends frames via the Peer Witness and processes the responses using `handleSagaFrame`.
234
+ * It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
267
235
  *
268
236
  * **Data Transport Note**:
269
237
  * Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
270
- * When `handleSagaFrame` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
238
+ * When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
271
239
  * the NEXT request context.
272
240
  * When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
273
241
  */
274
242
  protected async executeSagaLoop({
275
243
  initialFrame,
276
- srcGraph,
244
+ initialDomainGraph,
277
245
  peer,
278
246
  sessionIdentity,
279
247
  updates$,
@@ -282,131 +250,103 @@ export class SyncSagaCoordinator {
282
250
  metaspace
283
251
  }: {
284
252
  initialFrame: SyncIbGib_V1,
285
- srcGraph: { [addr: string]: IbGib_V1 },
253
+ initialDomainGraph: FlatIbGibGraph,
286
254
  peer: SyncPeerWitness,
287
255
  sessionIdentity?: KeystoneIbGib_V1,
288
256
  updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
257
+ metaspace: MetaspaceService
289
258
  localSpace: IbGibSpaceAny,
290
259
  tempSpace: IbGibSpaceAny,
291
- metaspace: MetaspaceService
292
- }): Promise<IbGib_V1[]> {
260
+ }): Promise<void> {
293
261
  const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
294
262
 
295
263
  // The current frame we just generated (e.g., Init or Delta Request)
296
264
  let currentFrame: SyncIbGib_V1 | null = initialFrame;
297
265
  // The payload we need to attach to the message (data we are sending)
298
266
  let nextDomainIbGibs: IbGib_V1[] = [];
299
- // Accumulator for all data we've successfully pulled from the remote
300
- const allReceivedIbGibs: IbGib_V1[] = [];
267
+
268
+ // First, inject local/tempSpace into peer so it can pull as
269
+ // needed...code smell?
270
+ peer.senderSpace = localSpace;
271
+ peer.senderTempSpace = tempSpace;
301
272
 
302
273
  while (currentFrame) {
303
274
  // A. Create Context (Request)
304
275
  // 1. Calculate Full Dependency Graph (including Ancestors/DNA)
305
- // TODO: adjust this algorithm to only do the dependencies that the other end doesn't need (diff between tip and LCA)
306
276
  // We must do this BEFORE creating the Context so we can list them
307
277
  // all in payloadAddrsDomain and payloadAddrsControl.
308
- // const depsDomainIbGibs: IbGib_V1[] = [];
309
- // const depsControlIbGibs: IbGib_V1[] = [];
310
- const payloadIbGibsControl: IbGib_V1[] = [];
311
- const payloadIbGibsDomain: IbGib_V1[] = [];
278
+ const nextDomainIbGibsAndDeps: IbGib_V1[] = [];
312
279
 
313
280
  // A. Payload (Standard Deep Deps)
281
+ // TODO: THIS IS EXTREMELY INEFFICIENT. adjust this algorithm to
282
+ // only do the dependencies that the other end doesn't need (diff
283
+ // between tip and LCA). This can be done using the information in
284
+ // the ack's conflicts array.
314
285
  for (const nextDomainIbGib of nextDomainIbGibs) {
315
286
  let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
316
287
  if (!nextDomainIbGibGraph) {
317
288
  nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
318
289
  }
319
-
320
290
  if (nextDomainIbGibGraph) {
321
- payloadIbGibsDomain.push(...Object.values(nextDomainIbGibGraph));
291
+ nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
322
292
  } else {
323
293
  throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
324
294
  }
325
295
  }
326
- if (logalot) { console.log(`${lc} payloadIbGibsDomain count: ${payloadIbGibsDomain.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`); }
327
-
328
- // B. Frames (Shallow Sync Deps)
329
- if (currentFrame) {
330
- const depsCurrentFrame = await getSyncSagaDependencyGraphForThisFrameOnly({ ibGib: currentFrame, space: tempSpace });
331
- if (depsCurrentFrame.length > 0) {
332
- depsCurrentFrame.forEach(x => payloadIbGibsControl.push(x));
333
- } else {
334
- throw new Error(`(UNEXPECTED) couldn't get deps for currentFrame? currentFrame: ${JSON.stringify(currentFrame)} (E: 06344d07adc80d80b809211171444d26)`);
335
- }
336
- }
296
+ if (logalot) { console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`); }
337
297
 
338
298
  // 2. Create Context (Envelope)
299
+ // set up subscription for response's payload ibgibs (if any)
339
300
  const domainPayloadsMap = new Map<string, IbGib_V1>();
340
301
  const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
341
- let subscription: SubscriptionWitness;
342
- if (peer && peer.payloadIbGibsDomainReceived$) {
343
- // Subscribe to stream
344
- subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
345
- next: async (ibgib: IbGib_V1) => {
346
- if (logalot) { console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`); }
347
- const addr = getIbGibAddr({ ibGib: ibgib });
348
- if (logalotControlDomain) { console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`); }
349
- domainPayloadsMap.set(addr, ibgib);
350
- },
351
- error: async (e: string | Error | ErrorIbGib_V1) => {
352
- if (isIbGib(e)) {
353
- console.error(`${sublc} error fired. error: ${JSON.stringify((e as IbGib_V1).data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
354
- } else {
355
- console.dir(e);
356
- console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
357
- }
358
- },
359
- complete: async () => {
360
- if (logalot) { console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`); }
361
- await subscription.unsubscribe();
362
- },
363
- }));
364
- }
365
-
366
- // 2b. Request Context
302
+ const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
303
+ next: async (ibgib: IbGib_V1) => {
304
+ if (logalot) { console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`); }
305
+ const addr = getIbGibAddr({ ibGib: ibgib });
306
+ if (logalotControlDomain) { console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`); }
307
+ domainPayloadsMap.set(addr, ibgib);
308
+ },
309
+ error: async (e: string | Error | ErrorIbGib_V1) => {
310
+ if (isIbGib(e)) {
311
+ console.error(`${sublc} error fired. error: ${JSON.stringify((e as IbGib_V1).data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
312
+ } else {
313
+ console.dir(e);
314
+ console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
315
+ }
316
+ },
317
+ complete: async () => {
318
+ if (logalot) { console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`); }
319
+ await subscription.unsubscribe();
320
+ },
321
+ }));
322
+
323
+ // Create the Request Context...
367
324
  const requestCtx = await createSyncSagaContext({
368
325
  sagaFrame: currentFrame,
369
326
  sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
370
- payloadIbGibsDomain,
327
+
328
+ /**
329
+ * init frame: empty
330
+ *
331
+ */
332
+ payloadIbGibsDomain: nextDomainIbGibsAndDeps,
333
+ localSpace,
371
334
  });
372
335
 
373
336
  // Log what we're sending
374
337
  if (logalotControlDomain) {
375
- const controlAddrs = payloadIbGibsControl.map(p => getIbGibAddr({ ibGib: p }));
376
- const domainAddrs = payloadIbGibsDomain.map(p => getIbGibAddr({ ibGib: p }));
338
+ const domainAddrs = nextDomainIbGibsAndDeps.map(p => getIbGibAddr({ ibGib: p }));
377
339
  console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
378
340
  console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
379
341
  console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
380
- console.log(`${lc}${lcControlDomain} CONTROL Payloads (${controlAddrs.length}): ${controlAddrs.join(', ') || '(none)'}`);
381
342
  console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
382
343
  }
383
344
 
384
- // Add Context Deps
385
- // do we need to add requestCtx to the payload ibgibs?
386
- // if (requestCtx) {
387
- // const deps = await getSyncSagaDependencyGraphForThisFrameOnly({ ibGib: requestCtx, space: tempSpace });
388
- // if (deps) { deps.forEach(x => payloadIbGibsControl.push(x)); }
389
- // }
390
-
391
- // 3. Identity (if exists)
392
- // Identity might be deep? Keystone? Usually self-contained or shallow references.
393
- if (sessionIdentity) {
394
- payloadIbGibsControl.push(sessionIdentity);
395
- }
396
-
397
- // we only put the **CONTROL** payload ibgibs in localSpace. we
398
- // don't put any domain ibgibs into this durable space until the
399
- // final commit phase.
400
- // The requestCtx envelope itself also goes to localSpace so transfer can find it.
401
- await putInSpace({ space: localSpace, ibGib: requestCtx });
402
- if (payloadIbGibsControl.length > 0) {
403
- await putInSpace({ space: localSpace, ibGibs: payloadIbGibsControl });
404
- }
405
-
406
- // B. Transmit
345
+ // update our saga listeners...
407
346
  // if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
408
- updates$.next(requestCtx); // spins off (don't remove this comment!)
347
+ updates$.next(requestCtx); // spins off
409
348
 
349
+ // ...And send the context
410
350
  const responseCtx = await peer.witness(requestCtx);
411
351
 
412
352
  // C. Handle Response
@@ -429,55 +369,51 @@ export class SyncSagaCoordinator {
429
369
  // ---------------------------------------------------------------------
430
370
  // 2d. HANDLE RESPONSE
431
371
  // ---------------------------------------------------------------------
372
+ // at this point, we have received the response context ibgib, but
373
+ // if there were payloads expected to be transferred, they may not
374
+ // be done yet.
432
375
  if (!responseCtx.data) { throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`); }
433
376
  updates$.next(responseCtx); // spins off -- don't remove this comment!
434
377
 
435
378
  // Extract expected domain addresses from response context
436
379
  const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] as string[] || [];
437
380
 
438
- // 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.
439
-
440
381
  // Poll for them if needed
441
382
  if (responsePayloadAddrsDomain.length > 0) {
442
- await this.pollForDomainPayloads({
383
+ responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
443
384
  expectedAddrs: responsePayloadAddrsDomain,
385
+ pollIntervalMs: 20, // relatively arbitrary right now
444
386
  domainPayloadsMap,
445
387
  tempSpace,
446
388
  });
447
389
  }
448
390
 
449
- // Extract Response Frame
450
- const responseFrameAddr = responseCtx.rel8ns?.sagaFrame?.[0];
451
- if (!responseFrameAddr) { throw new Error(`${lc} Peer response missing sagaFrame (E: 83a0)`); }
391
+ // at this point, we have received the context AND **all** of the
392
+ // domain payloads (if applicable), so we are ready to do the next
393
+ // iteration in the saga loop
452
394
 
453
395
  // Log what we received back
396
+ if (!responseCtx.sagaFrame) { throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`); }
454
397
  if (logalotControlDomain) {
455
398
  const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] as string[] || [];
456
399
  console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: c4d5e6f7a8b9c0d1)`);
457
400
  console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
458
- console.log(`${lc}${lcControlDomain} Response Frame: ${responseFrameAddr}`);
401
+ console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
459
402
  console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
460
403
  console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
461
404
  }
462
405
 
463
- // Get response frame from localSpace (SyncPeer puts it there)
464
- let resResponseFrame = await getFromSpace({ space: localSpace, addr: responseFrameAddr });
465
- if (!resResponseFrame.success || !resResponseFrame.ibGibs?.length) {
466
- // Fallback to tempSpace
467
- resResponseFrame = await getFromSpace({ space: tempSpace, addr: responseFrameAddr });
468
- }
469
- const responseFrame = resResponseFrame.ibGibs?.[0] as any;
470
- if (!responseFrame) throw new Error(`${lc} Response frame not found (E: 7c2a)`);
471
-
472
406
  // Handle Response Frame
473
- const handleResult = await this.handleSagaFrame({
474
- sagaIbGib: responseFrame,
475
- srcGraph: {},
476
- destSpace: localSpace,
477
- tempSpace,
407
+ // interface HandleSagaFrameResult {
408
+ // frame: SyncIbGib_V1;
409
+ // payloadIbGibsDomain?: IbGib_V1[];
410
+ // conflictInfos?: SyncSagaConflictInfo;
411
+ // }
412
+ const handleResult = await this.handleSagaContext({
413
+ sagaContext: responseCtx,
414
+ mySpace: localSpace,
415
+ myTempSpace: tempSpace,
478
416
  metaspace,
479
- domainPayloadsMap,
480
- expectedDomainAddrs: responsePayloadAddrsDomain,
481
417
  });
482
418
 
483
419
  if (!handleResult) {
@@ -488,23 +424,16 @@ export class SyncSagaCoordinator {
488
424
  currentFrame = handleResult.frame;
489
425
 
490
426
  // Collect next DOMAIN payloads for the NEXT request
491
- // Control payloads are handled separately by ensureSagaFrameInBothSpaces
492
427
  nextDomainIbGibs = [...(handleResult.payloadIbGibsDomain || [])];
493
428
 
494
429
  // Log handler output for next iteration
495
430
  if (logalotControlDomain) {
496
- const handlerControlAddrs = (handleResult.payloadIbGibsControl || []).map(p => getIbGibAddr({ ibGib: p }));
497
431
  const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
498
432
  console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: d5e6f7a8b9c0d1e2)`);
499
433
  console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
500
- console.log(`${lc}${lcControlDomain} CONTROL for next (${handlerControlAddrs.length}): ${handlerControlAddrs.join(', ') || '(none)'} (saved via ensureSagaFrameInBothSpaces)`);
501
434
  console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
502
435
  }
503
-
504
- // Note: currentFrame is automatically added to dependencies at start of next loop via B. Frames logic.
505
436
  }
506
-
507
- return allReceivedIbGibs;
508
437
  }
509
438
 
510
439
  /**
@@ -584,42 +513,41 @@ export class SyncSagaCoordinator {
584
513
  }
585
514
  }
586
515
 
587
- protected async analyzeTimelines({
516
+ protected async analyzeDomainIbGibs({
588
517
  domainIbGibs,
589
518
  space,
590
519
  }: {
591
520
  domainIbGibs: IbGib_V1[],
592
521
  space: IbGibSpaceAny,
593
- }): Promise<{
594
- srcStones: IbGib_V1[],
595
- srcTimelinesMap: { [tjp: string]: IbGib_V1[] },
596
- srcSortedTjps: string[],
597
- srcGraph: { [addr: string]: IbGib_V1 },
598
- }> {
599
- const lc = `${this.lc}[${this.analyzeTimelines.name}]`;
600
- const srcGraph = await getDependencyGraph({
522
+ }): Promise<DomainIbGibAnalysisInfo> {
523
+ const lc = `${this.lc}[${this.analyzeDomainIbGibs.name}]`;
524
+ if (domainIbGibs.length === 0) {
525
+ throw new Error(`invalid domainIbGibs. expected at least one, but array is empty. (E: 820a719bcac5a16878a2af697113b826)`);
526
+ }
527
+
528
+ const fullGraph = await getDependencyGraph({
601
529
  ibGibs: domainIbGibs,
602
530
  live: true,
603
531
  space,
604
532
  });
605
533
 
606
- const srcGraphIsValid = srcGraph && Object.keys(srcGraph).length > 0;
607
- if (logalot) { console.log(`${lc} graph generated. nodes: ${srcGraphIsValid ? Object.keys(srcGraph).length : 0}`); }
534
+ const graphIsValid = fullGraph && Object.keys(fullGraph).length > 0;
535
+ if (logalot) { console.log(`${lc} graph generated. nodes: ${graphIsValid ? Object.keys(fullGraph).length : 0}`); }
608
536
 
609
- const srcIbGibs = srcGraphIsValid ? Object.values(srcGraph) : [];
537
+ const srcIbGibs = graphIsValid ? Object.values(fullGraph) : [];
610
538
  const {
611
539
  mapWithTjp_YesDna: srcMapWithTjp_YesDna,
612
540
  mapWithTjp_NoDna: srcMapWithTjp_NoDna,
613
541
  mapWithoutTjps: src_MapWithoutTjps
614
542
  } = splitPerTjpAndOrDna({ ibGibs: srcIbGibs });
615
543
 
616
- const srcStones = Object.values(src_MapWithoutTjps);
617
- const srcLiving = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
544
+ const stones = Object.values(src_MapWithoutTjps);
545
+ const withTimelines = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
618
546
 
619
- const srcTimelinesMap = getTimelinesGroupedByTjp({ ibGibs: srcLiving });
620
- const srcSortedTjps = this.sortTimelinesTopologically(srcTimelinesMap);
547
+ const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: withTimelines });
548
+ const topologicallySortedTjpAddrs = this.sortTimelinesTopologically(timelinesMap);
621
549
 
622
- return { srcStones, srcTimelinesMap, srcSortedTjps, srcGraph };
550
+ return { stones, timelinesMap, topologicallySortedTjpAddrs, fullGraph };
623
551
  }
624
552
 
625
553
  /**
@@ -634,78 +562,72 @@ export class SyncSagaCoordinator {
634
562
  protected async createInitFrame({
635
563
  sagaId,
636
564
  sessionIdentity,
637
- localSpace,
638
565
  domainIbGibs,
639
- tempSpace,
640
- metaspace,
641
566
  conflictStrategy,
567
+ metaspace,
568
+ localSpace,
569
+ tempSpace,
642
570
  }: {
643
571
  sagaId: string,
644
572
  sessionIdentity?: KeystoneIbGib_V1,
645
- localSpace: IbGibSpaceAny,
646
573
  domainIbGibs: IbGib_V1[],
647
- tempSpace: IbGibSpaceAny,
648
- metaspace: MetaspaceService,
649
574
  conflictStrategy: SyncConflictStrategy,
650
- }): Promise<{ sagaFrame: SyncIbGib_V1, srcGraph: { [addr: string]: IbGib_V1 } }> {
575
+ metaspace: MetaspaceService,
576
+ localSpace: IbGibSpaceAny,
577
+ tempSpace: IbGibSpaceAny,
578
+ }): Promise<{ sagaFrame: SyncIbGib_V1, initialDomainGraph: { [addr: string]: IbGib_V1 } }> {
651
579
  const lc = `${this.lc}[${this.createInitFrame.name}]`;
652
580
  try {
653
581
  if (logalot) { console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`); }
654
582
 
655
583
  // Analyze Timelines & Stones
656
- const analysis = await this.analyzeTimelines({ domainIbGibs, space: localSpace });
657
- // this is a lot, so uncomment this only if you want even more logging specific to this analysis
584
+ const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
658
585
  // if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
659
- const { srcTimelinesMap, srcGraph, srcStones, } = analysis;
586
+ const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
587
+
588
+ // we need to store the fullGraph in our tempSpace for later...
589
+ await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
660
590
 
591
+ // populate our knowledge vector with tjp -> latest addr/tip in timeline
592
+ const knowledgeVector: { [tjp: string]: IbGibAddr } = {};
593
+ Object.keys(srcTimelinesMap).forEach(tjp => {
594
+ const timeline = srcTimelinesMap[tjp];
595
+ const tip = timeline.at(-1)!;
596
+ knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
597
+ });
661
598
  const initData: SyncSagaMessageInitData_V1 = {
662
599
  sagaId,
663
600
  stage: SyncStage.init,
664
- knowledgeVector: {},
601
+ knowledgeVector,
665
602
  identity: sessionIdentity, // KeystoneIbGib is already public data
666
603
  mode: SyncMode.sync,
667
604
  stones: srcStones.map(s => getIbGibAddr({ ibGib: s })),
668
605
  };
669
-
670
- // Populate Knowledge Vector
671
- Object.keys(srcTimelinesMap).forEach(tjp => {
672
- const timeline = srcTimelinesMap[tjp];
673
- const tip = timeline.at(-1)!;
674
- initData.knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
675
- });
676
-
677
606
  if (logalot) {
678
607
  console.log(`${lc} SyncStage.init: ${SyncStage.init}, SyncStage.commit: ${SyncStage.commit}`);
679
608
  console.log(`${lc} initData.stage: ${initData.stage}`);
680
609
  }
681
610
 
611
+ // create the stone and save/register it
682
612
  const initStone = await this.createSyncMsgStone({
683
613
  data: initData,
684
- space: tempSpace,
685
- metaspace
614
+ localSpace,
615
+ metaspace,
686
616
  });
687
617
  // if (logalot) { console.log(`${lc} initStone: ${pretty(initStone)} (I: 06e532f8a408549069474e96bed44826)`); }
688
618
 
619
+ // create the initial sync ibgib frame, save/register it
689
620
  const sagaFrame = await this.evolveSyncSagaIbGib({
690
621
  msgStones: [initStone],
691
- identity: sessionIdentity,
692
- space: tempSpace,
693
- metaspace,
694
622
  conflictStrategy,
695
- });
696
-
697
- // IMMEDIATELY persist to both spaces for audit trail
698
- await this.ensureSagaFrameInBothSpaces({
699
- frame: sagaFrame,
700
- destSpace: localSpace, // localSpace is the Sender's destSpace
701
- tempSpace,
702
- metaspace
623
+ sessionIdentity: sessionIdentity,
624
+ metaspace,
625
+ localSpace,
703
626
  });
704
627
 
705
628
  // if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
706
629
 
707
- return { sagaFrame, srcGraph };
708
-
630
+ return { sagaFrame, initialDomainGraph: fullGraph };
709
631
  } catch (error) {
710
632
  console.error(`${lc} ${extractErrorMsg(error)}`);
711
633
  throw error;
@@ -717,20 +639,26 @@ export class SyncSagaCoordinator {
717
639
  /**
718
640
  * Helper to poll for streaming domain payloads and put them in the
719
641
  * local {@link tempSpace}.
642
+ *
643
+ * @returns when all {@link expectedAddrs} are done being transmitted.
720
644
  */
721
645
  protected async pollForDomainPayloads({
722
646
  expectedAddrs,
647
+ pollIntervalMs,
723
648
  domainPayloadsMap,
724
649
  tempSpace,
725
650
  }: {
726
651
  expectedAddrs: string[],
652
+ pollIntervalMs: number,
727
653
  domainPayloadsMap: Map<string, IbGib_V1>,
728
654
  tempSpace: IbGibSpaceAny,
729
- }): Promise<void> {
655
+ }): Promise<IbGib_V1[]> {
730
656
  const lc = `${this.lc}[${this.pollForDomainPayloads.name}]`;
731
657
  try {
732
658
  if (logalot) { console.log(`${lc} starting... (I: 26dce86bfca572939885798802d6e926)`); }
733
659
 
660
+ let resultDomainPayloads: IbGib_V1[] = [];
661
+
734
662
  let pending = [...expectedAddrs];
735
663
  const start = Date.now();
736
664
  /**
@@ -757,14 +685,19 @@ export class SyncSagaCoordinator {
757
685
 
758
686
  if (found.length > 0) {
759
687
  await putInSpace({ space: tempSpace, ibGibs: found });
688
+ found.forEach(x => resultDomainPayloads.push(x));
760
689
  }
761
690
 
762
691
  pending = stillPending;
763
692
 
764
- if (pending.length > 0) {
765
- await new Promise(resolve => setTimeout(resolve, 50));
766
- }
693
+ if (pending.length > 0) { await delay(pollIntervalMs); }
767
694
  }
695
+
696
+ if (expectedAddrs.length !== resultDomainPayloads.length) {
697
+ throw new Error(`(UNEXPECTED) expectedAddrs.length !== resultDomainPayloads.length? at this point, we expect all of the payload ibgibs to have been received. (E: 03749a7478c4b8b28bfc86951887a826)`);
698
+ }
699
+
700
+ return resultDomainPayloads;
768
701
  } catch (error) {
769
702
  console.error(`${lc} ${extractErrorMsg(error)}`);
770
703
  throw error;
@@ -773,49 +706,85 @@ export class SyncSagaCoordinator {
773
706
  }
774
707
  }
775
708
 
776
- async handleSagaFrame({
777
- sagaIbGib,
778
- srcGraph,
779
- destSpace,
780
- tempSpace,
709
+ /**
710
+ * This is the heart of the "ping pong" transaction, where we send a context
711
+ * and receive a context. IOW, this drives the FSM of the sync saga ibgib as
712
+ * a whole.
713
+ *
714
+ * This is called in two places:
715
+ *
716
+ * ## 1. Sender
717
+ *
718
+ * On the sender, this is called within the {@link executeSagaLoop} which
719
+ * initiates and drives the sync.
720
+ *
721
+ * ## 2. Receiver
722
+ *
723
+ * On the receiver, this is called directly by the receiving endpoint. That
724
+ * endpoint's job is basically to get these things collocated and prepared
725
+ * to make this call.
726
+ *
727
+ * This is a one-off on the receiver.
728
+ */
729
+ public async handleSagaContext({
730
+ sagaContext,
731
+ mySpace,
732
+ myTempSpace,
781
733
  identity,
782
734
  identitySecret,
783
735
  metaspace,
784
736
  }: {
785
- sagaIbGib: SyncIbGib_V1,
786
- srcGraph: { [addr: string]: IbGib_V1 },
787
- destSpace: IbGibSpaceAny,
788
- tempSpace: IbGibSpaceAny,
737
+ sagaContext: SyncSagaContextIbGib_V1,
738
+ /**
739
+ * Local space relative to the execution context's POV
740
+ */
741
+ mySpace: IbGibSpaceAny,
742
+ /**
743
+ * Local temp space relative to the execution context's POV
744
+ */
745
+ myTempSpace: IbGibSpaceAny,
789
746
  identity?: KeystoneIbGib_V1,
790
747
  identitySecret?: string,
791
748
  metaspace: MetaspaceService,
792
- domainPayloadsMap?: Map<string, IbGib_V1>,
793
- expectedDomainAddrs?: string[],
794
749
  }): Promise<HandleSagaFrameResult | null> {
795
- const lc = `${this.lc}[${this.handleSagaFrame.name}]`;
750
+ const lc = `${this.lc}[${this.handleSagaContext.name}]`;
796
751
  try {
797
752
  if (logalot) { console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`); }
798
753
 
754
+ const sagaIbGib = sagaContext.sagaFrame;
799
755
  if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 71b938adf1d87c2527bfd4f86dfd0826)`); }
800
756
  if (logalot) { console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`); }
801
757
 
802
758
  // Get Stage from Stone (or Frame for Init fallback)
803
- const { stage, messageData } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
759
+ const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
804
760
 
805
761
  if (logalot) { console.log(`${lc} handling frame stage: ${stage}`); }
806
762
 
763
+ /**
764
+ * don't like this name, need to refactor
765
+ */
766
+ const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
767
+
807
768
  switch (stage) {
808
769
  case SyncStage.init:
809
- return await this.handleInitFrame({ sagaIbGib, messageData, metaspace, destSpace, tempSpace, identity, identitySecret });
770
+ return await this.handleInitFrame({
771
+ sagaIbGib,
772
+ messageData: messageData as SyncSagaMessageInitData_V1,
773
+ metaspace,
774
+ mySpace: mySpace,
775
+ myTempSpace: myTempSpace,
776
+ identity,
777
+ identitySecret
778
+ });
810
779
 
811
780
  case SyncStage.ack:
812
- return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity });
781
+ return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
813
782
 
814
783
  case SyncStage.delta:
815
- return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity, });
784
+ return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
816
785
 
817
786
  case SyncStage.commit:
818
- return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace, tempSpace, identity, });
787
+ return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
819
788
 
820
789
  default:
821
790
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
@@ -845,309 +814,297 @@ export class SyncSagaCoordinator {
845
814
  protected async handleInitFrame({
846
815
  sagaIbGib,
847
816
  messageData,
848
- destSpace,
849
- tempSpace,
817
+ mySpace,
818
+ myTempSpace,
850
819
  metaspace,
851
820
  identity,
852
- identitySecret,
821
+ // identitySecret,
853
822
  }: {
854
823
  sagaIbGib: SyncIbGib_V1,
855
- messageData: any,
856
- destSpace: IbGibSpaceAny,
857
- tempSpace: IbGibSpaceAny,
824
+ messageData: SyncSagaMessageInitData_V1,
825
+ /**
826
+ * Local space relative to the execution context's POV
827
+ */
828
+ mySpace: IbGibSpaceAny,
829
+ /**
830
+ * Local temp space relative to the execution context's POV.
831
+ *
832
+ * NOTE: Since this always executes on the receiver's end, this should
833
+ * be the receiver's temp space.
834
+ */
835
+ myTempSpace: IbGibSpaceAny,
858
836
  metaspace: MetaspaceService,
859
837
  identity?: KeystoneIbGib_V1,
860
838
  identitySecret?: string,
861
839
  }): Promise<HandleSagaFrameResult | null> {
862
840
  const lc = `${this.lc}[${this.handleInitFrame.name}]`;
863
- console.log(`${lc} [TEST DEBUG] Received destSpace: ${destSpace.data?.name || destSpace.ib} (uuid: ${destSpace.data?.uuid || '[no uuid]'})`);
864
- if (logalot) { console.log(`${lc} starting...`); }
841
+ try {
842
+ if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
865
843
 
866
- // Extract Init Data
867
- const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
868
- if (initData.stage !== SyncStage.init) {
869
- throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
870
- }
871
- // if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
872
- if (!initData || !initData.knowledgeVector) {
873
- throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
874
- }
844
+ console.log(`${lc} [TEST DEBUG] Received destSpace: ${mySpace.data?.name || mySpace.ib} (uuid: ${mySpace.data?.uuid || '[no uuid]'})`);
845
+ if (logalot) { console.log(`${lc} starting...`); }
875
846
 
876
- // Determine Strategy from Saga Data (since V1 stores it in root)
877
- const conflictStrategy = sagaIbGib.data!.conflictStrategy || 'abort';
878
-
879
- // 2. Gap Analysis
880
- const conflicts: { tjpAddr: string, localAddr: string, remoteAddr: string, timelineAddrs: string[], reason: string, terminal: boolean }[] = [];
881
-
882
- const deltaReqAddrs: string[] = [];
883
- const pushOfferAddrs: string[] = [];
884
-
885
- // Stones Analysis (Constants / Non-TJPs)
886
- const stones = initData.stones || [];
887
- if (stones.length > 0) {
888
- if (logalot) { console.log(`${lc} processing stones: ${stones.length}`); }
889
- // Check if we have these stones
890
- const resStones = await getFromSpace({ space: destSpace, addrs: stones });
891
- const addrsNotFound = resStones.rawResultIbGib?.data?.addrsNotFound;
892
- if (addrsNotFound && addrsNotFound.length > 0) {
893
- if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
894
- addrsNotFound.forEach(addr => {
895
- if (!deltaReqAddrs.includes(addr)) {
896
- deltaReqAddrs.push(addr);
897
- }
898
- });
847
+ // Extract Init Data
848
+ const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
849
+ if (initData.stage !== SyncStage.init) {
850
+ throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
851
+ }
852
+ // if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
853
+ if (!initData || !initData.knowledgeVector) {
854
+ throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
899
855
  }
900
- }
901
-
902
- const remoteKV = initData.knowledgeVector;
903
- if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
904
- const remoteTjps = Object.keys(remoteKV);
905
- console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
906
- if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
907
-
908
- // 1. Get Local Latest Addrs for all TJPs
909
- let localKV: { [tjp: string]: string | null } = {};
910
- if (remoteTjps.length > 0) {
911
- // Batch get latest addrs for the TJPs
912
- const resGetLatestAddrs = await getLatestAddrs({
913
- space: destSpace,
914
- tjpAddrs: remoteTjps,
915
- });
916
- if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
917
- if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
918
- localKV = resGetLatestAddrs.data.latestAddrsMap;
919
- console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
920
- if (logalot) { console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`); }
921
- }
922
-
923
- // 2. Gap Analysis
924
-
925
-
926
- for (const tjp of remoteTjps) {
927
- const remoteAddr = remoteKV[tjp];
928
- const localAddr = localKV[tjp];
929
856
 
930
- if (!localAddr) {
931
- // We (Receiver) don't have this timeline. Request it.
932
- console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
933
- deltaReqAddrs.push(remoteAddr);
934
- continue;
857
+ // Determine Strategy from Saga Data (since V1 stores it in root)
858
+ const conflictStrategy = sagaIbGib.data!.conflictStrategy || SyncConflictStrategy.abort;
859
+
860
+ // 2. Gap Analysis
861
+ const conflicts: SyncSagaConflictInfo[] = [];
862
+ const deltaReqAddrs: string[] = [];
863
+ const pushOfferAddrs: string[] = [];
864
+
865
+ // First do the consant stones (Non-TJPs)
866
+ const stones = initData.stones || [];
867
+ if (stones.length > 0) {
868
+ if (logalot) { console.log(`${lc} processing stones: ${stones.length}`); }
869
+ // Check if we have these stones
870
+ const resStones = await getFromSpace({ space: mySpace, addrs: stones });
871
+ const rawResultIbGib = resStones.rawResultIbGib as IbGibSpaceResultIbGib<IbGib_V1, IbGibSpaceResultData, IbGibSpaceResultRel8ns>;
872
+ if (!rawResultIbGib.data) { throw new Error(`(UNEXPECTED) rawResultIbGib.data falsy? (E: f3c40b492adc02c3f480b998fc7a5926)`); }
873
+ const addrsNotFound = rawResultIbGib.data.addrsNotFound;
874
+ if (addrsNotFound && addrsNotFound.length > 0) {
875
+ if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
876
+ addrsNotFound.forEach(addr => {
877
+ if (!deltaReqAddrs.includes(addr)) { deltaReqAddrs.push(addr); }
878
+ });
879
+ }
935
880
  }
936
881
 
937
- if (localAddr === remoteAddr) {
938
- // Synced
939
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
940
- continue;
882
+ /**
883
+ * "remote" local to receiver's context is the sender
884
+ */
885
+ const remoteKV = initData.knowledgeVector;
886
+ if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
887
+ const remoteTjps = Object.keys(remoteKV);
888
+ console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
889
+ if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
890
+
891
+ // 1. Get Local Latest Addrs for all TJPs
892
+ let localKV: { [tjp: string]: string | null } = {};
893
+ if (remoteTjps.length > 0) {
894
+ // Batch get latest addrs for the TJPs
895
+ const resGetLatestAddrs = await getLatestAddrs({
896
+ space: mySpace, // executing on receiver, so this is receiver's space
897
+ tjpAddrs: remoteTjps,
898
+ });
899
+ if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
900
+ if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
901
+ localKV = resGetLatestAddrs.data.latestAddrsMap;
902
+ console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
903
+ if (logalot) { console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`); }
941
904
  }
942
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
943
-
944
- // Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
945
- // (Sender has older version, Receiver has newer) -> Receiver Offers Push
946
- const isRemoteInPast = await isPastFrame({
947
- olderAddr: remoteAddr,
948
- newerAddr: localAddr,
949
- space: destSpace,
950
- });
951
905
 
952
- if (isRemoteInPast) {
953
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
954
- pushOfferAddrs.push(localAddr);
955
- } else {
956
- // Remote is not in our past.
957
- // Either Remote is ahead (Fast-Backward) OR Diverged.
906
+ // 2. Gap Analysis
907
+ for (const tjp of remoteTjps) {
908
+ const remoteAddr = remoteKV[tjp];
909
+ const localAddr = localKV[tjp];
958
910
 
959
- // Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
960
- const isLocalInPast = await isPastFrame({
961
- olderAddr: localAddr,
962
- newerAddr: remoteAddr,
963
- space: destSpace,
911
+ if (!localAddr) {
912
+ // We (Receiver) don't have this timeline at all. Request it.
913
+ console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
914
+ deltaReqAddrs.push(remoteAddr);
915
+ continue;
916
+ }
917
+
918
+ if (localAddr === remoteAddr) {
919
+ // Synced
920
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
921
+ continue;
922
+ }
923
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
924
+
925
+ // Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
926
+ // (Sender has older version, Receiver has newer) -> Receiver Offers Push
927
+ const isRemoteInPast = await isPastFrame({
928
+ olderAddr: remoteAddr,
929
+ newerAddr: localAddr,
930
+ space: mySpace,
964
931
  });
965
932
 
966
- if (isLocalInPast) {
967
- // Fast-Forward: We update to remote's tip.
968
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
969
- deltaReqAddrs.push(remoteAddr);
933
+ if (isRemoteInPast) {
934
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
935
+ pushOfferAddrs.push(localAddr);
970
936
  } else {
971
- // DIVERGENCE: Both have changes the other doesn't know about.
972
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
973
-
974
- if (conflictStrategy === 'abort') {
975
- // Abort Strategy: We will treat this as terminal.
976
- // But for Unified Ack, we just mark it terminal in the list?
977
- // Or do we actually throw/abort the saga?
978
- // Current logic (below) aborts the saga if ANY conflict is terminal/abort.
979
- conflicts.push({
980
- tjpAddr: tjp,
981
- localAddr: localAddr!,
982
- remoteAddr,
983
- timelineAddrs: [], // Not needed for abort
984
- reason: 'divergence',
985
- terminal: true
986
- });
987
- } else if (conflictStrategy === 'optimistic') {
988
- // Optimistic: We want to resolving this.
989
- // We need to send our history to the Sender so they can Merge.
990
-
991
- // Fetch Full History for Local Timeline
992
- // Note: We might optimize this to only send "recent" history if we had a KV?
993
- // But for now, get full past.
994
- // Optimization: localKV might not have full history.
995
- // We need to inspect the 'past' of the local tip.
996
-
997
- // We need the ACTUAL object to get the past.
998
- // We have localAddr.
999
- const resLocalTip = await getFromSpace({ space: destSpace, addr: localAddr! });
1000
- const localTip = resLocalTip.ibGibs?.[0];
1001
- if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`); }
1002
-
1003
- const timelineAddrs = [localAddr!, ...(localTip.rel8ns?.past || [])];
1004
-
1005
- conflicts.push({
1006
- tjpAddr: tjp,
1007
- localAddr: localAddr!,
1008
- remoteAddr,
1009
- timelineAddrs,
1010
- reason: 'divergence',
1011
- terminal: false
1012
- });
937
+ // Remote is not in our past.
938
+ // Either Remote is ahead (Fast-Backward) OR Diverged.
939
+
940
+ // Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
941
+ const isLocalInPast = await isPastFrame({
942
+ olderAddr: localAddr,
943
+ newerAddr: remoteAddr,
944
+ space: mySpace,
945
+ });
1013
946
 
947
+ if (isLocalInPast) {
948
+ // Fast-Forward: We update to remote's tip.
949
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
950
+ deltaReqAddrs.push(remoteAddr);
1014
951
  } else {
1015
- throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
952
+ // DIVERGENCE: Both have changes the other doesn't know about.
953
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
954
+
955
+ if (conflictStrategy === 'abort') {
956
+ // Abort Strategy: We will treat this as terminal.
957
+ // But for Unified Ack, we just mark it terminal in the list?
958
+ // Or do we actually throw/abort the saga?
959
+ // Current logic (below) aborts the saga if ANY conflict is terminal/abort.
960
+ conflicts.push({
961
+ tjpAddr: tjp,
962
+ localAddr: localAddr,
963
+ remoteAddr,
964
+ timelineAddrs: [], // Not needed for abort
965
+ reason: 'divergence',
966
+ terminal: true
967
+ });
968
+ } else if (conflictStrategy === 'optimistic') {
969
+ // Optimistic: We want to resolving this.
970
+ // We need to send our history to the Sender so they can Merge.
971
+
972
+ // Fetch Full History for Local Timeline
973
+ // Note: We might optimize this to only send "recent" history if we had a KV?
974
+ // But for now, get full past.
975
+ // Optimization: localKV might not have full history.
976
+ // We need to inspect the 'past' of the local tip.
977
+
978
+ // We need the ACTUAL object to get the past.
979
+ // We have localAddr.
980
+ const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
981
+ if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
982
+ throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: ff06ff849fa8e8dba32ce09807411226)`);
983
+ }
984
+ const localTip = resLocalTip.ibGibs[0];
985
+ if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`); }
986
+
987
+ const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
988
+
989
+ conflicts.push({
990
+ tjpAddr: tjp,
991
+ localAddr,
992
+ remoteAddr,
993
+ timelineAddrs,
994
+ reason: 'divergence',
995
+ terminal: false
996
+ });
997
+
998
+ } else {
999
+ throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
1000
+ }
1016
1001
  }
1017
1002
  }
1018
1003
  }
1019
- }
1020
-
1021
- // Check if we should ABORT (if any conflict is terminal)
1022
- const hasTerminalConflicts = conflicts.some(c => c.terminal);
1023
1004
 
1024
- if (hasTerminalConflicts) {
1025
- // Abort Strategy: Kill the saga.
1026
- if (logalot) { console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`); }
1005
+ // Check if we should ABORT (if any conflict is terminal)
1006
+ const hasTerminalConflicts = conflicts.some(c => c.terminal);
1027
1007
 
1028
- // We reuse the ConflictData structure for terminal aborts?
1029
- // Or do we send an Ack with terminal conflicts?
1030
- // Original design had explicit Conflict Frame for Abort.
1031
- // Let's stick to that for purely terminal cases to be safe/explicit?
1032
- // Or Unified: Just send Ack with terminal=true conflicts. Sender sees them and aborts.
1008
+ if (hasTerminalConflicts) {
1009
+ // Abort Strategy: Kill the saga.
1010
+ if (logalot) { console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`); }
1033
1011
 
1034
- // Decision: Unified Ack for everything is cleaner protocol.
1035
- // But wait, the original code below creates a Conflict Stone.
1036
- // Let's preserve the explicit 'Conflict' frame for total aborts if that's easier,
1037
- // OR fully switch to Ack.
1038
- // Protocol states: Init -> Ack. If Ack contains terminal errors, Sender can Commit(Fail).
1039
-
1040
- // Let's use Ack with conflicts.
1041
- }
1042
-
1043
- // 2. Add Push Offers (Missing in Local)
1044
- // Check if we have them. If not, ask for them.
1045
- for (const addr of pushOfferAddrs) {
1046
- const hasIt = await getFromSpace({ addr, space: destSpace });
1047
- if (!hasIt.success || !hasIt.ibGibs || hasIt.ibGibs.length === 0) {
1048
- // If we don't have it, we put it in `deltaReqAddrs` of the Ack.
1049
- deltaReqAddrs.push(addr);
1012
+ throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
1050
1013
  }
1051
- }
1052
1014
 
1053
- // 3. Build Knowledge Vector (Full History for known timelines)
1054
- // [NEW] Smart Diff
1055
- // We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
1056
- // Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
1057
-
1058
- const knowledgeVector: { [groupKey: string]: string[] } = {};
1059
- const relevantAddrs = new Set([...pushOfferAddrs, ...deltaReqAddrs]);
1060
-
1061
- // [Smart Diff] Populate knowledge from timelines identified by Sender
1062
- for (const tjp of remoteTjps) {
1063
- const localAddr = localKV[tjp];
1064
- if (localAddr) {
1065
- const res = await getFromSpace({ addr: localAddr, space: destSpace });
1066
- if (res.success && res.ibGibs?.[0]) {
1067
- const ibGib = res.ibGibs[0];
1068
- const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
1069
- if (!knowledgeVector[realTjp]) {
1070
- const past = ibGib.rel8ns?.past || [];
1071
- knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
1015
+ // 3. Build Knowledge Vector (Full History for known timelines)
1016
+ // [NEW] Smart Diff
1017
+ // We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
1018
+ // Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
1019
+
1020
+ /**
1021
+ * we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
1022
+ */
1023
+ const knowledgeVector: { [groupKey: string]: string[] } = {};
1024
+
1025
+ // [Smart Diff] Populate knowledge from timelines identified by Sender
1026
+ for (const tjp of remoteTjps) {
1027
+ const localAddr = localKV[tjp];
1028
+ if (localAddr) {
1029
+ const res = await getFromSpace({ addr: localAddr, space: mySpace });
1030
+ if (res.success && res.ibGibs?.[0]) {
1031
+ const ibGib = res.ibGibs[0];
1032
+ const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
1033
+ if (!knowledgeVector[realTjp]) {
1034
+ const past = ibGib.rel8ns?.past || [];
1035
+ knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
1036
+ }
1072
1037
  }
1073
1038
  }
1074
1039
  }
1075
- }
1076
1040
 
1077
- // [Smart Diff] Also check individual requested addresses (Fall back for constants/unknown TJPs)
1078
- for (const addr of relevantAddrs) {
1079
- // Only process if not already covered by TJP logic above
1080
- // We can't really know if it's covered easily without resolving.
1081
- // But if we don't have it (requesting), we won't find it here anyway.
1082
- // If we DO have it (push offer), we might find it.
1083
- const res = await getFromSpace({ addr, space: destSpace });
1084
- if (res.success && res.ibGibs?.[0]) {
1085
- const ibGib = res.ibGibs[0];
1086
- const tjpAddr = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib });
1087
-
1088
- if (!knowledgeVector[tjpAddr]) {
1089
- const past = ibGib.rel8ns?.past || [];
1090
- knowledgeVector[tjpAddr] = [getIbGibAddr({ ibGib }), ...past];
1091
- }
1092
- } else {
1093
- // We don't have `addr`.
1094
- }
1095
- }
1041
+ // Also populate from `knowledgeVector` in Init if we want bidirectional?
1042
+ // No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
1043
+ // Let's assume standard flow:
1044
+ // 1. Sender says "I have X"
1045
+ // 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
1046
+ // Problem: Receiver doesn't know X is related to Y without X.
1047
+
1048
+ // SOLUTION:
1049
+ // The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
1050
+ // `SyncSagaMessageInitData_V1` extends `SyncInitData`.
1051
+ // `SyncInitData` has `knowledgeVector`.
1052
+ // If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
1053
+
1054
+ // Let's implement Sender populating `Init.knowledgeVector`.
1055
+ // But `SyncSagaCoordinator.startSaga` creates Init.
1056
+
1057
+ // 3. Create Ack Frame
1058
+ const sagaId = sagaIbGib.data!.uuid;
1059
+ // Create Payload Stone
1060
+ const ackData: SyncSagaMessageAckData_V1 = {
1061
+ sagaId,
1062
+ stage: SyncStage.ack,
1063
+ deltaReqAddrs,
1064
+ pushOfferAddrs,
1065
+ knowledgeVector,
1066
+ };
1067
+ if (conflicts?.length > 0) { ackData.conflicts = conflicts; }
1096
1068
 
1097
- // Also populate from `knowledgeVector` in Init if we want bidirectional?
1098
- // No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
1099
- // Let's assume standard flow:
1100
- // 1. Sender says "I have X"
1101
- // 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
1102
- // Problem: Receiver doesn't know X is related to Y without X.
1103
-
1104
- // SOLUTION:
1105
- // The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
1106
- // `SyncSagaMessageInitData_V1` extends `SyncInitData`.
1107
- // `SyncInitData` has `knowledgeVector`.
1108
- // If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
1109
-
1110
- // Let's implement Sender populating `Init.knowledgeVector`.
1111
- // But `SyncSagaCoordinator.startSaga` creates Init.
1112
-
1113
- // 3. Create Ack Frame
1114
- const sagaId = sagaIbGib.data!.uuid;
1115
- // Create Payload Stone
1116
- const ackData: SyncSagaMessageAckData_V1 = {
1117
- sagaId,
1118
- stage: SyncStage.ack,
1119
- deltaReqAddrs,
1120
- pushOfferAddrs,
1121
- knowledgeVector,
1122
- conflicts: conflicts.length > 0 ? conflicts : undefined, // Include conflicts if any detected
1123
- };
1069
+ const ackStone = await this.createSyncMsgStone({
1070
+ data: ackData,
1071
+ localSpace: myTempSpace,
1072
+ metaspace,
1073
+ });
1074
+ if (logalot) { console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`); }
1124
1075
 
1125
- const ackStone = await this.createSyncMsgStone({
1126
- data: ackData,
1127
- space: tempSpace,
1128
- metaspace,
1129
- });
1130
- if (logalot) { console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`); }
1131
-
1132
- // Evolve Saga
1133
- const ackFrame = await this.evolveSyncSagaIbGib({
1134
- prevSagaIbGib: sagaIbGib,
1135
- msgStones: [ackStone],
1136
- identity,
1137
- space: tempSpace,
1138
- metaspace,
1139
- });
1076
+ // Evolve Saga
1077
+ const ackFrame = await this.evolveSyncSagaIbGib({
1078
+ prevSagaIbGib: sagaIbGib,
1079
+ msgStones: [ackStone],
1080
+ sessionIdentity: identity,
1081
+ localSpace: mySpace,
1082
+ metaspace,
1083
+ });
1140
1084
 
1141
- // IMMEDIATELY persist to both spaces for audit trail (before any errors can occur)
1142
- await this.ensureSagaFrameInBothSpaces({ frame: ackFrame, destSpace, tempSpace, metaspace });
1085
+ // if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
1143
1086
 
1144
- // if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
1087
+ /**
1088
+ * we want to push ibgibs to the remote/sender if we have push
1089
+ * offers. an ack frame's payloads, if any, are those push offers
1090
+ */
1091
+ // let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
1092
+
1093
+ if (pushOfferAddrs.length > 0) {
1094
+ }
1145
1095
 
1146
- // Build control payloads: frame + its dependencies (msg stone, identity)
1147
- const payloadIbGibsControl: IbGib_V1[] = [ackFrame, ackStone];
1148
- if (identity) { payloadIbGibsControl.push(identity); }
1096
+ return {
1097
+ frame: ackFrame,
1098
+ // conflictInfos,
1099
+ // payloadIbGibsDomain,
1100
+ };
1149
1101
 
1150
- return { frame: ackFrame, payloadIbGibsControl };
1102
+ } catch (error) {
1103
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1104
+ throw error;
1105
+ } finally {
1106
+ if (logalot) { console.log(`${lc} complete.`); }
1107
+ }
1151
1108
  }
1152
1109
 
1153
1110
  /**
@@ -1181,7 +1138,7 @@ export class SyncSagaCoordinator {
1181
1138
  try {
1182
1139
  if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
1183
1140
 
1184
- const { messageData, } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
1141
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1185
1142
  const ackData = messageData as SyncSagaMessageAckData_V1;
1186
1143
 
1187
1144
  if (!ackData) {
@@ -1444,28 +1401,26 @@ export class SyncSagaCoordinator {
1444
1401
 
1445
1402
  const deltaStone = await this.createSyncMsgStone({
1446
1403
  data: deltaData,
1447
- space: tempSpace,
1404
+ localSpace: tempSpace,
1448
1405
  metaspace,
1449
1406
  });
1450
1407
 
1451
1408
  const deltaFrame = await this.evolveSyncSagaIbGib({
1452
1409
  prevSagaIbGib: sagaIbGib,
1453
1410
  msgStones: [deltaStone],
1454
- identity,
1455
- space: tempSpace,
1411
+ sessionIdentity: identity,
1412
+ localSpace: tempSpace,
1456
1413
  metaspace,
1457
1414
  });
1458
1415
 
1459
- // IMMEDIATELY persist to both spaces for audit trail
1460
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1461
-
1462
1416
  if (logalot) { console.log(`${lc} Delta Frame created. Rel8ns: ${JSON.stringify(deltaFrame.rel8ns)}`); }
1463
1417
 
1464
1418
  // Build control payloads: frame + its dependencies (msg stone, identity)
1465
1419
  const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1466
1420
  if (identity) { payloadIbGibsControl.push(identity); }
1467
1421
 
1468
- return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1422
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1423
+ return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1469
1424
  } catch (error) {
1470
1425
  console.error(`${lc} ${extractErrorMsg(error)}`);
1471
1426
  throw error;
@@ -1502,7 +1457,7 @@ export class SyncSagaCoordinator {
1502
1457
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1503
1458
  if (logalot) { console.log(`${lc} starting...`); }
1504
1459
 
1505
- const { messageData } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
1460
+ const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1506
1461
  const deltaData = messageData as SyncSagaMessageDeltaData_V1;
1507
1462
 
1508
1463
  if (!deltaData) {
@@ -1708,26 +1663,24 @@ export class SyncSagaCoordinator {
1708
1663
 
1709
1664
  const deltaStone = await this.createSyncMsgStone({
1710
1665
  data: responseDeltaData,
1711
- space: tempSpace,
1666
+ localSpace: tempSpace,
1712
1667
  metaspace
1713
1668
  });
1714
1669
 
1715
1670
  const deltaFrame = await this.evolveSyncSagaIbGib({
1716
1671
  prevSagaIbGib: sagaIbGib,
1717
1672
  msgStones: [deltaStone],
1718
- identity,
1719
- space: tempSpace,
1673
+ sessionIdentity: identity,
1674
+ localSpace: tempSpace,
1720
1675
  metaspace
1721
1676
  });
1722
1677
 
1723
- // IMMEDIATELY persist to both spaces for audit trail
1724
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1725
-
1726
1678
  // Build control payloads: frame + its dependencies (msg stone, identity)
1727
1679
  const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1728
1680
  if (identity) { payloadIbGibsControl.push(identity); }
1729
1681
 
1730
- return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1682
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1683
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1731
1684
 
1732
1685
  } else {
1733
1686
  // We have nothing to send.
@@ -1742,26 +1695,24 @@ export class SyncSagaCoordinator {
1742
1695
 
1743
1696
  const commitStone = await this.createSyncMsgStone({
1744
1697
  data: commitData,
1745
- space: tempSpace,
1698
+ localSpace: tempSpace,
1746
1699
  metaspace
1747
1700
  });
1748
1701
 
1749
1702
  const commitFrame = await this.evolveSyncSagaIbGib({
1750
1703
  prevSagaIbGib: sagaIbGib,
1751
1704
  msgStones: [commitStone],
1752
- identity,
1753
- space: tempSpace,
1705
+ sessionIdentity: identity,
1706
+ localSpace: tempSpace,
1754
1707
  metaspace
1755
1708
  });
1756
1709
 
1757
- // IMMEDIATELY persist to both spaces for audit trail
1758
- await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
1759
-
1760
1710
  // Build control payloads for commit
1761
1711
  const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
1762
1712
  if (identity) { commitCtrlPayloads.push(identity); }
1763
1713
 
1764
- return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1714
+ // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1715
+ return { frame: commitFrame, };
1765
1716
 
1766
1717
  } else {
1767
1718
  // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
@@ -1776,21 +1727,18 @@ export class SyncSagaCoordinator {
1776
1727
 
1777
1728
  const deltaStone = await this.createSyncMsgStone({
1778
1729
  data: responseDeltaData,
1779
- space: tempSpace,
1730
+ localSpace: tempSpace,
1780
1731
  metaspace
1781
1732
  });
1782
1733
 
1783
1734
  const deltaFrame = await this.evolveSyncSagaIbGib({
1784
1735
  prevSagaIbGib: sagaIbGib,
1785
1736
  msgStones: [deltaStone],
1786
- identity,
1787
- space: tempSpace,
1737
+ sessionIdentity: identity,
1738
+ localSpace: tempSpace,
1788
1739
  metaspace
1789
1740
  });
1790
1741
 
1791
- // IMMEDIATELY persist to both spaces for audit trail
1792
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1793
-
1794
1742
  // Check if PEER proposed commit
1795
1743
  if (deltaData.proposeCommit) {
1796
1744
  if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
@@ -1805,33 +1753,32 @@ export class SyncSagaCoordinator {
1805
1753
 
1806
1754
  const commitStone = await this.createSyncMsgStone({
1807
1755
  data: commitData,
1808
- space: tempSpace,
1756
+ localSpace: tempSpace,
1809
1757
  metaspace
1810
1758
  });
1811
1759
 
1812
1760
  const commitFrame = await this.evolveSyncSagaIbGib({
1813
1761
  prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1814
1762
  msgStones: [commitStone],
1815
- identity,
1816
- space: tempSpace,
1763
+ sessionIdentity: identity,
1764
+ localSpace: tempSpace,
1817
1765
  metaspace
1818
1766
  });
1819
1767
 
1820
- // IMMEDIATELY persist to both spaces for audit trail
1821
- await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
1822
-
1823
1768
  // Build control payloads for commit
1824
1769
  const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
1825
1770
  if (identity) { commitCtrlPayloads2.push(identity); }
1826
1771
 
1827
- return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1772
+ // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1773
+ return { frame: commitFrame, };
1828
1774
  }
1829
1775
 
1830
1776
  // Build control payloads for delta propose
1831
1777
  const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
1832
1778
  if (identity) { deltaCtrlPayloads.push(identity); }
1833
1779
 
1834
- return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1780
+ // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1781
+ return { frame: deltaFrame, };
1835
1782
  }
1836
1783
  }
1837
1784
  }
@@ -1871,11 +1818,11 @@ export class SyncSagaCoordinator {
1871
1818
 
1872
1819
  protected async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
1873
1820
  data,
1874
- space,
1821
+ localSpace,
1875
1822
  metaspace,
1876
1823
  }: {
1877
1824
  data: TStoneData,
1878
- space: IbGibSpaceAny,
1825
+ localSpace: IbGibSpaceAny,
1879
1826
  metaspace: MetaspaceService,
1880
1827
  }): Promise<IbGib_V1<TStoneData>> {
1881
1828
  const lc = `${this.lc}[${this.createSyncMsgStone.name}]`;
@@ -1890,7 +1837,7 @@ export class SyncSagaCoordinator {
1890
1837
  uuid: true, // we want the stone to have its own uniqueness
1891
1838
  });
1892
1839
  if (logalot) { console.log(`${lc} Created stone: ${getIbGibAddr({ ibGib: stone })}`); }
1893
- await putInSpace({ space, ibGib: stone });
1840
+ await metaspace.put({ ibGib: stone, space: localSpace });
1894
1841
  await metaspace.registerNewIbGib({ ibGib: stone });
1895
1842
  return stone as IbGib_V1<TStoneData>;
1896
1843
  } catch (error) {
@@ -1902,67 +1849,23 @@ export class SyncSagaCoordinator {
1902
1849
  }
1903
1850
 
1904
1851
 
1905
- /**
1906
- * Ensures saga frame and its msg stone(s) are in BOTH spaces for audit trail.
1907
- * Control ibgibs (saga frames, msg stones, identity) must be in both destSpace and tempSpace.
1908
- */
1909
- protected async ensureSagaFrameInBothSpaces({
1910
- frame,
1911
- destSpace,
1912
- tempSpace,
1913
- metaspace,
1914
- }: {
1915
- frame: SyncIbGib_V1,
1916
- destSpace: IbGibSpaceAny,
1917
- tempSpace: IbGibSpaceAny,
1918
- metaspace: MetaspaceService,
1919
- }): Promise<void> {
1920
- // Frame itself (already in tempSpace from creation, need in destSpace for audit)
1921
- await putInSpace({ space: destSpace, ibGib: frame });
1922
- await metaspace.registerNewIbGib({ ibGib: frame });
1923
-
1924
- // Msg stone(s) (already in tempSpace, need in destSpace for audit)
1925
- const msgStoneAddrs = frame.rel8ns?.[SYNC_MSG_REL8N_NAME];
1926
- if (msgStoneAddrs && msgStoneAddrs.length > 0) {
1927
- const resMsgStones = await getFromSpace({ space: tempSpace, addrs: msgStoneAddrs });
1928
- if (resMsgStones.ibGibs) {
1929
- for (const msgStone of resMsgStones.ibGibs) {
1930
- await putInSpace({ space: destSpace, ibGib: msgStone });
1931
- await metaspace.registerNewIbGib({ ibGib: msgStone });
1932
- }
1933
- }
1934
- }
1935
-
1936
- // Identity (if present, already in tempSpace, need in destSpace)
1937
- const identityAddrs = frame.rel8ns?.identity;
1938
- if (identityAddrs && identityAddrs.length > 0) {
1939
- const resIdentity = await getFromSpace({ space: tempSpace, addrs: identityAddrs });
1940
- if (resIdentity.ibGibs) {
1941
- for (const identity of resIdentity.ibGibs) {
1942
- await putInSpace({ space: destSpace, ibGib: identity });
1943
- await metaspace.registerNewIbGib({ ibGib: identity });
1944
- }
1945
- }
1946
- }
1947
- }
1948
-
1949
1852
  /**
1950
1853
  * Evolves the saga timeline with a new frame.
1951
1854
  */
1952
1855
  protected async evolveSyncSagaIbGib({
1953
1856
  prevSagaIbGib,
1857
+ conflictStrategy,
1954
1858
  msgStones,
1955
- identity,
1956
- space,
1859
+ sessionIdentity,
1860
+ localSpace,
1957
1861
  metaspace,
1958
- conflictStrategy,
1959
1862
  }: {
1960
1863
  prevSagaIbGib?: SyncIbGib_V1,
1864
+ conflictStrategy?: SyncConflictStrategy,
1961
1865
  msgStones: IbGib_V1[],
1962
- identity?: KeystoneIbGib_V1,
1963
- space: IbGibSpaceAny,
1866
+ localSpace: IbGibSpaceAny,
1964
1867
  metaspace: MetaspaceService,
1965
- conflictStrategy?: SyncConflictStrategy,
1868
+ sessionIdentity?: KeystoneIbGib_V1,
1966
1869
  }): Promise<SyncIbGib_V1> {
1967
1870
  const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
1968
1871
  try {
@@ -1988,10 +1891,9 @@ export class SyncSagaCoordinator {
1988
1891
  }
1989
1892
  }
1990
1893
 
1991
- const identityAddr = identity ? getIbGibAddr({ ibGib: identity }) : undefined;
1894
+ const identityAddr = sessionIdentity ? getIbGibAddr({ ibGib: sessionIdentity }) : undefined;
1992
1895
 
1993
1896
  if (prevSagaIbGib) {
1994
-
1995
1897
  /**
1996
1898
  * rel8ns always include the new msg stone(s)
1997
1899
  */
@@ -2003,8 +1905,8 @@ export class SyncSagaCoordinator {
2003
1905
  ];
2004
1906
 
2005
1907
  // if we're authenticating/signing, we'll have identity
2006
- if (identity) {
2007
- rel8nInfos.push({ rel8nName: 'identity', ibGibs: [identity], });
1908
+ if (sessionIdentity) {
1909
+ rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
2008
1910
  }
2009
1911
 
2010
1912
  // remove the existing sync msg stones' addrs
@@ -2025,7 +1927,7 @@ export class SyncSagaCoordinator {
2025
1927
  rel8nInfos,
2026
1928
  rel8nRemovalInfos,
2027
1929
  metaspace,
2028
- space,
1930
+ space: localSpace,
2029
1931
  noDna: true, // Explicitly no DNA for sync frames
2030
1932
  });
2031
1933
 
@@ -2033,33 +1935,33 @@ export class SyncSagaCoordinator {
2033
1935
  return newFrame;
2034
1936
  } else {
2035
1937
  // Create New Timeline (Root Frame)
1938
+ // data
2036
1939
  const data: SyncData_V1 = {
2037
1940
  uuid: sagaId,
2038
- // stage, // Removed from V1
2039
- payload: undefined, // Data in stone
2040
1941
  n: 0,
2041
1942
  isTjp: true,
2042
1943
  conflictStrategy,
2043
1944
  };
1945
+ // ib
2044
1946
  const ib = await getSyncIb({ data });
2045
-
1947
+ // rel8ns
2046
1948
  const stoneAddrs = msgStones.map(s => getIbGibAddr({ ibGib: s }));
2047
- const rel8ns: IbGibRel8ns_V1 = {
2048
- [SYNC_MSG_REL8N_NAME]: stoneAddrs,
2049
- };
2050
- if (identityAddr) {
2051
- rel8ns.identity = [identityAddr];
2052
- }
1949
+ const rel8ns: SyncRel8ns_V1 = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
1950
+ if (identityAddr) { rel8ns.identity = [identityAddr]; }
2053
1951
 
2054
1952
  const resNew = await createTimeline({
2055
- space,
1953
+ space: localSpace,
2056
1954
  metaspace,
2057
1955
  ib,
2058
1956
  data,
2059
1957
  rel8ns,
2060
1958
  parentIb: SYNC_ATOM, // "sync"
1959
+ /**
1960
+ * this will squash the result into a single frame
1961
+ */
2061
1962
  noDna: true,
2062
1963
  });
1964
+ if (!!resNew.intermediateIbGibs) { throw new Error(`(UNEXPECTED) resNew.intermediateIbGibs? we were assuming we were creating a single frame. (E: 456818147235d991ccb4d10d0bbe4826)`); }
2063
1965
 
2064
1966
  return resNew.newIbGib as SyncIbGib_V1;
2065
1967
  }
@@ -2070,14 +1972,20 @@ export class SyncSagaCoordinator {
2070
1972
  }
2071
1973
  }
2072
1974
 
2073
- protected async getStageAndPayloadFromFrame({ ibGib, space }: { ibGib: IbGib_V1, space: IbGibSpaceAny }): Promise<{ stage: SyncStage, messageData: any }> {
1975
+ protected async getStageAndPayloadFromFrame({
1976
+ sagaFrame,
1977
+ space
1978
+ }: {
1979
+ sagaFrame: IbGib_V1,
1980
+ space: IbGibSpaceAny
1981
+ }): Promise<{ stage: SyncStage, messageData: unknown }> {
2074
1982
  const lc = `${this.lc}[${this.getStageAndPayloadFromFrame.name}]`;
2075
1983
  try {
2076
1984
  if (logalot) { console.log(`${lc} starting... (I: fddc287bd4d55253dc50e519fd352226)`); }
2077
1985
 
2078
- if (!ibGib.rel8ns) { throw new Error(`(UNEXPECTED) ibGib.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`); }
1986
+ if (!sagaFrame.rel8ns) { throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`); }
2079
1987
 
2080
- const stoneAddrs = ibGib.rel8ns?.[SYNC_MSG_REL8N_NAME];
1988
+ const stoneAddrs = sagaFrame.rel8ns?.[SYNC_MSG_REL8N_NAME];
2081
1989
  if (stoneAddrs && stoneAddrs.length > 0) {
2082
1990
  const stoneAddr = stoneAddrs[0];
2083
1991
  const res = await getFromSpace({ addr: stoneAddr, space });
@@ -2090,7 +1998,7 @@ export class SyncSagaCoordinator {
2090
1998
  throw new Error(`couldn't get stoneAddr (${stoneAddr}) from space (${space.ib}) (E: f3c826e756f8a5c358beb88aa4a65e26)`);
2091
1999
  }
2092
2000
  } else {
2093
- throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? ibGib addr: ${getIbGibAddr(ibGib)} (E: 43eae8579e289d016741b5526052f226)`);
2001
+ throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? sagaFrame addr: ${getIbGibAddr(sagaFrame)} (E: 43eae8579e289d016741b5526052f226)`);
2094
2002
  }
2095
2003
  } catch (error) {
2096
2004
  console.error(`${lc} ${extractErrorMsg(error)}`);