@ibgib/core-gib 0.1.27 → 0.1.29

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 (48) hide show
  1. package/dist/common/meta-stone/meta-stone-helper.d.mts.map +1 -1
  2. package/dist/common/meta-stone/meta-stone-helper.mjs +19 -7
  3. package/dist/common/meta-stone/meta-stone-helper.mjs.map +1 -1
  4. package/dist/sync/sync-helpers.d.mts +11 -5
  5. package/dist/sync/sync-helpers.d.mts.map +1 -1
  6. package/dist/sync/sync-helpers.mjs +33 -9
  7. package/dist/sync/sync-helpers.mjs.map +1 -1
  8. package/dist/sync/sync-innerspace-constants.respec.mjs +36 -36
  9. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  10. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +5 -3
  11. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  12. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
  13. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  14. package/dist/sync/sync-innerspace-partial-update.respec.mjs +3 -3
  15. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  16. package/dist/sync/sync-innerspace.respec.mjs +49 -20
  17. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  18. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +0 -9
  19. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  20. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +13 -42
  21. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  22. package/dist/sync/sync-peer/sync-peer-v1.mjs +2 -2
  23. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  24. package/dist/sync/sync-saga-coordinator.d.mts +23 -159
  25. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  26. package/dist/sync/sync-saga-coordinator.mjs +508 -172
  27. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  28. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +12 -17
  29. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  30. package/dist/sync/sync-types.d.mts +18 -23
  31. package/dist/sync/sync-types.d.mts.map +1 -1
  32. package/dist/sync/sync-types.mjs +15 -21
  33. package/dist/sync/sync-types.mjs.map +1 -1
  34. package/package.json +1 -1
  35. package/src/common/meta-stone/meta-stone-helper.mts +17 -7
  36. package/src/sync/sync-helpers.mts +36 -13
  37. package/src/sync/sync-innerspace-constants.respec.mts +39 -39
  38. package/src/sync/sync-innerspace-deep-updates.respec.mts +6 -6
  39. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
  40. package/src/sync/sync-innerspace-partial-update.respec.mts +2 -2
  41. package/src/sync/sync-innerspace.respec.mts +20 -19
  42. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +11 -58
  43. package/src/sync/sync-peer/sync-peer-v1.mts +2 -2
  44. package/src/sync/sync-saga-coordinator.mts +472 -191
  45. package/src/sync/sync-saga-message/sync-saga-message-types.mts +13 -18
  46. package/src/sync/sync-types.mts +26 -33
  47. package/test_output.log +0 -0
  48. package/tmp.md +170 -62
@@ -29,10 +29,9 @@ import {
29
29
  SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
30
30
  SyncRel8ns_V1, DomainIbGibAnalysisInfo, NextSagaFrameInfo,
31
31
  SYNC_CONFLICT_STRATEGY_VALID_VALUES, HandleSagaResponseContextResult,
32
- SyncExecutionContext,
33
- SYNC_EXECUTION_CONTEXT_VALID_VALUES,
32
+ SyncSagaFrameDependencyGraph,
34
33
  } from "./sync-types.mjs";
35
- import { getExecutionContext, getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
34
+ import { getSyncSagaFrameOrigin, getFullSyncSagaHistory, getSyncIb, getTempSpaceName, isPastFrame, putInSpace_dnasThenNonDnas, validateFullSyncSagaHistory } from "./sync-helpers.mjs";
36
35
  import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
37
36
  import {
38
37
  SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
@@ -57,9 +56,8 @@ import { IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns } f
57
56
  import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
58
57
 
59
58
 
60
- // const logalot = GLOBAL_LOG_A_LOT || true;
61
- const logalot = true;
62
- const logalotControlDomain = true;
59
+ const logalot = GLOBAL_LOG_A_LOT;
60
+ const logalotControlDomain = false;
63
61
  const lcControlDomain = '[ControlDomain]';
64
62
 
65
63
  /**
@@ -76,10 +74,10 @@ const lcControlDomain = '[ControlDomain]';
76
74
  * to a specific Saga session, not fixed node identities.
77
75
  */
78
76
  export class SyncSagaCoordinator {
79
- protected lc: string = `[${SyncSagaCoordinator.name}]`;
77
+ private lc: string = `[${SyncSagaCoordinator.name}]`;
80
78
 
81
79
  constructor(
82
- protected keystone: KeystoneService_V1
80
+ private keystone: KeystoneService_V1
83
81
  ) {
84
82
 
85
83
  }
@@ -240,7 +238,11 @@ export class SyncSagaCoordinator {
240
238
 
241
239
  if (!contextResult) {
242
240
  if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`); }
243
- return null;
241
+ // does this ever hit now?
242
+ return null; /* <<<< returns early */
243
+ } else if (contextResult.nextFrameInfo?.sagaComplete) {
244
+ // this is the current proper workflow I believe as of 01/22/2026
245
+ return null; /* <<<< returns early */
244
246
  }
245
247
 
246
248
  // #region error conditions throw
@@ -248,8 +250,6 @@ export class SyncSagaCoordinator {
248
250
  throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
249
251
  } else if (!contextResult.nextFrameInfo) {
250
252
  throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
251
- } else if (contextResult.nextFrameInfo?.responseWasNull) {
252
- throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
253
253
  }
254
254
  // #endregion error conditions throw
255
255
 
@@ -278,7 +278,7 @@ export class SyncSagaCoordinator {
278
278
  }
279
279
  }
280
280
 
281
- protected async getSessionIdentity({
281
+ private async getSessionIdentity({
282
282
  sagaId,
283
283
  metaspace,
284
284
  tempSpace,
@@ -329,7 +329,7 @@ export class SyncSagaCoordinator {
329
329
  * the NEXT request context.
330
330
  * When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
331
331
  */
332
- protected async executeSagaLoop({
332
+ private async executeSagaLoop({
333
333
  initFrame,
334
334
  initDomainGraph,
335
335
  peer,
@@ -502,7 +502,11 @@ export class SyncSagaCoordinator {
502
502
  });
503
503
 
504
504
  if (!contextResult) {
505
- if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`); }
505
+ // should this ever hit?
506
+ console.error(`${lc} NAG ERROR (DOES NOT THROW): does this ever hit now? (E: e04d02efc2a8e72a88b79f1f0f95ca26)`);
507
+ break;
508
+ } else if (contextResult.nextFrameInfo?.sagaComplete) {
509
+ if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: 123bf9e7dca8886de72553a8d4f29e26)`); }
506
510
  break;
507
511
  }
508
512
 
@@ -511,8 +515,6 @@ export class SyncSagaCoordinator {
511
515
  throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
512
516
  } else if (!contextResult.nextFrameInfo) {
513
517
  throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
514
- } else if (contextResult.nextFrameInfo?.responseWasNull) {
515
- throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
516
518
  }
517
519
  // #endregion error conditions throw
518
520
 
@@ -610,7 +612,7 @@ export class SyncSagaCoordinator {
610
612
  }
611
613
  }
612
614
 
613
- protected async analyzeDomainIbGibs({
615
+ private async analyzeDomainIbGibs({
614
616
  domainIbGibs,
615
617
  space,
616
618
  }: {
@@ -656,7 +658,7 @@ export class SyncSagaCoordinator {
656
658
  * Generates the first frame containing the Knowledge Vector of the Local Space.
657
659
  * This is sent to the Receiver to begin Gap Analysis.
658
660
  */
659
- protected async createInitFrame({
661
+ private async createInitFrame({
660
662
  sagaId,
661
663
  sessionIdentity,
662
664
  domainIbGibs,
@@ -739,7 +741,7 @@ export class SyncSagaCoordinator {
739
741
  *
740
742
  * @returns when all {@link expectedAddrs} are done being transmitted.
741
743
  */
742
- protected async pollForDomainPayloads({
744
+ private async pollForDomainPayloads({
743
745
  expectedAddrs,
744
746
  pollIntervalMs,
745
747
  domainPayloadsMap,
@@ -824,7 +826,7 @@ export class SyncSagaCoordinator {
824
826
  *
825
827
  * This is a one-off on the receiver.
826
828
  */
827
- public async handleResponseSagaContext({
829
+ private async handleResponseSagaContext({
828
830
  sagaContext,
829
831
  initDomainGraph,
830
832
  mySpace,
@@ -898,15 +900,18 @@ export class SyncSagaCoordinator {
898
900
  sagaContext,
899
901
  sagaIbGib,
900
902
  srcGraph,
901
- metaspace,
902
- mySpace,
903
- myTempSpace,
903
+ metaspace, mySpace, myTempSpace,
904
904
  identity,
905
905
  });
906
906
  break;
907
907
 
908
908
  case SyncStage.commit:
909
- nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
909
+ if (logalot) { console.log(`${lc}[${getSyncSagaFrameOrigin({ sagaFrame: sagaIbGib })}] mySpace.ib: ${mySpace.ib} (I: 5b270996d848907238d817fffa64a126)`); }
910
+ nextFrameInfo = await this.handleCommitFrame({
911
+ sagaIbGib,
912
+ metaspace, mySpace, myTempSpace,
913
+ identity,
914
+ });
910
915
  break;
911
916
 
912
917
  default:
@@ -940,7 +945,7 @@ export class SyncSagaCoordinator {
940
945
  * 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
941
946
  * 4. Returns an `Ack` frame containing these lists.
942
947
  */
943
- protected async handleInitFrame({
948
+ private async handleInitFrame({
944
949
  sagaIbGib,
945
950
  messageData,
946
951
  mySpace,
@@ -970,7 +975,7 @@ export class SyncSagaCoordinator {
970
975
  try {
971
976
  if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
972
977
 
973
- console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
978
+ if (logalot) { console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`); }
974
979
 
975
980
  // Extract Init Data
976
981
  const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
@@ -1015,7 +1020,7 @@ export class SyncSagaCoordinator {
1015
1020
  const remoteKV = initData.knowledgeVector;
1016
1021
  if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
1017
1022
  const remoteTjps = Object.keys(remoteKV);
1018
- console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
1023
+ if (logalot) { console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`); }
1019
1024
  if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
1020
1025
 
1021
1026
  // 1. Get Local Latest Addrs for all TJPs
@@ -1029,7 +1034,7 @@ export class SyncSagaCoordinator {
1029
1034
  if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
1030
1035
  if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
1031
1036
  localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
1032
- console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
1037
+ if (logalot) { console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`); }
1033
1038
  if (logalot) { console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`); }
1034
1039
  }
1035
1040
 
@@ -1040,7 +1045,7 @@ export class SyncSagaCoordinator {
1040
1045
 
1041
1046
  if (!localAddr) {
1042
1047
  // We (Receiver) don't have this timeline at all. Request it.
1043
- console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
1048
+ if (logalot) { console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`); }
1044
1049
  deltaRequestAddrInfos.push({
1045
1050
  addr: remoteAddr,
1046
1051
  tjpAddr: tjp,
@@ -1054,10 +1059,10 @@ export class SyncSagaCoordinator {
1054
1059
 
1055
1060
  if (localAddr === remoteAddr) {
1056
1061
  // ...already synced
1057
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
1062
+ if (logalot) { console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`); }
1058
1063
  continue;
1059
1064
  }
1060
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
1065
+ if (logalot) { console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`); }
1061
1066
 
1062
1067
  // we have this timeline but it's not synced...
1063
1068
 
@@ -1073,7 +1078,7 @@ export class SyncSagaCoordinator {
1073
1078
  if (remoteIsInPast) {
1074
1079
  // we're ahead, so push the delta of what the sender doesn't
1075
1080
  // have (we have full knowledge)
1076
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
1081
+ if (logalot) { console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`); }
1077
1082
  const deltaGraph = await getDeltaDependencyGraph({
1078
1083
  ibGibAddr: localAddr,
1079
1084
  live: false, // always live: false right?
@@ -1107,7 +1112,7 @@ export class SyncSagaCoordinator {
1107
1112
 
1108
1113
  if (localIsInPast) {
1109
1114
  // Fast-Forward: We update to remote's tip.
1110
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
1115
+ if (logalot) { console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`); }
1111
1116
  deltaRequestAddrInfos.push({
1112
1117
  addr: remoteAddr,
1113
1118
  tjpAddr: tjp,
@@ -1115,7 +1120,7 @@ export class SyncSagaCoordinator {
1115
1120
  });
1116
1121
  } else {
1117
1122
  // DIVERGENCE: Both have changes the other doesn't know about.
1118
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
1123
+ if (logalot) { console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`); }
1119
1124
 
1120
1125
  if (conflictStrategy === 'abort') {
1121
1126
  // Abort Strategy: We will treat this as terminal.
@@ -1289,7 +1294,7 @@ export class SyncSagaCoordinator {
1289
1294
  *
1290
1295
  * Returns a `Delta` frame.
1291
1296
  */
1292
- protected async handleAckFrame({
1297
+ private async handleAckFrame({
1293
1298
  sagaContext,
1294
1299
  sagaIbGib,
1295
1300
  initDomainGraph,
@@ -1336,14 +1341,14 @@ export class SyncSagaCoordinator {
1336
1341
 
1337
1342
  // 1. Check for Conflicts
1338
1343
  const conflicts = ackData.conflicts || [];
1339
- console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`);
1340
- if (conflicts.length > 0) {
1344
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`); }
1345
+ if (logalot && conflicts.length > 0) {
1341
1346
  console.log(`${lc} [CONFLICT DEBUG] Conflicts detail: ${JSON.stringify(conflicts, null, 2)}`);
1342
1347
  }
1343
1348
 
1344
1349
  const terminalConflicts = conflicts.filter(c => c.terminal);
1345
1350
  if (terminalConflicts.length > 0) {
1346
- console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
1351
+ if (logalot) { console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`); }
1347
1352
  // Terminal failure. Sender should probably Commit(Fail) or just Abort.
1348
1353
  // For now, throw to trigger abort.
1349
1354
  throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
@@ -1359,7 +1364,7 @@ export class SyncSagaCoordinator {
1359
1364
  const outgoingDeltaAddrRequestInfos: SyncSagaRequestAddrInfo[] = []; // Additional requests for merging
1360
1365
 
1361
1366
  if (conflicts.length > 0) {
1362
- console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
1367
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`); }
1363
1368
  // We need to resolve these.
1364
1369
  // Strategy:
1365
1370
  // 1. Analyze Divergence (Sender vs Receiver)
@@ -1492,9 +1497,9 @@ export class SyncSagaCoordinator {
1492
1497
  // }
1493
1498
  }
1494
1499
 
1495
- console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
1500
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`); }
1496
1501
  } else {
1497
- console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
1502
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`); }
1498
1503
  }
1499
1504
 
1500
1505
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
@@ -1565,70 +1570,6 @@ export class SyncSagaCoordinator {
1565
1570
  }
1566
1571
  }
1567
1572
 
1568
- private async getPayloadsForRequestedInfos({
1569
- deltaRequestAddrInfos,
1570
- mySpace,
1571
- }: {
1572
- deltaRequestAddrInfos: SyncSagaRequestAddrInfo[];
1573
- mySpace: IbGibSpaceAny;
1574
- }): Promise<IbGib_V1[]> {
1575
- const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
1576
- try {
1577
- if (logalot) { console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`); }
1578
- /**
1579
- * graph of ibgibs we will send to the receiver. addr-based, so will
1580
- * already be unique (no need to call `unique` on the domains
1581
- * ibgibs)
1582
- */
1583
- const outgoingPayloadIbGibsDomainGraph: FlatIbGibGraph = {};
1584
- for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
1585
- let deltaDepGraph: FlatIbGibGraph;
1586
- if (latestAddrAlreadyHave) {
1587
- // already has some, so only get the delta
1588
- // remember: if we didn't have the other's latest addr, then
1589
- // this would be in the conflicts not requested addrs
1590
- deltaDepGraph = await getDeltaDependencyGraph({
1591
- ibGibAddr: addr,
1592
- latestCommonFrameAddr: latestAddrAlreadyHave,
1593
- space: mySpace,
1594
- live: true,
1595
- });
1596
- } else {
1597
- // doesn't have anything, so get the entire dependency graph
1598
- // INEFFICIENT: we've already gotten all of the domain
1599
- // dependencies in initDomainGraph, but getDependencyGraph
1600
- // only works against a space so we are going to rerun this.
1601
- // an optimization would be to adapt/create a new
1602
- // getDependencyGraph to work against an existing flat ibgib
1603
- // map.
1604
- deltaDepGraph = await getDependencyGraph({
1605
- ibGibAddr: addr,
1606
- space: mySpace,
1607
- live: true,
1608
- });
1609
- }
1610
-
1611
- const depGraphSize = Object.keys(deltaDepGraph).length;
1612
- if (depGraphSize === 0) {
1613
- 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)`);
1614
- } else {
1615
- // we have dependencies!
1616
- Object.values(deltaDepGraph).forEach(x => {
1617
- outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
1618
- });
1619
- }
1620
- }
1621
-
1622
- const result = Object.values(outgoingPayloadIbGibsDomainGraph);
1623
- return result;
1624
- } catch (error) {
1625
- console.error(`${lc} ${extractErrorMsg(error)}`);
1626
- throw error;
1627
- } finally {
1628
- if (logalot) { console.log(`${lc} complete.`); }
1629
- }
1630
- }
1631
-
1632
1573
  /**
1633
1574
  * Handles the `Delta` frame.
1634
1575
  *
@@ -1639,7 +1580,7 @@ export class SyncSagaCoordinator {
1639
1580
  * 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
1640
1581
  * 3. **Completion**: If no more requests, transitions to `Commit`.
1641
1582
  */
1642
- protected async handleDeltaFrame({
1583
+ private async handleDeltaFrame({
1643
1584
  sagaContext,
1644
1585
  sagaIbGib,
1645
1586
  srcGraph,
@@ -1669,7 +1610,7 @@ export class SyncSagaCoordinator {
1669
1610
  if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1670
1611
  // #endregion validate/sanity
1671
1612
 
1672
- console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1613
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`); }
1673
1614
 
1674
1615
  const peerProposesCommit = deltaData.proposeCommit || false;
1675
1616
 
@@ -1679,12 +1620,12 @@ export class SyncSagaCoordinator {
1679
1620
  const receivedPayloadIbGibs: IbGib_V1[] = sagaContext.payloadIbGibsDomain ?? [];
1680
1621
 
1681
1622
  // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1682
- console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`);
1623
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`); }
1683
1624
  const outgoingPayload = await this.getPayloadsForRequestedInfos({
1684
1625
  deltaRequestAddrInfos: deltaData.deltaRequestAddrInfos || [],
1685
1626
  mySpace,
1686
1627
  });
1687
- console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1628
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`); }
1688
1629
 
1689
1630
  // 3. Execute Merges (If applicable)
1690
1631
  // Check if we have pending conflicts that we CAN resolve now that we have data.
@@ -1692,36 +1633,41 @@ export class SyncSagaCoordinator {
1692
1633
  // Optimization: Do this only if we received payloads.
1693
1634
  const mergeResultIbGibs: IbGib_V1[] = [];
1694
1635
 
1695
- console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1636
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`); }
1696
1637
 
1697
1638
  if (receivedPayloadIbGibs.length > 0) {
1698
- console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1639
+ if (logalot) { console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`); }
1699
1640
  // Find the Ack frame in history to get conflicts
1700
1641
  // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1701
1642
  // V1 timelines carry full history in `past`.
1702
1643
  const pastAddrs = sagaIbGib.rel8ns?.past || [];
1703
- console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1704
- let ackData: SyncSagaMessageAckData_V1 | undefined;
1705
-
1706
- if (pastAddrs.length > 0) {
1707
- // Batch fetch all past frames
1708
- const resPast = await getFromSpace({ addrs: pastAddrs, space: myTempSpace });
1709
- if (resPast.success && resPast.ibGibs) {
1710
- // Iterate backwards (most recent first) to find the latest Ack
1711
- for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1712
- const pastFrame = resPast.ibGibs[i];
1713
- const messageStone = await getSyncSagaMessageFromFrame({
1714
- frameIbGib: pastFrame,
1715
- space: myTempSpace
1716
- });
1717
- if (messageStone?.data?.stage === SyncStage.ack) {
1718
- ackData = messageStone.data as SyncSagaMessageAckData_V1;
1719
- console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1720
- break;
1721
- }
1722
- }
1723
- }
1644
+ if (logalot) { console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`); }
1645
+
1646
+ const sagaHistory = await getFullSyncSagaHistory({
1647
+ sagaIbGib,
1648
+ space: mySpace,
1649
+ });
1650
+ // #region validate/sanity sagaHistory
1651
+ 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)`); }
1652
+ 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)`); }
1653
+ // #endregion validate/sanity sagaHistory
1654
+
1655
+ const ackGraphs = sagaHistory.filter(x =>
1656
+ x.msgStones.at(0)!.data!.stage === SyncStage.ack
1657
+ );
1658
+ // #region validate/sanity ackGraphs
1659
+ if (ackGraphs.length > 1) {
1660
+ throw new Error(`(UNEXPECTED) more than one ack stage found in sagaHistory? we're expecting exactly one. (E: 7150983bb3a841caf8acd60826ba4f26)`);
1661
+ } else if (ackGraphs.length === 0) {
1662
+ throw new Error(`(UNEXPECTED) couldn't find ack stage in sagaHistory? (E: 7d2da859196b86de28e7c8183af1e826)`);
1663
+ }
1664
+ if (ackGraphs[0].msgStones.length !== 1) {
1665
+ throw new Error(`(UNEXPECTED) ackGraph has more than one msg stone? only one expected right now. (E: f07d388abff8d26f98cbf6e8d3932826)`);
1724
1666
  }
1667
+ // #endregion validate/sanity ackGraphs
1668
+
1669
+ const ackData = ackGraphs[0].msgStones[0].data as SyncSagaMessageAckData_V1;
1670
+ if (!ackData) { throw new Error(`(UNEXPECTED) ackData falsy? (E: f9af88427f66a8db1ba868b810e8b826)`); }
1725
1671
 
1726
1672
  if (ackData && ackData.conflicts) {
1727
1673
  const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
@@ -1735,9 +1681,9 @@ export class SyncSagaCoordinator {
1735
1681
  // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1736
1682
 
1737
1683
  // Check if we have receiverTip in space
1738
- console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1684
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`); }
1739
1685
  const resRecTip = await getFromSpace({ addr: receiverTip, space: myTempSpace }); // Check myTempSpace for incoming data
1740
- console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1686
+ if (logalot) { console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`); }
1741
1687
  if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1742
1688
  // We have the tip!
1743
1689
  // Do we have the full history?
@@ -1753,13 +1699,13 @@ export class SyncSagaCoordinator {
1753
1699
  metaspace,
1754
1700
  });
1755
1701
  if (mergeResult) {
1756
- console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1702
+ if (logalot) { console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`); }
1757
1703
  if (logalot) { console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`); }
1758
1704
  mergeResultIbGibs.push(mergeResult);
1759
1705
  outgoingPayload.push(mergeResult); // Send result to peer
1760
1706
  }
1761
1707
  } catch (e) {
1762
- console.error(`${lc} Merge failed: ${e}`);
1708
+ console.error(`${lc} (NOT THROWN) Merge failed: ${e} (E: 491b464fc6f4857f52ff3df213105826)`);
1763
1709
  // If merge fails, we might Abort or just continue?
1764
1710
  }
1765
1711
  }
@@ -1785,20 +1731,8 @@ export class SyncSagaCoordinator {
1785
1731
  stage: SyncStage.delta,
1786
1732
  payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1787
1733
  requests: hasMyRequests ? myRequests : undefined,
1788
- proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1789
- // Wait. If we send data, we are NOT committing yet.
1790
- // We are sending data. The OTHER side must ingest it.
1791
- // So proposeCommit = true?
1792
- // "Here is the data. I'm done. If you are good, let's commit."
1793
- // Yes.
1794
1734
  };
1795
1735
 
1796
- // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1797
- // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1798
-
1799
- // So yes, proposeCommit = true.
1800
- responseDeltaData.proposeCommit = true;
1801
-
1802
1736
  const deltaStone = await this.createSyncMsgStone({
1803
1737
  data: responseDeltaData,
1804
1738
  localSpace: mySpace,
@@ -1813,42 +1747,45 @@ export class SyncSagaCoordinator {
1813
1747
  metaspace,
1814
1748
  });
1815
1749
 
1816
- // Build control payloads: frame + its dependencies (msg stone, identity)
1817
- const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1818
- if (identity) { payloadIbGibsControl.push(identity); }
1819
-
1820
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1821
1750
  return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1822
- // throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1823
-
1824
1751
  } else {
1825
1752
  // We have nothing to send.
1826
1753
 
1827
1754
  if (peerProposesCommit) {
1828
1755
  // Peer is done. We are done. -> Commit.
1829
- const commitData: SyncSagaMessageCommitData_V1 = {
1830
- sagaId: deltaData.sagaId,
1831
- stage: SyncStage.commit,
1832
- success: true,
1833
- };
1834
-
1835
- const commitStone = await this.createSyncMsgStone({
1836
- data: commitData,
1837
- localSpace: mySpace,
1838
- metaspace
1756
+ // one last validate entire history?
1757
+ const history = await getFullSyncSagaHistory({
1758
+ sagaIbGib,
1759
+ space: mySpace,
1760
+ });
1761
+ const validationErrors = await validateFullSyncSagaHistory({
1762
+ history,
1839
1763
  });
1764
+ if (validationErrors.length > 0) {
1765
+ const errorCommitFrame = await this.createCommitFrame({
1766
+ sagaIbGib,
1767
+ errors: validationErrors,
1768
+ metaspace, mySpace,
1769
+ identity,
1770
+ });
1771
+ return { frame: errorCommitFrame, }; /* <<<< returns early */
1772
+ }
1840
1773
 
1841
- const commitFrame = await this.evolveSyncSagaIbGib({
1842
- prevSagaIbGib: sagaIbGib,
1843
- msgStones: [commitStone],
1844
- sessionIdentity: identity,
1774
+ await this.executeLocalCommit({
1775
+ sagaHistory: history,
1776
+ deltaFrame: sagaIbGib,
1777
+ localTempSpace: myTempSpace,
1845
1778
  localSpace: mySpace,
1846
- metaspace
1847
- });
1779
+ metaspace,
1780
+ })
1848
1781
 
1849
- // Build control payloads for commit
1850
- const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
1851
- if (identity) { commitCtrlPayloads.push(identity); }
1782
+ const commitFrame = await this.createCommitFrame({
1783
+ sagaIbGib,
1784
+ errors: undefined,
1785
+ metaspace,
1786
+ mySpace,
1787
+ identity,
1788
+ });
1852
1789
 
1853
1790
  return { frame: commitFrame, };
1854
1791
 
@@ -1924,9 +1861,246 @@ export class SyncSagaCoordinator {
1924
1861
  }
1925
1862
 
1926
1863
  }
1864
+ /**
1865
+ * should throw if fails
1866
+ */
1867
+ private async executeLocalCommit({
1868
+ deltaFrame,
1869
+ commitFrame,
1870
+ sagaHistory,
1871
+ metaspace, localSpace, localTempSpace,
1872
+ identity,
1873
+ }: {
1874
+ deltaFrame?: SyncIbGib_V1;
1875
+ commitFrame?: SyncIbGib_V1;
1876
+ sagaHistory: SyncSagaFrameDependencyGraph[];
1877
+ metaspace: MetaspaceService;
1878
+ localSpace: IbGibSpaceAny;
1879
+ localTempSpace: IbGibSpaceAny;
1880
+ identity?: KeystoneIbGib_V1;
1881
+ }): Promise<void> {
1882
+ const lc = `${this.lc}[${this.executeLocalCommit.name}]`;
1883
+ try {
1884
+ if (logalot) { console.log(`${lc} starting... (I: 6734980446b86a63c1af6e2e206de826)`); }
1885
+ if (!deltaFrame && !commitFrame) {
1886
+ throw new Error(`(UNEXPECTED) !deltaFrame && !commitFrame? we're expecting to execute the commit based off of EITHER a delta OR a commit frame. (E: 10cae319a2685a672866f5583514e326)`);
1887
+ } else if (deltaFrame && commitFrame) {
1888
+ throw new Error(`(UNEXPECTED) deltaFrame && commitFrame? we're expecting to execute the commit based off of EITHER a delta OR a commit frame. (E: b9037b1ed6d8684ac8a5d01328bad826)`);
1889
+ }
1890
+
1891
+ /**
1892
+ * Sync has a two-stage commit. The first is from a delta frame with
1893
+ * `proposeCommit: true`. That produces a commit frame, which on the
1894
+ * other end triggers the second leg.
1895
+ */
1896
+ const isFirstCommitLeg = !!deltaFrame;
1897
+ const currentFrame = isFirstCommitLeg ? deltaFrame : commitFrame;
1898
+ if (!currentFrame) { throw new Error(`(UNEXPECTED) !currentFrame? something wrong with my logic. (E: 4ec0ce6625cc1d677bc902c839ca1a26)`); }
1899
+
1900
+ /**
1901
+ * we only want to register new ibgibs from the other end, so we
1902
+ * will compare the history payloads/push offers with the context
1903
+ * that provided those payloads.
1904
+ *
1905
+ * So we will filter out the history that we produced, which should
1906
+ * leave us only with externally created payload addrs.
1907
+ *
1908
+ * NOTE: I have this at the beginning of the function PURELY to get
1909
+ * a log value in to understand the context of this function's
1910
+ * output.
1911
+ */
1912
+ const currentFrameOrigin = getSyncSagaFrameOrigin({ sagaFrame: currentFrame });
1927
1913
 
1914
+ if (logalot) { console.log(`${lc} currentFrameOrigin: ${currentFrameOrigin} (I: 3add8f8390c89743ae554bd3cc60b826)`); }
1928
1915
 
1929
- protected async handleCommitFrame({
1916
+ // #region validate/sanity
1917
+ if (!currentFrame.data) { throw new Error(`(UNEXPECTED) currentFrame.data falsy? (E: a8be68d48668d93d992d793834823826)`); }
1918
+ // #endregion validate/sanity
1919
+
1920
+ // * move all payload addrs from temp space to local space
1921
+ // * register each and every iteration of each timeline (including
1922
+ // stones, which are like their own sealed timeline of length 1)
1923
+
1924
+ const allPayloadAddrsDomainTransferred: IbGibAddr[] = [];
1925
+ const fnAddPayloadAddr = (addr: IbGibAddr) => {
1926
+ if (!allPayloadAddrsDomainTransferred.includes(addr)) {
1927
+ allPayloadAddrsDomainTransferred.push(addr);
1928
+ }
1929
+ }
1930
+
1931
+ sagaHistory.filter(x => {
1932
+ const frameExecutionContext = getSyncSagaFrameOrigin({ sagaFrame: x.sagaIbGib });
1933
+ return currentFrameOrigin === frameExecutionContext;
1934
+ }).forEach(x => {
1935
+ if (!x.sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 34d7f8cdee14717ce828878d98f89826)`); }
1936
+ if (logalot) { console.log(`${lc}[${currentFrameOrigin}] history slice: ${pretty(x)} (I: e5a9436fb66b0df3183bd6d81be2ca26)`); }
1937
+ x.msgStones.forEach(msgStone => {
1938
+ if (!msgStone.data) { throw new Error(`(UNEXPECTED) msgStone.data falsy? (E: 21406101f91847514cc759c8a7382f26)`); }
1939
+ if (msgStone.data.stage === SyncStage.ack) {
1940
+ const ackData = msgStone.data as SyncSagaMessageAckData_V1;
1941
+ if (ackData.pushOfferInfos && ackData.pushOfferInfos.length > 0) {
1942
+ ackData.pushOfferInfos.forEach(info => {
1943
+ info.addrs.forEach(addr => fnAddPayloadAddr(addr));
1944
+ });
1945
+ }
1946
+ } else if (msgStone.data.stage === SyncStage.delta) {
1947
+ const deltaData = msgStone.data as SyncSagaMessageDeltaData_V1;
1948
+ if (deltaData.payloadAddrsDomain && deltaData.payloadAddrsDomain.length > 0) {
1949
+ deltaData.payloadAddrsDomain.forEach(addr => fnAddPayloadAddr(addr));
1950
+ }
1951
+ }
1952
+ })
1953
+ });
1954
+
1955
+ if (logalot) { console.log(`${lc}[${currentFrameOrigin}] allPayloadAddrsDomainTransferred: ${allPayloadAddrsDomainTransferred.length > 0 ? allPayloadAddrsDomainTransferred.join(', ') : 'none'} (I: a1950eb3ca95bdc9ec18a4b8823e9826)`); }
1956
+
1957
+ // at this point, we have a list of ALL payload addrs retrieved.
1958
+ let allPayloadIbGibsDomainTransferred: IbGib_V1[] = [];
1959
+ if (allPayloadAddrsDomainTransferred.length > 0) {
1960
+ const resGetAllTransferred =
1961
+ await getFromSpace({ addrs: allPayloadAddrsDomainTransferred, space: localTempSpace });
1962
+ if (resGetAllTransferred.success && resGetAllTransferred.ibGibs && resGetAllTransferred.ibGibs.length === allPayloadAddrsDomainTransferred.length) {
1963
+ allPayloadIbGibsDomainTransferred = resGetAllTransferred.ibGibs.concat();
1964
+ } else {
1965
+ // errored out, gather info
1966
+ if (!resGetAllTransferred.rawResultIbGib) { throw new Error(`(UNEXPECTED) !resGetAllTransferred.rawResultIbGib falsy? (E: dc6cf8729668f4fbe8c024f887d97a26)`); }
1967
+ const { addrsNotFound, addrsErrored, errors } = resGetAllTransferred.rawResultIbGib.data as IbGibSpaceResultData;
1968
+ 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)`);
1969
+ }
1970
+ }
1971
+
1972
+ // now we have all ibgibs transferred, first put them all the local space
1973
+ if (allPayloadIbGibsDomainTransferred.length === 0) {
1974
+ // nothing was synced
1975
+ if (logalot) { console.log(`${lc}[${currentFrameOrigin}] no changes on this end (I: fa30d18e83a87ee9e8487fbb5d632b26)`); }
1976
+ } else {
1977
+ if (logalot) { console.log(`${lc}[${currentFrameOrigin}] ${allPayloadIbGibsDomainTransferred.length} changes detected (I: fa30d18e83a87ee9e8487fbb5d632b26)`); }
1978
+
1979
+ if (logalot) { console.log(`${lc} put all into localSpace (${localSpace.ib}) starting... (I: d5e0d9870e380b61e80c729660058826)`); }
1980
+ const { payload_Dnas, payload_NonDnas } =
1981
+ await putInSpace_dnasThenNonDnas({
1982
+ ibGibs: allPayloadIbGibsDomainTransferred,
1983
+ space: localSpace
1984
+ });
1985
+ if (logalot) { console.log(`${lc} put all into localSpace (${localSpace.ib}) complete. (I: d5e0d9870e380b61e80c729660058826)`); }
1986
+
1987
+ const { mapWithTjp_NoDna, mapWithTjp_YesDna, mapWithoutTjps } =
1988
+ splitPerTjpAndOrDna({ ibGibs: payload_NonDnas, filterPrimitives: true });
1989
+
1990
+ // first register all non-tjp stones
1991
+ if (logalot) { console.log(`${lc} register mapWithoutTjps starting... (I: 2b84d8f8dda85adde88696d8419bf726)`); }
1992
+ const nontjps = Object.values(mapWithoutTjps);
1993
+ for (const nontjp of nontjps) {
1994
+ if (logalot) { console.log(`${lc} registering ${getIbGibAddr({ ibGib: nontjp })} (I: 4647b4f42d27cffe0fbfce8846755826)`); }
1995
+ await metaspace.registerNewIbGib({
1996
+ ibGib: nontjp,
1997
+ space: localSpace,
1998
+ });
1999
+ }
2000
+ if (logalot) { console.log(`${lc} mapWithoutTjps complete. (I: 2b84d8f8dda85adde88696d8419bf726)`); }
2001
+
2002
+ // next register each timeline in order...
2003
+
2004
+ // ...first the ones without dna...
2005
+ if (logalot) { console.log(`${lc} register mapWithTjp_NoDna starting... (I: 96e648e4db382499b8bfaa1b37c24826)`); }
2006
+ const timelinesByTjpAddr_NoDna =
2007
+ getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_NoDna) });
2008
+ const tjpAddrs_NoDna = Object.keys(timelinesByTjpAddr_NoDna);
2009
+ for (const tjpAddr_NoDna of tjpAddrs_NoDna) {
2010
+ const timelineIbGibs = timelinesByTjpAddr_NoDna[tjpAddr_NoDna];
2011
+ for (const ibGib of timelineIbGibs) {
2012
+ await metaspace.registerNewIbGib({
2013
+ ibGib,
2014
+ space: localSpace,
2015
+ });
2016
+ }
2017
+ }
2018
+ if (logalot) { console.log(`${lc} register mapWithTjp_NoDna complete. (I: 96e648e4db382499b8bfaa1b37c24826)`); }
2019
+
2020
+ // ...then the ones WITH dna.
2021
+ if (logalot) { console.log(`${lc} register mapWithTjp_YesDna starting... (I: d54a820e9442dee4681f6228a6795926)`); }
2022
+ const timelinesByTjpAddr_YesDna =
2023
+ getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_YesDna) });
2024
+ const tjpAddrs_YesDna = Object.keys(timelinesByTjpAddr_YesDna);
2025
+ for (const tjpAddr_YesDna of tjpAddrs_YesDna) {
2026
+ const timelineIbGibs = timelinesByTjpAddr_YesDna[tjpAddr_YesDna];
2027
+ for (const ibGib of timelineIbGibs) {
2028
+ await metaspace.registerNewIbGib({
2029
+ ibGib,
2030
+ space: localSpace,
2031
+ });
2032
+ }
2033
+ }
2034
+ if (logalot) { console.log(`${lc} register mapWithTjp_YesDna complete. (I: d54a820e9442dee4681f6228a6795926)`); }
2035
+ }
2036
+
2037
+
2038
+
2039
+ } catch (error) {
2040
+ console.error(`${lc} ${extractErrorMsg(error)}`);
2041
+ throw error;
2042
+ } finally {
2043
+ if (logalot) { console.log(`${lc} complete.`); }
2044
+ }
2045
+ }
2046
+
2047
+ private async createCommitFrame({
2048
+ sagaIbGib,
2049
+ errors,
2050
+ metaspace,
2051
+ mySpace,
2052
+ identity,
2053
+ }: {
2054
+ sagaIbGib: SyncIbGib_V1,
2055
+ /**
2056
+ * if truthy and non-empty, will create an errored commit frame (data
2057
+ * has `success: false`)
2058
+ */
2059
+ errors?: string[],
2060
+ metaspace: MetaspaceService,
2061
+ mySpace: IbGibSpaceAny,
2062
+ identity: KeystoneIbGib_V1 | undefined,
2063
+ }): Promise<SyncIbGib_V1> {
2064
+ const lc = `[${this.createCommitFrame.name}]`;
2065
+ try {
2066
+ if (logalot) { console.log(`${lc} starting... (I: 48fc57c023f80122135a284855757526)`); }
2067
+ const commitData: SyncSagaMessageCommitData_V1 = errors && errors.length > 0 ?
2068
+ {
2069
+ // errored
2070
+ sagaId: sagaIbGib.data!.uuid,
2071
+ stage: SyncStage.commit,
2072
+ success: false,
2073
+ errors,
2074
+ } :
2075
+ {
2076
+ // not errored
2077
+ sagaId: sagaIbGib.data!.uuid,
2078
+ stage: SyncStage.commit,
2079
+ success: true,
2080
+ };
2081
+
2082
+ const commitStone = await this.createSyncMsgStone({
2083
+ data: commitData,
2084
+ localSpace: mySpace,
2085
+ metaspace
2086
+ });
2087
+ const commitFrame = await this.evolveSyncSagaIbGib({
2088
+ prevSagaIbGib: sagaIbGib,
2089
+ msgStones: [commitStone],
2090
+ sessionIdentity: identity,
2091
+ localSpace: mySpace,
2092
+ metaspace,
2093
+ });
2094
+ return commitFrame;
2095
+ } catch (error) {
2096
+ console.error(`${lc} ${extractErrorMsg(error)}`);
2097
+ throw error;
2098
+ } finally {
2099
+ if (logalot) { console.log(`${lc} complete.`); }
2100
+ }
2101
+ }
2102
+
2103
+ private async handleCommitFrame({
1930
2104
  sagaIbGib,
1931
2105
  mySpace,
1932
2106
  myTempSpace,
@@ -1943,23 +2117,66 @@ export class SyncSagaCoordinator {
1943
2117
  try {
1944
2118
  if (logalot) { console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`); }
1945
2119
 
2120
+ if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: cbc31dbb7d28b5b8c8cddb510d7d2826)`); }
1946
2121
 
2122
+ if (sagaIbGib.data.errors && sagaIbGib.data.errors.length > 0) {
2123
+ // our saga errored out but we already committed the darn
2124
+ // changes previously
2125
+ throw new Error(`not implemented...saga errored out on other end but we've already committed our changes. we need to implement undoing our changes, i.e., rollback commit, which basically is deleting all the payloads that we already put in in our previous commit. (E: e8eb789e0c08f0b3cf93607cb73e9f26)`);
2126
+ } else {
2127
+ // Sender Logic (Finalizing):
2128
+ // If we are here, we received a Commit frame from the Peer.
2129
+ // This implies the Peer has successfully committed.
2130
+ // We should now:
2131
+ // 1. Validate (implicitly done by receiving valid frame)
2132
+ // 2. Perform our own cleanup (Temp -> Dest, if applicable)
2133
+ // 3. Return saga completion.
2134
+
2135
+ // one last validate entire history ?
2136
+ const history = await getFullSyncSagaHistory({
2137
+ sagaIbGib,
2138
+ space: mySpace,
2139
+ });
2140
+ const validationErrors = await validateFullSyncSagaHistory({
2141
+ history,
2142
+ });
2143
+ if (validationErrors.length > 0) {
2144
+ const errorCommitFrame = await this.createCommitFrame({
2145
+ sagaIbGib,
2146
+ metaspace,
2147
+ mySpace,
2148
+ identity,
2149
+ errors: validationErrors,
2150
+ });
2151
+ return { frame: errorCommitFrame, }; /* <<<< returns early */
2152
+ }
1947
2153
 
1948
- // Sender Logic (Finalizing):
1949
- // If we are here, we received a Commit frame from the Peer.
1950
- // This implies the Peer has successfully committed.
1951
- // We should now:
1952
- // 1. Validate (implicitly done by receiving valid frame)
1953
- // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1954
- // 3. Return null to signal saga completion.
2154
+ await this.executeLocalCommit({
2155
+ commitFrame: sagaIbGib,
2156
+ sagaHistory: history,
2157
+ localSpace: mySpace,
2158
+ localTempSpace: myTempSpace,
2159
+ metaspace,
2160
+ });
1955
2161
 
1956
- // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1957
- if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
2162
+ // todo: implement explicit cleanup logic here and in peer
2163
+ console.error(`${lc} NAG ERROR (NOT THROWN): implement cleanup logic, including add a cleanup method to the peer (E: 3a9a24befb98a981a88fbdbf52920e26)`);
2164
+ if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1958
2165
 
1959
- return { responseWasNull: true };
2166
+ // the holy grail!
2167
+ return { sagaComplete: true };
2168
+ }
1960
2169
  } catch (error) {
1961
- console.error(`${lc} ${extractErrorMsg(error)}`);
1962
- throw error;
2170
+ const emsg = `${lc} ${extractErrorMsg(error)}`;
2171
+ console.error(emsg);
2172
+ const errorCommitFrame = await this.createCommitFrame({
2173
+ sagaIbGib,
2174
+ errors: [emsg],
2175
+ metaspace,
2176
+ mySpace,
2177
+ identity,
2178
+ });
2179
+ return { frame: errorCommitFrame }
1963
2180
  } finally {
1964
2181
  if (logalot) { console.log(`${lc} complete.`); }
1965
2182
  }
@@ -1968,7 +2185,7 @@ export class SyncSagaCoordinator {
1968
2185
 
1969
2186
  // #endregion Handlers
1970
2187
 
1971
- protected async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
2188
+ private async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
1972
2189
  data,
1973
2190
  localSpace,
1974
2191
  metaspace,
@@ -2000,11 +2217,75 @@ export class SyncSagaCoordinator {
2000
2217
  }
2001
2218
  }
2002
2219
 
2220
+ private async getPayloadsForRequestedInfos({
2221
+ deltaRequestAddrInfos,
2222
+ mySpace,
2223
+ }: {
2224
+ deltaRequestAddrInfos: SyncSagaRequestAddrInfo[];
2225
+ mySpace: IbGibSpaceAny;
2226
+ }): Promise<IbGib_V1[]> {
2227
+ const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
2228
+ try {
2229
+ if (logalot) { console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`); }
2230
+ /**
2231
+ * graph of ibgibs we will send to the receiver. addr-based, so will
2232
+ * already be unique (no need to call `unique` on the domains
2233
+ * ibgibs)
2234
+ */
2235
+ const outgoingPayloadIbGibsDomainGraph: FlatIbGibGraph = {};
2236
+ for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
2237
+ let deltaDepGraph: FlatIbGibGraph;
2238
+ if (latestAddrAlreadyHave) {
2239
+ // already has some, so only get the delta
2240
+ // remember: if we didn't have the other's latest addr, then
2241
+ // this would be in the conflicts not requested addrs
2242
+ deltaDepGraph = await getDeltaDependencyGraph({
2243
+ ibGibAddr: addr,
2244
+ latestCommonFrameAddr: latestAddrAlreadyHave,
2245
+ space: mySpace,
2246
+ live: true,
2247
+ });
2248
+ } else {
2249
+ // doesn't have anything, so get the entire dependency graph
2250
+ // INEFFICIENT: we've already gotten all of the domain
2251
+ // dependencies in initDomainGraph, but getDependencyGraph
2252
+ // only works against a space so we are going to rerun this.
2253
+ // an optimization would be to adapt/create a new
2254
+ // getDependencyGraph to work against an existing flat ibgib
2255
+ // map.
2256
+ deltaDepGraph = await getDependencyGraph({
2257
+ ibGibAddr: addr,
2258
+ space: mySpace,
2259
+ live: true,
2260
+ });
2261
+ }
2262
+
2263
+ const depGraphSize = Object.keys(deltaDepGraph).length;
2264
+ if (depGraphSize === 0) {
2265
+ 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)`);
2266
+ } else {
2267
+ // we have dependencies!
2268
+ Object.values(deltaDepGraph).forEach(x => {
2269
+ outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
2270
+ });
2271
+ }
2272
+ }
2273
+
2274
+ const result = Object.values(outgoingPayloadIbGibsDomainGraph);
2275
+ return result;
2276
+ } catch (error) {
2277
+ console.error(`${lc} ${extractErrorMsg(error)}`);
2278
+ throw error;
2279
+ } finally {
2280
+ if (logalot) { console.log(`${lc} complete.`); }
2281
+ }
2282
+ }
2283
+
2003
2284
 
2004
2285
  /**
2005
2286
  * Evolves the saga timeline with a new frame.
2006
2287
  */
2007
- protected async evolveSyncSagaIbGib({
2288
+ private async evolveSyncSagaIbGib({
2008
2289
  prevSagaIbGib,
2009
2290
  conflictStrategy,
2010
2291
  msgStones,
@@ -2128,7 +2409,7 @@ export class SyncSagaCoordinator {
2128
2409
  }
2129
2410
  }
2130
2411
 
2131
- protected async getStageAndPayloadFromFrame({
2412
+ private async getStageAndPayloadFromFrame({
2132
2413
  sagaFrame,
2133
2414
  space
2134
2415
  }: {
@@ -2164,7 +2445,7 @@ export class SyncSagaCoordinator {
2164
2445
  }
2165
2446
  }
2166
2447
 
2167
- protected sortTimelinesTopologically(timelines: { [tjp: string]: IbGib_V1[] }): string[] {
2448
+ private sortTimelinesTopologically(timelines: { [tjp: string]: IbGib_V1[] }): string[] {
2168
2449
  const lc = `${this.lc}[${this.sortTimelinesTopologically.name}]`;
2169
2450
  const tjps = Object.keys(timelines);
2170
2451
  if (tjps.length === 0) { return []; }