@ibgib/core-gib 0.1.22 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/common/other/graph-helper.d.mts +25 -0
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +75 -1
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/sync/graft-info/graft-info-helpers.mjs +2 -2
  6. package/dist/sync/graft-info/graft-info-helpers.mjs.map +1 -1
  7. package/dist/sync/sync-conflict.respec.mjs +8 -12
  8. package/dist/sync/sync-conflict.respec.mjs.map +1 -1
  9. package/dist/sync/sync-constants.d.mts +14 -4
  10. package/dist/sync/sync-constants.d.mts.map +1 -1
  11. package/dist/sync/sync-constants.mjs +15 -3
  12. package/dist/sync/sync-constants.mjs.map +1 -1
  13. package/dist/sync/sync-helpers.d.mts +22 -15
  14. package/dist/sync/sync-helpers.d.mts.map +1 -1
  15. package/dist/sync/sync-helpers.mjs +159 -90
  16. package/dist/sync/sync-helpers.mjs.map +1 -1
  17. package/dist/sync/sync-innerspace-constants.respec.mjs +8 -9
  18. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  19. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +8 -9
  20. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  21. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +8 -9
  22. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  23. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +8 -9
  24. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  25. package/dist/sync/sync-innerspace-partial-update.respec.mjs +8 -9
  26. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  27. package/dist/sync/sync-innerspace.respec.mjs +6 -7
  28. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  29. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts +2 -0
  30. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts.map +1 -1
  31. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs +4 -0
  32. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs.map +1 -1
  33. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +0 -15
  34. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts.map +1 -1
  35. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +30 -16
  36. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  37. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +47 -79
  38. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  39. package/dist/sync/sync-peer/sync-peer-types.d.mts +40 -1
  40. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  41. package/dist/sync/sync-peer/sync-peer-v1.d.mts +47 -14
  42. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  43. package/dist/sync/sync-peer/sync-peer-v1.mjs +188 -144
  44. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  45. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +39 -3
  46. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  47. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +137 -31
  48. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  49. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +5 -0
  50. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  51. package/dist/sync/sync-saga-coordinator.d.mts +81 -77
  52. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  53. package/dist/sync/sync-saga-coordinator.mjs +608 -597
  54. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  55. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +154 -26
  56. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  57. package/dist/sync/sync-types.d.mts +87 -92
  58. package/dist/sync/sync-types.d.mts.map +1 -1
  59. package/dist/sync/sync-types.mjs +6 -2
  60. package/dist/sync/sync-types.mjs.map +1 -1
  61. package/dist/timeline/timeline-api.d.mts.map +1 -1
  62. package/dist/timeline/timeline-api.mjs +15 -8
  63. package/dist/timeline/timeline-api.mjs.map +1 -1
  64. package/package.json +1 -1
  65. package/src/common/other/graph-helper.mts +79 -1
  66. package/src/sync/graft-info/graft-info-helpers.mts +3 -3
  67. package/src/sync/sync-conflict.respec.mts +8 -14
  68. package/src/sync/sync-constants.mts +15 -4
  69. package/src/sync/sync-helpers.mts +173 -101
  70. package/src/sync/sync-innerspace-constants.respec.mts +8 -9
  71. package/src/sync/sync-innerspace-deep-updates.respec.mts +8 -9
  72. package/src/sync/sync-innerspace-dest-ahead.respec.mts +8 -9
  73. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +8 -9
  74. package/src/sync/sync-innerspace-partial-update.respec.mts +9 -12
  75. package/src/sync/sync-innerspace.respec.mts +6 -7
  76. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mts +7 -0
  77. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +0 -15
  78. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +72 -96
  79. package/src/sync/sync-peer/sync-peer-types.mts +43 -2
  80. package/src/sync/sync-peer/sync-peer-v1.mts +215 -142
  81. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +145 -37
  82. package/src/sync/sync-saga-context/sync-saga-context-types.mts +5 -0
  83. package/src/sync/sync-saga-coordinator.mts +680 -714
  84. package/src/sync/sync-saga-message/sync-saga-message-types.mts +160 -24
  85. package/src/sync/sync-types.mts +96 -105
  86. 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,39 @@ 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, NextSagaFrameInfo,
27
+ SYNC_CONFLICT_STRATEGY_VALID_VALUES,
28
+ HandleSagaResponseContextResult,
28
29
  } 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";
30
+ import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
31
+ import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
32
32
  import {
33
33
  SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
34
34
  SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
35
- SyncSagaMessageCommitData_V1
35
+ SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
36
+ SyncSagaPushOfferInfo,
37
+ SyncSagaRequestAddrInfo,
36
38
  } from "./sync-saga-message/sync-saga-message-types.mjs";
37
39
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
38
40
  import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
39
41
  import { SyncSagaInfo } from "./sync-types.mjs";
42
+ import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
40
43
  import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
41
44
  import { SyncSagaContextIbGib_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
42
- import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
45
+ import { createSyncSagaContext, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
43
46
  import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
44
47
  import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
45
-
46
48
  import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
47
49
  import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
48
50
  import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
49
- import { SubscriptionWitness } from "../common/pubsub/subscription/subscription-types.mjs";
50
51
  import { ErrorIbGib_V1 } from "../common/error/error-types.mjs";
51
- import { SyncPeerInnerspace_V1 } from "./sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs";
52
+ import {
53
+ IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns
54
+ } from "../witness/space/space-types.mjs";
55
+ import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
52
56
 
53
57
 
54
58
  // const logalot = GLOBAL_LOG_A_LOT || true;
@@ -56,26 +60,16 @@ const logalot = false;
56
60
  const logalotControlDomain = true;
57
61
  const lcControlDomain = '[ControlDomain]';
58
62
 
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
63
  /**
70
64
  * Orchestrates the synchronization process between two spaces (Source and Destination).
71
- *
65
+ *
72
66
  * ## Execution Contexts
73
- *
67
+ *
74
68
  * The methods in this class are designed to be run on specific nodes in the sync topology:
75
- *
69
+ *
76
70
  * * **Sender/Local Node**: The node *initiating* the sync (calling `sync()`).
77
71
  * * **Receiver/Remote Node**: The node *accepting* the sync request (the Peer).
78
- *
72
+ *
79
73
  * Note that in a peer-to-peer architecture, "Sender" and "Receiver" are roles relative
80
74
  * to a specific Saga session, not fixed node identities.
81
75
  */
@@ -90,11 +84,11 @@ export class SyncSagaCoordinator {
90
84
 
91
85
  /**
92
86
  * Executes a synchronization saga using the Symmetric Sync Protocol.
93
- *
87
+ *
94
88
  * @remarks
95
89
  * **Execution Context**: **Sender (Local)**.
96
90
  * This method is the entry point for starting a sync session.
97
- *
91
+ *
98
92
  * @param opts.peer - The remote peer witness to communicate with.
99
93
  * @param opts.localSpace - The local space that will be read from and written to.
100
94
  * @param opts.metaspace - Service for creating temp spaces and managing ibgibs.
@@ -103,18 +97,16 @@ export class SyncSagaCoordinator {
103
97
  */
104
98
  async sync({
105
99
  peer,
106
- localSpace: _localSpace,
107
- source: _source,
108
- metaspace,
109
100
  domainIbGibs,
110
- conflictStrategy = 'abort',
101
+ conflictStrategy = SyncConflictStrategy.abort,
111
102
  useSessionIdentity = true,
103
+ metaspace,
104
+ localSpace,
112
105
  }: SyncOptions): Promise<SyncSagaInfo> {
113
106
  const lc = `${this.lc}[${this.sync.name}]`;
114
107
  if (logalot) { console.log(`${lc} starting...`); }
115
108
 
116
- const localSpace = (_source || _localSpace)!;
117
- if (!localSpace) { throw new Error(`${lc} source (or localSpace) required (E: 8a9b0c1d)`); }
109
+ if (!localSpace) { throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`); }
118
110
 
119
111
  // 1. SETUP SAGA METADATA
120
112
  const sagaId = await getUUID();
@@ -130,11 +122,6 @@ export class SyncSagaCoordinator {
130
122
  rejectDone = reject;
131
123
  });
132
124
 
133
- async function getTempSpaceName(): Promise<string> {
134
- const uuid = await getUUID();
135
- return `tmp_sync_space_${uuid.substring(0, 8)}`;
136
- }
137
-
138
125
  // WORKING CONTEXT (Transactional)
139
126
  const tempSpaceName = await getTempSpaceName();
140
127
  const tempSpace = await metaspace.createNewLocalSpace({
@@ -157,34 +144,24 @@ export class SyncSagaCoordinator {
157
144
  (async () => {
158
145
  try {
159
146
 
160
- // 2. BOOTSTRAP IDENTITY (Session Keystone)
147
+ // BOOTSTRAP IDENTITY (Session Keystone)
161
148
  const sessionIdentity = useSessionIdentity
162
149
  ? await this.getSessionIdentity({ sagaId, metaspace, tempSpace })
163
150
  : undefined;
164
151
  // if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
165
152
 
166
- // 3. CREATE INITIAL FRAME (Stage.init)
167
- const { sagaFrame: initFrame, srcGraph } = await this.createInitFrame({
153
+ // CREATE INITIAL FRAME (Stage.init)
154
+ const { initFrame, initDomainGraph } = await this.createInitFrame({
168
155
  sagaId,
169
156
  sessionIdentity,
170
- localSpace,
171
157
  domainIbGibs,
172
- tempSpace,
173
- metaspace,
174
- conflictStrategy
158
+ conflictStrategy,
159
+ metaspace, localSpace, tempSpace,
175
160
  });
176
161
 
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,
162
+ // KICK OFF THE PING-PONG SAGA LOOP (FSM)
163
+ await this.executeSagaLoop({
164
+ initFrame, initDomainGraph,
188
165
  peer,
189
166
  sessionIdentity,
190
167
  updates$,
@@ -193,12 +170,6 @@ export class SyncSagaCoordinator {
193
170
  metaspace
194
171
  });
195
172
 
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
- }
201
-
202
173
  resolveDone();
203
174
  if (!updates$.complete) { throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`); }
204
175
  updates$.complete();
@@ -258,22 +229,22 @@ export class SyncSagaCoordinator {
258
229
 
259
230
  /**
260
231
  * Drives the FSM loop of the Saga.
261
- *
232
+ *
262
233
  * @remarks
263
234
  * **Execution Context**: **Sender (Local)**.
264
- *
235
+ *
265
236
  * 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`.
267
- *
237
+ * It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
238
+ *
268
239
  * **Data Transport Note**:
269
240
  * Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
270
- * When `handleSagaFrame` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
241
+ * When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
271
242
  * the NEXT request context.
272
243
  * When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
273
244
  */
274
245
  protected async executeSagaLoop({
275
- initialFrame,
276
- srcGraph,
246
+ initFrame,
247
+ initDomainGraph,
277
248
  peer,
278
249
  sessionIdentity,
279
250
  updates$,
@@ -281,141 +252,98 @@ export class SyncSagaCoordinator {
281
252
  tempSpace,
282
253
  metaspace
283
254
  }: {
284
- initialFrame: SyncIbGib_V1,
285
- srcGraph: { [addr: string]: IbGib_V1 },
255
+ initFrame: SyncIbGib_V1,
256
+ initDomainGraph: FlatIbGibGraph,
286
257
  peer: SyncPeerWitness,
287
258
  sessionIdentity?: KeystoneIbGib_V1,
288
259
  updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
260
+ metaspace: MetaspaceService
289
261
  localSpace: IbGibSpaceAny,
290
262
  tempSpace: IbGibSpaceAny,
291
- metaspace: MetaspaceService
292
- }): Promise<IbGib_V1[]> {
263
+ }): Promise<void> {
293
264
  const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
294
265
 
295
- // The current frame we just generated (e.g., Init or Delta Request)
296
- let currentFrame: SyncIbGib_V1 | null = initialFrame;
297
- // The payload we need to attach to the message (data we are sending)
266
+ /** The current frame we just generated (e.g., Init or Delta Request) */
267
+ let currentFrame: SyncIbGib_V1 | null = initFrame;
268
+ /** The **domain** payload we need to transmit (data we are sending) */
298
269
  let nextDomainIbGibs: IbGib_V1[] = [];
299
- // Accumulator for all data we've successfully pulled from the remote
300
- const allReceivedIbGibs: IbGib_V1[] = [];
301
270
 
302
271
  while (currentFrame) {
303
- // A. Create Context (Request)
304
- // 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
- // We must do this BEFORE creating the Context so we can list them
307
- // 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[] = [];
312
-
313
- // A. Payload (Standard Deep Deps)
314
- for (const nextDomainIbGib of nextDomainIbGibs) {
315
- let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
316
- if (!nextDomainIbGibGraph) {
317
- nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
318
- }
319
-
320
- if (nextDomainIbGibGraph) {
321
- payloadIbGibsDomain.push(...Object.values(nextDomainIbGibGraph));
322
- } else {
323
- throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
324
- }
325
- }
326
- if (logalot) { console.log(`${lc} payloadIbGibsDomain count: ${payloadIbGibsDomain.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`); }
272
+ // first, prepare for context transmission...
327
273
 
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
- }
337
-
338
- // 2. Create Context (Envelope)
274
+ /**
275
+ * this is used later in pollForDomainPayloads **iif** payloads are
276
+ * received **in the response**, i.e., on the return trip. So if we
277
+ * request addrs, the response should have these addrs and their
278
+ * dependencies. The ibgibs corresponding to these addrs will be
279
+ * streamed and placed into this map.
280
+ */
339
281
  const domainPayloadsMap = new Map<string, IbGib_V1>();
282
+ // #region set up peer observable for any domainPayloadsMap
340
283
  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
284
+ const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
285
+ next: async (ibgib: IbGib_V1) => {
286
+ if (logalot) { console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`); }
287
+ const addr = getIbGibAddr({ ibGib: ibgib });
288
+ if (logalotControlDomain) { console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`); }
289
+ domainPayloadsMap.set(addr, ibgib);
290
+ },
291
+ error: async (e: string | Error | ErrorIbGib_V1) => {
292
+ if (isIbGib(e)) {
293
+ console.error(`${sublc} error fired. error: ${JSON.stringify((e as IbGib_V1).data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
294
+ } else {
295
+ console.dir(e);
296
+ console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
297
+ }
298
+ },
299
+ complete: async () => {
300
+ if (logalot) { console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`); }
301
+ await subscription.unsubscribe();
302
+ },
303
+ }));
304
+ // #endregion set up peer observable for any domainPayloadsMap
305
+
306
+ // ...create/compose the Request Context itself...
367
307
  const requestCtx = await createSyncSagaContext({
368
308
  sagaFrame: currentFrame,
369
309
  sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
370
- payloadIbGibsDomain,
310
+ /**
311
+ * init frame: empty
312
+ * ack frame: possible push offers
313
+ * delta frame: requested ibgibs or commit offer
314
+ * commit frame: empty
315
+ */
316
+ payloadIbGibsDomain: nextDomainIbGibs,
317
+ localSpace,
371
318
  });
372
319
 
373
- // Log what we're sending
320
+ // #region Log what we're sending
374
321
  if (logalotControlDomain) {
375
- const controlAddrs = payloadIbGibsControl.map(p => getIbGibAddr({ ibGib: p }));
376
- const domainAddrs = payloadIbGibsDomain.map(p => getIbGibAddr({ ibGib: p }));
377
- console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
322
+ const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
323
+ console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
378
324
  console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
379
325
  console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
380
- console.log(`${lc}${lcControlDomain} CONTROL Payloads (${controlAddrs.length}): ${controlAddrs.join(', ') || '(none)'}`);
381
326
  console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
382
327
  }
328
+ // #endregion Log what we're sending
383
329
 
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
330
+ // update our saga listeners...
407
331
  // if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
408
- updates$.next(requestCtx); // spins off (don't remove this comment!)
332
+ updates$.next(requestCtx); // spins off for saga UI updates
409
333
 
334
+ // ...And send the context.
335
+ peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
410
336
  const responseCtx = await peer.witness(requestCtx);
411
337
 
412
- // C. Handle Response
338
+ // the send returned, but a peer can return a falsy responseCtx, if
339
+ // we just sent a commit to them and they're done. If so, there will
340
+ // necessarily be no payload to wait to receive
413
341
  if (!responseCtx) {
414
342
  if (currentFrame) {
415
343
  // Check for Commit (Peer silence expected)
416
344
  const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
417
345
  if (msg?.data?.stage === SyncStage.commit) {
418
- if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete.`); }
346
+ if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`); }
419
347
  currentFrame = null;
420
348
  break;
421
349
  } else {
@@ -426,85 +354,86 @@ export class SyncSagaCoordinator {
426
354
  }
427
355
  }
428
356
 
429
- // ---------------------------------------------------------------------
430
- // 2d. HANDLE RESPONSE
431
- // ---------------------------------------------------------------------
357
+ // at this point, we did indeed receive a response to analyze. BUT!
358
+ // we have only necessarily received the response context ibgib.
359
+ // if there were payloads expected to be transferred, they may not
360
+ // be done yet.
361
+
432
362
  if (!responseCtx.data) { throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`); }
433
- updates$.next(responseCtx); // spins off -- don't remove this comment!
363
+ updates$.next(responseCtx); // spins off for saga UI updating
364
+
365
+ // validate context
366
+ await validateContextAndSagaFrame
434
367
 
435
368
  // Extract expected domain addresses from response context
436
369
  const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] as string[] || [];
437
370
 
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
- // Poll for them if needed
371
+ // Poll for them if needed. see above jsdocs for domainPayloadsMap
441
372
  if (responsePayloadAddrsDomain.length > 0) {
442
- await this.pollForDomainPayloads({
373
+ responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
443
374
  expectedAddrs: responsePayloadAddrsDomain,
375
+ pollIntervalMs: 20, // relatively arbitrary right now
444
376
  domainPayloadsMap,
445
377
  tempSpace,
446
378
  });
447
379
  }
448
380
 
449
- // Extract Response Frame
450
- const responseFrameAddr = responseCtx.rel8ns?.sagaFrame?.[0];
451
- if (!responseFrameAddr) { throw new Error(`${lc} Peer response missing sagaFrame (E: 83a0)`); }
452
-
453
- // Log what we received back
381
+ // #region Log what we received back
382
+ 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
383
  if (logalotControlDomain) {
455
384
  const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] as string[] || [];
456
- console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: c4d5e6f7a8b9c0d1)`);
385
+ console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
457
386
  console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
458
- console.log(`${lc}${lcControlDomain} Response Frame: ${responseFrameAddr}`);
387
+ console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
459
388
  console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
460
389
  console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
461
390
  }
462
-
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
- // Handle Response Frame
473
- const handleResult = await this.handleSagaFrame({
474
- sagaIbGib: responseFrame,
475
- srcGraph: {},
476
- destSpace: localSpace,
477
- tempSpace,
391
+ // #endregion Log what we received back
392
+
393
+ // at this point, we have received the context AND **all** of the
394
+ // domain payloads (if applicable), so we are ready to do the next
395
+ // iteration in the saga loop
396
+
397
+ // this is the part that drives the FSM forward, i.e., when we
398
+ // evolve the sync saga ibgib itself to the next frame if we aren't
399
+ // finished/errored out.
400
+ const contextResult = await this.handleResponseSagaContext({
401
+ sagaContext: responseCtx,
402
+ mySpace: localSpace,
403
+ myTempSpace: tempSpace,
478
404
  metaspace,
479
- domainPayloadsMap,
480
- expectedDomainAddrs: responsePayloadAddrsDomain,
481
405
  });
482
406
 
483
- if (!handleResult) {
484
- if (logalot) { console.log(`${lc} Handler returned null (Saga End).`); }
407
+ if (!contextResult) {
408
+ if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`); }
485
409
  break;
486
410
  }
487
411
 
488
- currentFrame = handleResult.frame;
412
+ // #region error conditions throw
413
+ if (contextResult.errorMsg) {
414
+ throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
415
+ } else if (!contextResult.nextFrameInfo) {
416
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
417
+ } else if (contextResult.nextFrameInfo?.responseWasNull) {
418
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
419
+ }
420
+ // #endregion error conditions throw
489
421
 
490
- // Collect next DOMAIN payloads for the NEXT request
491
- // Control payloads are handled separately by ensureSagaFrameInBothSpaces
492
- nextDomainIbGibs = [...(handleResult.payloadIbGibsDomain || [])];
422
+ // we have another frame to process and send out, with possibly
423
+ // payload domain ibgibs as well
424
+ const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
425
+ currentFrame = frame;
426
+ nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
493
427
 
494
- // Log handler output for next iteration
428
+ // #region Log handler output for next iteration
495
429
  if (logalotControlDomain) {
496
- const handlerControlAddrs = (handleResult.payloadIbGibsControl || []).map(p => getIbGibAddr({ ibGib: p }));
497
430
  const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
498
- console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: d5e6f7a8b9c0d1e2)`);
431
+ console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
499
432
  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
433
  console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
502
434
  }
503
-
504
- // Note: currentFrame is automatically added to dependencies at start of next loop via B. Frames logic.
435
+ // #endregion Log handler output for next iteration
505
436
  }
506
-
507
- return allReceivedIbGibs;
508
437
  }
509
438
 
510
439
  /**
@@ -570,7 +499,7 @@ export class SyncSagaCoordinator {
570
499
 
571
500
  const res = await getLatestAddrs({ space, tjpAddrs: tjps });
572
501
  if (!res.data || !res.data.latestAddrsMap) {
573
- throw new Error(`${lc} Failed to get latest addrs. (E: 7a8b9c0d)`);
502
+ throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
574
503
  }
575
504
 
576
505
  // if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
@@ -584,128 +513,121 @@ 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
  /**
626
554
  * Creates the Initial Saga Frame (Init Stage).
627
- *
555
+ *
628
556
  * @remarks
629
557
  * **Execution Context**: **Sender (Local)**.
630
- *
558
+ *
631
559
  * Generates the first frame containing the Knowledge Vector of the Local Space.
632
560
  * This is sent to the Receiver to begin Gap Analysis.
633
561
  */
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<{ initFrame: SyncIbGib_V1, initDomainGraph: { [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
658
- // if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
659
- const { srcTimelinesMap, srcGraph, srcStones, } = analysis;
584
+ const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
585
+ // if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
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 { initFrame: sagaFrame, initDomainGraph: fullGraph };
709
631
  } catch (error) {
710
632
  console.error(`${lc} ${extractErrorMsg(error)}`);
711
633
  throw error;
@@ -717,24 +639,30 @@ 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
  /**
737
- * This needs
665
+ * This needs
738
666
  */
739
667
  const timeoutMs = 5 * 60 * 1000; // 5 minutes...arbitrary at this point. This needs to be pulled out and improved eesh.
740
668
 
@@ -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); }
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)`);
767
698
  }
699
+
700
+ return resultDomainPayloads;
768
701
  } catch (error) {
769
702
  console.error(`${lc} ${extractErrorMsg(error)}`);
770
703
  throw error;
@@ -773,56 +706,101 @@ 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 handleResponseSagaContext({
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
- }): Promise<HandleSagaFrameResult | null> {
795
- const lc = `${this.lc}[${this.handleSagaFrame.name}]`;
749
+ }): Promise<HandleSagaResponseContextResult | null> {
750
+ const lc = `${this.lc}[${this.handleResponseSagaContext.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
+
768
+ let nextFrameInfo: NextSagaFrameInfo;
807
769
  switch (stage) {
808
770
  case SyncStage.init:
809
- return await this.handleInitFrame({ sagaIbGib, messageData, metaspace, destSpace, tempSpace, identity, identitySecret });
771
+ nextFrameInfo = await this.handleInitFrame({
772
+ sagaIbGib,
773
+ messageData: messageData as SyncSagaMessageInitData_V1,
774
+ metaspace,
775
+ mySpace: mySpace,
776
+ myTempSpace: myTempSpace,
777
+ identity,
778
+ identitySecret
779
+ });
780
+ break;
810
781
 
811
782
  case SyncStage.ack:
812
- return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity });
783
+ nextFrameInfo = await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
784
+ break;
813
785
 
814
786
  case SyncStage.delta:
815
- return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity, });
787
+ nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
788
+ break;
816
789
 
817
790
  case SyncStage.commit:
818
- return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace, tempSpace, identity, });
791
+ nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
792
+ break;
819
793
 
820
794
  default:
821
795
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
822
796
  }
797
+
798
+ return { errorMsg: undefined, nextFrameInfo, }
799
+
823
800
  } catch (error) {
824
- console.error(`${lc} ${extractErrorMsg(error)}`);
825
- throw error;
801
+ const errorMsg = `${lc} ${extractErrorMsg(error)}`;
802
+ console.error(errorMsg);
803
+ return { errorMsg }
826
804
  } finally {
827
805
  if (logalot) { console.log(`${lc} complete.`); }
828
806
  }
@@ -832,10 +810,10 @@ export class SyncSagaCoordinator {
832
810
 
833
811
  /**
834
812
  * Handles the `Init` frame.
835
- *
813
+ *
836
814
  * @remarks
837
815
  * **Execution Context**: **Receiver (Remote)**.
838
- *
816
+ *
839
817
  * The Receiver performs Gap Analysis here:
840
818
  * 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
841
819
  * 2. Identifies what Sender needs (`pushOfferAddrs`).
@@ -845,321 +823,351 @@ export class SyncSagaCoordinator {
845
823
  protected async handleInitFrame({
846
824
  sagaIbGib,
847
825
  messageData,
848
- destSpace,
849
- tempSpace,
826
+ mySpace,
827
+ myTempSpace,
850
828
  metaspace,
851
829
  identity,
852
- identitySecret,
830
+ // identitySecret,
853
831
  }: {
854
832
  sagaIbGib: SyncIbGib_V1,
855
- messageData: any,
856
- destSpace: IbGibSpaceAny,
857
- tempSpace: IbGibSpaceAny,
833
+ messageData: SyncSagaMessageInitData_V1,
834
+ /**
835
+ * Local space relative to the execution context's POV
836
+ */
837
+ mySpace: IbGibSpaceAny,
838
+ /**
839
+ * Local temp space relative to the execution context's POV.
840
+ *
841
+ * NOTE: Since this always executes on the receiver's end, this should
842
+ * be the receiver's temp space.
843
+ */
844
+ myTempSpace: IbGibSpaceAny,
858
845
  metaspace: MetaspaceService,
859
846
  identity?: KeystoneIbGib_V1,
860
847
  identitySecret?: string,
861
- }): Promise<HandleSagaFrameResult | null> {
848
+ }): Promise<NextSagaFrameInfo> {
862
849
  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...`); }
850
+ try {
851
+ if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
865
852
 
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
- }
853
+ console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
875
854
 
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
- });
855
+ // Extract Init Data
856
+ const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
857
+ if (initData.stage !== SyncStage.init) {
858
+ throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
859
+ }
860
+ // if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
861
+ if (!initData || !initData.knowledgeVector) {
862
+ throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
899
863
  }
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
864
 
923
- // 2. Gap Analysis
865
+ // Determine Strategy from Saga Data (since V1 stores it in root)
866
+ const conflictStrategy = sagaIbGib.data!.conflictStrategy || SyncConflictStrategy.abort;
867
+
868
+ // 2. Gap Analysis
869
+ const conflicts: SyncSagaConflictInfo[] = [];
870
+ const deltaRequestAddrInfos: SyncSagaRequestAddrInfo[] = [];
871
+ const pushOfferInfos: SyncSagaPushOfferInfo[] = [];
872
+
873
+ // First do the consant stones (Non-TJPs)
874
+ const stones = initData.stones || [];
875
+ if (stones.length > 0) {
876
+ if (logalot) { console.log(`${lc} processing stones: ${stones.length}`); }
877
+ // Check if we have these stones
878
+ const resStones = await getFromSpace({ space: mySpace, addrs: stones });
879
+ const rawResultIbGib = resStones.rawResultIbGib as IbGibSpaceResultIbGib<IbGib_V1, IbGibSpaceResultData, IbGibSpaceResultRel8ns>;
880
+ if (!rawResultIbGib.data) { throw new Error(`(UNEXPECTED) rawResultIbGib.data falsy? (E: f3c40b492adc02c3f480b998fc7a5926)`); }
881
+ const addrsNotFound = rawResultIbGib.data.addrsNotFound;
882
+ if (addrsNotFound && addrsNotFound.length > 0) {
883
+ if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
884
+ addrsNotFound.forEach(addr => {
885
+ if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
886
+ deltaRequestAddrInfos.push({ addr }); // no tjpAddr
887
+ }
888
+ });
889
+ }
890
+ }
924
891
 
892
+ /**
893
+ * "remote" is sender in this case
894
+ */
895
+ const remoteKV = initData.knowledgeVector;
896
+ if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
897
+ const remoteTjps = Object.keys(remoteKV);
898
+ console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
899
+ if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
900
+
901
+ // 1. Get Local Latest Addrs for all TJPs
902
+ let localLatestAddrsMap: { [tjp: string]: string | null } = {};
903
+ if (remoteTjps.length > 0) {
904
+ // Batch get latest addrs for the TJPs
905
+ const resGetLatestAddrs = await getLatestAddrs({
906
+ space: mySpace, // executing on receiver, so this is receiver's space
907
+ tjpAddrs: remoteTjps,
908
+ });
909
+ if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
910
+ if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
911
+ localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
912
+ console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
913
+ if (logalot) { console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`); }
914
+ }
925
915
 
926
- for (const tjp of remoteTjps) {
927
- const remoteAddr = remoteKV[tjp];
928
- const localAddr = localKV[tjp];
916
+ // 2. Gap Analysis
917
+ for (const tjp of remoteTjps) {
918
+ const remoteAddr = remoteKV[tjp];
919
+ const localAddr = localLatestAddrsMap[tjp];
920
+
921
+ if (!localAddr) {
922
+ // We (Receiver) don't have this timeline at all. Request it.
923
+ console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
924
+ deltaRequestAddrInfos.push({
925
+ addr: remoteAddr,
926
+ tjpAddr: tjp,
927
+ // we don't have this timeline at all
928
+ // latestAddrAlreadyHave: undefined
929
+ });
930
+ continue;
931
+ }
929
932
 
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;
935
- }
933
+ // we do have this timeline...
936
934
 
937
- if (localAddr === remoteAddr) {
938
- // Synced
939
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
940
- continue;
941
- }
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
- });
935
+ if (localAddr === remoteAddr) {
936
+ // ...already synced
937
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
938
+ continue;
939
+ }
940
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
951
941
 
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.
942
+ // we have this timeline but it's not synced...
958
943
 
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,
944
+ // We're executing on receiver. Check if Remote (Sender) is in
945
+ // our past, and if so, we are ahead and need to push the delta
946
+ // to remote
947
+ const remoteIsInPast = await isPastFrame({
948
+ olderAddr: remoteAddr,
949
+ newerAddr: localAddr,
950
+ space: mySpace,
964
951
  });
965
952
 
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);
953
+ if (remoteIsInPast) {
954
+ // we're ahead, so push the delta of what the sender doesn't
955
+ // have (we have full knowledge)
956
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
957
+ const deltaGraph = await getDeltaDependencyGraph({
958
+ ibGibAddr: localAddr,
959
+ live: false, // always live: false right?
960
+ latestCommonFrameAddr: remoteAddr,
961
+ space: mySpace,
962
+ });
963
+ pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
970
964
  } 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
965
+ // Remote tip is not in our past. So, either Remote is
966
+ // ahead (we're in THEIR past, i.e., Fast-Backward) OR
967
+ // Diverged/conflict (we've both made edits).
968
+
969
+ // Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
970
+ /**
971
+ * we could first check for existence of remoteAddr in
972
+ * mySpace, but this is quick and easy. If it throws, we
973
+ * don't have it so it can't be in the past.
974
+ */
975
+ let localIsInPast = false;
976
+ try {
977
+ // we could
978
+ localIsInPast = await isPastFrame({
979
+ olderAddr: localAddr,
980
+ newerAddr: remoteAddr,
981
+ space: mySpace,
986
982
  });
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 || [])];
983
+ } catch (error) {
984
+ // couldn't get one of them, so localIsInPast remains false.
985
+ if (logalot) { console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`); }
986
+ }
1004
987
 
1005
- conflicts.push({
988
+ if (localIsInPast) {
989
+ // Fast-Forward: We update to remote's tip.
990
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
991
+ deltaRequestAddrInfos.push({
992
+ addr: remoteAddr,
1006
993
  tjpAddr: tjp,
1007
- localAddr: localAddr!,
1008
- remoteAddr,
1009
- timelineAddrs,
1010
- reason: 'divergence',
1011
- terminal: false
994
+ latestAddrAlreadyHave: localAddr,
1012
995
  });
1013
-
1014
996
  } else {
1015
- throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
997
+ // DIVERGENCE: Both have changes the other doesn't know about.
998
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
999
+
1000
+ if (conflictStrategy === 'abort') {
1001
+ // Abort Strategy: We will treat this as terminal.
1002
+ // But for Unified Ack, we just mark it terminal in the list?
1003
+ // Or do we actually throw/abort the saga?
1004
+ // Current logic (below) aborts the saga if ANY conflict is terminal/abort.
1005
+ conflicts.push({
1006
+ tjpAddr: tjp,
1007
+ localAddr: localAddr,
1008
+ remoteAddr,
1009
+ timelineAddrs: [], // Not needed for abort
1010
+ reason: 'divergence',
1011
+ terminal: true
1012
+ });
1013
+ } else if (conflictStrategy === 'optimistic') {
1014
+ // Optimistic: We want resolve this.
1015
+ // We need to send our history to the Sender so they can Merge.
1016
+
1017
+ // Fetch Full History for Local Timeline
1018
+ // Note: We might optimize this to only send "recent" history if we had a KV?
1019
+ // But for now, get full past.
1020
+ // Optimization: localKV might not have full history.
1021
+ // We need to inspect the 'past' of the local tip.
1022
+
1023
+ // We need the ACTUAL object to get the past.
1024
+ // We have localAddr.
1025
+ const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
1026
+ if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
1027
+ throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
1028
+ }
1029
+ const localTip = resLocalTip.ibGibs[0];
1030
+ if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`); }
1031
+
1032
+ const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
1033
+
1034
+ conflicts.push({
1035
+ tjpAddr: tjp,
1036
+ localAddr,
1037
+ remoteAddr,
1038
+ timelineAddrs,
1039
+ reason: 'divergence',
1040
+ terminal: false
1041
+ });
1042
+ } else {
1043
+ throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
1044
+ }
1016
1045
  }
1017
1046
  }
1018
1047
  }
1019
- }
1020
1048
 
1021
- // Check if we should ABORT (if any conflict is terminal)
1022
- const hasTerminalConflicts = conflicts.some(c => c.terminal);
1049
+ // Check if we should ABORT (if any conflict is terminal)
1050
+ const hasTerminalConflicts = conflicts.some(c => c.terminal);
1023
1051
 
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)}`); }
1052
+ if (hasTerminalConflicts) {
1053
+ // Abort Strategy: Kill the saga.
1054
+ if (logalot) { console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`); }
1027
1055
 
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.
1056
+ throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
1057
+ }
1033
1058
 
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).
1059
+ // 3. Create Ack Frame
1060
+ if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`); }
1061
+ const sagaId = sagaIbGib.data.uuid;
1062
+ // Create Payload Stone
1063
+ const ackData: SyncSagaMessageAckData_V1 = {
1064
+ sagaId,
1065
+ stage: SyncStage.ack,
1066
+ deltaRequestAddrInfos,
1067
+ pushOfferInfos,
1068
+ };
1069
+ if (conflicts?.length > 0) { ackData.conflicts = conflicts; }
1039
1070
 
1040
- // Let's use Ack with conflicts.
1041
- }
1071
+ const ackStone = await this.createSyncMsgStone({
1072
+ data: ackData,
1073
+ localSpace: myTempSpace,
1074
+ metaspace,
1075
+ });
1076
+ if (logalot) { console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`); }
1042
1077
 
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);
1050
- }
1051
- }
1078
+ // Evolve Saga
1079
+ const ackFrame = await this.evolveSyncSagaIbGib({
1080
+ prevSagaIbGib: sagaIbGib,
1081
+ msgStones: [ackStone],
1082
+ sessionIdentity: identity,
1083
+ localSpace: mySpace,
1084
+ metaspace,
1085
+ });
1086
+
1087
+ // if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
1052
1088
 
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];
1089
+ /**
1090
+ * we want to push ibgibs to the remote/sender if we have push
1091
+ * offers. an ack frame's payloads, if any, are those push offers
1092
+ */
1093
+ // let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
1094
+
1095
+ let payloadIbGibsDomain: IbGib_V1[] | undefined;
1096
+ if (pushOfferInfos.length > 0) {
1097
+ const searchSecondSpaceAddrs: IbGibAddr[] = [];
1098
+ payloadIbGibsDomain = [];
1099
+ const domainAddrs: IbGibAddr[] = pushOfferInfos.flatMap(x => x.addrs);
1100
+ const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
1101
+ const resGetRawData = resGet.rawResultIbGib?.data as IbGibSpaceResultData | undefined;
1102
+ if (!resGetRawData) { throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`); }
1103
+ const addrsNotFound = resGetRawData.addrsNotFound ?? [];
1104
+ const addrsErrored = resGetRawData.addrsErrored ?? [];
1105
+ if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
1106
+ // found all of them
1107
+ resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
1108
+ } else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
1109
+ // found some of them
1110
+ resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
1111
+ const foundPlusNotFoundCount =
1112
+ payloadIbGibsDomain.length +
1113
+ addrsNotFound.length +
1114
+ addrsErrored.length;
1115
+ if (foundPlusNotFoundCount === domainAddrs.length) {
1116
+ // we can still conceivably get the remaining addrs
1117
+ addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
1118
+ addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
1119
+ } else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
1120
+ throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
1121
+ } else {
1122
+ throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
1072
1123
  }
1124
+ } else {
1125
+ // found none of them(?)
1126
+ console.warn(`${lc} we were expecting all domainAddrs in mySpace (${mySpace.ib}) but we couldn't get all of them. addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. Trying temp space (${myTempSpace.ib}). (W: f61908e264f8dddd2e3fb9084463d826)`);
1127
+ domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
1073
1128
  }
1074
- }
1075
- }
1076
-
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];
1129
+ if (searchSecondSpaceAddrs.length > 0) {
1130
+ if (logalot) { console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`); }
1131
+ const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
1132
+ if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
1133
+ // got them all the second try
1134
+ resGet2.ibGibs.forEach(x => payloadIbGibsDomain!.push(x));
1135
+ } else {
1136
+ resGet2.ibGibs?.forEach(x => payloadIbGibsDomain!.push(x));
1137
+ const addrsFailed = domainAddrs.filter(x =>
1138
+ !payloadIbGibsDomain!.map(pid => getIbGibAddr({ ibGib: pid })).includes(x)
1139
+ );
1140
+ throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
1141
+ }
1091
1142
  }
1092
- } else {
1093
- // We don't have `addr`.
1143
+ // we have now populated payloadIbGibsDomain
1094
1144
  }
1095
- }
1096
-
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
- };
1124
-
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
- });
1140
1145
 
1141
- // IMMEDIATELY persist to both spaces for audit trail (before any errors can occur)
1142
- await this.ensureSagaFrameInBothSpaces({ frame: ackFrame, destSpace, tempSpace, metaspace });
1146
+ throw new Error(`not implemented (E: ed3f98abb0988c5ae8038bb8d741fb26)`);
1147
+ // return {
1148
+ // frame: ackFrame,
1149
+ // // conflictInfos,
1150
+ // payloadIbGibsDomain,
1151
+ // };
1143
1152
 
1144
- // if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
1145
-
1146
- // Build control payloads: frame + its dependencies (msg stone, identity)
1147
- const payloadIbGibsControl: IbGib_V1[] = [ackFrame, ackStone];
1148
- if (identity) { payloadIbGibsControl.push(identity); }
1149
-
1150
- return { frame: ackFrame, payloadIbGibsControl };
1153
+ } catch (error) {
1154
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1155
+ throw error;
1156
+ } finally {
1157
+ if (logalot) { console.log(`${lc} complete.`); }
1158
+ }
1151
1159
  }
1152
1160
 
1153
1161
  /**
1154
1162
  * Handles the `Ack` frame.
1155
- *
1163
+ *
1156
1164
  * @remarks
1157
1165
  * **Execution Context**: **Sender (Local)**.
1158
- *
1166
+ *
1159
1167
  * The Sender reacts to the Receiver's requirements:
1160
1168
  * 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
1161
1169
  * 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
1162
- *
1170
+ *
1163
1171
  * Returns a `Delta` frame.
1164
1172
  */
1165
1173
  protected async handleAckFrame({
@@ -1176,12 +1184,12 @@ export class SyncSagaCoordinator {
1176
1184
  tempSpace: IbGibSpaceAny,
1177
1185
  metaspace: MetaspaceService,
1178
1186
  identity?: KeystoneIbGib_V1,
1179
- }): Promise<HandleSagaFrameResult | null> {
1187
+ }): Promise<NextSagaFrameInfo> {
1180
1188
  const lc = `${this.lc}[${this.handleAckFrame.name}]`;
1181
1189
  try {
1182
1190
  if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
1183
1191
 
1184
- const { messageData, } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
1192
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1185
1193
  const ackData = messageData as SyncSagaMessageAckData_V1;
1186
1194
 
1187
1195
  if (!ackData) {
@@ -1204,7 +1212,7 @@ export class SyncSagaCoordinator {
1204
1212
  console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
1205
1213
  // Terminal failure. Sender should probably Commit(Fail) or just Abort.
1206
1214
  // For now, throw to trigger abort.
1207
- throw new Error(`${lc} Peer reported terminal conflicts. (E: a1b2c3d4e5f6g7h8i9j0k)`);
1215
+ throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
1208
1216
  }
1209
1217
 
1210
1218
  const optimisticConflicts = conflicts.filter(c => !c.terminal);
@@ -1232,7 +1240,7 @@ export class SyncSagaCoordinator {
1232
1240
  // Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
1233
1241
 
1234
1242
  // If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
1235
- // Or we just proceed to Delta (sending what Receiver wants),
1243
+ // Or we just proceed to Delta (sending what Receiver wants),
1236
1244
  // AND we piggyback our own requests?
1237
1245
  // OR: We treat the Conflict Resolution as a sub-saga or side-effect?
1238
1246
 
@@ -1261,7 +1269,7 @@ export class SyncSagaCoordinator {
1261
1269
  // We can fetch valid 'past' from space.
1262
1270
  const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
1263
1271
  const senderTipIbGib = resSenderTip.ibGibs?.[0];
1264
- if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 9c8d7e6f5g4h3i2j1k0l)`); }
1272
+ if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
1265
1273
 
1266
1274
  // Basic Diff: Find what Receiver has that we don't.
1267
1275
  // Actually, we need to traverse OUR past to find commonality.
@@ -1346,24 +1354,25 @@ export class SyncSagaCoordinator {
1346
1354
 
1347
1355
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
1348
1356
 
1349
- const deltaReqAddrs = ackData.deltaReqAddrs || [];
1350
- const pushOfferAddrs = ackData.pushOfferAddrs || [];
1357
+ const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
1358
+ const pushOfferAddrs = ackData.pushOfferInfos || [];
1351
1359
 
1352
1360
  // 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
1353
1361
  const pullReqAddrs: string[] = [];
1354
1362
  for (const addr of pushOfferAddrs) {
1355
- const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1356
- if (!existing) {
1357
- pullReqAddrs.push(addr);
1358
- }
1363
+ // const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1364
+ // if (!existing) {
1365
+ // pullReqAddrs.push(addr);
1366
+ // }
1359
1367
  }
1360
1368
 
1361
1369
  // 2. Process Delta Requests (Push Payload)
1362
1370
  // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
1371
+ // const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
1363
1372
  const skipAddrs = new Set<string>();
1364
1373
  if (ackData.knowledgeVector) {
1365
1374
  Object.values(ackData.knowledgeVector).forEach(addrs => {
1366
- addrs.forEach(a => skipAddrs.add(a));
1375
+ // addrs.forEach(a => skipAddrs.add(a));
1367
1376
  });
1368
1377
  }
1369
1378
 
@@ -1371,24 +1380,24 @@ export class SyncSagaCoordinator {
1371
1380
  // Gather all tips to sync first
1372
1381
  const tipsToSync: IbGib_V1[] = [];
1373
1382
  for (const addr of deltaReqAddrs) {
1374
- let ibGib = srcGraph[addr];
1375
- if (!ibGib) {
1376
- const res = await getFromSpace({ addr, space: destSpace });
1377
- if (res.ibGibs && res.ibGibs.length > 0) {
1378
- ibGib = res.ibGibs[0];
1379
- }
1380
- }
1381
- if (ibGib) {
1382
- tipsToSync.push(ibGib);
1383
- } else {
1384
- throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1385
- }
1383
+ // let ibGib = srcGraph[addr];
1384
+ // if (!ibGib) {
1385
+ // const res = await getFromSpace({ addr, space: destSpace });
1386
+ // if (res.ibGibs && res.ibGibs.length > 0) {
1387
+ // ibGib = res.ibGibs[0];
1388
+ // }
1389
+ // }
1390
+ // if (ibGib) {
1391
+ // tipsToSync.push(ibGib);
1392
+ // } else {
1393
+ // throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1394
+ // }
1386
1395
  }
1387
1396
 
1388
1397
  // Calculate Dependency Graph for ALL tips, effectively utilizing common history
1389
1398
  // Pass skipAddrs to `getDependencyGraph` or gather manually.
1390
1399
  // `getDependencyGraph` takes a single ibGib.
1391
- // We can optimize by doing it for each tip and unioning the result?
1400
+ // We can optimize by doing it for each tip and unioning the result?
1392
1401
  // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
1393
1402
  // We will loop.
1394
1403
 
@@ -1444,28 +1453,27 @@ export class SyncSagaCoordinator {
1444
1453
 
1445
1454
  const deltaStone = await this.createSyncMsgStone({
1446
1455
  data: deltaData,
1447
- space: tempSpace,
1456
+ localSpace: tempSpace,
1448
1457
  metaspace,
1449
1458
  });
1450
1459
 
1451
1460
  const deltaFrame = await this.evolveSyncSagaIbGib({
1452
1461
  prevSagaIbGib: sagaIbGib,
1453
1462
  msgStones: [deltaStone],
1454
- identity,
1455
- space: tempSpace,
1463
+ sessionIdentity: identity,
1464
+ localSpace: tempSpace,
1456
1465
  metaspace,
1457
1466
  });
1458
1467
 
1459
- // IMMEDIATELY persist to both spaces for audit trail
1460
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1461
-
1462
1468
  if (logalot) { console.log(`${lc} Delta Frame created. Rel8ns: ${JSON.stringify(deltaFrame.rel8ns)}`); }
1463
1469
 
1464
1470
  // Build control payloads: frame + its dependencies (msg stone, identity)
1465
1471
  const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1466
1472
  if (identity) { payloadIbGibsControl.push(identity); }
1467
1473
 
1468
- return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1474
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1475
+ // return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1476
+ throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
1469
1477
  } catch (error) {
1470
1478
  console.error(`${lc} ${extractErrorMsg(error)}`);
1471
1479
  throw error;
@@ -1476,10 +1484,10 @@ export class SyncSagaCoordinator {
1476
1484
 
1477
1485
  /**
1478
1486
  * Handles the `Delta` frame.
1479
- *
1487
+ *
1480
1488
  * @remarks
1481
1489
  * **Execution Context**: **Both**.
1482
- *
1490
+ *
1483
1491
  * 1. **Ingestion**: Receives data sent by Peer (`payloadAddrs`). Resolves them from `Rel8ns.payload` (implicitly via space/context) and returns them in `receivedPayload` to be saved.
1484
1492
  * 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
1485
1493
  * 3. **Completion**: If no more requests, transitions to `Commit`.
@@ -1498,11 +1506,11 @@ export class SyncSagaCoordinator {
1498
1506
  tempSpace: IbGibSpaceAny,
1499
1507
  metaspace: MetaspaceService,
1500
1508
  identity?: KeystoneIbGib_V1,
1501
- }): Promise<HandleSagaFrameResult | null> {
1509
+ }): Promise<NextSagaFrameInfo> {
1502
1510
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1503
1511
  if (logalot) { console.log(`${lc} starting...`); }
1504
1512
 
1505
- const { messageData } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
1513
+ const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1506
1514
  const deltaData = messageData as SyncSagaMessageDeltaData_V1;
1507
1515
 
1508
1516
  if (!deltaData) {
@@ -1511,7 +1519,7 @@ export class SyncSagaCoordinator {
1511
1519
  if (deltaData.stage !== SyncStage.delta) {
1512
1520
  throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1513
1521
  }
1514
- if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: 8d7e6f5g4h3i2j1k0l9m)`); }
1522
+ if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1515
1523
 
1516
1524
  console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1517
1525
 
@@ -1522,8 +1530,8 @@ export class SyncSagaCoordinator {
1522
1530
  // 1. Process Received Payload (Ingest)
1523
1531
  const receivedPayloadIbGibs: IbGib_V1[] = [];
1524
1532
  if (payloadAddrs.length > 0) {
1525
- // We use `payloadAddrs` as the manifest.
1526
- // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1533
+ // We use `payloadAddrs` as the manifest.
1534
+ // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1527
1535
  // assuming the "Transport" layer put them there implicitly?
1528
1536
  // OR, if we are local-only, we just get them.
1529
1537
  // The `handleDeltaFrame` contract assumes data is reachable in `space`.
@@ -1551,7 +1559,7 @@ export class SyncSagaCoordinator {
1551
1559
  // Get the requested ibGib
1552
1560
  let ibGib = srcGraph[addr];
1553
1561
  if (!ibGib) {
1554
- const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1562
+ const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1555
1563
  if (res.ibGibs && res.ibGibs.length > 0) {
1556
1564
  ibGib = res.ibGibs[0];
1557
1565
  }
@@ -1647,7 +1655,7 @@ export class SyncSagaCoordinator {
1647
1655
  console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1648
1656
  if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1649
1657
  // We have the tip!
1650
- // Do we have the full history?
1658
+ // Do we have the full history?
1651
1659
  // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1652
1660
  // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1653
1661
 
@@ -1693,9 +1701,9 @@ export class SyncSagaCoordinator {
1693
1701
  payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1694
1702
  requests: hasMyRequests ? myRequests : undefined,
1695
1703
  proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1696
- // Wait. If we send data, we are NOT committing yet.
1704
+ // Wait. If we send data, we are NOT committing yet.
1697
1705
  // We are sending data. The OTHER side must ingest it.
1698
- // So proposeCommit = true?
1706
+ // So proposeCommit = true?
1699
1707
  // "Here is the data. I'm done. If you are good, let's commit."
1700
1708
  // Yes.
1701
1709
  };
@@ -1708,26 +1716,25 @@ export class SyncSagaCoordinator {
1708
1716
 
1709
1717
  const deltaStone = await this.createSyncMsgStone({
1710
1718
  data: responseDeltaData,
1711
- space: tempSpace,
1719
+ localSpace: tempSpace,
1712
1720
  metaspace
1713
1721
  });
1714
1722
 
1715
1723
  const deltaFrame = await this.evolveSyncSagaIbGib({
1716
1724
  prevSagaIbGib: sagaIbGib,
1717
1725
  msgStones: [deltaStone],
1718
- identity,
1719
- space: tempSpace,
1726
+ sessionIdentity: identity,
1727
+ localSpace: tempSpace,
1720
1728
  metaspace
1721
1729
  });
1722
1730
 
1723
- // IMMEDIATELY persist to both spaces for audit trail
1724
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1725
-
1726
1731
  // Build control payloads: frame + its dependencies (msg stone, identity)
1727
1732
  const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1728
1733
  if (identity) { payloadIbGibsControl.push(identity); }
1729
1734
 
1730
- return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1735
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1736
+ // return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1737
+ throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1731
1738
 
1732
1739
  } else {
1733
1740
  // We have nothing to send.
@@ -1742,26 +1749,25 @@ export class SyncSagaCoordinator {
1742
1749
 
1743
1750
  const commitStone = await this.createSyncMsgStone({
1744
1751
  data: commitData,
1745
- space: tempSpace,
1752
+ localSpace: tempSpace,
1746
1753
  metaspace
1747
1754
  });
1748
1755
 
1749
1756
  const commitFrame = await this.evolveSyncSagaIbGib({
1750
1757
  prevSagaIbGib: sagaIbGib,
1751
1758
  msgStones: [commitStone],
1752
- identity,
1753
- space: tempSpace,
1759
+ sessionIdentity: identity,
1760
+ localSpace: tempSpace,
1754
1761
  metaspace
1755
1762
  });
1756
1763
 
1757
- // IMMEDIATELY persist to both spaces for audit trail
1758
- await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
1759
-
1760
1764
  // Build control payloads for commit
1761
1765
  const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
1762
1766
  if (identity) { commitCtrlPayloads.push(identity); }
1763
1767
 
1764
- return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1768
+ // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1769
+ // return { frame: commitFrame, };
1770
+ throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
1765
1771
 
1766
1772
  } else {
1767
1773
  // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
@@ -1776,21 +1782,18 @@ export class SyncSagaCoordinator {
1776
1782
 
1777
1783
  const deltaStone = await this.createSyncMsgStone({
1778
1784
  data: responseDeltaData,
1779
- space: tempSpace,
1785
+ localSpace: tempSpace,
1780
1786
  metaspace
1781
1787
  });
1782
1788
 
1783
1789
  const deltaFrame = await this.evolveSyncSagaIbGib({
1784
1790
  prevSagaIbGib: sagaIbGib,
1785
1791
  msgStones: [deltaStone],
1786
- identity,
1787
- space: tempSpace,
1792
+ sessionIdentity: identity,
1793
+ localSpace: tempSpace,
1788
1794
  metaspace
1789
1795
  });
1790
1796
 
1791
- // IMMEDIATELY persist to both spaces for audit trail
1792
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1793
-
1794
1797
  // Check if PEER proposed commit
1795
1798
  if (deltaData.proposeCommit) {
1796
1799
  if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
@@ -1805,33 +1808,34 @@ export class SyncSagaCoordinator {
1805
1808
 
1806
1809
  const commitStone = await this.createSyncMsgStone({
1807
1810
  data: commitData,
1808
- space: tempSpace,
1811
+ localSpace: tempSpace,
1809
1812
  metaspace
1810
1813
  });
1811
1814
 
1812
1815
  const commitFrame = await this.evolveSyncSagaIbGib({
1813
1816
  prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1814
1817
  msgStones: [commitStone],
1815
- identity,
1816
- space: tempSpace,
1818
+ sessionIdentity: identity,
1819
+ localSpace: tempSpace,
1817
1820
  metaspace
1818
1821
  });
1819
1822
 
1820
- // IMMEDIATELY persist to both spaces for audit trail
1821
- await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
1822
-
1823
1823
  // Build control payloads for commit
1824
1824
  const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
1825
1825
  if (identity) { commitCtrlPayloads2.push(identity); }
1826
1826
 
1827
- return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1827
+ // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1828
+ // return { frame: commitFrame, };
1829
+ throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
1828
1830
  }
1829
1831
 
1830
1832
  // Build control payloads for delta propose
1831
1833
  const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
1832
1834
  if (identity) { deltaCtrlPayloads.push(identity); }
1833
1835
 
1834
- return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1836
+ // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1837
+ // return { frame: deltaFrame, };
1838
+ throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
1835
1839
  }
1836
1840
  }
1837
1841
  }
@@ -1849,7 +1853,7 @@ export class SyncSagaCoordinator {
1849
1853
  tempSpace: IbGibSpaceAny,
1850
1854
  metaspace: MetaspaceService,
1851
1855
  identity?: KeystoneIbGib_V1,
1852
- }): Promise<HandleSagaFrameResult | null> {
1856
+ }): Promise<NextSagaFrameInfo> {
1853
1857
  const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
1854
1858
  if (logalot) { console.log(`${lc} Commit received.`); }
1855
1859
 
@@ -1864,18 +1868,19 @@ export class SyncSagaCoordinator {
1864
1868
  // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1865
1869
 
1866
1870
  if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1867
- return null;
1871
+ // return { responseWasNull: true };
1872
+ throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
1868
1873
  }
1869
1874
 
1870
1875
  // #endregion Handlers
1871
1876
 
1872
1877
  protected async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
1873
1878
  data,
1874
- space,
1879
+ localSpace,
1875
1880
  metaspace,
1876
1881
  }: {
1877
1882
  data: TStoneData,
1878
- space: IbGibSpaceAny,
1883
+ localSpace: IbGibSpaceAny,
1879
1884
  metaspace: MetaspaceService,
1880
1885
  }): Promise<IbGib_V1<TStoneData>> {
1881
1886
  const lc = `${this.lc}[${this.createSyncMsgStone.name}]`;
@@ -1890,7 +1895,7 @@ export class SyncSagaCoordinator {
1890
1895
  uuid: true, // we want the stone to have its own uniqueness
1891
1896
  });
1892
1897
  if (logalot) { console.log(`${lc} Created stone: ${getIbGibAddr({ ibGib: stone })}`); }
1893
- await putInSpace({ space, ibGib: stone });
1898
+ await metaspace.put({ ibGib: stone, space: localSpace });
1894
1899
  await metaspace.registerNewIbGib({ ibGib: stone });
1895
1900
  return stone as IbGib_V1<TStoneData>;
1896
1901
  } catch (error) {
@@ -1902,67 +1907,23 @@ export class SyncSagaCoordinator {
1902
1907
  }
1903
1908
 
1904
1909
 
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
1910
  /**
1950
1911
  * Evolves the saga timeline with a new frame.
1951
1912
  */
1952
1913
  protected async evolveSyncSagaIbGib({
1953
1914
  prevSagaIbGib,
1915
+ conflictStrategy,
1954
1916
  msgStones,
1955
- identity,
1956
- space,
1917
+ sessionIdentity,
1918
+ localSpace,
1957
1919
  metaspace,
1958
- conflictStrategy,
1959
1920
  }: {
1960
1921
  prevSagaIbGib?: SyncIbGib_V1,
1922
+ conflictStrategy?: SyncConflictStrategy,
1961
1923
  msgStones: IbGib_V1[],
1962
- identity?: KeystoneIbGib_V1,
1963
- space: IbGibSpaceAny,
1924
+ localSpace: IbGibSpaceAny,
1964
1925
  metaspace: MetaspaceService,
1965
- conflictStrategy?: SyncConflictStrategy,
1926
+ sessionIdentity?: KeystoneIbGib_V1,
1966
1927
  }): Promise<SyncIbGib_V1> {
1967
1928
  const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
1968
1929
  try {
@@ -1988,10 +1949,9 @@ export class SyncSagaCoordinator {
1988
1949
  }
1989
1950
  }
1990
1951
 
1991
- const identityAddr = identity ? getIbGibAddr({ ibGib: identity }) : undefined;
1952
+ const identityAddr = sessionIdentity ? getIbGibAddr({ ibGib: sessionIdentity }) : undefined;
1992
1953
 
1993
1954
  if (prevSagaIbGib) {
1994
-
1995
1955
  /**
1996
1956
  * rel8ns always include the new msg stone(s)
1997
1957
  */
@@ -2003,11 +1963,11 @@ export class SyncSagaCoordinator {
2003
1963
  ];
2004
1964
 
2005
1965
  // if we're authenticating/signing, we'll have identity
2006
- if (identity) {
2007
- rel8nInfos.push({ rel8nName: 'identity', ibGibs: [identity], });
1966
+ if (sessionIdentity) {
1967
+ rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
2008
1968
  }
2009
1969
 
2010
- // remove the existing sync msg stones' addrs
1970
+ // remove the existing sync msg stones' addrs
2011
1971
  if (!prevSagaIbGib.rel8ns) { throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`); }
2012
1972
  if (!prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] || prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME].length === 0) {
2013
1973
  throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] falsy/empty? (E: 15156baad26fcccda80aa3a31718c726)`);
@@ -2025,7 +1985,7 @@ export class SyncSagaCoordinator {
2025
1985
  rel8nInfos,
2026
1986
  rel8nRemovalInfos,
2027
1987
  metaspace,
2028
- space,
1988
+ space: localSpace,
2029
1989
  noDna: true, // Explicitly no DNA for sync frames
2030
1990
  });
2031
1991
 
@@ -2033,33 +1993,33 @@ export class SyncSagaCoordinator {
2033
1993
  return newFrame;
2034
1994
  } else {
2035
1995
  // Create New Timeline (Root Frame)
1996
+ // data
2036
1997
  const data: SyncData_V1 = {
2037
1998
  uuid: sagaId,
2038
- // stage, // Removed from V1
2039
- payload: undefined, // Data in stone
2040
1999
  n: 0,
2041
2000
  isTjp: true,
2042
2001
  conflictStrategy,
2043
2002
  };
2003
+ // ib
2044
2004
  const ib = await getSyncIb({ data });
2045
-
2005
+ // rel8ns
2046
2006
  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
- }
2007
+ const rel8ns: SyncRel8ns_V1 = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
2008
+ if (identityAddr) { rel8ns.identity = [identityAddr]; }
2053
2009
 
2054
2010
  const resNew = await createTimeline({
2055
- space,
2011
+ space: localSpace,
2056
2012
  metaspace,
2057
2013
  ib,
2058
2014
  data,
2059
2015
  rel8ns,
2060
2016
  parentIb: SYNC_ATOM, // "sync"
2017
+ /**
2018
+ * this will squash the result into a single frame
2019
+ */
2061
2020
  noDna: true,
2062
2021
  });
2022
+ if (!!resNew.intermediateIbGibs) { throw new Error(`(UNEXPECTED) resNew.intermediateIbGibs? we were assuming we were creating a single frame. (E: 456818147235d991ccb4d10d0bbe4826)`); }
2063
2023
 
2064
2024
  return resNew.newIbGib as SyncIbGib_V1;
2065
2025
  }
@@ -2070,14 +2030,20 @@ export class SyncSagaCoordinator {
2070
2030
  }
2071
2031
  }
2072
2032
 
2073
- protected async getStageAndPayloadFromFrame({ ibGib, space }: { ibGib: IbGib_V1, space: IbGibSpaceAny }): Promise<{ stage: SyncStage, messageData: any }> {
2033
+ protected async getStageAndPayloadFromFrame({
2034
+ sagaFrame,
2035
+ space
2036
+ }: {
2037
+ sagaFrame: IbGib_V1,
2038
+ space: IbGibSpaceAny
2039
+ }): Promise<{ stage: SyncStage, messageData: unknown }> {
2074
2040
  const lc = `${this.lc}[${this.getStageAndPayloadFromFrame.name}]`;
2075
2041
  try {
2076
2042
  if (logalot) { console.log(`${lc} starting... (I: fddc287bd4d55253dc50e519fd352226)`); }
2077
2043
 
2078
- if (!ibGib.rel8ns) { throw new Error(`(UNEXPECTED) ibGib.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`); }
2044
+ if (!sagaFrame.rel8ns) { throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`); }
2079
2045
 
2080
- const stoneAddrs = ibGib.rel8ns?.[SYNC_MSG_REL8N_NAME];
2046
+ const stoneAddrs = sagaFrame.rel8ns?.[SYNC_MSG_REL8N_NAME];
2081
2047
  if (stoneAddrs && stoneAddrs.length > 0) {
2082
2048
  const stoneAddr = stoneAddrs[0];
2083
2049
  const res = await getFromSpace({ addr: stoneAddr, space });
@@ -2090,7 +2056,7 @@ export class SyncSagaCoordinator {
2090
2056
  throw new Error(`couldn't get stoneAddr (${stoneAddr}) from space (${space.ib}) (E: f3c826e756f8a5c358beb88aa4a65e26)`);
2091
2057
  }
2092
2058
  } else {
2093
- throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? ibGib addr: ${getIbGibAddr(ibGib)} (E: 43eae8579e289d016741b5526052f226)`);
2059
+ throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? sagaFrame addr: ${getIbGibAddr(sagaFrame)} (E: 43eae8579e289d016741b5526052f226)`);
2094
2060
  }
2095
2061
  } catch (error) {
2096
2062
  console.error(`${lc} ${extractErrorMsg(error)}`);