@ibgib/core-gib 0.1.25 → 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 (46) hide show
  1. package/dist/common/other/ibgib-helper.d.mts +1 -1
  2. package/dist/common/other/ibgib-helper.d.mts.map +1 -1
  3. package/dist/common/other/ibgib-helper.mjs.map +1 -1
  4. package/dist/sync/sync-constants.d.mts +1 -0
  5. package/dist/sync/sync-constants.d.mts.map +1 -1
  6. package/dist/sync/sync-constants.mjs +1 -0
  7. package/dist/sync/sync-constants.mjs.map +1 -1
  8. package/dist/sync/sync-helpers.d.mts +21 -1
  9. package/dist/sync/sync-helpers.d.mts.map +1 -1
  10. package/dist/sync/sync-helpers.mjs +137 -4
  11. package/dist/sync/sync-helpers.mjs.map +1 -1
  12. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +0 -2
  13. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  14. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +169 -62
  15. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  16. package/dist/sync/sync-peer/sync-peer-v1.d.mts +13 -29
  17. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  18. package/dist/sync/sync-peer/sync-peer-v1.mjs +18 -57
  19. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  20. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +15 -1
  21. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  22. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +77 -7
  23. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  24. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +8 -0
  25. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  26. package/dist/sync/sync-saga-coordinator.d.mts +76 -17
  27. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  28. package/dist/sync/sync-saga-coordinator.mjs +545 -480
  29. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  30. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +1 -1
  31. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  32. package/dist/sync/sync-types.d.mts +31 -4
  33. package/dist/sync/sync-types.d.mts.map +1 -1
  34. package/dist/sync/sync-types.mjs +32 -0
  35. package/dist/sync/sync-types.mjs.map +1 -1
  36. package/package.json +2 -2
  37. package/src/common/other/ibgib-helper.mts +1 -1
  38. package/src/sync/sync-constants.mts +1 -0
  39. package/src/sync/sync-helpers.mts +132 -4
  40. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +169 -47
  41. package/src/sync/sync-peer/sync-peer-v1.mts +33 -74
  42. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +69 -9
  43. package/src/sync/sync-saga-context/sync-saga-context-types.mts +13 -5
  44. package/src/sync/sync-saga-coordinator.mts +628 -530
  45. package/src/sync/sync-saga-message/sync-saga-message-types.mts +18 -18
  46. package/src/sync/sync-types.mts +40 -3
@@ -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
  /**
@@ -142,6 +142,75 @@ export class SyncSagaCoordinator {
142
142
  done
143
143
  };
144
144
  }
145
+ /**
146
+ * This is what the receiving side of the sync calls to drive the FSM to the
147
+ * next stage.
148
+ *
149
+ * So whereas the sender executes a saga loop and drives the entire process,
150
+ * this is a reactive one-off that drives just the single step that the
151
+ * receiver does in that saga.
152
+ *
153
+ * @returns next context result if another round, else if commit returns
154
+ * null
155
+ */
156
+ async continueSync({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
157
+ const lc = `${this.lc}[${this.continueSync.name}]`;
158
+ try {
159
+ if (logalot) {
160
+ console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`);
161
+ }
162
+ const contextResult = await this.handleResponseSagaContext({
163
+ sagaContext,
164
+ mySpace,
165
+ myTempSpace,
166
+ identity,
167
+ identitySecret,
168
+ metaspace,
169
+ });
170
+ if (!contextResult) {
171
+ if (logalot) {
172
+ console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`);
173
+ }
174
+ return null;
175
+ }
176
+ // #region error conditions throw
177
+ if (contextResult.errorMsg) {
178
+ throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
179
+ }
180
+ else if (!contextResult.nextFrameInfo) {
181
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
182
+ }
183
+ else if (contextResult.nextFrameInfo?.responseWasNull) {
184
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
185
+ }
186
+ // #endregion error conditions throw
187
+ // create the return context
188
+ const { frame, payloadIbGibsDomain } = contextResult.nextFrameInfo;
189
+ const responseCtx = await createSyncSagaContext({
190
+ sagaFrame: frame,
191
+ localSpace: mySpace,
192
+ payloadIbGibsDomain,
193
+ // todo: we need to thoroughly go through the identity per each step after getting basic merging
194
+ sessionKeystones: identity ? [identity] : undefined, // ??
195
+ });
196
+ const immediateValidationErrors = await validateContextAndSagaFrame({
197
+ context: responseCtx,
198
+ });
199
+ if (immediateValidationErrors.length > 0) {
200
+ throw new Error(`(UNEXPECTED) just created sync saga context () and there were immediateValidationErrors? immediateValidationErrors: ${immediateValidationErrors} (E: c120e8e0aa98673d685267a8a36e5826)`);
201
+ }
202
+ return responseCtx;
203
+ }
204
+ catch (error) {
205
+ console.error(`${lc} ${extractErrorMsg(error)}`);
206
+ throw error;
207
+ }
208
+ finally {
209
+ if (logalot) {
210
+ console.log(`${lc} complete.`);
211
+ }
212
+ }
213
+ }
145
214
  async getSessionIdentity({ sagaId, metaspace, tempSpace, }) {
146
215
  const lc = `${this.lc}[${this.getSessionIdentity.name}]`;
147
216
  try {
@@ -292,8 +361,12 @@ export class SyncSagaCoordinator {
292
361
  throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
293
362
  }
294
363
  updates$.next(responseCtx); // spins off for saga UI updating
295
- // validate context
296
- await validateContextAndSagaFrame;
364
+ // immediately validate context/saga frame (but not payloads because
365
+ // we may not have those yet)
366
+ const contextAndSagaFrameValidationErrors = await validateContextAndSagaFrame({ context: responseCtx });
367
+ if (contextAndSagaFrameValidationErrors.length > 0) {
368
+ throw new Error(`contextAndSagaFrameValidationErrors: ${contextAndSagaFrameValidationErrors} (E: 6eebe8e7fa437c00a8cde3ada3c66826)`);
369
+ }
297
370
  // Extract expected domain addresses from response context
298
371
  const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
299
372
  // Poll for them if needed. see above jsdocs for domainPayloadsMap
@@ -326,6 +399,7 @@ export class SyncSagaCoordinator {
326
399
  // finished/errored out.
327
400
  const contextResult = await this.handleResponseSagaContext({
328
401
  sagaContext: responseCtx,
402
+ initDomainGraph,
329
403
  mySpace: localSpace,
330
404
  myTempSpace: tempSpace,
331
405
  metaspace,
@@ -511,7 +585,9 @@ export class SyncSagaCoordinator {
511
585
  metaspace,
512
586
  localSpace,
513
587
  });
514
- // 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
+ }
515
591
  return { initFrame: sagaFrame, initDomainGraph: fullGraph };
516
592
  }
517
593
  catch (error) {
@@ -584,6 +660,7 @@ export class SyncSagaCoordinator {
584
660
  }
585
661
  /**
586
662
  * This is the heart of the "ping pong" transaction, where we send a context
663
+ *
587
664
  * and receive a context. IOW, this drives the FSM of the sync saga ibgib as
588
665
  * a whole.
589
666
  *
@@ -602,7 +679,7 @@ export class SyncSagaCoordinator {
602
679
  *
603
680
  * This is a one-off on the receiver.
604
681
  */
605
- async handleResponseSagaContext({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
682
+ async handleResponseSagaContext({ sagaContext, initDomainGraph, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
606
683
  const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
607
684
  try {
608
685
  if (logalot) {
@@ -616,7 +693,7 @@ export class SyncSagaCoordinator {
616
693
  console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
617
694
  }
618
695
  // Get Stage from Stone (or Frame for Init fallback)
619
- const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
696
+ const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
620
697
  if (logalot) {
621
698
  console.log(`${lc} handling frame stage: ${stage}`);
622
699
  }
@@ -630,25 +707,42 @@ export class SyncSagaCoordinator {
630
707
  nextFrameInfo = await this.handleInitFrame({
631
708
  sagaIbGib,
632
709
  messageData: messageData,
633
- metaspace,
634
- mySpace: mySpace,
635
- myTempSpace: myTempSpace,
636
- identity,
637
- identitySecret
710
+ metaspace, mySpace, myTempSpace,
711
+ identity, identitySecret
638
712
  });
639
713
  break;
640
714
  case SyncStage.ack:
641
- nextFrameInfo = await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
715
+ if (!initDomainGraph) {
716
+ throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`);
717
+ }
718
+ nextFrameInfo = await this.handleAckFrame({
719
+ sagaContext,
720
+ sagaIbGib,
721
+ initDomainGraph,
722
+ metaspace, mySpace, myTempSpace,
723
+ identity,
724
+ });
642
725
  break;
643
726
  case SyncStage.delta:
644
- 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
+ });
645
736
  break;
646
737
  case SyncStage.commit:
647
- nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
738
+ nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
648
739
  break;
649
740
  default:
650
741
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
651
742
  }
743
+ if (logalot) {
744
+ console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`);
745
+ }
652
746
  return { errorMsg: undefined, nextFrameInfo, };
653
747
  }
654
748
  catch (error) {
@@ -672,7 +766,7 @@ export class SyncSagaCoordinator {
672
766
  * The Receiver performs Gap Analysis here:
673
767
  * 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
674
768
  * 2. Identifies what Sender needs (`pushOfferAddrs`).
675
- * 3. Identifies what Receiver needs (`deltaReqAddrs`).
769
+ * 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
676
770
  * 4. Returns an `Ack` frame containing these lists.
677
771
  */
678
772
  async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
@@ -909,7 +1003,7 @@ export class SyncSagaCoordinator {
909
1003
  }
910
1004
  const ackStone = await this.createSyncMsgStone({
911
1005
  data: ackData,
912
- localSpace: myTempSpace,
1006
+ localSpace: mySpace,
913
1007
  metaspace,
914
1008
  });
915
1009
  if (logalot) {
@@ -928,7 +1022,6 @@ export class SyncSagaCoordinator {
928
1022
  * we want to push ibgibs to the remote/sender if we have push
929
1023
  * offers. an ack frame's payloads, if any, are those push offers
930
1024
  */
931
- // let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
932
1025
  let payloadIbGibsDomain;
933
1026
  if (pushOfferInfos.length > 0) {
934
1027
  const searchSecondSpaceAddrs = [];
@@ -985,12 +1078,10 @@ export class SyncSagaCoordinator {
985
1078
  }
986
1079
  // we have now populated payloadIbGibsDomain
987
1080
  }
988
- throw new Error(`not implemented (E: ed3f98abb0988c5ae8038bb8d741fb26)`);
989
- // return {
990
- // frame: ackFrame,
991
- // // conflictInfos,
992
- // payloadIbGibsDomain,
993
- // };
1081
+ return {
1082
+ frame: ackFrame,
1083
+ payloadIbGibsDomain,
1084
+ };
994
1085
  }
995
1086
  catch (error) {
996
1087
  console.error(`${lc} ${extractErrorMsg(error)}`);
@@ -1009,19 +1100,23 @@ export class SyncSagaCoordinator {
1009
1100
  * **Execution Context**: **Sender (Local)**.
1010
1101
  *
1011
1102
  * The Sender reacts to the Receiver's requirements:
1012
- * 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
1013
- * 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..
1014
1108
  *
1015
1109
  * Returns a `Delta` frame.
1016
1110
  */
1017
- async handleAckFrame({ sagaIbGib, srcGraph, destSpace, tempSpace, metaspace, identity, }) {
1111
+ async handleAckFrame({ sagaContext, sagaIbGib, initDomainGraph, mySpace, myTempSpace, metaspace, identity, }) {
1018
1112
  const lc = `${this.lc}[${this.handleAckFrame.name}]`;
1019
1113
  try {
1020
1114
  if (logalot) {
1021
1115
  console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
1022
1116
  }
1023
- const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1117
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1024
1118
  const ackData = messageData;
1119
+ // #region sanity/validation
1025
1120
  if (!ackData) {
1026
1121
  throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
1027
1122
  }
@@ -1031,6 +1126,10 @@ export class SyncSagaCoordinator {
1031
1126
  if (logalot) {
1032
1127
  console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`);
1033
1128
  }
1129
+ if (!sagaIbGib.data) {
1130
+ throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`);
1131
+ }
1132
+ // #region sanity/validation
1034
1133
  // 1. Check for Conflicts
1035
1134
  const conflicts = ackData.conflicts || [];
1036
1135
  console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`);
@@ -1044,10 +1143,15 @@ export class SyncSagaCoordinator {
1044
1143
  // For now, throw to trigger abort.
1045
1144
  throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
1046
1145
  }
1047
- const optimisticConflicts = conflicts.filter(c => !c.terminal);
1048
- const mergeDeltaReqs = []; // Additional requests for merging
1049
- if (optimisticConflicts.length > 0) {
1050
- console.log(`${lc} [CONFLICT DEBUG] Processing ${optimisticConflicts.length} optimistic conflicts`);
1146
+ // at this point, if we have conflicts, they are non-terminal
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
1153
+ if (conflicts.length > 0) {
1154
+ console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
1051
1155
  // We need to resolve these.
1052
1156
  // Strategy:
1053
1157
  // 1. Analyze Divergence (Sender vs Receiver)
@@ -1055,8 +1159,8 @@ export class SyncSagaCoordinator {
1055
1159
  // 3. Request that data (as Delta Reqs)
1056
1160
  // 4. (Later in Delta Phase) Perform Merge.
1057
1161
  // BUT: The Delta Phase is usually generic "Send me these Addrs".
1058
- // If we just add to `deltaReqAddrs` (which are requests for Sender to send to Receiver?),
1059
- // 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.
1060
1164
  // We (Sender) are processing the Ack.
1061
1165
  // We need to request data FROM Receiver.
1062
1166
  // But the protocol 'Ack' step typically leads to 'Delta' (Sender sending data).
@@ -1073,198 +1177,140 @@ export class SyncSagaCoordinator {
1073
1177
  // `peer.pull(addr)`?
1074
1178
  // Yes! The Coordinator has the `peer`.
1075
1179
  // Let's analyze and pull immediately.
1076
- for (const conflict of optimisticConflicts) {
1077
- const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1078
- // Sender History
1079
- // We need our own history for this timeline.
1080
- // We know the 'senderTip' (remoteAddr in Ack).
1081
- // Sender should verify it has this tip.
1082
- // Compute Diffs
1083
- // We need to find `receiverOnly` addrs.
1084
- // Receiver sent us `timelineAddrs` (Full History).
1085
- const receiverHistorySet = new Set(timelineAddrs);
1086
- // We need our execution context's history for this senderTip.
1087
- // We can fetch valid 'past' from space.
1088
- const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
1089
- const senderTipIbGib = resSenderTip.ibGibs?.[0];
1090
- if (!senderTipIbGib) {
1091
- throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`);
1092
- }
1093
- // Basic Diff: Find what Receiver has that we don't.
1094
- // Actually, we need to traverse OUR past to find commonality.
1095
- const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
1096
- const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
1097
- if (receiverOnlyAddrs.length > 0) {
1098
- console.log(`${lc} [CONFLICT DEBUG] Found ${receiverOnlyAddrs.length} receiver-only frames - need to pull for merge`);
1099
- console.log(`${lc} [CONFLICT DEBUG] Receiver-only addrs:`, receiverOnlyAddrs);
1100
- // PULL these frames from Peer into Local Space
1101
- // (Validation: We trust peer for now / verification happens on put)
1102
- for (const addr of receiverOnlyAddrs) {
1103
- console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
1104
- }
1105
- // Compute DELTA dependencies for each receiver-only frame
1106
- // Find LCA to determine what dependencies we already have
1107
- const lcaAddr = timelineAddrs.find(addr => senderHistory.includes(addr));
1108
- console.log(`${lc} [CONFLICT DEBUG] LCA: ${lcaAddr || 'NONE'}`);
1109
- const skipAddrsSet = new Set();
1110
- if (lcaAddr) {
1111
- try {
1112
- const lcaRes = await getFromSpace({ addr: lcaAddr, space: destSpace });
1113
- const lcaIbGib = lcaRes.ibGibs?.[0];
1114
- if (lcaIbGib) {
1115
- const lcaDeps = await getDependencyGraph({ ibGib: lcaIbGib, space: destSpace });
1116
- if (lcaDeps)
1117
- Object.keys(lcaDeps).forEach(a => skipAddrsSet.add(a));
1118
- console.log(`${lc} [CONFLICT DEBUG] LCA deps to skip: ${skipAddrsSet.size}`);
1119
- }
1120
- }
1121
- catch (e) {
1122
- console.warn(`${lc} Error getting LCA deps: ${extractErrorMsg(e)}`);
1123
- }
1124
- }
1125
- // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
1126
- for (const addr of receiverOnlyAddrs) {
1127
- // Add the frame itself first
1128
- if (!mergeDeltaReqs.includes(addr)) {
1129
- mergeDeltaReqs.push(addr);
1130
- }
1131
- // Get the frame's delta dependencies (skip LCA's deps)
1132
- try {
1133
- const frameRes = await getFromSpace({ addr, space: destSpace });
1134
- const frameIbGib = frameRes.ibGibs?.[0];
1135
- if (frameIbGib) {
1136
- // Get dependency graph, skipping all LCA dependencies
1137
- const frameDeltaDeps = await getDependencyGraph({
1138
- ibGib: frameIbGib,
1139
- space: destSpace,
1140
- skipAddrs: Array.from(skipAddrsSet), // Skip entire LCA dep graph
1141
- });
1142
- if (frameDeltaDeps) {
1143
- // Add all delta dependencies (Object.keys gives us the addresses)
1144
- Object.keys(frameDeltaDeps).forEach(depAddr => {
1145
- if (!mergeDeltaReqs.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1146
- mergeDeltaReqs.push(depAddr);
1147
- }
1148
- });
1149
- }
1150
- }
1151
- }
1152
- catch (depError) {
1153
- console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
1154
- }
1155
- }
1156
- console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${mergeDeltaReqs.length}`);
1157
- }
1158
- else {
1159
- console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
1160
- }
1180
+ for (const conflict of conflicts) {
1181
+ // todo: integrate conflict strategies into this point...this whole block needs to be redone, but I want to check the fast-forward/backward simpler case tests first
1182
+ throw new Error(`conflicts not (re)implemented yet (E: 3b7d0819f83842a6de3ae988819bc826)`);
1183
+ // const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1184
+ // // Sender History
1185
+ // // We need our own history for this timeline.
1186
+ // // We know the 'senderTip' (remoteAddr in Ack).
1187
+ // // Sender should verify it has this tip.
1188
+ // // Compute Diffs
1189
+ // // We need to find `receiverOnly` addrs.
1190
+ // // Receiver sent us `timelineAddrs` (Full History).
1191
+ // const receiverHistorySet = new Set(timelineAddrs);
1192
+ // // We need our execution context's history for this senderTip.
1193
+ // // We can fetch valid 'past' from space.
1194
+ // const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
1195
+ // const senderTipIbGib = resSenderTip.ibGibs?.[0];
1196
+ // if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
1197
+ // // Basic Diff: Find what Receiver has that we don't.
1198
+ // // Actually, we need to traverse OUR past to find commonality.
1199
+ // const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
1200
+ // const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
1201
+ // if (receiverOnlyAddrs.length > 0) {
1202
+ // console.log(`${lc} [CONFLICT DEBUG] Found ${receiverOnlyAddrs.length} receiver-only frames - need to pull for merge`);
1203
+ // console.log(`${lc} [CONFLICT DEBUG] Receiver-only addrs:`, receiverOnlyAddrs);
1204
+ // // PULL these frames from Peer into Local Space
1205
+ // // (Validation: We trust peer for now / verification happens on put)
1206
+ // for (const addr of receiverOnlyAddrs) {
1207
+ // console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
1208
+ // }
1209
+ // // Compute DELTA dependencies for each receiver-only frame
1210
+ // // Find LCA to determine what dependencies we already have
1211
+ // const lcaAddr = timelineAddrs.find(addr => senderHistory.includes(addr));
1212
+ // console.log(`${lc} [CONFLICT DEBUG] LCA: ${lcaAddr || 'NONE'}`);
1213
+ // const skipAddrsSet = new Set<string>();
1214
+ // if (lcaAddr) {
1215
+ // try {
1216
+ // const lcaRes = await getFromSpace({ addr: lcaAddr, space: destSpace });
1217
+ // const lcaIbGib = lcaRes.ibGibs?.[0];
1218
+ // if (lcaIbGib) {
1219
+ // const lcaDeps = await getDependencyGraph({ ibGib: lcaIbGib, space: destSpace });
1220
+ // if (lcaDeps) Object.keys(lcaDeps).forEach(a => skipAddrsSet.add(a));
1221
+ // console.log(`${lc} [CONFLICT DEBUG] LCA deps to skip: ${skipAddrsSet.size}`);
1222
+ // }
1223
+ // } catch (e) {
1224
+ // console.warn(`${lc} Error getting LCA deps: ${extractErrorMsg(e)}`);
1225
+ // }
1226
+ // }
1227
+ // // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
1228
+ // for (const addr of receiverOnlyAddrs) {
1229
+ // // Add the frame itself first
1230
+ // if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
1231
+ // outgoingDeltaAddrRequestInfos.push(addr);
1232
+ // }
1233
+ // // Get the frame's delta dependencies (skip LCA's deps)
1234
+ // try {
1235
+ // const frameRes = await getFromSpace({ addr, space: destSpace });
1236
+ // const frameIbGib = frameRes.ibGibs?.[0];
1237
+ // if (frameIbGib) {
1238
+ // // Get dependency graph, skipping all LCA dependencies
1239
+ // const frameDeltaDeps = await getDependencyGraph({
1240
+ // ibGib: frameIbGib,
1241
+ // space: destSpace,
1242
+ // skipAddrs: Array.from(skipAddrsSet), // Skip entire LCA dep graph
1243
+ // });
1244
+ // if (frameDeltaDeps) {
1245
+ // // Add all delta dependencies (Object.keys gives us the addresses)
1246
+ // Object.keys(frameDeltaDeps).forEach(depAddr => {
1247
+ // if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1248
+ // outgoingDeltaAddrRequestInfos.push(depAddr);
1249
+ // }
1250
+ // });
1251
+ // }
1252
+ // }
1253
+ // } catch (depError) {
1254
+ // console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
1255
+ // }
1256
+ // }
1257
+ // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
1258
+ // } else {
1259
+ // console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
1260
+ // }
1161
1261
  }
1162
- console.log(`${lc} [CONFLICT DEBUG] Finished processing ${optimisticConflicts.length} conflicts. mergeDeltaReqs: ${mergeDeltaReqs.length}`);
1262
+ console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
1163
1263
  }
1164
1264
  else {
1165
1265
  console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
1166
1266
  }
1167
1267
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
1168
- const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
1169
- const pushOfferAddrs = ackData.pushOfferInfos || [];
1170
- // 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
1171
- const pullReqAddrs = [];
1172
- for (const addr of pushOfferAddrs) {
1173
- // const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1174
- // if (!existing) {
1175
- // pullReqAddrs.push(addr);
1176
- // }
1177
- }
1178
- // 2. Process Delta Requests (Push Payload)
1179
- // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
1180
- // const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
1181
- const skipAddrs = new Set();
1182
- if (ackData.knowledgeVector) {
1183
- Object.values(ackData.knowledgeVector).forEach(addrs => {
1184
- // addrs.forEach(a => skipAddrs.add(a));
1185
- });
1186
- }
1187
- const payloadIbGibs = [];
1188
- // Gather all tips to sync first
1189
- const tipsToSync = [];
1190
- for (const addr of deltaReqAddrs) {
1191
- // let ibGib = srcGraph[addr];
1192
- // if (!ibGib) {
1193
- // const res = await getFromSpace({ addr, space: destSpace });
1194
- // if (res.ibGibs && res.ibGibs.length > 0) {
1195
- // ibGib = res.ibGibs[0];
1196
- // }
1197
- // }
1198
- // if (ibGib) {
1199
- // tipsToSync.push(ibGib);
1200
- // } else {
1201
- // throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1202
- // }
1203
- }
1204
- // Calculate Dependency Graph for ALL tips, effectively utilizing common history
1205
- // Pass skipAddrs to `getDependencyGraph` or gather manually.
1206
- // `getDependencyGraph` takes a single ibGib.
1207
- // We can optimize by doing it for each tip and unioning the result?
1208
- // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
1209
- // We will loop.
1210
- const allDepsSet = new Set();
1211
- for (const tip of tipsToSync) {
1212
- // Always include the tip itself
1213
- const tipAddr = getIbGibAddr({ ibGib: tip });
1214
- // Only process if not skipped (though deltaReq implies they barely just asked for it)
1215
- // But detailed deps might be skipped.
1216
- // Get Graph with Skips
1217
- // Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
1218
- const deps = await getDependencyGraph({
1219
- ibGib: tip,
1220
- space: destSpace,
1221
- skipAddrs: Array.from(skipAddrs)
1222
- });
1223
- // [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
1224
- let tipIncluded = false;
1225
- if (deps) {
1226
- Object.values(deps).forEach(d => {
1227
- const dAddr = getIbGibAddr({ ibGib: d });
1228
- if (!allDepsSet.has(dAddr)) {
1229
- allDepsSet.add(dAddr);
1230
- payloadIbGibs.push(d);
1231
- }
1232
- if (dAddr === tipAddr) {
1233
- tipIncluded = true;
1234
- }
1235
- });
1236
- }
1237
- if (!tipIncluded && !skipAddrs.has(tipAddr)) {
1238
- if (logalot) {
1239
- console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`);
1240
- }
1241
- if (!allDepsSet.has(tipAddr)) {
1242
- allDepsSet.add(tipAddr);
1243
- payloadIbGibs.push(tip);
1244
- }
1245
- }
1246
- }
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
+ });
1247
1278
  // 3. Create Delta Frame
1248
- const sagaId = ackData.sagaId;
1249
1279
  const deltaData = {
1250
1280
  sagaId: sagaIbGib.data.uuid,
1251
1281
  stage: SyncStage.delta,
1252
- payloadAddrs: payloadIbGibs.map(p => getIbGibAddr({ ibGib: p })),
1253
- 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,
1254
1300
  };
1255
1301
  if (logalot) {
1256
1302
  console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`);
1257
1303
  }
1258
1304
  const deltaStone = await this.createSyncMsgStone({
1259
1305
  data: deltaData,
1260
- localSpace: tempSpace,
1306
+ localSpace: mySpace,
1261
1307
  metaspace,
1262
1308
  });
1263
1309
  const deltaFrame = await this.evolveSyncSagaIbGib({
1264
1310
  prevSagaIbGib: sagaIbGib,
1265
1311
  msgStones: [deltaStone],
1266
1312
  sessionIdentity: identity,
1267
- localSpace: tempSpace,
1313
+ localSpace: mySpace,
1268
1314
  metaspace,
1269
1315
  });
1270
1316
  if (logalot) {
@@ -1275,9 +1321,70 @@ export class SyncSagaCoordinator {
1275
1321
  if (identity) {
1276
1322
  payloadIbGibsControl.push(identity);
1277
1323
  }
1278
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1279
- // return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1280
- 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;
1281
1388
  }
1282
1389
  catch (error) {
1283
1390
  console.error(`${lc} ${extractErrorMsg(error)}`);
@@ -1299,275 +1406,165 @@ export class SyncSagaCoordinator {
1299
1406
  * 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
1300
1407
  * 3. **Completion**: If no more requests, transitions to `Commit`.
1301
1408
  */
1302
- async handleDeltaFrame({ sagaIbGib, srcGraph, destSpace, tempSpace, metaspace, identity, }) {
1409
+ async handleDeltaFrame({ sagaContext, sagaIbGib, srcGraph, mySpace, myTempSpace, metaspace, identity, }) {
1303
1410
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1304
- if (logalot) {
1305
- console.log(`${lc} starting...`);
1306
- }
1307
- const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1308
- const deltaData = messageData;
1309
- if (!deltaData) {
1310
- throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
1311
- }
1312
- if (deltaData.stage !== SyncStage.delta) {
1313
- throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1314
- }
1315
- if (logalot) {
1316
- console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
1317
- }
1318
- console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1319
- const payloadAddrs = deltaData.payloadAddrs || [];
1320
- const peerRequests = deltaData.requests || [];
1321
- const peerProposesCommit = deltaData.proposeCommit || false;
1322
- // 1. Process Received Payload (Ingest)
1323
- const receivedPayloadIbGibs = [];
1324
- if (payloadAddrs.length > 0) {
1325
- // We use `payloadAddrs` as the manifest.
1326
- // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1327
- // assuming the "Transport" layer put them there implicitly?
1328
- // OR, if we are local-only, we just get them.
1329
- // The `handleDeltaFrame` contract assumes data is reachable in `space`.
1330
- const res = await getFromSpace({
1331
- addrs: payloadAddrs,
1332
- space: tempSpace, // Incoming data is in tempSpace
1333
- });
1334
- if (res.ibGibs) {
1335
- receivedPayloadIbGibs.push(...res.ibGibs);
1336
- // Also put them? `getFromSpace` retrieves. If they are in space, they are persisted.
1337
- // If this is a Temp Space, they are safe.
1338
- }
1339
- else {
1340
- console.warn(`${lc} Failed to retrieve payloads listed in delta: ${payloadAddrs.join(', ')}`);
1411
+ try {
1412
+ if (logalot) {
1413
+ console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`);
1341
1414
  }
1342
- }
1343
- // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1344
- const outgoingPayload = [];
1345
- const outgoingAddrsSet = new Set(); // Track what we've added
1346
- console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${peerRequests.length} peer requests`);
1347
- for (const addr of peerRequests) {
1348
- // Get the requested ibGib
1349
- let ibGib = srcGraph[addr];
1350
- if (!ibGib) {
1351
- const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1352
- if (res.ibGibs && res.ibGibs.length > 0) {
1353
- ibGib = res.ibGibs[0];
1354
- }
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)`);
1355
1420
  }
1356
- if (ibGib) {
1357
- // Add the requested ibGib itself
1358
- const ibGibAddr = getIbGibAddr({ ibGib });
1359
- if (!outgoingAddrsSet.has(ibGibAddr)) {
1360
- outgoingPayload.push(ibGib);
1361
- outgoingAddrsSet.add(ibGibAddr);
1362
- }
1363
- // Expand to include full dependency graph for this ibGib
1364
- // (Receiver needs all deps to properly process/merge)
1365
- try {
1366
- const deps = await getDependencyGraph({
1367
- ibGib,
1368
- space: destSpace,
1369
- });
1370
- if (deps) {
1371
- Object.values(deps).forEach(depIbGib => {
1372
- const depAddr = getIbGibAddr({ ibGib: depIbGib });
1373
- if (!outgoingAddrsSet.has(depAddr)) {
1374
- outgoingPayload.push(depIbGib);
1375
- outgoingAddrsSet.add(depAddr);
1376
- }
1377
- });
1378
- }
1379
- }
1380
- catch (depError) {
1381
- console.warn(`${lc} [CONFLICT DEBUG] Error expanding deps for ${addr}: ${extractErrorMsg(depError)}`);
1382
- }
1421
+ if (deltaData.stage !== SyncStage.delta) {
1422
+ throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1383
1423
  }
1384
- else {
1385
- console.warn(`${lc} Requested addr not found during delta fulfillment: ${addr}`);
1424
+ if (logalot) {
1425
+ console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
1386
1426
  }
1387
- }
1388
- console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1389
- // 3. Execute Merges (If applicable)
1390
- // Check if we have pending conflicts that we CAN resolve now that we have data.
1391
- // We look at the Saga History (Ack Frame) to find conflicts.
1392
- // Optimization: Do this only if we received payloads.
1393
- const mergeResultIbGibs = [];
1394
- console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1395
- if (receivedPayloadIbGibs.length > 0) {
1396
- console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1397
- // Find the Ack frame in history to get conflicts
1398
- // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1399
- // V1 timelines carry full history in `past`.
1400
- const pastAddrs = sagaIbGib.rel8ns?.past || [];
1401
- console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1402
- let ackData;
1403
- if (pastAddrs.length > 0) {
1404
- // Batch fetch all past frames
1405
- const resPast = await getFromSpace({ addrs: pastAddrs, space: tempSpace });
1406
- if (resPast.success && resPast.ibGibs) {
1407
- // Iterate backwards (most recent first) to find the latest Ack
1408
- for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1409
- const pastFrame = resPast.ibGibs[i];
1410
- const messageStone = await getSyncSagaMessageFromFrame({
1411
- frameIbGib: pastFrame,
1412
- space: tempSpace
1413
- });
1414
- if (messageStone?.data?.stage === SyncStage.ack) {
1415
- ackData = messageStone.data;
1416
- console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1417
- 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
+ }
1418
1471
  }
1419
1472
  }
1420
1473
  }
1421
- }
1422
- if (ackData && ackData.conflicts) {
1423
- const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1424
- for (const conflict of optimisticConflicts) {
1425
- const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1426
- // We are Sender (usually) here if we are merging.
1427
- // Check if we have the history needed (timelineAddrs).
1428
- // Specifically, we needed the `receiverOnly` parts.
1429
- // We blindly attempt merge if we have both tips accessible?
1430
- // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1431
- // Check if we have receiverTip in space
1432
- console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1433
- const resRecTip = await getFromSpace({ addr: receiverTip, space: tempSpace }); // Check tempSpace for incoming data
1434
- console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1435
- if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1436
- // We have the tip!
1437
- // Do we have the full history?
1438
- // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1439
- // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1440
- // Perform Merge!
1441
- try {
1442
- const mergeResult = await mergeDivergentTimelines({
1443
- tipA: (await getFromSpace({ addr: senderTip, space: destSpace })).ibGibs[0], // Our tip from destSpace
1444
- tipB: resRecTip.ibGibs[0], // Their tip (from tempSpace)
1445
- space: tempSpace, // Merge uses tempSpace
1446
- metaspace,
1447
- });
1448
- if (mergeResult) {
1449
- console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1450
- if (logalot) {
1451
- 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
1452
1507
  }
1453
- mergeResultIbGibs.push(mergeResult);
1454
- outgoingPayload.push(mergeResult); // Send result to peer
1455
1508
  }
1456
- }
1457
- catch (e) {
1458
- console.error(`${lc} Merge failed: ${e}`);
1459
- // 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
+ }
1460
1513
  }
1461
1514
  }
1462
1515
  }
1463
1516
  }
1464
- }
1465
- // 4. Determine Next Action
1466
- // We have `outgoingPayload` (Requests + Merge Results).
1467
- // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1468
- // Do WE have outstanding requests?
1469
- // We might if `mergeResult` requires further sync? Usually no, result is complete.
1470
- const myRequests = []; // If we had more needs (e.g. partial payload), we'd add here.
1471
- const hasOutgoing = outgoingPayload.length > 0;
1472
- const hasMyRequests = myRequests.length > 0;
1473
- if (hasOutgoing || hasMyRequests) {
1474
- // We have business to attend to -> Send Delta
1475
- const responseDeltaData = {
1476
- sagaId: deltaData.sagaId,
1477
- stage: SyncStage.delta,
1478
- payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1479
- requests: hasMyRequests ? myRequests : undefined,
1480
- proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1481
- // Wait. If we send data, we are NOT committing yet.
1482
- // We are sending data. The OTHER side must ingest it.
1483
- // So proposeCommit = true?
1484
- // "Here is the data. I'm done. If you are good, let's commit."
1485
- // Yes.
1486
- };
1487
- // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1488
- // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1489
- // So yes, proposeCommit = true.
1490
- responseDeltaData.proposeCommit = true;
1491
- const deltaStone = await this.createSyncMsgStone({
1492
- data: responseDeltaData,
1493
- localSpace: tempSpace,
1494
- metaspace
1495
- });
1496
- const deltaFrame = await this.evolveSyncSagaIbGib({
1497
- prevSagaIbGib: sagaIbGib,
1498
- msgStones: [deltaStone],
1499
- sessionIdentity: identity,
1500
- localSpace: tempSpace,
1501
- metaspace
1502
- });
1503
- // Build control payloads: frame + its dependencies (msg stone, identity)
1504
- const payloadIbGibsControl = [deltaFrame, deltaStone];
1505
- if (identity) {
1506
- payloadIbGibsControl.push(identity);
1507
- }
1508
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1509
- // return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1510
- throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1511
- }
1512
- else {
1513
- // We have nothing to send.
1514
- if (peerProposesCommit) {
1515
- // Peer is done. We are done. -> Commit.
1516
- const commitData = {
1517
- sagaId: deltaData.sagaId,
1518
- stage: SyncStage.commit,
1519
- success: true,
1520
- };
1521
- const commitStone = await this.createSyncMsgStone({
1522
- data: commitData,
1523
- localSpace: tempSpace,
1524
- metaspace
1525
- });
1526
- const commitFrame = await this.evolveSyncSagaIbGib({
1527
- prevSagaIbGib: sagaIbGib,
1528
- msgStones: [commitStone],
1529
- sessionIdentity: identity,
1530
- localSpace: tempSpace,
1531
- metaspace
1532
- });
1533
- // Build control payloads for commit
1534
- const commitCtrlPayloads = [commitFrame, commitStone];
1535
- if (identity) {
1536
- commitCtrlPayloads.push(identity);
1537
- }
1538
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1539
- // return { frame: commitFrame, };
1540
- throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
1541
- }
1542
- else {
1543
- // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1544
- // But we are empty.
1545
- // 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
1546
1527
  const responseDeltaData = {
1547
1528
  sagaId: deltaData.sagaId,
1548
1529
  stage: SyncStage.delta,
1549
- proposeCommit: true,
1550
- 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.
1551
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;
1552
1543
  const deltaStone = await this.createSyncMsgStone({
1553
1544
  data: responseDeltaData,
1554
- localSpace: tempSpace,
1555
- metaspace
1545
+ localSpace: mySpace,
1546
+ metaspace,
1556
1547
  });
1557
1548
  const deltaFrame = await this.evolveSyncSagaIbGib({
1558
1549
  prevSagaIbGib: sagaIbGib,
1559
1550
  msgStones: [deltaStone],
1560
1551
  sessionIdentity: identity,
1561
- localSpace: tempSpace,
1562
- metaspace
1552
+ localSpace: mySpace,
1553
+ metaspace,
1563
1554
  });
1564
- // Check if PEER proposed commit
1565
- if (deltaData.proposeCommit) {
1566
- if (logalot) {
1567
- console.log(`${lc} Peer proposed commit. Accepting & Committing.`);
1568
- }
1569
- // Peer wants to commit and has no more requests.
1570
- // 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.
1571
1568
  const commitData = {
1572
1569
  sagaId: deltaData.sagaId,
1573
1570
  stage: SyncStage.commit,
@@ -1575,54 +1572,122 @@ export class SyncSagaCoordinator {
1575
1572
  };
1576
1573
  const commitStone = await this.createSyncMsgStone({
1577
1574
  data: commitData,
1578
- localSpace: tempSpace,
1575
+ localSpace: mySpace,
1579
1576
  metaspace
1580
1577
  });
1581
1578
  const commitFrame = await this.evolveSyncSagaIbGib({
1582
- prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1579
+ prevSagaIbGib: sagaIbGib,
1583
1580
  msgStones: [commitStone],
1584
1581
  sessionIdentity: identity,
1585
- localSpace: tempSpace,
1582
+ localSpace: mySpace,
1586
1583
  metaspace
1587
1584
  });
1588
1585
  // Build control payloads for commit
1589
- const commitCtrlPayloads2 = [commitFrame, commitStone];
1586
+ const commitCtrlPayloads = [commitFrame, commitStone];
1590
1587
  if (identity) {
1591
- commitCtrlPayloads2.push(identity);
1588
+ commitCtrlPayloads.push(identity);
1592
1589
  }
1593
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1594
- // return { frame: commitFrame, };
1595
- throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
1590
+ return { frame: commitFrame, };
1596
1591
  }
1597
- // Build control payloads for delta propose
1598
- const deltaCtrlPayloads = [deltaFrame, deltaStone];
1599
- if (identity) {
1600
- 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 };
1601
1650
  }
1602
- // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1603
- // return { frame: deltaFrame, };
1604
- 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.`);
1605
1660
  }
1606
1661
  }
1607
1662
  }
1608
- async handleCommitFrame({ sagaIbGib, destSpace, tempSpace, metaspace, identity, }) {
1663
+ async handleCommitFrame({ sagaIbGib, mySpace, myTempSpace, metaspace, identity, }) {
1609
1664
  const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
1610
- if (logalot) {
1611
- 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 };
1612
1681
  }
1613
- // Sender Logic (Finalizing):
1614
- // If we are here, we received a Commit frame from the Peer.
1615
- // This implies the Peer has successfully committed.
1616
- // We should now:
1617
- // 1. Validate (implicitly done by receiving valid frame)
1618
- // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1619
- // 3. Return null to signal saga completion.
1620
- // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1621
- if (logalot) {
1622
- 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
+ }
1623
1690
  }
1624
- // return { responseWasNull: true };
1625
- throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
1626
1691
  }
1627
1692
  // #endregion Handlers
1628
1693
  async createSyncMsgStone({ data, localSpace, metaspace, }) {