@ibgib/core-gib 0.1.26 → 0.1.27

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 +16 -1
  2. package/dist/sync/sync-helpers.d.mts.map +1 -1
  3. package/dist/sync/sync-helpers.mjs +101 -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 +29 -16
  17. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  18. package/dist/sync/sync-saga-coordinator.mjs +378 -392
  19. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  20. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +1 -1
  21. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  22. package/dist/sync/sync-types.d.mts +30 -1
  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 +92 -4
  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 +408 -432
  33. package/src/sync/sync-saga-message/sync-saga-message-types.mts +18 -18
  34. package/src/sync/sync-types.mts +38 -1
@@ -3,7 +3,7 @@ pretty, delay, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
3
3
  import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
4
4
  import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
5
5
  import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space-helper.mjs";
6
- import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
6
+ import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN, } from "./sync-constants.mjs";
7
7
  import { appendToTimeline, createTimeline } from "../timeline/timeline-api.mjs";
8
8
  import { SyncConflictStrategy, SyncMode, SYNC_CONFLICT_STRATEGY_VALID_VALUES, } from "./sync-types.mjs";
9
9
  import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
@@ -17,7 +17,7 @@ import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
17
17
  import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
18
18
  import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
19
19
  // const logalot = GLOBAL_LOG_A_LOT || true;
20
- const logalot = false;
20
+ const logalot = true;
21
21
  const logalotControlDomain = true;
22
22
  const lcControlDomain = '[ControlDomain]';
23
23
  /**
@@ -153,8 +153,8 @@ export class SyncSagaCoordinator {
153
153
  * @returns next context result if another round, else if commit returns
154
154
  * null
155
155
  */
156
- async receiverContinueSync({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
157
- const lc = `${this.lc}[${this.receiverContinueSync.name}]`;
156
+ async continueSync({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
157
+ const lc = `${this.lc}[${this.continueSync.name}]`;
158
158
  try {
159
159
  if (logalot) {
160
160
  console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`);
@@ -585,7 +585,9 @@ export class SyncSagaCoordinator {
585
585
  metaspace,
586
586
  localSpace,
587
587
  });
588
- // if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
588
+ if (logalot) {
589
+ console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`);
590
+ }
589
591
  return { initFrame: sagaFrame, initDomainGraph: fullGraph };
590
592
  }
591
593
  catch (error) {
@@ -691,7 +693,7 @@ export class SyncSagaCoordinator {
691
693
  console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
692
694
  }
693
695
  // Get Stage from Stone (or Frame for Init fallback)
694
- const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
696
+ const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
695
697
  if (logalot) {
696
698
  console.log(`${lc} handling frame stage: ${stage}`);
697
699
  }
@@ -705,11 +707,8 @@ export class SyncSagaCoordinator {
705
707
  nextFrameInfo = await this.handleInitFrame({
706
708
  sagaIbGib,
707
709
  messageData: messageData,
708
- metaspace,
709
- mySpace: mySpace,
710
- myTempSpace: myTempSpace,
711
- identity,
712
- identitySecret
710
+ metaspace, mySpace, myTempSpace,
711
+ identity, identitySecret
713
712
  });
714
713
  break;
715
714
  case SyncStage.ack:
@@ -717,24 +716,33 @@ export class SyncSagaCoordinator {
717
716
  throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`);
718
717
  }
719
718
  nextFrameInfo = await this.handleAckFrame({
719
+ sagaContext,
720
720
  sagaIbGib,
721
- srcGraph,
722
721
  initDomainGraph,
723
- metaspace,
724
- destSpace: mySpace,
725
- tempSpace: myTempSpace,
722
+ metaspace, mySpace, myTempSpace,
726
723
  identity,
727
724
  });
728
725
  break;
729
726
  case SyncStage.delta:
730
- nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
727
+ nextFrameInfo = await this.handleDeltaFrame({
728
+ sagaContext,
729
+ sagaIbGib,
730
+ srcGraph,
731
+ metaspace,
732
+ mySpace,
733
+ myTempSpace,
734
+ identity,
735
+ });
731
736
  break;
732
737
  case SyncStage.commit:
733
- nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
738
+ nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
734
739
  break;
735
740
  default:
736
741
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
737
742
  }
743
+ if (logalot) {
744
+ console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`);
745
+ }
738
746
  return { errorMsg: undefined, nextFrameInfo, };
739
747
  }
740
748
  catch (error) {
@@ -758,7 +766,7 @@ export class SyncSagaCoordinator {
758
766
  * The Receiver performs Gap Analysis here:
759
767
  * 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
760
768
  * 2. Identifies what Sender needs (`pushOfferAddrs`).
761
- * 3. Identifies what Receiver needs (`deltaReqAddrs`).
769
+ * 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
762
770
  * 4. Returns an `Ack` frame containing these lists.
763
771
  */
764
772
  async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
@@ -995,7 +1003,7 @@ export class SyncSagaCoordinator {
995
1003
  }
996
1004
  const ackStone = await this.createSyncMsgStone({
997
1005
  data: ackData,
998
- localSpace: myTempSpace,
1006
+ localSpace: mySpace,
999
1007
  metaspace,
1000
1008
  });
1001
1009
  if (logalot) {
@@ -1092,19 +1100,23 @@ export class SyncSagaCoordinator {
1092
1100
  * **Execution Context**: **Sender (Local)**.
1093
1101
  *
1094
1102
  * The Sender reacts to the Receiver's requirements:
1095
- * 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
1096
- * 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
1103
+ * 1. `deltaRequestAddrInfos`: Receiver wants this data. Sender takes this
1104
+ * into account, plus gathers it from `initDomainGraph` and puts them in
1105
+ * `payloadIbGibs` (for next frame).
1106
+ * 2. `pushOfferAddrs`: Receiver has newer data. these should have been
1107
+ * included in the incoming context from the receiver..
1097
1108
  *
1098
1109
  * Returns a `Delta` frame.
1099
1110
  */
1100
- async handleAckFrame({ sagaIbGib, srcGraph, initDomainGraph, destSpace, tempSpace, metaspace, identity, }) {
1111
+ async handleAckFrame({ sagaContext, sagaIbGib, initDomainGraph, mySpace, myTempSpace, metaspace, identity, }) {
1101
1112
  const lc = `${this.lc}[${this.handleAckFrame.name}]`;
1102
1113
  try {
1103
1114
  if (logalot) {
1104
1115
  console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
1105
1116
  }
1106
- const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1117
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1107
1118
  const ackData = messageData;
1119
+ // #region sanity/validation
1108
1120
  if (!ackData) {
1109
1121
  throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
1110
1122
  }
@@ -1114,6 +1126,10 @@ export class SyncSagaCoordinator {
1114
1126
  if (logalot) {
1115
1127
  console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`);
1116
1128
  }
1129
+ if (!sagaIbGib.data) {
1130
+ throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`);
1131
+ }
1132
+ // #region sanity/validation
1117
1133
  // 1. Check for Conflicts
1118
1134
  const conflicts = ackData.conflicts || [];
1119
1135
  console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`);
@@ -1128,7 +1144,12 @@ export class SyncSagaCoordinator {
1128
1144
  throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
1129
1145
  }
1130
1146
  // at this point, if we have conflicts, they are non-terminal
1131
- const mergeDeltaReqs = []; // Additional requests for merging
1147
+ /**
1148
+ * at this point, we only request ibgibs for conflicted timelines.
1149
+ * If the receiver had known of any ibgibs this sender needed, it
1150
+ * would have been in the push offer.
1151
+ */
1152
+ const outgoingDeltaAddrRequestInfos = []; // Additional requests for merging
1132
1153
  if (conflicts.length > 0) {
1133
1154
  console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
1134
1155
  // We need to resolve these.
@@ -1138,8 +1159,8 @@ export class SyncSagaCoordinator {
1138
1159
  // 3. Request that data (as Delta Reqs)
1139
1160
  // 4. (Later in Delta Phase) Perform Merge.
1140
1161
  // BUT: The Delta Phase is usually generic "Send me these Addrs".
1141
- // If we just add to `deltaReqAddrs` (which are requests for Sender to send to Receiver?),
1142
- // wait. `ackData.deltaReqAddrs` are what RECEIVER wants from SENDER.
1162
+ // If we just add to `deltaRequestAddrInfos` (which are requests for Sender to send to Receiver?),
1163
+ // wait. `ackData.deltaRequestAddrInfos` are what RECEIVER wants from SENDER.
1143
1164
  // We (Sender) are processing the Ack.
1144
1165
  // We need to request data FROM Receiver.
1145
1166
  // But the protocol 'Ack' step typically leads to 'Delta' (Sender sending data).
@@ -1206,8 +1227,8 @@ export class SyncSagaCoordinator {
1206
1227
  // // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
1207
1228
  // for (const addr of receiverOnlyAddrs) {
1208
1229
  // // Add the frame itself first
1209
- // if (!mergeDeltaReqs.includes(addr)) {
1210
- // mergeDeltaReqs.push(addr);
1230
+ // if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
1231
+ // outgoingDeltaAddrRequestInfos.push(addr);
1211
1232
  // }
1212
1233
  // // Get the frame's delta dependencies (skip LCA's deps)
1213
1234
  // try {
@@ -1223,8 +1244,8 @@ export class SyncSagaCoordinator {
1223
1244
  // if (frameDeltaDeps) {
1224
1245
  // // Add all delta dependencies (Object.keys gives us the addresses)
1225
1246
  // Object.keys(frameDeltaDeps).forEach(depAddr => {
1226
- // if (!mergeDeltaReqs.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1227
- // mergeDeltaReqs.push(depAddr);
1247
+ // if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1248
+ // outgoingDeltaAddrRequestInfos.push(depAddr);
1228
1249
  // }
1229
1250
  // });
1230
1251
  // }
@@ -1233,117 +1254,63 @@ export class SyncSagaCoordinator {
1233
1254
  // console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
1234
1255
  // }
1235
1256
  // }
1236
- // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${mergeDeltaReqs.length}`);
1257
+ // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
1237
1258
  // } else {
1238
1259
  // console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
1239
1260
  // }
1240
1261
  }
1241
- console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. mergeDeltaReqs: ${mergeDeltaReqs.length}`);
1262
+ console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
1242
1263
  }
1243
1264
  else {
1244
1265
  console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
1245
1266
  }
1246
1267
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
1247
- const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
1248
- const pushOfferAddrs = ackData.pushOfferInfos || [];
1249
- // 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
1250
- const pullReqAddrs = [];
1251
- for (const addr of pushOfferAddrs) {
1252
- // const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1253
- // if (!existing) {
1254
- // pullReqAddrs.push(addr);
1255
- // }
1256
- }
1257
- // 2. Process Delta Requests (Push Payload)
1258
- // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
1259
- // const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
1260
- const skipAddrs = new Set();
1261
- if (ackData.knowledgeVector) {
1262
- Object.values(ackData.knowledgeVector).forEach(addrs => {
1263
- // addrs.forEach(a => skipAddrs.add(a));
1264
- });
1265
- }
1266
- const payloadIbGibs = [];
1267
- // Gather all tips to sync first
1268
- const tipsToSync = [];
1269
- for (const addr of deltaReqAddrs) {
1270
- // let ibGib = srcGraph[addr];
1271
- // if (!ibGib) {
1272
- // const res = await getFromSpace({ addr, space: destSpace });
1273
- // if (res.ibGibs && res.ibGibs.length > 0) {
1274
- // ibGib = res.ibGibs[0];
1275
- // }
1276
- // }
1277
- // if (ibGib) {
1278
- // tipsToSync.push(ibGib);
1279
- // } else {
1280
- // throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1281
- // }
1282
- }
1283
- // Calculate Dependency Graph for ALL tips, effectively utilizing common history
1284
- // Pass skipAddrs to `getDependencyGraph` or gather manually.
1285
- // `getDependencyGraph` takes a single ibGib.
1286
- // We can optimize by doing it for each tip and unioning the result?
1287
- // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
1288
- // We will loop.
1289
- const allDepsSet = new Set();
1290
- for (const tip of tipsToSync) {
1291
- // Always include the tip itself
1292
- const tipAddr = getIbGibAddr({ ibGib: tip });
1293
- // Only process if not skipped (though deltaReq implies they barely just asked for it)
1294
- // But detailed deps might be skipped.
1295
- // Get Graph with Skips
1296
- // Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
1297
- const deps = await getDependencyGraph({
1298
- ibGib: tip,
1299
- space: destSpace,
1300
- skipAddrs: Array.from(skipAddrs)
1301
- });
1302
- // [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
1303
- let tipIncluded = false;
1304
- if (deps) {
1305
- Object.values(deps).forEach(d => {
1306
- const dAddr = getIbGibAddr({ ibGib: d });
1307
- if (!allDepsSet.has(dAddr)) {
1308
- allDepsSet.add(dAddr);
1309
- payloadIbGibs.push(d);
1310
- }
1311
- if (dAddr === tipAddr) {
1312
- tipIncluded = true;
1313
- }
1314
- });
1315
- }
1316
- if (!tipIncluded && !skipAddrs.has(tipAddr)) {
1317
- if (logalot) {
1318
- console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`);
1319
- }
1320
- if (!allDepsSet.has(tipAddr)) {
1321
- allDepsSet.add(tipAddr);
1322
- payloadIbGibs.push(tip);
1323
- }
1324
- }
1325
- }
1268
+ /**
1269
+ * these were requested addrs on the INCOMING frame (the ack data).
1270
+ *
1271
+ * This is in contrast to any OUTGOING requests we make in this
1272
+ * method.
1273
+ */
1274
+ const payloadIbGibsDomain = await this.getPayloadsForRequestedInfos({
1275
+ deltaRequestAddrInfos: ackData.deltaRequestAddrInfos || [],
1276
+ mySpace,
1277
+ });
1326
1278
  // 3. Create Delta Frame
1327
- const sagaId = ackData.sagaId;
1328
1279
  const deltaData = {
1329
1280
  sagaId: sagaIbGib.data.uuid,
1330
1281
  stage: SyncStage.delta,
1331
- payloadAddrs: payloadIbGibs.map(p => getIbGibAddr({ ibGib: p })),
1332
- requests: [...(pullReqAddrs || []), ...(mergeDeltaReqs || [])].length > 0 ? [...(pullReqAddrs || []), ...(mergeDeltaReqs || [])] : undefined,
1282
+ /**
1283
+ * we're sending these domain ibgibs as payload (they were
1284
+ * requested by receiver)
1285
+ */
1286
+ payloadAddrsDomain: payloadIbGibsDomain.length > 0 ?
1287
+ payloadIbGibsDomain.map(x => getIbGibAddr({ ibGib: x })) :
1288
+ undefined,
1289
+ /**
1290
+ * we're asking for these addrs
1291
+ */
1292
+ deltaRequestAddrInfos: outgoingDeltaAddrRequestInfos.length > 0 ?
1293
+ outgoingDeltaAddrRequestInfos :
1294
+ undefined,
1295
+ /**
1296
+ * if we have no changes and request none, propose commit to
1297
+ * finish the sync transaction.
1298
+ */
1299
+ proposeCommit: payloadIbGibsDomain.length === 0 && outgoingDeltaAddrRequestInfos.length === 0,
1333
1300
  };
1334
1301
  if (logalot) {
1335
1302
  console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`);
1336
1303
  }
1337
1304
  const deltaStone = await this.createSyncMsgStone({
1338
1305
  data: deltaData,
1339
- localSpace: tempSpace,
1306
+ localSpace: mySpace,
1340
1307
  metaspace,
1341
1308
  });
1342
1309
  const deltaFrame = await this.evolveSyncSagaIbGib({
1343
1310
  prevSagaIbGib: sagaIbGib,
1344
1311
  msgStones: [deltaStone],
1345
1312
  sessionIdentity: identity,
1346
- localSpace: tempSpace,
1313
+ localSpace: mySpace,
1347
1314
  metaspace,
1348
1315
  });
1349
1316
  if (logalot) {
@@ -1354,9 +1321,70 @@ export class SyncSagaCoordinator {
1354
1321
  if (identity) {
1355
1322
  payloadIbGibsControl.push(identity);
1356
1323
  }
1357
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1358
- // return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1359
- throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
1324
+ return { frame: deltaFrame, payloadIbGibsDomain, };
1325
+ }
1326
+ catch (error) {
1327
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1328
+ throw error;
1329
+ }
1330
+ finally {
1331
+ if (logalot) {
1332
+ console.log(`${lc} complete.`);
1333
+ }
1334
+ }
1335
+ }
1336
+ async getPayloadsForRequestedInfos({ deltaRequestAddrInfos, mySpace, }) {
1337
+ const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
1338
+ try {
1339
+ if (logalot) {
1340
+ console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`);
1341
+ }
1342
+ /**
1343
+ * graph of ibgibs we will send to the receiver. addr-based, so will
1344
+ * already be unique (no need to call `unique` on the domains
1345
+ * ibgibs)
1346
+ */
1347
+ const outgoingPayloadIbGibsDomainGraph = {};
1348
+ for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
1349
+ let deltaDepGraph;
1350
+ if (latestAddrAlreadyHave) {
1351
+ // already has some, so only get the delta
1352
+ // remember: if we didn't have the other's latest addr, then
1353
+ // this would be in the conflicts not requested addrs
1354
+ deltaDepGraph = await getDeltaDependencyGraph({
1355
+ ibGibAddr: addr,
1356
+ latestCommonFrameAddr: latestAddrAlreadyHave,
1357
+ space: mySpace,
1358
+ live: true,
1359
+ });
1360
+ }
1361
+ else {
1362
+ // doesn't have anything, so get the entire dependency graph
1363
+ // INEFFICIENT: we've already gotten all of the domain
1364
+ // dependencies in initDomainGraph, but getDependencyGraph
1365
+ // only works against a space so we are going to rerun this.
1366
+ // an optimization would be to adapt/create a new
1367
+ // getDependencyGraph to work against an existing flat ibgib
1368
+ // map.
1369
+ deltaDepGraph = await getDependencyGraph({
1370
+ ibGibAddr: addr,
1371
+ space: mySpace,
1372
+ live: true,
1373
+ });
1374
+ }
1375
+ const depGraphSize = Object.keys(deltaDepGraph).length;
1376
+ if (depGraphSize === 0) {
1377
+ 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)`);
1378
+ }
1379
+ else {
1380
+ // we have dependencies!
1381
+ Object.values(deltaDepGraph).forEach(x => {
1382
+ outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
1383
+ });
1384
+ }
1385
+ }
1386
+ const result = Object.values(outgoingPayloadIbGibsDomainGraph);
1387
+ return result;
1360
1388
  }
1361
1389
  catch (error) {
1362
1390
  console.error(`${lc} ${extractErrorMsg(error)}`);
@@ -1378,275 +1406,165 @@ export class SyncSagaCoordinator {
1378
1406
  * 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
1379
1407
  * 3. **Completion**: If no more requests, transitions to `Commit`.
1380
1408
  */
1381
- async handleDeltaFrame({ sagaIbGib, srcGraph, destSpace, tempSpace, metaspace, identity, }) {
1409
+ async handleDeltaFrame({ sagaContext, sagaIbGib, srcGraph, mySpace, myTempSpace, metaspace, identity, }) {
1382
1410
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1383
- if (logalot) {
1384
- console.log(`${lc} starting...`);
1385
- }
1386
- const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1387
- const deltaData = messageData;
1388
- if (!deltaData) {
1389
- throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
1390
- }
1391
- if (deltaData.stage !== SyncStage.delta) {
1392
- throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1393
- }
1394
- if (logalot) {
1395
- console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
1396
- }
1397
- console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1398
- const payloadAddrs = deltaData.payloadAddrs || [];
1399
- const peerRequests = deltaData.requests || [];
1400
- const peerProposesCommit = deltaData.proposeCommit || false;
1401
- // 1. Process Received Payload (Ingest)
1402
- const receivedPayloadIbGibs = [];
1403
- if (payloadAddrs.length > 0) {
1404
- // We use `payloadAddrs` as the manifest.
1405
- // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1406
- // assuming the "Transport" layer put them there implicitly?
1407
- // OR, if we are local-only, we just get them.
1408
- // The `handleDeltaFrame` contract assumes data is reachable in `space`.
1409
- const res = await getFromSpace({
1410
- addrs: payloadAddrs,
1411
- space: tempSpace, // Incoming data is in tempSpace
1412
- });
1413
- if (res.ibGibs) {
1414
- receivedPayloadIbGibs.push(...res.ibGibs);
1415
- // Also put them? `getFromSpace` retrieves. If they are in space, they are persisted.
1416
- // If this is a Temp Space, they are safe.
1417
- }
1418
- else {
1419
- console.warn(`${lc} Failed to retrieve payloads listed in delta: ${payloadAddrs.join(', ')}`);
1411
+ try {
1412
+ if (logalot) {
1413
+ console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`);
1420
1414
  }
1421
- }
1422
- // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1423
- const outgoingPayload = [];
1424
- const outgoingAddrsSet = new Set(); // Track what we've added
1425
- console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${peerRequests.length} peer requests`);
1426
- for (const addr of peerRequests) {
1427
- // Get the requested ibGib
1428
- let ibGib = srcGraph[addr];
1429
- if (!ibGib) {
1430
- const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1431
- if (res.ibGibs && res.ibGibs.length > 0) {
1432
- ibGib = res.ibGibs[0];
1433
- }
1415
+ const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1416
+ const deltaData = messageData;
1417
+ // #region validate/sanity
1418
+ if (!deltaData) {
1419
+ throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
1434
1420
  }
1435
- if (ibGib) {
1436
- // Add the requested ibGib itself
1437
- const ibGibAddr = getIbGibAddr({ ibGib });
1438
- if (!outgoingAddrsSet.has(ibGibAddr)) {
1439
- outgoingPayload.push(ibGib);
1440
- outgoingAddrsSet.add(ibGibAddr);
1441
- }
1442
- // Expand to include full dependency graph for this ibGib
1443
- // (Receiver needs all deps to properly process/merge)
1444
- try {
1445
- const deps = await getDependencyGraph({
1446
- ibGib,
1447
- space: destSpace,
1448
- });
1449
- if (deps) {
1450
- Object.values(deps).forEach(depIbGib => {
1451
- const depAddr = getIbGibAddr({ ibGib: depIbGib });
1452
- if (!outgoingAddrsSet.has(depAddr)) {
1453
- outgoingPayload.push(depIbGib);
1454
- outgoingAddrsSet.add(depAddr);
1455
- }
1456
- });
1457
- }
1458
- }
1459
- catch (depError) {
1460
- console.warn(`${lc} [CONFLICT DEBUG] Error expanding deps for ${addr}: ${extractErrorMsg(depError)}`);
1461
- }
1421
+ if (deltaData.stage !== SyncStage.delta) {
1422
+ throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1462
1423
  }
1463
- else {
1464
- console.warn(`${lc} Requested addr not found during delta fulfillment: ${addr}`);
1424
+ if (logalot) {
1425
+ console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
1465
1426
  }
1466
- }
1467
- console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1468
- // 3. Execute Merges (If applicable)
1469
- // Check if we have pending conflicts that we CAN resolve now that we have data.
1470
- // We look at the Saga History (Ack Frame) to find conflicts.
1471
- // Optimization: Do this only if we received payloads.
1472
- const mergeResultIbGibs = [];
1473
- console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1474
- if (receivedPayloadIbGibs.length > 0) {
1475
- console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1476
- // Find the Ack frame in history to get conflicts
1477
- // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1478
- // V1 timelines carry full history in `past`.
1479
- const pastAddrs = sagaIbGib.rel8ns?.past || [];
1480
- console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1481
- let ackData;
1482
- if (pastAddrs.length > 0) {
1483
- // Batch fetch all past frames
1484
- const resPast = await getFromSpace({ addrs: pastAddrs, space: tempSpace });
1485
- if (resPast.success && resPast.ibGibs) {
1486
- // Iterate backwards (most recent first) to find the latest Ack
1487
- for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1488
- const pastFrame = resPast.ibGibs[i];
1489
- const messageStone = await getSyncSagaMessageFromFrame({
1490
- frameIbGib: pastFrame,
1491
- space: tempSpace
1492
- });
1493
- if (messageStone?.data?.stage === SyncStage.ack) {
1494
- ackData = messageStone.data;
1495
- console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1496
- break;
1427
+ // #endregion validate/sanity
1428
+ console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1429
+ const peerProposesCommit = deltaData.proposeCommit || false;
1430
+ /**
1431
+ * these are already in the local temp space
1432
+ */
1433
+ const receivedPayloadIbGibs = sagaContext.payloadIbGibsDomain ?? [];
1434
+ // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1435
+ console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`);
1436
+ const outgoingPayload = await this.getPayloadsForRequestedInfos({
1437
+ deltaRequestAddrInfos: deltaData.deltaRequestAddrInfos || [],
1438
+ mySpace,
1439
+ });
1440
+ console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1441
+ // 3. Execute Merges (If applicable)
1442
+ // Check if we have pending conflicts that we CAN resolve now that we have data.
1443
+ // We look at the Saga History (Ack Frame) to find conflicts.
1444
+ // Optimization: Do this only if we received payloads.
1445
+ const mergeResultIbGibs = [];
1446
+ console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1447
+ if (receivedPayloadIbGibs.length > 0) {
1448
+ console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1449
+ // Find the Ack frame in history to get conflicts
1450
+ // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1451
+ // V1 timelines carry full history in `past`.
1452
+ const pastAddrs = sagaIbGib.rel8ns?.past || [];
1453
+ console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1454
+ let ackData;
1455
+ if (pastAddrs.length > 0) {
1456
+ // Batch fetch all past frames
1457
+ const resPast = await getFromSpace({ addrs: pastAddrs, space: myTempSpace });
1458
+ if (resPast.success && resPast.ibGibs) {
1459
+ // Iterate backwards (most recent first) to find the latest Ack
1460
+ for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1461
+ const pastFrame = resPast.ibGibs[i];
1462
+ const messageStone = await getSyncSagaMessageFromFrame({
1463
+ frameIbGib: pastFrame,
1464
+ space: myTempSpace
1465
+ });
1466
+ if (messageStone?.data?.stage === SyncStage.ack) {
1467
+ ackData = messageStone.data;
1468
+ console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1469
+ break;
1470
+ }
1497
1471
  }
1498
1472
  }
1499
1473
  }
1500
- }
1501
- if (ackData && ackData.conflicts) {
1502
- const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1503
- for (const conflict of optimisticConflicts) {
1504
- const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1505
- // We are Sender (usually) here if we are merging.
1506
- // Check if we have the history needed (timelineAddrs).
1507
- // Specifically, we needed the `receiverOnly` parts.
1508
- // We blindly attempt merge if we have both tips accessible?
1509
- // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1510
- // Check if we have receiverTip in space
1511
- console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1512
- const resRecTip = await getFromSpace({ addr: receiverTip, space: tempSpace }); // Check tempSpace for incoming data
1513
- console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1514
- if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1515
- // We have the tip!
1516
- // Do we have the full history?
1517
- // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1518
- // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1519
- // Perform Merge!
1520
- try {
1521
- const mergeResult = await mergeDivergentTimelines({
1522
- tipA: (await getFromSpace({ addr: senderTip, space: destSpace })).ibGibs[0], // Our tip from destSpace
1523
- tipB: resRecTip.ibGibs[0], // Their tip (from tempSpace)
1524
- space: tempSpace, // Merge uses tempSpace
1525
- metaspace,
1526
- });
1527
- if (mergeResult) {
1528
- console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1529
- if (logalot) {
1530
- console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1474
+ if (ackData && ackData.conflicts) {
1475
+ const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1476
+ for (const conflict of optimisticConflicts) {
1477
+ const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1478
+ // We are Sender (usually) here if we are merging.
1479
+ // Check if we have the history needed (timelineAddrs).
1480
+ // Specifically, we needed the `receiverOnly` parts.
1481
+ // We blindly attempt merge if we have both tips accessible?
1482
+ // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1483
+ // Check if we have receiverTip in space
1484
+ console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1485
+ const resRecTip = await getFromSpace({ addr: receiverTip, space: myTempSpace }); // Check myTempSpace for incoming data
1486
+ console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1487
+ if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1488
+ // We have the tip!
1489
+ // Do we have the full history?
1490
+ // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1491
+ // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1492
+ // Perform Merge!
1493
+ try {
1494
+ const mergeResult = await mergeDivergentTimelines({
1495
+ tipA: (await getFromSpace({ addr: senderTip, space: mySpace })).ibGibs[0], // Our tip from destSpace
1496
+ tipB: resRecTip.ibGibs[0], // Their tip (from myTempSpace)
1497
+ space: myTempSpace, // Merge uses myTempSpace
1498
+ metaspace,
1499
+ });
1500
+ if (mergeResult) {
1501
+ console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1502
+ if (logalot) {
1503
+ console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1504
+ }
1505
+ mergeResultIbGibs.push(mergeResult);
1506
+ outgoingPayload.push(mergeResult); // Send result to peer
1531
1507
  }
1532
- mergeResultIbGibs.push(mergeResult);
1533
- outgoingPayload.push(mergeResult); // Send result to peer
1534
1508
  }
1535
- }
1536
- catch (e) {
1537
- console.error(`${lc} Merge failed: ${e}`);
1538
- // If merge fails, we might Abort or just continue?
1509
+ catch (e) {
1510
+ console.error(`${lc} Merge failed: ${e}`);
1511
+ // If merge fails, we might Abort or just continue?
1512
+ }
1539
1513
  }
1540
1514
  }
1541
1515
  }
1542
1516
  }
1543
- }
1544
- // 4. Determine Next Action
1545
- // We have `outgoingPayload` (Requests + Merge Results).
1546
- // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1547
- // Do WE have outstanding requests?
1548
- // We might if `mergeResult` requires further sync? Usually no, result is complete.
1549
- const myRequests = []; // If we had more needs (e.g. partial payload), we'd add here.
1550
- const hasOutgoing = outgoingPayload.length > 0;
1551
- const hasMyRequests = myRequests.length > 0;
1552
- if (hasOutgoing || hasMyRequests) {
1553
- // We have business to attend to -> Send Delta
1554
- const responseDeltaData = {
1555
- sagaId: deltaData.sagaId,
1556
- stage: SyncStage.delta,
1557
- payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1558
- requests: hasMyRequests ? myRequests : undefined,
1559
- proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1560
- // Wait. If we send data, we are NOT committing yet.
1561
- // We are sending data. The OTHER side must ingest it.
1562
- // So proposeCommit = true?
1563
- // "Here is the data. I'm done. If you are good, let's commit."
1564
- // Yes.
1565
- };
1566
- // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1567
- // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1568
- // So yes, proposeCommit = true.
1569
- responseDeltaData.proposeCommit = true;
1570
- const deltaStone = await this.createSyncMsgStone({
1571
- data: responseDeltaData,
1572
- localSpace: tempSpace,
1573
- metaspace
1574
- });
1575
- const deltaFrame = await this.evolveSyncSagaIbGib({
1576
- prevSagaIbGib: sagaIbGib,
1577
- msgStones: [deltaStone],
1578
- sessionIdentity: identity,
1579
- localSpace: tempSpace,
1580
- metaspace
1581
- });
1582
- // Build control payloads: frame + its dependencies (msg stone, identity)
1583
- const payloadIbGibsControl = [deltaFrame, deltaStone];
1584
- if (identity) {
1585
- payloadIbGibsControl.push(identity);
1586
- }
1587
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1588
- // return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1589
- throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1590
- }
1591
- else {
1592
- // We have nothing to send.
1593
- if (peerProposesCommit) {
1594
- // Peer is done. We are done. -> Commit.
1595
- const commitData = {
1596
- sagaId: deltaData.sagaId,
1597
- stage: SyncStage.commit,
1598
- success: true,
1599
- };
1600
- const commitStone = await this.createSyncMsgStone({
1601
- data: commitData,
1602
- localSpace: tempSpace,
1603
- metaspace
1604
- });
1605
- const commitFrame = await this.evolveSyncSagaIbGib({
1606
- prevSagaIbGib: sagaIbGib,
1607
- msgStones: [commitStone],
1608
- sessionIdentity: identity,
1609
- localSpace: tempSpace,
1610
- metaspace
1611
- });
1612
- // Build control payloads for commit
1613
- const commitCtrlPayloads = [commitFrame, commitStone];
1614
- if (identity) {
1615
- commitCtrlPayloads.push(identity);
1616
- }
1617
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1618
- // return { frame: commitFrame, };
1619
- throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
1620
- }
1621
- else {
1622
- // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1623
- // But we are empty.
1624
- // So WE propose commit.
1517
+ // 4. Determine Next Action
1518
+ // We have `outgoingPayload` (Requests + Merge Results).
1519
+ // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1520
+ // Do WE have outstanding requests?
1521
+ // We might if `mergeResult` requires further sync? Usually no, result is complete.
1522
+ const myRequests = []; // If we had more needs (e.g. partial payload), we'd add here.
1523
+ const hasOutgoing = outgoingPayload.length > 0;
1524
+ const hasMyRequests = myRequests.length > 0;
1525
+ if (hasOutgoing || hasMyRequests) {
1526
+ // We have business to attend to -> Send Delta
1625
1527
  const responseDeltaData = {
1626
1528
  sagaId: deltaData.sagaId,
1627
1529
  stage: SyncStage.delta,
1628
- proposeCommit: true,
1629
- payloadAddrs: [], // Always include empty array if sending delta
1530
+ payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1531
+ requests: hasMyRequests ? myRequests : undefined,
1532
+ proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1533
+ // Wait. If we send data, we are NOT committing yet.
1534
+ // We are sending data. The OTHER side must ingest it.
1535
+ // So proposeCommit = true?
1536
+ // "Here is the data. I'm done. If you are good, let's commit."
1537
+ // Yes.
1630
1538
  };
1539
+ // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1540
+ // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1541
+ // So yes, proposeCommit = true.
1542
+ responseDeltaData.proposeCommit = true;
1631
1543
  const deltaStone = await this.createSyncMsgStone({
1632
1544
  data: responseDeltaData,
1633
- localSpace: tempSpace,
1634
- metaspace
1545
+ localSpace: mySpace,
1546
+ metaspace,
1635
1547
  });
1636
1548
  const deltaFrame = await this.evolveSyncSagaIbGib({
1637
1549
  prevSagaIbGib: sagaIbGib,
1638
1550
  msgStones: [deltaStone],
1639
1551
  sessionIdentity: identity,
1640
- localSpace: tempSpace,
1641
- metaspace
1552
+ localSpace: mySpace,
1553
+ metaspace,
1642
1554
  });
1643
- // Check if PEER proposed commit
1644
- if (deltaData.proposeCommit) {
1645
- if (logalot) {
1646
- console.log(`${lc} Peer proposed commit. Accepting & Committing.`);
1647
- }
1648
- // Peer wants to commit and has no more requests.
1649
- // We should Commit.
1555
+ // Build control payloads: frame + its dependencies (msg stone, identity)
1556
+ const payloadIbGibsControl = [deltaFrame, deltaStone];
1557
+ if (identity) {
1558
+ payloadIbGibsControl.push(identity);
1559
+ }
1560
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1561
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1562
+ // throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1563
+ }
1564
+ else {
1565
+ // We have nothing to send.
1566
+ if (peerProposesCommit) {
1567
+ // Peer is done. We are done. -> Commit.
1650
1568
  const commitData = {
1651
1569
  sagaId: deltaData.sagaId,
1652
1570
  stage: SyncStage.commit,
@@ -1654,54 +1572,122 @@ export class SyncSagaCoordinator {
1654
1572
  };
1655
1573
  const commitStone = await this.createSyncMsgStone({
1656
1574
  data: commitData,
1657
- localSpace: tempSpace,
1575
+ localSpace: mySpace,
1658
1576
  metaspace
1659
1577
  });
1660
1578
  const commitFrame = await this.evolveSyncSagaIbGib({
1661
- prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1579
+ prevSagaIbGib: sagaIbGib,
1662
1580
  msgStones: [commitStone],
1663
1581
  sessionIdentity: identity,
1664
- localSpace: tempSpace,
1582
+ localSpace: mySpace,
1665
1583
  metaspace
1666
1584
  });
1667
1585
  // Build control payloads for commit
1668
- const commitCtrlPayloads2 = [commitFrame, commitStone];
1586
+ const commitCtrlPayloads = [commitFrame, commitStone];
1669
1587
  if (identity) {
1670
- commitCtrlPayloads2.push(identity);
1588
+ commitCtrlPayloads.push(identity);
1671
1589
  }
1672
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1673
- // return { frame: commitFrame, };
1674
- throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
1590
+ return { frame: commitFrame, };
1675
1591
  }
1676
- // Build control payloads for delta propose
1677
- const deltaCtrlPayloads = [deltaFrame, deltaStone];
1678
- if (identity) {
1679
- deltaCtrlPayloads.push(identity);
1592
+ else {
1593
+ // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1594
+ // But we are empty.
1595
+ // So WE propose commit.
1596
+ const responseDeltaData = {
1597
+ sagaId: deltaData.sagaId,
1598
+ stage: SyncStage.delta,
1599
+ proposeCommit: true,
1600
+ };
1601
+ const deltaStone = await this.createSyncMsgStone({
1602
+ data: responseDeltaData,
1603
+ localSpace: mySpace,
1604
+ metaspace
1605
+ });
1606
+ const deltaFrame = await this.evolveSyncSagaIbGib({
1607
+ prevSagaIbGib: sagaIbGib,
1608
+ msgStones: [deltaStone],
1609
+ sessionIdentity: identity,
1610
+ localSpace: mySpace,
1611
+ metaspace
1612
+ });
1613
+ // Check if PEER proposed commit
1614
+ if (deltaData.proposeCommit) {
1615
+ if (logalot) {
1616
+ console.log(`${lc} Peer proposed commit. Accepting & Committing.`);
1617
+ }
1618
+ // Peer wants to commit and has no more requests.
1619
+ // We should Commit.
1620
+ const commitData = {
1621
+ sagaId: deltaData.sagaId,
1622
+ stage: SyncStage.commit,
1623
+ success: true,
1624
+ };
1625
+ const commitStone = await this.createSyncMsgStone({
1626
+ data: commitData,
1627
+ localSpace: mySpace,
1628
+ metaspace
1629
+ });
1630
+ const commitFrame = await this.evolveSyncSagaIbGib({
1631
+ prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1632
+ msgStones: [commitStone],
1633
+ sessionIdentity: identity,
1634
+ localSpace: mySpace,
1635
+ metaspace
1636
+ });
1637
+ // Build control payloads for commit
1638
+ const commitCtrlPayloads2 = [commitFrame, commitStone];
1639
+ if (identity) {
1640
+ commitCtrlPayloads2.push(identity);
1641
+ }
1642
+ return { frame: commitFrame, };
1643
+ }
1644
+ // Build control payloads for delta propose
1645
+ const deltaCtrlPayloads = [deltaFrame, deltaStone];
1646
+ if (identity) {
1647
+ deltaCtrlPayloads.push(identity);
1648
+ }
1649
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1680
1650
  }
1681
- // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1682
- // return { frame: deltaFrame, };
1683
- throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
1651
+ }
1652
+ }
1653
+ catch (error) {
1654
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1655
+ throw error;
1656
+ }
1657
+ finally {
1658
+ if (logalot) {
1659
+ console.log(`${lc} complete.`);
1684
1660
  }
1685
1661
  }
1686
1662
  }
1687
- async handleCommitFrame({ sagaIbGib, destSpace, tempSpace, metaspace, identity, }) {
1663
+ async handleCommitFrame({ sagaIbGib, mySpace, myTempSpace, metaspace, identity, }) {
1688
1664
  const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
1689
- if (logalot) {
1690
- console.log(`${lc} Commit received.`);
1665
+ try {
1666
+ if (logalot) {
1667
+ console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`);
1668
+ }
1669
+ // Sender Logic (Finalizing):
1670
+ // If we are here, we received a Commit frame from the Peer.
1671
+ // This implies the Peer has successfully committed.
1672
+ // We should now:
1673
+ // 1. Validate (implicitly done by receiving valid frame)
1674
+ // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1675
+ // 3. Return null to signal saga completion.
1676
+ // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1677
+ if (logalot) {
1678
+ console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
1679
+ }
1680
+ return { responseWasNull: true };
1691
1681
  }
1692
- // Sender Logic (Finalizing):
1693
- // If we are here, we received a Commit frame from the Peer.
1694
- // This implies the Peer has successfully committed.
1695
- // We should now:
1696
- // 1. Validate (implicitly done by receiving valid frame)
1697
- // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1698
- // 3. Return null to signal saga completion.
1699
- // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1700
- if (logalot) {
1701
- console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
1682
+ catch (error) {
1683
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1684
+ throw error;
1685
+ }
1686
+ finally {
1687
+ if (logalot) {
1688
+ console.log(`${lc} complete.`);
1689
+ }
1702
1690
  }
1703
- // return { responseWasNull: true };
1704
- throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
1705
1691
  }
1706
1692
  // #endregion Handlers
1707
1693
  async createSyncMsgStone({ data, localSpace, metaspace, }) {