@ibgib/core-gib 0.1.26 → 0.1.28

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 (34) hide show
  1. package/dist/sync/sync-helpers.d.mts +23 -2
  2. package/dist/sync/sync-helpers.d.mts.map +1 -1
  3. package/dist/sync/sync-helpers.mjs +121 -4
  4. package/dist/sync/sync-helpers.mjs.map +1 -1
  5. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  6. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +30 -9
  7. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  8. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  9. package/dist/sync/sync-peer/sync-peer-v1.mjs +1 -0
  10. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  11. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  12. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +17 -3
  13. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  14. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +8 -0
  15. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  16. package/dist/sync/sync-saga-coordinator.d.mts +30 -153
  17. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  18. package/dist/sync/sync-saga-coordinator.mjs +585 -406
  19. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  20. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +13 -18
  21. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  22. package/dist/sync/sync-types.d.mts +36 -6
  23. package/dist/sync/sync-types.d.mts.map +1 -1
  24. package/dist/sync/sync-types.mjs +32 -0
  25. package/dist/sync/sync-types.mjs.map +1 -1
  26. package/package.json +2 -2
  27. package/src/sync/sync-helpers.mts +113 -6
  28. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +30 -8
  29. package/src/sync/sync-peer/sync-peer-v1.mts +2 -0
  30. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +14 -2
  31. package/src/sync/sync-saga-context/sync-saga-context-types.mts +13 -5
  32. package/src/sync/sync-saga-coordinator.mts +656 -463
  33. package/src/sync/sync-saga-message/sync-saga-message-types.mts +27 -32
  34. package/src/sync/sync-types.mts +45 -8
@@ -19,21 +19,26 @@ import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space
19
19
  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
- import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
23
- import { appendToTimeline, createTimeline, getHistory, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
22
+ import {
23
+ SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN,
24
+ } from "./sync-constants.mjs";
25
+ import {
26
+ appendToTimeline, createTimeline, Rel8nInfo, Rel8nRemovalInfo
27
+ } from "../timeline/timeline-api.mjs";
24
28
  import {
25
29
  SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
26
30
  SyncRel8ns_V1, DomainIbGibAnalysisInfo, NextSagaFrameInfo,
27
- SYNC_CONFLICT_STRATEGY_VALID_VALUES,
28
- HandleSagaResponseContextResult,
31
+ SYNC_CONFLICT_STRATEGY_VALID_VALUES, HandleSagaResponseContextResult,
32
+ SyncExecutionContext,
33
+ SYNC_EXECUTION_CONTEXT_VALID_VALUES,
34
+ SyncSagaFrameDependencyGraph,
29
35
  } from "./sync-types.mjs";
30
- import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
36
+ import { getExecutionContext, getFullSyncSagaHistory, getSyncIb, getTempSpaceName, isPastFrame, putInSpace_dnasThenNonDnas, validateFullSyncSagaHistory } from "./sync-helpers.mjs";
31
37
  import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
32
38
  import {
33
39
  SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
34
40
  SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
35
- SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
36
- SyncSagaPushOfferInfo,
41
+ SyncSagaMessageCommitData_V1, SyncSagaConflictInfo, SyncSagaPushOfferInfo,
37
42
  SyncSagaRequestAddrInfo,
38
43
  } from "./sync-saga-message/sync-saga-message-types.mjs";
39
44
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
@@ -49,14 +54,12 @@ import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
49
54
  import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
50
55
  import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
51
56
  import { ErrorIbGib_V1 } from "../common/error/error-types.mjs";
52
- import {
53
- IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns
54
- } from "../witness/space/space-types.mjs";
57
+ import { IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns } from "../witness/space/space-types.mjs";
55
58
  import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
56
59
 
57
60
 
58
61
  // const logalot = GLOBAL_LOG_A_LOT || true;
59
- const logalot = false;
62
+ const logalot = true;
60
63
  const logalotControlDomain = true;
61
64
  const lcControlDomain = '[ControlDomain]';
62
65
 
@@ -74,10 +77,10 @@ const lcControlDomain = '[ControlDomain]';
74
77
  * to a specific Saga session, not fixed node identities.
75
78
  */
76
79
  export class SyncSagaCoordinator {
77
- protected lc: string = `[${SyncSagaCoordinator.name}]`;
80
+ private lc: string = `[${SyncSagaCoordinator.name}]`;
78
81
 
79
82
  constructor(
80
- protected keystone: KeystoneService_V1
83
+ private keystone: KeystoneService_V1
81
84
  ) {
82
85
 
83
86
  }
@@ -202,7 +205,7 @@ export class SyncSagaCoordinator {
202
205
  * @returns next context result if another round, else if commit returns
203
206
  * null
204
207
  */
205
- public async receiverContinueSync({
208
+ public async continueSync({
206
209
  sagaContext,
207
210
  mySpace,
208
211
  myTempSpace,
@@ -223,7 +226,7 @@ export class SyncSagaCoordinator {
223
226
  identitySecret?: string,
224
227
  metaspace: MetaspaceService,
225
228
  }): Promise<SyncSagaContextIbGib_V1 | null> {
226
- const lc = `${this.lc}[${this.receiverContinueSync.name}]`;
229
+ const lc = `${this.lc}[${this.continueSync.name}]`;
227
230
  try {
228
231
  if (logalot) { console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`); }
229
232
 
@@ -238,7 +241,11 @@ export class SyncSagaCoordinator {
238
241
 
239
242
  if (!contextResult) {
240
243
  if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`); }
241
- return null;
244
+ // does this ever hit now?
245
+ return null; /* <<<< returns early */
246
+ } else if (contextResult.nextFrameInfo?.sagaComplete) {
247
+ // this is the current proper workflow I believe as of 01/22/2026
248
+ return null; /* <<<< returns early */
242
249
  }
243
250
 
244
251
  // #region error conditions throw
@@ -246,8 +253,6 @@ export class SyncSagaCoordinator {
246
253
  throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
247
254
  } else if (!contextResult.nextFrameInfo) {
248
255
  throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
249
- } else if (contextResult.nextFrameInfo?.responseWasNull) {
250
- throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
251
256
  }
252
257
  // #endregion error conditions throw
253
258
 
@@ -276,7 +281,7 @@ export class SyncSagaCoordinator {
276
281
  }
277
282
  }
278
283
 
279
- protected async getSessionIdentity({
284
+ private async getSessionIdentity({
280
285
  sagaId,
281
286
  metaspace,
282
287
  tempSpace,
@@ -327,7 +332,7 @@ export class SyncSagaCoordinator {
327
332
  * the NEXT request context.
328
333
  * When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
329
334
  */
330
- protected async executeSagaLoop({
335
+ private async executeSagaLoop({
331
336
  initFrame,
332
337
  initDomainGraph,
333
338
  peer,
@@ -509,8 +514,8 @@ export class SyncSagaCoordinator {
509
514
  throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
510
515
  } else if (!contextResult.nextFrameInfo) {
511
516
  throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
512
- } else if (contextResult.nextFrameInfo?.responseWasNull) {
513
- throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
517
+ } else if (contextResult.nextFrameInfo?.sagaComplete) {
518
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.sagaComplete? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
514
519
  }
515
520
  // #endregion error conditions throw
516
521
 
@@ -608,7 +613,7 @@ export class SyncSagaCoordinator {
608
613
  }
609
614
  }
610
615
 
611
- protected async analyzeDomainIbGibs({
616
+ private async analyzeDomainIbGibs({
612
617
  domainIbGibs,
613
618
  space,
614
619
  }: {
@@ -654,7 +659,7 @@ export class SyncSagaCoordinator {
654
659
  * Generates the first frame containing the Knowledge Vector of the Local Space.
655
660
  * This is sent to the Receiver to begin Gap Analysis.
656
661
  */
657
- protected async createInitFrame({
662
+ private async createInitFrame({
658
663
  sagaId,
659
664
  sessionIdentity,
660
665
  domainIbGibs,
@@ -720,7 +725,7 @@ export class SyncSagaCoordinator {
720
725
  localSpace,
721
726
  });
722
727
 
723
- // if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
728
+ if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
724
729
 
725
730
  return { initFrame: sagaFrame, initDomainGraph: fullGraph };
726
731
  } catch (error) {
@@ -737,7 +742,7 @@ export class SyncSagaCoordinator {
737
742
  *
738
743
  * @returns when all {@link expectedAddrs} are done being transmitted.
739
744
  */
740
- protected async pollForDomainPayloads({
745
+ private async pollForDomainPayloads({
741
746
  expectedAddrs,
742
747
  pollIntervalMs,
743
748
  domainPayloadsMap,
@@ -822,7 +827,7 @@ export class SyncSagaCoordinator {
822
827
  *
823
828
  * This is a one-off on the receiver.
824
829
  */
825
- public async handleResponseSagaContext({
830
+ private async handleResponseSagaContext({
826
831
  sagaContext,
827
832
  initDomainGraph,
828
833
  mySpace,
@@ -860,7 +865,7 @@ export class SyncSagaCoordinator {
860
865
  if (logalot) { console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`); }
861
866
 
862
867
  // Get Stage from Stone (or Frame for Init fallback)
863
- const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
868
+ const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
864
869
 
865
870
  if (logalot) { console.log(`${lc} handling frame stage: ${stage}`); }
866
871
 
@@ -875,39 +880,44 @@ export class SyncSagaCoordinator {
875
880
  nextFrameInfo = await this.handleInitFrame({
876
881
  sagaIbGib,
877
882
  messageData: messageData as SyncSagaMessageInitData_V1,
878
- metaspace,
879
- mySpace: mySpace,
880
- myTempSpace: myTempSpace,
881
- identity,
882
- identitySecret
883
+ metaspace, mySpace, myTempSpace,
884
+ identity, identitySecret
883
885
  });
884
886
  break;
885
887
 
886
888
  case SyncStage.ack:
887
889
  if (!initDomainGraph) { throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`); }
888
890
  nextFrameInfo = await this.handleAckFrame({
891
+ sagaContext,
889
892
  sagaIbGib,
890
- srcGraph,
891
893
  initDomainGraph,
892
- metaspace,
893
- destSpace: mySpace,
894
- tempSpace: myTempSpace,
894
+ metaspace, mySpace, myTempSpace,
895
895
  identity,
896
896
  });
897
897
  break;
898
898
 
899
899
  case SyncStage.delta:
900
- nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
900
+ nextFrameInfo = await this.handleDeltaFrame({
901
+ sagaContext,
902
+ sagaIbGib,
903
+ srcGraph,
904
+ metaspace,
905
+ mySpace,
906
+ myTempSpace,
907
+ identity,
908
+ });
901
909
  break;
902
910
 
903
911
  case SyncStage.commit:
904
- nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
912
+ nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
905
913
  break;
906
914
 
907
915
  default:
908
916
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
909
917
  }
910
918
 
919
+ if (logalot) { console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`); }
920
+
911
921
  return { errorMsg: undefined, nextFrameInfo, }
912
922
 
913
923
  } catch (error) {
@@ -930,10 +940,10 @@ export class SyncSagaCoordinator {
930
940
  * The Receiver performs Gap Analysis here:
931
941
  * 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
932
942
  * 2. Identifies what Sender needs (`pushOfferAddrs`).
933
- * 3. Identifies what Receiver needs (`deltaReqAddrs`).
943
+ * 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
934
944
  * 4. Returns an `Ack` frame containing these lists.
935
945
  */
936
- protected async handleInitFrame({
946
+ private async handleInitFrame({
937
947
  sagaIbGib,
938
948
  messageData,
939
949
  mySpace,
@@ -1183,7 +1193,7 @@ export class SyncSagaCoordinator {
1183
1193
 
1184
1194
  const ackStone = await this.createSyncMsgStone({
1185
1195
  data: ackData,
1186
- localSpace: myTempSpace,
1196
+ localSpace: mySpace,
1187
1197
  metaspace,
1188
1198
  });
1189
1199
  if (logalot) { console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`); }
@@ -1274,22 +1284,29 @@ export class SyncSagaCoordinator {
1274
1284
  * **Execution Context**: **Sender (Local)**.
1275
1285
  *
1276
1286
  * The Sender reacts to the Receiver's requirements:
1277
- * 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
1278
- * 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
1287
+ * 1. `deltaRequestAddrInfos`: Receiver wants this data. Sender takes this
1288
+ * into account, plus gathers it from `initDomainGraph` and puts them in
1289
+ * `payloadIbGibs` (for next frame).
1290
+ * 2. `pushOfferAddrs`: Receiver has newer data. these should have been
1291
+ * included in the incoming context from the receiver..
1279
1292
  *
1280
1293
  * Returns a `Delta` frame.
1281
1294
  */
1282
- protected async handleAckFrame({
1295
+ private async handleAckFrame({
1296
+ sagaContext,
1283
1297
  sagaIbGib,
1284
- srcGraph,
1285
1298
  initDomainGraph,
1286
- destSpace,
1287
- tempSpace,
1288
- metaspace,
1299
+ mySpace, myTempSpace, metaspace,
1289
1300
  identity,
1290
1301
  }: {
1302
+ /**
1303
+ * todo: figure out if we need to do something about incoming push offer payload domain ibgibs
1304
+ * I'm adding this because we should be checking incoming payloads
1305
+ * right? for push offers? This is when the receiver had new ibgibs.
1306
+ * Not sure if I need to put this here though...
1307
+ */
1308
+ sagaContext: SyncSagaContextIbGib_V1,
1291
1309
  sagaIbGib: SyncIbGib_V1,
1292
- srcGraph: { [addr: string]: IbGib_V1 },
1293
1310
  /**
1294
1311
  * This is the initial dependency graph of all domain ibgibs passed in
1295
1312
  * to the original {@link sync} call.
@@ -1297,8 +1314,8 @@ export class SyncSagaCoordinator {
1297
1314
  * if we're executing on the sender, this will be populated
1298
1315
  */
1299
1316
  initDomainGraph: FlatIbGibGraph,
1300
- destSpace: IbGibSpaceAny,
1301
- tempSpace: IbGibSpaceAny,
1317
+ mySpace: IbGibSpaceAny,
1318
+ myTempSpace: IbGibSpaceAny,
1302
1319
  metaspace: MetaspaceService,
1303
1320
  identity?: KeystoneIbGib_V1,
1304
1321
  }): Promise<NextSagaFrameInfo> {
@@ -1306,9 +1323,10 @@ export class SyncSagaCoordinator {
1306
1323
  try {
1307
1324
  if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
1308
1325
 
1309
- const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1326
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1310
1327
  const ackData = messageData as SyncSagaMessageAckData_V1;
1311
1328
 
1329
+ // #region sanity/validation
1312
1330
  if (!ackData) {
1313
1331
  throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
1314
1332
  }
@@ -1316,6 +1334,8 @@ export class SyncSagaCoordinator {
1316
1334
  throw new Error(`${lc} Invalid ack frame: ackData.stage !== SyncStage.ack (E: 2e8b0a94b5954a66a6a1a7a0b3f5b7a1)`);
1317
1335
  }
1318
1336
  if (logalot) { console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`); }
1337
+ if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`); }
1338
+ // #region sanity/validation
1319
1339
 
1320
1340
  // 1. Check for Conflicts
1321
1341
  const conflicts = ackData.conflicts || [];
@@ -1334,7 +1354,12 @@ export class SyncSagaCoordinator {
1334
1354
 
1335
1355
  // at this point, if we have conflicts, they are non-terminal
1336
1356
 
1337
- const mergeDeltaReqs: string[] = []; // Additional requests for merging
1357
+ /**
1358
+ * at this point, we only request ibgibs for conflicted timelines.
1359
+ * If the receiver had known of any ibgibs this sender needed, it
1360
+ * would have been in the push offer.
1361
+ */
1362
+ const outgoingDeltaAddrRequestInfos: SyncSagaRequestAddrInfo[] = []; // Additional requests for merging
1338
1363
 
1339
1364
  if (conflicts.length > 0) {
1340
1365
  console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
@@ -1346,8 +1371,8 @@ export class SyncSagaCoordinator {
1346
1371
  // 4. (Later in Delta Phase) Perform Merge.
1347
1372
 
1348
1373
  // BUT: The Delta Phase is usually generic "Send me these Addrs".
1349
- // If we just add to `deltaReqAddrs` (which are requests for Sender to send to Receiver?),
1350
- // wait. `ackData.deltaReqAddrs` are what RECEIVER wants from SENDER.
1374
+ // If we just add to `deltaRequestAddrInfos` (which are requests for Sender to send to Receiver?),
1375
+ // wait. `ackData.deltaRequestAddrInfos` are what RECEIVER wants from SENDER.
1351
1376
 
1352
1377
  // We (Sender) are processing the Ack.
1353
1378
  // We need to request data FROM Receiver.
@@ -1375,6 +1400,7 @@ export class SyncSagaCoordinator {
1375
1400
 
1376
1401
  throw new Error(`conflicts not (re)implemented yet (E: 3b7d0819f83842a6de3ae988819bc826)`);
1377
1402
 
1403
+
1378
1404
  // const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1379
1405
 
1380
1406
  // // Sender History
@@ -1432,8 +1458,8 @@ export class SyncSagaCoordinator {
1432
1458
  // // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
1433
1459
  // for (const addr of receiverOnlyAddrs) {
1434
1460
  // // Add the frame itself first
1435
- // if (!mergeDeltaReqs.includes(addr)) {
1436
- // mergeDeltaReqs.push(addr);
1461
+ // if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
1462
+ // outgoingDeltaAddrRequestInfos.push(addr);
1437
1463
  // }
1438
1464
 
1439
1465
  // // Get the frame's delta dependencies (skip LCA's deps)
@@ -1452,8 +1478,8 @@ export class SyncSagaCoordinator {
1452
1478
  // if (frameDeltaDeps) {
1453
1479
  // // Add all delta dependencies (Object.keys gives us the addresses)
1454
1480
  // Object.keys(frameDeltaDeps).forEach(depAddr => {
1455
- // if (!mergeDeltaReqs.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1456
- // mergeDeltaReqs.push(depAddr);
1481
+ // if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1482
+ // outgoingDeltaAddrRequestInfos.push(depAddr);
1457
1483
  // }
1458
1484
  // });
1459
1485
  // }
@@ -1463,119 +1489,59 @@ export class SyncSagaCoordinator {
1463
1489
  // }
1464
1490
  // }
1465
1491
 
1466
- // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${mergeDeltaReqs.length}`);
1492
+ // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
1467
1493
  // } else {
1468
1494
  // console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
1469
1495
  // }
1470
1496
  }
1471
1497
 
1472
- console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. mergeDeltaReqs: ${mergeDeltaReqs.length}`);
1498
+ console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
1473
1499
  } else {
1474
1500
  console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
1475
1501
  }
1476
1502
 
1477
1503
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
1478
1504
 
1479
- const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
1480
- const pushOfferAddrs = ackData.pushOfferInfos || [];
1481
-
1482
- // 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
1483
- const pullReqAddrs: string[] = [];
1484
- for (const addr of pushOfferAddrs) {
1485
- // const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1486
- // if (!existing) {
1487
- // pullReqAddrs.push(addr);
1488
- // }
1489
- }
1490
-
1491
- // 2. Process Delta Requests (Push Payload)
1492
- // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
1493
- // const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
1494
- const skipAddrs = new Set<string>();
1495
- if (ackData.knowledgeVector) {
1496
- Object.values(ackData.knowledgeVector).forEach(addrs => {
1497
- // addrs.forEach(a => skipAddrs.add(a));
1498
- });
1499
- }
1500
-
1501
- const payloadIbGibs: IbGib_V1[] = [];
1502
- // Gather all tips to sync first
1503
- const tipsToSync: IbGib_V1[] = [];
1504
- for (const addr of deltaReqAddrs) {
1505
- // let ibGib = srcGraph[addr];
1506
- // if (!ibGib) {
1507
- // const res = await getFromSpace({ addr, space: destSpace });
1508
- // if (res.ibGibs && res.ibGibs.length > 0) {
1509
- // ibGib = res.ibGibs[0];
1510
- // }
1511
- // }
1512
- // if (ibGib) {
1513
- // tipsToSync.push(ibGib);
1514
- // } else {
1515
- // throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1516
- // }
1517
- }
1518
-
1519
- // Calculate Dependency Graph for ALL tips, effectively utilizing common history
1520
- // Pass skipAddrs to `getDependencyGraph` or gather manually.
1521
- // `getDependencyGraph` takes a single ibGib.
1522
- // We can optimize by doing it for each tip and unioning the result?
1523
- // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
1524
- // We will loop.
1525
-
1526
- const allDepsSet = new Set<string>();
1527
-
1528
- for (const tip of tipsToSync) {
1529
- // Always include the tip itself
1530
- const tipAddr = getIbGibAddr({ ibGib: tip });
1531
- // Only process if not skipped (though deltaReq implies they barely just asked for it)
1532
- // But detailed deps might be skipped.
1533
-
1534
- // Get Graph with Skips
1535
- // Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
1536
- const deps = await getDependencyGraph({
1537
- ibGib: tip,
1538
- space: destSpace,
1539
- skipAddrs: Array.from(skipAddrs)
1540
- });
1541
-
1542
- // [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
1543
- let tipIncluded = false;
1544
-
1545
- if (deps) {
1546
- Object.values(deps).forEach(d => {
1547
- const dAddr = getIbGibAddr({ ibGib: d });
1548
- if (!allDepsSet.has(dAddr)) {
1549
- allDepsSet.add(dAddr);
1550
- payloadIbGibs.push(d);
1551
- }
1552
- if (dAddr === tipAddr) { tipIncluded = true; }
1553
- });
1554
- }
1555
-
1556
- if (!tipIncluded && !skipAddrs.has(tipAddr)) {
1557
- if (logalot) { console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`); }
1558
- if (!allDepsSet.has(tipAddr)) {
1559
- allDepsSet.add(tipAddr);
1560
- payloadIbGibs.push(tip);
1561
- }
1562
- }
1563
- }
1505
+ /**
1506
+ * these were requested addrs on the INCOMING frame (the ack data).
1507
+ *
1508
+ * This is in contrast to any OUTGOING requests we make in this
1509
+ * method.
1510
+ */
1511
+ const payloadIbGibsDomain = await this.getPayloadsForRequestedInfos({
1512
+ deltaRequestAddrInfos: ackData.deltaRequestAddrInfos || [],
1513
+ mySpace,
1514
+ });
1564
1515
 
1565
1516
  // 3. Create Delta Frame
1566
- const sagaId = ackData.sagaId;
1567
1517
  const deltaData: SyncSagaMessageDeltaData_V1 = {
1568
- sagaId: sagaIbGib.data!.uuid,
1518
+ sagaId: sagaIbGib.data.uuid,
1569
1519
  stage: SyncStage.delta,
1570
- payloadAddrs: payloadIbGibs.map(p => getIbGibAddr({ ibGib: p })),
1571
- requests: [...(pullReqAddrs || []), ...(mergeDeltaReqs || [])].length > 0 ? [...(pullReqAddrs || []), ...(mergeDeltaReqs || [])] : undefined,
1520
+ /**
1521
+ * we're sending these domain ibgibs as payload (they were
1522
+ * requested by receiver)
1523
+ */
1524
+ payloadAddrsDomain: payloadIbGibsDomain.length > 0 ?
1525
+ payloadIbGibsDomain.map(x => getIbGibAddr({ ibGib: x })) :
1526
+ undefined,
1527
+ /**
1528
+ * we're asking for these addrs
1529
+ */
1530
+ deltaRequestAddrInfos: outgoingDeltaAddrRequestInfos.length > 0 ?
1531
+ outgoingDeltaAddrRequestInfos :
1532
+ undefined,
1533
+ /**
1534
+ * if we have no changes and request none, propose commit to
1535
+ * finish the sync transaction.
1536
+ */
1537
+ proposeCommit: payloadIbGibsDomain.length === 0 && outgoingDeltaAddrRequestInfos.length === 0,
1572
1538
  };
1573
1539
 
1574
1540
  if (logalot) { console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`); }
1575
1541
 
1576
1542
  const deltaStone = await this.createSyncMsgStone({
1577
1543
  data: deltaData,
1578
- localSpace: tempSpace,
1544
+ localSpace: mySpace,
1579
1545
  metaspace,
1580
1546
  });
1581
1547
 
@@ -1583,7 +1549,7 @@ export class SyncSagaCoordinator {
1583
1549
  prevSagaIbGib: sagaIbGib,
1584
1550
  msgStones: [deltaStone],
1585
1551
  sessionIdentity: identity,
1586
- localSpace: tempSpace,
1552
+ localSpace: mySpace,
1587
1553
  metaspace,
1588
1554
  });
1589
1555
 
@@ -1593,9 +1559,7 @@ export class SyncSagaCoordinator {
1593
1559
  const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1594
1560
  if (identity) { payloadIbGibsControl.push(identity); }
1595
1561
 
1596
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1597
- // return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1598
- throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
1562
+ return { frame: deltaFrame, payloadIbGibsDomain, };
1599
1563
  } catch (error) {
1600
1564
  console.error(`${lc} ${extractErrorMsg(error)}`);
1601
1565
  throw error;
@@ -1614,389 +1578,550 @@ export class SyncSagaCoordinator {
1614
1578
  * 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
1615
1579
  * 3. **Completion**: If no more requests, transitions to `Commit`.
1616
1580
  */
1617
- protected async handleDeltaFrame({
1581
+ private async handleDeltaFrame({
1582
+ sagaContext,
1618
1583
  sagaIbGib,
1619
1584
  srcGraph,
1620
- destSpace,
1621
- tempSpace,
1585
+ mySpace,
1586
+ myTempSpace,
1622
1587
  metaspace,
1623
1588
  identity,
1624
1589
  }: {
1590
+ sagaContext: SyncSagaContextIbGib_V1,
1625
1591
  sagaIbGib: SyncIbGib_V1,
1626
1592
  srcGraph: { [addr: string]: IbGib_V1 },
1627
- destSpace: IbGibSpaceAny,
1628
- tempSpace: IbGibSpaceAny,
1593
+ mySpace: IbGibSpaceAny,
1594
+ myTempSpace: IbGibSpaceAny,
1629
1595
  metaspace: MetaspaceService,
1630
1596
  identity?: KeystoneIbGib_V1,
1631
1597
  }): Promise<NextSagaFrameInfo> {
1632
1598
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1633
- if (logalot) { console.log(`${lc} starting...`); }
1634
-
1635
- const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1636
- const deltaData = messageData as SyncSagaMessageDeltaData_V1;
1637
-
1638
- if (!deltaData) {
1639
- throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
1640
- }
1641
- if (deltaData.stage !== SyncStage.delta) {
1642
- throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1643
- }
1644
- if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1645
-
1646
- console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1647
-
1648
- const payloadAddrs = deltaData.payloadAddrs || [];
1649
- const peerRequests = deltaData.requests || [];
1650
- const peerProposesCommit = deltaData.proposeCommit || false;
1651
-
1652
- // 1. Process Received Payload (Ingest)
1653
- const receivedPayloadIbGibs: IbGib_V1[] = [];
1654
- if (payloadAddrs.length > 0) {
1655
- // We use `payloadAddrs` as the manifest.
1656
- // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1657
- // assuming the "Transport" layer put them there implicitly?
1658
- // OR, if we are local-only, we just get them.
1659
- // The `handleDeltaFrame` contract assumes data is reachable in `space`.
1660
-
1661
- const res = await getFromSpace({
1662
- addrs: payloadAddrs,
1663
- space: tempSpace, // Incoming data is in tempSpace
1664
- });
1665
- if (res.ibGibs) {
1666
- receivedPayloadIbGibs.push(...res.ibGibs);
1667
- // Also put them? `getFromSpace` retrieves. If they are in space, they are persisted.
1668
- // If this is a Temp Space, they are safe.
1669
- } else {
1670
- console.warn(`${lc} Failed to retrieve payloads listed in delta: ${payloadAddrs.join(', ')}`);
1671
- }
1672
- }
1599
+ try {
1600
+ if (logalot) { console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`); }
1673
1601
 
1674
- // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1675
- const outgoingPayload: IbGib_V1[] = [];
1676
- const outgoingAddrsSet = new Set<string>(); // Track what we've added
1602
+ const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1603
+ const deltaData = messageData as SyncSagaMessageDeltaData_V1;
1677
1604
 
1678
- console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${peerRequests.length} peer requests`);
1605
+ // #region validate/sanity
1606
+ if (!deltaData) { throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`); }
1607
+ if (deltaData.stage !== SyncStage.delta) { throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`); }
1608
+ if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1609
+ // #endregion validate/sanity
1679
1610
 
1680
- for (const addr of peerRequests) {
1681
- // Get the requested ibGib
1682
- let ibGib = srcGraph[addr];
1683
- if (!ibGib) {
1684
- const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1685
- if (res.ibGibs && res.ibGibs.length > 0) {
1686
- ibGib = res.ibGibs[0];
1687
- }
1688
- }
1611
+ console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1689
1612
 
1690
- if (ibGib) {
1691
- // Add the requested ibGib itself
1692
- const ibGibAddr = getIbGibAddr({ ibGib });
1693
- if (!outgoingAddrsSet.has(ibGibAddr)) {
1694
- outgoingPayload.push(ibGib);
1695
- outgoingAddrsSet.add(ibGibAddr);
1696
- }
1613
+ const peerProposesCommit = deltaData.proposeCommit || false;
1697
1614
 
1698
- // Expand to include full dependency graph for this ibGib
1699
- // (Receiver needs all deps to properly process/merge)
1700
- try {
1701
- const deps = await getDependencyGraph({
1702
- ibGib,
1703
- space: destSpace,
1704
- });
1615
+ /**
1616
+ * these are already in the local temp space
1617
+ */
1618
+ const receivedPayloadIbGibs: IbGib_V1[] = sagaContext.payloadIbGibsDomain ?? [];
1705
1619
 
1706
- if (deps) {
1707
- Object.values(deps).forEach(depIbGib => {
1708
- const depAddr = getIbGibAddr({ ibGib: depIbGib });
1709
- if (!outgoingAddrsSet.has(depAddr)) {
1710
- outgoingPayload.push(depIbGib);
1711
- outgoingAddrsSet.add(depAddr);
1712
- }
1713
- });
1714
- }
1715
- } catch (depError) {
1716
- console.warn(`${lc} [CONFLICT DEBUG] Error expanding deps for ${addr}: ${extractErrorMsg(depError)}`);
1620
+ // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1621
+ console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`);
1622
+ const outgoingPayload = await this.getPayloadsForRequestedInfos({
1623
+ deltaRequestAddrInfos: deltaData.deltaRequestAddrInfos || [],
1624
+ mySpace,
1625
+ });
1626
+ console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1627
+
1628
+ // 3. Execute Merges (If applicable)
1629
+ // Check if we have pending conflicts that we CAN resolve now that we have data.
1630
+ // We look at the Saga History (Ack Frame) to find conflicts.
1631
+ // Optimization: Do this only if we received payloads.
1632
+ const mergeResultIbGibs: IbGib_V1[] = [];
1633
+
1634
+ console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1635
+
1636
+ if (receivedPayloadIbGibs.length > 0) {
1637
+ console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1638
+ // Find the Ack frame in history to get conflicts
1639
+ // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1640
+ // V1 timelines carry full history in `past`.
1641
+ const pastAddrs = sagaIbGib.rel8ns?.past || [];
1642
+ console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1643
+
1644
+ const sagaHistory = await getFullSyncSagaHistory({
1645
+ sagaIbGib,
1646
+ space: mySpace,
1647
+ });
1648
+ // #region validate/sanity sagaHistory
1649
+ if (sagaHistory.length === 0) { throw new Error(`(UNEXPECTED) sagaHistory.length is 0? We're in handleDeltaFrame. So we should have at least init and ack. (E: 815735c9cf756b275719bf434f180826)`); }
1650
+ if (sagaHistory.length === 1) { throw new Error(`(UNEXPECTED) sagaHistory.length is 1? We're in handleDeltaFrame. So we should have at least init and ack. (E: 0e4ef8e3ed088e83b2cdcd18ecea9826)`); }
1651
+ // #endregion validate/sanity sagaHistory
1652
+
1653
+ const ackGraphs = sagaHistory.filter(x =>
1654
+ x.msgStones.at(0)!.data!.stage === SyncStage.ack
1655
+ );
1656
+ // #region validate/sanity ackGraphs
1657
+ if (ackGraphs.length > 1) {
1658
+ throw new Error(`(UNEXPECTED) more than one ack stage found in sagaHistory? we're expecting exactly one. (E: 7150983bb3a841caf8acd60826ba4f26)`);
1659
+ } else if (ackGraphs.length === 0) {
1660
+ throw new Error(`(UNEXPECTED) couldn't find ack stage in sagaHistory? (E: 7d2da859196b86de28e7c8183af1e826)`);
1717
1661
  }
1718
- } else {
1719
- console.warn(`${lc} Requested addr not found during delta fulfillment: ${addr}`);
1720
- }
1721
- }
1722
-
1723
- console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1724
-
1725
-
1726
- // 3. Execute Merges (If applicable)
1727
- // Check if we have pending conflicts that we CAN resolve now that we have data.
1728
- // We look at the Saga History (Ack Frame) to find conflicts.
1729
- // Optimization: Do this only if we received payloads.
1730
- const mergeResultIbGibs: IbGib_V1[] = [];
1731
-
1732
- console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1733
-
1734
- if (receivedPayloadIbGibs.length > 0) {
1735
- console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1736
- // Find the Ack frame in history to get conflicts
1737
- // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1738
- // V1 timelines carry full history in `past`.
1739
- const pastAddrs = sagaIbGib.rel8ns?.past || [];
1740
- console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1741
- let ackData: SyncSagaMessageAckData_V1 | undefined;
1742
-
1743
- if (pastAddrs.length > 0) {
1744
- // Batch fetch all past frames
1745
- const resPast = await getFromSpace({ addrs: pastAddrs, space: tempSpace });
1746
- if (resPast.success && resPast.ibGibs) {
1747
- // Iterate backwards (most recent first) to find the latest Ack
1748
- for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1749
- const pastFrame = resPast.ibGibs[i];
1750
- const messageStone = await getSyncSagaMessageFromFrame({
1751
- frameIbGib: pastFrame,
1752
- space: tempSpace
1753
- });
1754
- if (messageStone?.data?.stage === SyncStage.ack) {
1755
- ackData = messageStone.data as SyncSagaMessageAckData_V1;
1756
- console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1757
- break;
1758
- }
1759
- }
1662
+ if (ackGraphs[0].msgStones.length !== 1) {
1663
+ throw new Error(`(UNEXPECTED) ackGraph has more than one msg stone? only one expected right now. (E: f07d388abff8d26f98cbf6e8d3932826)`);
1760
1664
  }
1761
- }
1762
-
1763
- if (ackData && ackData.conflicts) {
1764
- const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1765
- for (const conflict of optimisticConflicts) {
1766
- const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1767
- // We are Sender (usually) here if we are merging.
1768
- // Check if we have the history needed (timelineAddrs).
1769
- // Specifically, we needed the `receiverOnly` parts.
1770
-
1771
- // We blindly attempt merge if we have both tips accessible?
1772
- // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1773
-
1774
- // Check if we have receiverTip in space
1775
- console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1776
- const resRecTip = await getFromSpace({ addr: receiverTip, space: tempSpace }); // Check tempSpace for incoming data
1777
- console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1778
- if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1779
- // We have the tip!
1780
- // Do we have the full history?
1781
- // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1782
- // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1783
-
1784
- // Perform Merge!
1785
- try {
1786
- const mergeResult = await mergeDivergentTimelines({
1787
- tipA: (await getFromSpace({ addr: senderTip, space: destSpace })).ibGibs![0], // Our tip from destSpace
1788
- tipB: resRecTip.ibGibs[0], // Their tip (from tempSpace)
1789
- space: tempSpace, // Merge uses tempSpace
1790
- metaspace,
1791
- });
1792
- if (mergeResult) {
1793
- console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1794
- if (logalot) { console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`); }
1795
- mergeResultIbGibs.push(mergeResult);
1796
- outgoingPayload.push(mergeResult); // Send result to peer
1665
+ // #endregion validate/sanity ackGraphs
1666
+
1667
+ const ackData = ackGraphs[0].msgStones[0].data as SyncSagaMessageAckData_V1;
1668
+ if (!ackData) { throw new Error(`(UNEXPECTED) ackData falsy? (E: f9af88427f66a8db1ba868b810e8b826)`); }
1669
+
1670
+ if (ackData && ackData.conflicts) {
1671
+ const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1672
+ for (const conflict of optimisticConflicts) {
1673
+ const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1674
+ // We are Sender (usually) here if we are merging.
1675
+ // Check if we have the history needed (timelineAddrs).
1676
+ // Specifically, we needed the `receiverOnly` parts.
1677
+
1678
+ // We blindly attempt merge if we have both tips accessible?
1679
+ // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1680
+
1681
+ // Check if we have receiverTip in space
1682
+ console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1683
+ const resRecTip = await getFromSpace({ addr: receiverTip, space: myTempSpace }); // Check myTempSpace for incoming data
1684
+ console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1685
+ if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1686
+ // We have the tip!
1687
+ // Do we have the full history?
1688
+ // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1689
+ // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1690
+
1691
+ // Perform Merge!
1692
+ try {
1693
+ const mergeResult = await mergeDivergentTimelines({
1694
+ tipA: (await getFromSpace({ addr: senderTip, space: mySpace })).ibGibs![0], // Our tip from destSpace
1695
+ tipB: resRecTip.ibGibs[0], // Their tip (from myTempSpace)
1696
+ space: myTempSpace, // Merge uses myTempSpace
1697
+ metaspace,
1698
+ });
1699
+ if (mergeResult) {
1700
+ console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1701
+ if (logalot) { console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`); }
1702
+ mergeResultIbGibs.push(mergeResult);
1703
+ outgoingPayload.push(mergeResult); // Send result to peer
1704
+ }
1705
+ } catch (e) {
1706
+ console.error(`${lc} Merge failed: ${e}`);
1707
+ // If merge fails, we might Abort or just continue?
1797
1708
  }
1798
- } catch (e) {
1799
- console.error(`${lc} Merge failed: ${e}`);
1800
- // If merge fails, we might Abort or just continue?
1801
1709
  }
1802
1710
  }
1803
1711
  }
1804
1712
  }
1805
- }
1806
-
1807
- // 4. Determine Next Action
1808
- // We have `outgoingPayload` (Requests + Merge Results).
1809
- // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1810
- // Do WE have outstanding requests?
1811
- // We might if `mergeResult` requires further sync? Usually no, result is complete.
1812
-
1813
- const myRequests: string[] = []; // If we had more needs (e.g. partial payload), we'd add here.
1814
-
1815
- const hasOutgoing = outgoingPayload.length > 0;
1816
- const hasMyRequests = myRequests.length > 0;
1817
-
1818
- if (hasOutgoing || hasMyRequests) {
1819
- // We have business to attend to -> Send Delta
1820
- const responseDeltaData: SyncSagaMessageDeltaData_V1 = {
1821
- sagaId: deltaData.sagaId,
1822
- stage: SyncStage.delta,
1823
- payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1824
- requests: hasMyRequests ? myRequests : undefined,
1825
- proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1826
- // Wait. If we send data, we are NOT committing yet.
1827
- // We are sending data. The OTHER side must ingest it.
1828
- // So proposeCommit = true?
1829
- // "Here is the data. I'm done. If you are good, let's commit."
1830
- // Yes.
1831
- };
1832
-
1833
- // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1834
- // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1835
-
1836
- // So yes, proposeCommit = true.
1837
- responseDeltaData.proposeCommit = true;
1838
-
1839
- const deltaStone = await this.createSyncMsgStone({
1840
- data: responseDeltaData,
1841
- localSpace: tempSpace,
1842
- metaspace
1843
- });
1844
1713
 
1845
- const deltaFrame = await this.evolveSyncSagaIbGib({
1846
- prevSagaIbGib: sagaIbGib,
1847
- msgStones: [deltaStone],
1848
- sessionIdentity: identity,
1849
- localSpace: tempSpace,
1850
- metaspace
1851
- });
1852
-
1853
- // Build control payloads: frame + its dependencies (msg stone, identity)
1854
- const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1855
- if (identity) { payloadIbGibsControl.push(identity); }
1856
-
1857
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1858
- // return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1859
- throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1860
-
1861
- } else {
1862
- // We have nothing to send.
1863
-
1864
- if (peerProposesCommit) {
1865
- // Peer is done. We are done. -> Commit.
1866
- const commitData: SyncSagaMessageCommitData_V1 = {
1867
- sagaId: deltaData.sagaId,
1868
- stage: SyncStage.commit,
1869
- success: true,
1870
- };
1871
-
1872
- const commitStone = await this.createSyncMsgStone({
1873
- data: commitData,
1874
- localSpace: tempSpace,
1875
- metaspace
1876
- });
1877
-
1878
- const commitFrame = await this.evolveSyncSagaIbGib({
1879
- prevSagaIbGib: sagaIbGib,
1880
- msgStones: [commitStone],
1881
- sessionIdentity: identity,
1882
- localSpace: tempSpace,
1883
- metaspace
1884
- });
1714
+ // 4. Determine Next Action
1715
+ // We have `outgoingPayload` (Requests + Merge Results).
1716
+ // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1717
+ // Do WE have outstanding requests?
1718
+ // We might if `mergeResult` requires further sync? Usually no, result is complete.
1885
1719
 
1886
- // Build control payloads for commit
1887
- const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
1888
- if (identity) { commitCtrlPayloads.push(identity); }
1720
+ const myRequests: string[] = []; // If we had more needs (e.g. partial payload), we'd add here.
1889
1721
 
1890
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1891
- // return { frame: commitFrame, };
1892
- throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
1722
+ const hasOutgoing = outgoingPayload.length > 0;
1723
+ const hasMyRequests = myRequests.length > 0;
1893
1724
 
1894
- } else {
1895
- // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1896
- // But we are empty.
1897
- // So WE propose commit.
1725
+ if (hasOutgoing || hasMyRequests) {
1726
+ // We have business to attend to -> Send Delta
1898
1727
  const responseDeltaData: SyncSagaMessageDeltaData_V1 = {
1899
1728
  sagaId: deltaData.sagaId,
1900
1729
  stage: SyncStage.delta,
1901
- proposeCommit: true,
1902
- payloadAddrs: [], // Always include empty array if sending delta
1730
+ payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1731
+ requests: hasMyRequests ? myRequests : undefined,
1903
1732
  };
1904
1733
 
1905
1734
  const deltaStone = await this.createSyncMsgStone({
1906
1735
  data: responseDeltaData,
1907
- localSpace: tempSpace,
1908
- metaspace
1736
+ localSpace: mySpace,
1737
+ metaspace,
1909
1738
  });
1910
1739
 
1911
1740
  const deltaFrame = await this.evolveSyncSagaIbGib({
1912
1741
  prevSagaIbGib: sagaIbGib,
1913
1742
  msgStones: [deltaStone],
1914
1743
  sessionIdentity: identity,
1915
- localSpace: tempSpace,
1916
- metaspace
1744
+ localSpace: mySpace,
1745
+ metaspace,
1917
1746
  });
1918
1747
 
1919
- // Check if PEER proposed commit
1920
- if (deltaData.proposeCommit) {
1921
- if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
1922
- // Peer wants to commit and has no more requests.
1923
- // We should Commit.
1748
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1749
+ } else {
1750
+ // We have nothing to send.
1751
+
1752
+ if (peerProposesCommit) {
1753
+ // Peer is done. We are done. -> Commit.
1754
+ // one last validate entire history?
1755
+ const history = await getFullSyncSagaHistory({
1756
+ sagaIbGib,
1757
+ space: mySpace,
1758
+ });
1759
+ const validationErrors = await validateFullSyncSagaHistory({
1760
+ history,
1761
+ });
1762
+ if (validationErrors.length > 0) {
1763
+ const errorCommitFrame = await this.createCommitFrame({
1764
+ sagaIbGib,
1765
+ errors: validationErrors,
1766
+ metaspace, mySpace,
1767
+ identity,
1768
+ });
1769
+ return { frame: errorCommitFrame, }; /* <<<< returns early */
1770
+ }
1771
+
1772
+ await this.executeLocalCommit({
1773
+ sagaHistory: history,
1774
+ deltaFrame: sagaIbGib,
1775
+ localTempSpace: myTempSpace,
1776
+ localSpace: mySpace,
1777
+ metaspace,
1778
+ })
1779
+
1780
+ const commitFrame = await this.createCommitFrame({
1781
+ sagaIbGib,
1782
+ errors: undefined,
1783
+ metaspace,
1784
+ mySpace,
1785
+ identity,
1786
+ });
1787
+
1788
+ return { frame: commitFrame, };
1924
1789
 
1925
- const commitData: SyncSagaMessageCommitData_V1 = {
1790
+ } else {
1791
+ // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1792
+ // But we are empty.
1793
+ // So WE propose commit.
1794
+ const responseDeltaData: SyncSagaMessageDeltaData_V1 = {
1926
1795
  sagaId: deltaData.sagaId,
1927
- stage: SyncStage.commit,
1928
- success: true,
1796
+ stage: SyncStage.delta,
1797
+ proposeCommit: true,
1929
1798
  };
1930
1799
 
1931
- const commitStone = await this.createSyncMsgStone({
1932
- data: commitData,
1933
- localSpace: tempSpace,
1800
+ const deltaStone = await this.createSyncMsgStone({
1801
+ data: responseDeltaData,
1802
+ localSpace: mySpace,
1934
1803
  metaspace
1935
1804
  });
1936
1805
 
1937
- const commitFrame = await this.evolveSyncSagaIbGib({
1938
- prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1939
- msgStones: [commitStone],
1806
+ const deltaFrame = await this.evolveSyncSagaIbGib({
1807
+ prevSagaIbGib: sagaIbGib,
1808
+ msgStones: [deltaStone],
1940
1809
  sessionIdentity: identity,
1941
- localSpace: tempSpace,
1810
+ localSpace: mySpace,
1942
1811
  metaspace
1943
1812
  });
1944
1813
 
1945
- // Build control payloads for commit
1946
- const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
1947
- if (identity) { commitCtrlPayloads2.push(identity); }
1814
+ // Check if PEER proposed commit
1815
+ if (deltaData.proposeCommit) {
1816
+ if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
1817
+ // Peer wants to commit and has no more requests.
1818
+ // We should Commit.
1819
+
1820
+ const commitData: SyncSagaMessageCommitData_V1 = {
1821
+ sagaId: deltaData.sagaId,
1822
+ stage: SyncStage.commit,
1823
+ success: true,
1824
+ };
1825
+
1826
+ const commitStone = await this.createSyncMsgStone({
1827
+ data: commitData,
1828
+ localSpace: mySpace,
1829
+ metaspace
1830
+ });
1831
+
1832
+ const commitFrame = await this.evolveSyncSagaIbGib({
1833
+ prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1834
+ msgStones: [commitStone],
1835
+ sessionIdentity: identity,
1836
+ localSpace: mySpace,
1837
+ metaspace
1838
+ });
1839
+
1840
+ // Build control payloads for commit
1841
+ const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
1842
+ if (identity) { commitCtrlPayloads2.push(identity); }
1843
+
1844
+ return { frame: commitFrame, };
1845
+ }
1846
+
1847
+ // Build control payloads for delta propose
1848
+ const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
1849
+ if (identity) { deltaCtrlPayloads.push(identity); }
1850
+
1851
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1852
+ }
1853
+ }
1854
+ } catch (error) {
1855
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1856
+ throw error;
1857
+ } finally {
1858
+ if (logalot) { console.log(`${lc} complete.`); }
1859
+ }
1860
+
1861
+ }
1862
+ /**
1863
+ * should throw if fails
1864
+ */
1865
+ private async executeLocalCommit({
1866
+ deltaFrame,
1867
+ sagaHistory,
1868
+ metaspace, localSpace, localTempSpace,
1869
+ identity,
1870
+ }: {
1871
+ deltaFrame: SyncIbGib_V1;
1872
+ sagaHistory: SyncSagaFrameDependencyGraph[];
1873
+ metaspace: MetaspaceService;
1874
+ localSpace: IbGibSpaceAny;
1875
+ localTempSpace: IbGibSpaceAny;
1876
+ identity?: KeystoneIbGib_V1;
1877
+ }): Promise<void> {
1878
+ const lc = `${this.lc}[${this.executeLocalCommit.name}]`;
1879
+ try {
1880
+ if (logalot) { console.log(`${lc} starting... (I: 6734980446b86a63c1af6e2e206de826)`); }
1881
+
1882
+ // #region validate/sanity
1883
+ if (!deltaFrame.data) { throw new Error(`(UNEXPECTED) deltaFrame.data falsy? (E: a8be68d48668d93d992d793834823826)`); }
1884
+ // #endregion validate/sanity
1948
1885
 
1949
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1950
- // return { frame: commitFrame, };
1951
- throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
1886
+ // * move all payload addrs from temp space to local space
1887
+ // * register each and every iteration of each timeline (including
1888
+ // stones, which are like their own sealed timeline of length 1)
1889
+
1890
+ const allPayloadAddrsDomainTransferred: IbGibAddr[] = [];
1891
+ const fnAddPayloadAddr = (addr: IbGibAddr) => {
1892
+ if (!allPayloadAddrsDomainTransferred.includes(addr)) {
1893
+ allPayloadAddrsDomainTransferred.push(addr);
1952
1894
  }
1895
+ }
1896
+
1897
+ sagaHistory.forEach(x => {
1898
+ if (!x.sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 34d7f8cdee14717ce828878d98f89826)`); }
1899
+ x.msgStones.forEach(msgStone => {
1900
+ if (!msgStone.data) { throw new Error(`(UNEXPECTED) msgStone.data falsy? (E: 21406101f91847514cc759c8a7382f26)`); }
1901
+ if (msgStone.data.stage === SyncStage.ack) {
1902
+ const ackData = msgStone.data as SyncSagaMessageAckData_V1;
1903
+ if (ackData.pushOfferInfos && ackData.pushOfferInfos.length > 0) {
1904
+ ackData.pushOfferInfos.forEach(info => {
1905
+ info.addrs.forEach(addr => fnAddPayloadAddr(addr));
1906
+ });
1907
+ }
1908
+ } else if (msgStone.data.stage === SyncStage.delta) {
1909
+ const deltaData = msgStone.data as SyncSagaMessageDeltaData_V1;
1910
+ if (deltaData.payloadAddrsDomain && deltaData.payloadAddrsDomain.length > 0) {
1911
+ deltaData.payloadAddrsDomain.forEach(addr => fnAddPayloadAddr(addr));
1912
+ }
1913
+ }
1914
+ })
1915
+ });
1916
+
1917
+ // at this point, we have a list of ALL payload addrs retrieved.
1918
+ let allPayloadIbGibsDomainTransferred: IbGib_V1[] = [];
1919
+ const resGetAllTransferred =
1920
+ await getFromSpace({ addrs: allPayloadAddrsDomainTransferred, space: localTempSpace });
1921
+ if (resGetAllTransferred.success && resGetAllTransferred.ibGibs && resGetAllTransferred.ibGibs.length === allPayloadAddrsDomainTransferred.length) {
1922
+ allPayloadIbGibsDomainTransferred = resGetAllTransferred.ibGibs.concat();
1923
+ } else {
1924
+ // errored out, gather info
1925
+ if (!resGetAllTransferred.rawResultIbGib) { throw new Error(`(UNEXPECTED) !resGetAllTransferred.rawResultIbGib falsy? (E: dc6cf8729668f4fbe8c024f887d97a26)`); }
1926
+ const { addrsNotFound, addrsErrored, errors } = resGetAllTransferred.rawResultIbGib.data as IbGibSpaceResultData;
1927
+ throw new Error(`(UNEXPECTED) we couldn't get all addrs transferred throughout the saga from the tempspace (${localTempSpace.ib})? addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. errors: ${errors ?? []}(E: 222e341e634862b4d88ae7282584d826)`);
1928
+ }
1929
+
1930
+ // now we have all ibgibs transferred, first put them all the local space
1931
+
1932
+ const { payload_Dnas, payload_NonDnas } =
1933
+ await putInSpace_dnasThenNonDnas({
1934
+ ibGibs: allPayloadIbGibsDomainTransferred,
1935
+ space: localSpace
1936
+ });
1953
1937
 
1954
- // Build control payloads for delta propose
1955
- const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
1956
- if (identity) { deltaCtrlPayloads.push(identity); }
1938
+ const { mapWithTjp_NoDna, mapWithTjp_YesDna, mapWithoutTjps } =
1939
+ splitPerTjpAndOrDna({ ibGibs: payload_NonDnas, filterPrimitives: true });
1940
+
1941
+ // first register all non-tjp stones
1942
+ const nontjps = Object.values(mapWithoutTjps);
1943
+ for (const nontjp of nontjps) {
1944
+ await metaspace.registerNewIbGib({
1945
+ ibGib: nontjp,
1946
+ space: localSpace,
1947
+ });
1948
+ }
1949
+
1950
+ // next register each timeline in order...
1951
+
1952
+ // ...first the ones without dna...
1953
+ const timelinesByTjpAddr_NoDna =
1954
+ getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_NoDna) });
1955
+ const tjpAddrs_NoDna = Object.keys(timelinesByTjpAddr_NoDna);
1956
+ for (const tjpAddr_NoDna of tjpAddrs_NoDna) {
1957
+ const timelineIbGibs = timelinesByTjpAddr_NoDna[tjpAddr_NoDna];
1958
+ for (const ibGib of timelineIbGibs) {
1959
+ await metaspace.registerNewIbGib({
1960
+ ibGib,
1961
+ space: localSpace,
1962
+ });
1963
+ }
1964
+ }
1957
1965
 
1958
- // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1959
- // return { frame: deltaFrame, };
1960
- throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
1966
+ // ...then the ones WITH dna.
1967
+ const timelinesByTjpAddr_YesDna =
1968
+ getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_YesDna) });
1969
+ const tjpAddrs_YesDna = Object.keys(timelinesByTjpAddr_YesDna);
1970
+ for (const tjpAddr_YesDna of tjpAddrs_YesDna) {
1971
+ const timelineIbGibs = timelinesByTjpAddr_YesDna[tjpAddr_YesDna];
1972
+ for (const ibGib of timelineIbGibs) {
1973
+ await metaspace.registerNewIbGib({
1974
+ ibGib,
1975
+ space: localSpace,
1976
+ });
1977
+ }
1961
1978
  }
1979
+
1980
+
1981
+ } catch (error) {
1982
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1983
+ throw error;
1984
+ } finally {
1985
+ if (logalot) { console.log(`${lc} complete.`); }
1962
1986
  }
1963
1987
  }
1964
1988
 
1989
+ private async createCommitFrame({
1990
+ sagaIbGib,
1991
+ errors,
1992
+ metaspace,
1993
+ mySpace,
1994
+ identity,
1995
+ }: {
1996
+ sagaIbGib: SyncIbGib_V1,
1997
+ /**
1998
+ * if truthy and non-empty, will create an errored commit frame (data
1999
+ * has `success: false`)
2000
+ */
2001
+ errors?: string[],
2002
+ metaspace: MetaspaceService,
2003
+ mySpace: IbGibSpaceAny,
2004
+ identity: KeystoneIbGib_V1 | undefined,
2005
+ }): Promise<SyncIbGib_V1> {
2006
+ const lc = `[${this.createCommitFrame.name}]`;
2007
+ try {
2008
+ if (logalot) { console.log(`${lc} starting... (I: 48fc57c023f80122135a284855757526)`); }
2009
+ const commitData: SyncSagaMessageCommitData_V1 = errors && errors.length > 0 ?
2010
+ {
2011
+ // errored
2012
+ sagaId: sagaIbGib.data!.uuid,
2013
+ stage: SyncStage.commit,
2014
+ success: false,
2015
+ errors,
2016
+ } :
2017
+ {
2018
+ // not errored
2019
+ sagaId: sagaIbGib.data!.uuid,
2020
+ stage: SyncStage.commit,
2021
+ success: true,
2022
+ };
2023
+
2024
+ const commitStone = await this.createSyncMsgStone({
2025
+ data: commitData,
2026
+ localSpace: mySpace,
2027
+ metaspace
2028
+ });
2029
+ const commitFrame = await this.evolveSyncSagaIbGib({
2030
+ prevSagaIbGib: sagaIbGib,
2031
+ msgStones: [commitStone],
2032
+ sessionIdentity: identity,
2033
+ localSpace: mySpace,
2034
+ metaspace,
2035
+ });
2036
+ return commitFrame;
2037
+ } catch (error) {
2038
+ console.error(`${lc} ${extractErrorMsg(error)}`);
2039
+ throw error;
2040
+ } finally {
2041
+ if (logalot) { console.log(`${lc} complete.`); }
2042
+ }
2043
+ }
1965
2044
 
1966
- protected async handleCommitFrame({
2045
+ private async handleCommitFrame({
1967
2046
  sagaIbGib,
1968
- destSpace,
1969
- tempSpace,
2047
+ mySpace,
2048
+ myTempSpace,
1970
2049
  metaspace,
1971
2050
  identity,
1972
2051
  }: {
1973
2052
  sagaIbGib: SyncIbGib_V1,
1974
- destSpace: IbGibSpaceAny,
1975
- tempSpace: IbGibSpaceAny,
2053
+ mySpace: IbGibSpaceAny,
2054
+ myTempSpace: IbGibSpaceAny,
1976
2055
  metaspace: MetaspaceService,
1977
2056
  identity?: KeystoneIbGib_V1,
1978
2057
  }): Promise<NextSagaFrameInfo> {
1979
2058
  const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
1980
- if (logalot) { console.log(`${lc} Commit received.`); }
2059
+ try {
2060
+ if (logalot) { console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`); }
2061
+
2062
+ let resNextSagaFrameInfo: NextSagaFrameInfo;
2063
+
2064
+ // Sender Logic (Finalizing):
2065
+ // If we are here, we received a Commit frame from the Peer.
2066
+ // This implies the Peer has successfully committed.
2067
+ // We should now:
2068
+ // 1. Validate (implicitly done by receiving valid frame)
2069
+ // 2. Perform our own cleanup (Temp -> Dest, if applicable)
2070
+ // 3. Return saga completion.
2071
+
2072
+ // one last validate entire history?
2073
+ const history = await getFullSyncSagaHistory({
2074
+ sagaIbGib,
2075
+ space: mySpace,
2076
+ });
2077
+ const validationErrors = await validateFullSyncSagaHistory({
2078
+ history,
2079
+ });
2080
+ if (validationErrors.length > 0) {
2081
+ const errorCommitFrame = await this.createCommitFrame({
2082
+ sagaIbGib,
2083
+ metaspace,
2084
+ mySpace,
2085
+ identity,
2086
+ errors: validationErrors,
2087
+ });
2088
+ return { frame: errorCommitFrame, }; /* <<<< returns early */
2089
+ }
2090
+
2091
+ await this.executeLocalCommit({
2092
+ deltaFrame: sagaIbGib,
2093
+ sagaHistory: history,
2094
+ localSpace: mySpace,
2095
+ localTempSpace: myTempSpace,
2096
+ metaspace,
2097
+ });
1981
2098
 
1982
- // Sender Logic (Finalizing):
1983
- // If we are here, we received a Commit frame from the Peer.
1984
- // This implies the Peer has successfully committed.
1985
- // We should now:
1986
- // 1. Validate (implicitly done by receiving valid frame)
1987
- // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1988
- // 3. Return null to signal saga completion.
2099
+ // todo: implement explicit cleanup logic here and in peer
2100
+ console.error(`${lc} NAG ERROR (NOT THROWN): implement cleanup logic, including add a cleanup method to the peer (E: 3a9a24befb98a981a88fbdbf52920e26)`);
2101
+ if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1989
2102
 
1990
- // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
2103
+ // the holy grail!
2104
+ return { sagaComplete: true };
2105
+ } catch (error) {
2106
+ const emsg = `${lc} ${extractErrorMsg(error)}`;
2107
+ console.error(emsg);
2108
+ const errorCommitFrame = await this.createCommitFrame({
2109
+ sagaIbGib,
2110
+ errors: [emsg],
2111
+ metaspace,
2112
+ mySpace,
2113
+ identity,
2114
+ });
2115
+ return { frame: errorCommitFrame }
2116
+ } finally {
2117
+ if (logalot) { console.log(`${lc} complete.`); }
2118
+ }
1991
2119
 
1992
- if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1993
- // return { responseWasNull: true };
1994
- throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
1995
2120
  }
1996
2121
 
1997
2122
  // #endregion Handlers
1998
2123
 
1999
- protected async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
2124
+ private async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
2000
2125
  data,
2001
2126
  localSpace,
2002
2127
  metaspace,
@@ -2028,11 +2153,75 @@ export class SyncSagaCoordinator {
2028
2153
  }
2029
2154
  }
2030
2155
 
2156
+ private async getPayloadsForRequestedInfos({
2157
+ deltaRequestAddrInfos,
2158
+ mySpace,
2159
+ }: {
2160
+ deltaRequestAddrInfos: SyncSagaRequestAddrInfo[];
2161
+ mySpace: IbGibSpaceAny;
2162
+ }): Promise<IbGib_V1[]> {
2163
+ const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
2164
+ try {
2165
+ if (logalot) { console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`); }
2166
+ /**
2167
+ * graph of ibgibs we will send to the receiver. addr-based, so will
2168
+ * already be unique (no need to call `unique` on the domains
2169
+ * ibgibs)
2170
+ */
2171
+ const outgoingPayloadIbGibsDomainGraph: FlatIbGibGraph = {};
2172
+ for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
2173
+ let deltaDepGraph: FlatIbGibGraph;
2174
+ if (latestAddrAlreadyHave) {
2175
+ // already has some, so only get the delta
2176
+ // remember: if we didn't have the other's latest addr, then
2177
+ // this would be in the conflicts not requested addrs
2178
+ deltaDepGraph = await getDeltaDependencyGraph({
2179
+ ibGibAddr: addr,
2180
+ latestCommonFrameAddr: latestAddrAlreadyHave,
2181
+ space: mySpace,
2182
+ live: true,
2183
+ });
2184
+ } else {
2185
+ // doesn't have anything, so get the entire dependency graph
2186
+ // INEFFICIENT: we've already gotten all of the domain
2187
+ // dependencies in initDomainGraph, but getDependencyGraph
2188
+ // only works against a space so we are going to rerun this.
2189
+ // an optimization would be to adapt/create a new
2190
+ // getDependencyGraph to work against an existing flat ibgib
2191
+ // map.
2192
+ deltaDepGraph = await getDependencyGraph({
2193
+ ibGibAddr: addr,
2194
+ space: mySpace,
2195
+ live: true,
2196
+ });
2197
+ }
2198
+
2199
+ const depGraphSize = Object.keys(deltaDepGraph).length;
2200
+ if (depGraphSize === 0) {
2201
+ throw new Error(`(UNEXPECTED) couldn't get requested addrs in mySpace (${mySpace.ib})? How did the receiver know to ask for these addrs if they weren't in our original graph? (E: 281eaebcdf77dd73e8245b2872100826)`);
2202
+ } else {
2203
+ // we have dependencies!
2204
+ Object.values(deltaDepGraph).forEach(x => {
2205
+ outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
2206
+ });
2207
+ }
2208
+ }
2209
+
2210
+ const result = Object.values(outgoingPayloadIbGibsDomainGraph);
2211
+ return result;
2212
+ } catch (error) {
2213
+ console.error(`${lc} ${extractErrorMsg(error)}`);
2214
+ throw error;
2215
+ } finally {
2216
+ if (logalot) { console.log(`${lc} complete.`); }
2217
+ }
2218
+ }
2219
+
2031
2220
 
2032
2221
  /**
2033
2222
  * Evolves the saga timeline with a new frame.
2034
2223
  */
2035
- protected async evolveSyncSagaIbGib({
2224
+ private async evolveSyncSagaIbGib({
2036
2225
  prevSagaIbGib,
2037
2226
  conflictStrategy,
2038
2227
  msgStones,
@@ -2045,6 +2234,10 @@ export class SyncSagaCoordinator {
2045
2234
  msgStones: IbGib_V1[],
2046
2235
  localSpace: IbGibSpaceAny,
2047
2236
  metaspace: MetaspaceService,
2237
+ /**
2238
+ * does NOT evolve the keystone. this should be done by the caller.
2239
+ * (NOT IMPLEMENTED YET ANYWAY)
2240
+ */
2048
2241
  sessionIdentity?: KeystoneIbGib_V1,
2049
2242
  }): Promise<SyncIbGib_V1> {
2050
2243
  const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
@@ -2152,7 +2345,7 @@ export class SyncSagaCoordinator {
2152
2345
  }
2153
2346
  }
2154
2347
 
2155
- protected async getStageAndPayloadFromFrame({
2348
+ private async getStageAndPayloadFromFrame({
2156
2349
  sagaFrame,
2157
2350
  space
2158
2351
  }: {
@@ -2188,7 +2381,7 @@ export class SyncSagaCoordinator {
2188
2381
  }
2189
2382
  }
2190
2383
 
2191
- protected sortTimelinesTopologically(timelines: { [tjp: string]: IbGib_V1[] }): string[] {
2384
+ private sortTimelinesTopologically(timelines: { [tjp: string]: IbGib_V1[] }): string[] {
2192
2385
  const lc = `${this.lc}[${this.sortTimelinesTopologically.name}]`;
2193
2386
  const tjps = Object.keys(timelines);
2194
2387
  if (tjps.length === 0) { return []; }