@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
@@ -19,21 +19,25 @@ import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space
19
19
  import { KeystoneIbGib_V1 } from "../keystone/keystone-types.mjs";
20
20
  import { KeystoneService_V1 } from "../keystone/keystone-service-v1.mjs";
21
21
  import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
22
- import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
23
- import { appendToTimeline, createTimeline, getHistory, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
22
+ import {
23
+ SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN,
24
+ } from "./sync-constants.mjs";
25
+ import {
26
+ appendToTimeline, createTimeline, Rel8nInfo, Rel8nRemovalInfo
27
+ } from "../timeline/timeline-api.mjs";
24
28
  import {
25
29
  SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
26
30
  SyncRel8ns_V1, DomainIbGibAnalysisInfo, NextSagaFrameInfo,
27
- SYNC_CONFLICT_STRATEGY_VALID_VALUES,
28
- HandleSagaResponseContextResult,
31
+ SYNC_CONFLICT_STRATEGY_VALID_VALUES, HandleSagaResponseContextResult,
32
+ SyncExecutionContext,
33
+ SYNC_EXECUTION_CONTEXT_VALID_VALUES,
29
34
  } from "./sync-types.mjs";
30
- import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
35
+ import { getExecutionContext, getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
31
36
  import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
32
37
  import {
33
38
  SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
34
39
  SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
35
- SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
36
- SyncSagaPushOfferInfo,
40
+ SyncSagaMessageCommitData_V1, SyncSagaConflictInfo, SyncSagaPushOfferInfo,
37
41
  SyncSagaRequestAddrInfo,
38
42
  } from "./sync-saga-message/sync-saga-message-types.mjs";
39
43
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
@@ -49,14 +53,12 @@ import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
49
53
  import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
50
54
  import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
51
55
  import { ErrorIbGib_V1 } from "../common/error/error-types.mjs";
52
- import {
53
- IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns
54
- } from "../witness/space/space-types.mjs";
56
+ import { IbGibSpaceResultData, IbGibSpaceResultIbGib, IbGibSpaceResultRel8ns } from "../witness/space/space-types.mjs";
55
57
  import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
56
58
 
57
59
 
58
60
  // const logalot = GLOBAL_LOG_A_LOT || true;
59
- const logalot = false;
61
+ const logalot = true;
60
62
  const logalotControlDomain = true;
61
63
  const lcControlDomain = '[ControlDomain]';
62
64
 
@@ -202,7 +204,7 @@ export class SyncSagaCoordinator {
202
204
  * @returns next context result if another round, else if commit returns
203
205
  * null
204
206
  */
205
- public async receiverContinueSync({
207
+ public async continueSync({
206
208
  sagaContext,
207
209
  mySpace,
208
210
  myTempSpace,
@@ -223,7 +225,7 @@ export class SyncSagaCoordinator {
223
225
  identitySecret?: string,
224
226
  metaspace: MetaspaceService,
225
227
  }): Promise<SyncSagaContextIbGib_V1 | null> {
226
- const lc = `${this.lc}[${this.receiverContinueSync.name}]`;
228
+ const lc = `${this.lc}[${this.continueSync.name}]`;
227
229
  try {
228
230
  if (logalot) { console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`); }
229
231
 
@@ -720,7 +722,7 @@ export class SyncSagaCoordinator {
720
722
  localSpace,
721
723
  });
722
724
 
723
- // if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
725
+ if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
724
726
 
725
727
  return { initFrame: sagaFrame, initDomainGraph: fullGraph };
726
728
  } catch (error) {
@@ -860,7 +862,7 @@ export class SyncSagaCoordinator {
860
862
  if (logalot) { console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`); }
861
863
 
862
864
  // Get Stage from Stone (or Frame for Init fallback)
863
- const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
865
+ const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
864
866
 
865
867
  if (logalot) { console.log(`${lc} handling frame stage: ${stage}`); }
866
868
 
@@ -875,39 +877,44 @@ export class SyncSagaCoordinator {
875
877
  nextFrameInfo = await this.handleInitFrame({
876
878
  sagaIbGib,
877
879
  messageData: messageData as SyncSagaMessageInitData_V1,
878
- metaspace,
879
- mySpace: mySpace,
880
- myTempSpace: myTempSpace,
881
- identity,
882
- identitySecret
880
+ metaspace, mySpace, myTempSpace,
881
+ identity, identitySecret
883
882
  });
884
883
  break;
885
884
 
886
885
  case SyncStage.ack:
887
886
  if (!initDomainGraph) { throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`); }
888
887
  nextFrameInfo = await this.handleAckFrame({
888
+ sagaContext,
889
889
  sagaIbGib,
890
- srcGraph,
891
890
  initDomainGraph,
892
- metaspace,
893
- destSpace: mySpace,
894
- tempSpace: myTempSpace,
891
+ metaspace, mySpace, myTempSpace,
895
892
  identity,
896
893
  });
897
894
  break;
898
895
 
899
896
  case SyncStage.delta:
900
- nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
897
+ nextFrameInfo = await this.handleDeltaFrame({
898
+ sagaContext,
899
+ sagaIbGib,
900
+ srcGraph,
901
+ metaspace,
902
+ mySpace,
903
+ myTempSpace,
904
+ identity,
905
+ });
901
906
  break;
902
907
 
903
908
  case SyncStage.commit:
904
- nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
909
+ nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
905
910
  break;
906
911
 
907
912
  default:
908
913
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
909
914
  }
910
915
 
916
+ if (logalot) { console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`); }
917
+
911
918
  return { errorMsg: undefined, nextFrameInfo, }
912
919
 
913
920
  } catch (error) {
@@ -930,7 +937,7 @@ export class SyncSagaCoordinator {
930
937
  * The Receiver performs Gap Analysis here:
931
938
  * 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
932
939
  * 2. Identifies what Sender needs (`pushOfferAddrs`).
933
- * 3. Identifies what Receiver needs (`deltaReqAddrs`).
940
+ * 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
934
941
  * 4. Returns an `Ack` frame containing these lists.
935
942
  */
936
943
  protected async handleInitFrame({
@@ -1183,7 +1190,7 @@ export class SyncSagaCoordinator {
1183
1190
 
1184
1191
  const ackStone = await this.createSyncMsgStone({
1185
1192
  data: ackData,
1186
- localSpace: myTempSpace,
1193
+ localSpace: mySpace,
1187
1194
  metaspace,
1188
1195
  });
1189
1196
  if (logalot) { console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`); }
@@ -1274,22 +1281,29 @@ export class SyncSagaCoordinator {
1274
1281
  * **Execution Context**: **Sender (Local)**.
1275
1282
  *
1276
1283
  * The Sender reacts to the Receiver's requirements:
1277
- * 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
1278
- * 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
1284
+ * 1. `deltaRequestAddrInfos`: Receiver wants this data. Sender takes this
1285
+ * into account, plus gathers it from `initDomainGraph` and puts them in
1286
+ * `payloadIbGibs` (for next frame).
1287
+ * 2. `pushOfferAddrs`: Receiver has newer data. these should have been
1288
+ * included in the incoming context from the receiver..
1279
1289
  *
1280
1290
  * Returns a `Delta` frame.
1281
1291
  */
1282
1292
  protected async handleAckFrame({
1293
+ sagaContext,
1283
1294
  sagaIbGib,
1284
- srcGraph,
1285
1295
  initDomainGraph,
1286
- destSpace,
1287
- tempSpace,
1288
- metaspace,
1296
+ mySpace, myTempSpace, metaspace,
1289
1297
  identity,
1290
1298
  }: {
1299
+ /**
1300
+ * todo: figure out if we need to do something about incoming push offer payload domain ibgibs
1301
+ * I'm adding this because we should be checking incoming payloads
1302
+ * right? for push offers? This is when the receiver had new ibgibs.
1303
+ * Not sure if I need to put this here though...
1304
+ */
1305
+ sagaContext: SyncSagaContextIbGib_V1,
1291
1306
  sagaIbGib: SyncIbGib_V1,
1292
- srcGraph: { [addr: string]: IbGib_V1 },
1293
1307
  /**
1294
1308
  * This is the initial dependency graph of all domain ibgibs passed in
1295
1309
  * to the original {@link sync} call.
@@ -1297,8 +1311,8 @@ export class SyncSagaCoordinator {
1297
1311
  * if we're executing on the sender, this will be populated
1298
1312
  */
1299
1313
  initDomainGraph: FlatIbGibGraph,
1300
- destSpace: IbGibSpaceAny,
1301
- tempSpace: IbGibSpaceAny,
1314
+ mySpace: IbGibSpaceAny,
1315
+ myTempSpace: IbGibSpaceAny,
1302
1316
  metaspace: MetaspaceService,
1303
1317
  identity?: KeystoneIbGib_V1,
1304
1318
  }): Promise<NextSagaFrameInfo> {
@@ -1306,9 +1320,10 @@ export class SyncSagaCoordinator {
1306
1320
  try {
1307
1321
  if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
1308
1322
 
1309
- const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1323
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1310
1324
  const ackData = messageData as SyncSagaMessageAckData_V1;
1311
1325
 
1326
+ // #region sanity/validation
1312
1327
  if (!ackData) {
1313
1328
  throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
1314
1329
  }
@@ -1316,6 +1331,8 @@ export class SyncSagaCoordinator {
1316
1331
  throw new Error(`${lc} Invalid ack frame: ackData.stage !== SyncStage.ack (E: 2e8b0a94b5954a66a6a1a7a0b3f5b7a1)`);
1317
1332
  }
1318
1333
  if (logalot) { console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`); }
1334
+ if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`); }
1335
+ // #region sanity/validation
1319
1336
 
1320
1337
  // 1. Check for Conflicts
1321
1338
  const conflicts = ackData.conflicts || [];
@@ -1334,7 +1351,12 @@ export class SyncSagaCoordinator {
1334
1351
 
1335
1352
  // at this point, if we have conflicts, they are non-terminal
1336
1353
 
1337
- const mergeDeltaReqs: string[] = []; // Additional requests for merging
1354
+ /**
1355
+ * at this point, we only request ibgibs for conflicted timelines.
1356
+ * If the receiver had known of any ibgibs this sender needed, it
1357
+ * would have been in the push offer.
1358
+ */
1359
+ const outgoingDeltaAddrRequestInfos: SyncSagaRequestAddrInfo[] = []; // Additional requests for merging
1338
1360
 
1339
1361
  if (conflicts.length > 0) {
1340
1362
  console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
@@ -1346,8 +1368,8 @@ export class SyncSagaCoordinator {
1346
1368
  // 4. (Later in Delta Phase) Perform Merge.
1347
1369
 
1348
1370
  // BUT: The Delta Phase is usually generic "Send me these Addrs".
1349
- // If we just add to `deltaReqAddrs` (which are requests for Sender to send to Receiver?),
1350
- // wait. `ackData.deltaReqAddrs` are what RECEIVER wants from SENDER.
1371
+ // If we just add to `deltaRequestAddrInfos` (which are requests for Sender to send to Receiver?),
1372
+ // wait. `ackData.deltaRequestAddrInfos` are what RECEIVER wants from SENDER.
1351
1373
 
1352
1374
  // We (Sender) are processing the Ack.
1353
1375
  // We need to request data FROM Receiver.
@@ -1375,6 +1397,7 @@ export class SyncSagaCoordinator {
1375
1397
 
1376
1398
  throw new Error(`conflicts not (re)implemented yet (E: 3b7d0819f83842a6de3ae988819bc826)`);
1377
1399
 
1400
+
1378
1401
  // const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1379
1402
 
1380
1403
  // // Sender History
@@ -1432,8 +1455,8 @@ export class SyncSagaCoordinator {
1432
1455
  // // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
1433
1456
  // for (const addr of receiverOnlyAddrs) {
1434
1457
  // // Add the frame itself first
1435
- // if (!mergeDeltaReqs.includes(addr)) {
1436
- // mergeDeltaReqs.push(addr);
1458
+ // if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
1459
+ // outgoingDeltaAddrRequestInfos.push(addr);
1437
1460
  // }
1438
1461
 
1439
1462
  // // Get the frame's delta dependencies (skip LCA's deps)
@@ -1452,8 +1475,8 @@ export class SyncSagaCoordinator {
1452
1475
  // if (frameDeltaDeps) {
1453
1476
  // // Add all delta dependencies (Object.keys gives us the addresses)
1454
1477
  // Object.keys(frameDeltaDeps).forEach(depAddr => {
1455
- // if (!mergeDeltaReqs.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1456
- // mergeDeltaReqs.push(depAddr);
1478
+ // if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
1479
+ // outgoingDeltaAddrRequestInfos.push(depAddr);
1457
1480
  // }
1458
1481
  // });
1459
1482
  // }
@@ -1463,119 +1486,59 @@ export class SyncSagaCoordinator {
1463
1486
  // }
1464
1487
  // }
1465
1488
 
1466
- // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${mergeDeltaReqs.length}`);
1489
+ // console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
1467
1490
  // } else {
1468
1491
  // console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
1469
1492
  // }
1470
1493
  }
1471
1494
 
1472
- console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. mergeDeltaReqs: ${mergeDeltaReqs.length}`);
1495
+ console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
1473
1496
  } else {
1474
1497
  console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
1475
1498
  }
1476
1499
 
1477
1500
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
1478
1501
 
1479
- const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
1480
- const pushOfferAddrs = ackData.pushOfferInfos || [];
1481
-
1482
- // 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
1483
- const pullReqAddrs: string[] = [];
1484
- for (const addr of pushOfferAddrs) {
1485
- // const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1486
- // if (!existing) {
1487
- // pullReqAddrs.push(addr);
1488
- // }
1489
- }
1490
-
1491
- // 2. Process Delta Requests (Push Payload)
1492
- // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
1493
- // const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
1494
- const skipAddrs = new Set<string>();
1495
- if (ackData.knowledgeVector) {
1496
- Object.values(ackData.knowledgeVector).forEach(addrs => {
1497
- // addrs.forEach(a => skipAddrs.add(a));
1498
- });
1499
- }
1500
-
1501
- const payloadIbGibs: IbGib_V1[] = [];
1502
- // Gather all tips to sync first
1503
- const tipsToSync: IbGib_V1[] = [];
1504
- for (const addr of deltaReqAddrs) {
1505
- // let ibGib = srcGraph[addr];
1506
- // if (!ibGib) {
1507
- // const res = await getFromSpace({ addr, space: destSpace });
1508
- // if (res.ibGibs && res.ibGibs.length > 0) {
1509
- // ibGib = res.ibGibs[0];
1510
- // }
1511
- // }
1512
- // if (ibGib) {
1513
- // tipsToSync.push(ibGib);
1514
- // } else {
1515
- // throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1516
- // }
1517
- }
1518
-
1519
- // Calculate Dependency Graph for ALL tips, effectively utilizing common history
1520
- // Pass skipAddrs to `getDependencyGraph` or gather manually.
1521
- // `getDependencyGraph` takes a single ibGib.
1522
- // We can optimize by doing it for each tip and unioning the result?
1523
- // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
1524
- // We will loop.
1525
-
1526
- const allDepsSet = new Set<string>();
1527
-
1528
- for (const tip of tipsToSync) {
1529
- // Always include the tip itself
1530
- const tipAddr = getIbGibAddr({ ibGib: tip });
1531
- // Only process if not skipped (though deltaReq implies they barely just asked for it)
1532
- // But detailed deps might be skipped.
1533
-
1534
- // Get Graph with Skips
1535
- // Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
1536
- const deps = await getDependencyGraph({
1537
- ibGib: tip,
1538
- space: destSpace,
1539
- skipAddrs: Array.from(skipAddrs)
1540
- });
1541
-
1542
- // [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
1543
- let tipIncluded = false;
1544
-
1545
- if (deps) {
1546
- Object.values(deps).forEach(d => {
1547
- const dAddr = getIbGibAddr({ ibGib: d });
1548
- if (!allDepsSet.has(dAddr)) {
1549
- allDepsSet.add(dAddr);
1550
- payloadIbGibs.push(d);
1551
- }
1552
- if (dAddr === tipAddr) { tipIncluded = true; }
1553
- });
1554
- }
1555
-
1556
- if (!tipIncluded && !skipAddrs.has(tipAddr)) {
1557
- if (logalot) { console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`); }
1558
- if (!allDepsSet.has(tipAddr)) {
1559
- allDepsSet.add(tipAddr);
1560
- payloadIbGibs.push(tip);
1561
- }
1562
- }
1563
- }
1502
+ /**
1503
+ * these were requested addrs on the INCOMING frame (the ack data).
1504
+ *
1505
+ * This is in contrast to any OUTGOING requests we make in this
1506
+ * method.
1507
+ */
1508
+ const payloadIbGibsDomain = await this.getPayloadsForRequestedInfos({
1509
+ deltaRequestAddrInfos: ackData.deltaRequestAddrInfos || [],
1510
+ mySpace,
1511
+ });
1564
1512
 
1565
1513
  // 3. Create Delta Frame
1566
- const sagaId = ackData.sagaId;
1567
1514
  const deltaData: SyncSagaMessageDeltaData_V1 = {
1568
- sagaId: sagaIbGib.data!.uuid,
1515
+ sagaId: sagaIbGib.data.uuid,
1569
1516
  stage: SyncStage.delta,
1570
- payloadAddrs: payloadIbGibs.map(p => getIbGibAddr({ ibGib: p })),
1571
- requests: [...(pullReqAddrs || []), ...(mergeDeltaReqs || [])].length > 0 ? [...(pullReqAddrs || []), ...(mergeDeltaReqs || [])] : undefined,
1517
+ /**
1518
+ * we're sending these domain ibgibs as payload (they were
1519
+ * requested by receiver)
1520
+ */
1521
+ payloadAddrsDomain: payloadIbGibsDomain.length > 0 ?
1522
+ payloadIbGibsDomain.map(x => getIbGibAddr({ ibGib: x })) :
1523
+ undefined,
1524
+ /**
1525
+ * we're asking for these addrs
1526
+ */
1527
+ deltaRequestAddrInfos: outgoingDeltaAddrRequestInfos.length > 0 ?
1528
+ outgoingDeltaAddrRequestInfos :
1529
+ undefined,
1530
+ /**
1531
+ * if we have no changes and request none, propose commit to
1532
+ * finish the sync transaction.
1533
+ */
1534
+ proposeCommit: payloadIbGibsDomain.length === 0 && outgoingDeltaAddrRequestInfos.length === 0,
1572
1535
  };
1573
1536
 
1574
1537
  if (logalot) { console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`); }
1575
1538
 
1576
1539
  const deltaStone = await this.createSyncMsgStone({
1577
1540
  data: deltaData,
1578
- localSpace: tempSpace,
1541
+ localSpace: mySpace,
1579
1542
  metaspace,
1580
1543
  });
1581
1544
 
@@ -1583,7 +1546,7 @@ export class SyncSagaCoordinator {
1583
1546
  prevSagaIbGib: sagaIbGib,
1584
1547
  msgStones: [deltaStone],
1585
1548
  sessionIdentity: identity,
1586
- localSpace: tempSpace,
1549
+ localSpace: mySpace,
1587
1550
  metaspace,
1588
1551
  });
1589
1552
 
@@ -1593,9 +1556,71 @@ export class SyncSagaCoordinator {
1593
1556
  const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1594
1557
  if (identity) { payloadIbGibsControl.push(identity); }
1595
1558
 
1596
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1597
- // return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1598
- throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
1559
+ return { frame: deltaFrame, payloadIbGibsDomain, };
1560
+ } catch (error) {
1561
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1562
+ throw error;
1563
+ } finally {
1564
+ if (logalot) { console.log(`${lc} complete.`); }
1565
+ }
1566
+ }
1567
+
1568
+ private async getPayloadsForRequestedInfos({
1569
+ deltaRequestAddrInfos,
1570
+ mySpace,
1571
+ }: {
1572
+ deltaRequestAddrInfos: SyncSagaRequestAddrInfo[];
1573
+ mySpace: IbGibSpaceAny;
1574
+ }): Promise<IbGib_V1[]> {
1575
+ const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
1576
+ try {
1577
+ if (logalot) { console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`); }
1578
+ /**
1579
+ * graph of ibgibs we will send to the receiver. addr-based, so will
1580
+ * already be unique (no need to call `unique` on the domains
1581
+ * ibgibs)
1582
+ */
1583
+ const outgoingPayloadIbGibsDomainGraph: FlatIbGibGraph = {};
1584
+ for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
1585
+ let deltaDepGraph: FlatIbGibGraph;
1586
+ if (latestAddrAlreadyHave) {
1587
+ // already has some, so only get the delta
1588
+ // remember: if we didn't have the other's latest addr, then
1589
+ // this would be in the conflicts not requested addrs
1590
+ deltaDepGraph = await getDeltaDependencyGraph({
1591
+ ibGibAddr: addr,
1592
+ latestCommonFrameAddr: latestAddrAlreadyHave,
1593
+ space: mySpace,
1594
+ live: true,
1595
+ });
1596
+ } else {
1597
+ // doesn't have anything, so get the entire dependency graph
1598
+ // INEFFICIENT: we've already gotten all of the domain
1599
+ // dependencies in initDomainGraph, but getDependencyGraph
1600
+ // only works against a space so we are going to rerun this.
1601
+ // an optimization would be to adapt/create a new
1602
+ // getDependencyGraph to work against an existing flat ibgib
1603
+ // map.
1604
+ deltaDepGraph = await getDependencyGraph({
1605
+ ibGibAddr: addr,
1606
+ space: mySpace,
1607
+ live: true,
1608
+ });
1609
+ }
1610
+
1611
+ const depGraphSize = Object.keys(deltaDepGraph).length;
1612
+ if (depGraphSize === 0) {
1613
+ throw new Error(`(UNEXPECTED) couldn't get requested addrs in mySpace (${mySpace.ib})? How did the receiver know to ask for these addrs if they weren't in our original graph? (E: 281eaebcdf77dd73e8245b2872100826)`);
1614
+ } else {
1615
+ // we have dependencies!
1616
+ Object.values(deltaDepGraph).forEach(x => {
1617
+ outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
1618
+ });
1619
+ }
1620
+ }
1621
+
1622
+ const result = Object.values(outgoingPayloadIbGibsDomainGraph);
1623
+ return result;
1599
1624
  } catch (error) {
1600
1625
  console.error(`${lc} ${extractErrorMsg(error)}`);
1601
1626
  throw error;
@@ -1615,313 +1640,192 @@ export class SyncSagaCoordinator {
1615
1640
  * 3. **Completion**: If no more requests, transitions to `Commit`.
1616
1641
  */
1617
1642
  protected async handleDeltaFrame({
1643
+ sagaContext,
1618
1644
  sagaIbGib,
1619
1645
  srcGraph,
1620
- destSpace,
1621
- tempSpace,
1646
+ mySpace,
1647
+ myTempSpace,
1622
1648
  metaspace,
1623
1649
  identity,
1624
1650
  }: {
1651
+ sagaContext: SyncSagaContextIbGib_V1,
1625
1652
  sagaIbGib: SyncIbGib_V1,
1626
1653
  srcGraph: { [addr: string]: IbGib_V1 },
1627
- destSpace: IbGibSpaceAny,
1628
- tempSpace: IbGibSpaceAny,
1654
+ mySpace: IbGibSpaceAny,
1655
+ myTempSpace: IbGibSpaceAny,
1629
1656
  metaspace: MetaspaceService,
1630
1657
  identity?: KeystoneIbGib_V1,
1631
1658
  }): Promise<NextSagaFrameInfo> {
1632
1659
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1633
- if (logalot) { console.log(`${lc} starting...`); }
1634
-
1635
- const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1636
- const deltaData = messageData as SyncSagaMessageDeltaData_V1;
1637
-
1638
- if (!deltaData) {
1639
- throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
1640
- }
1641
- if (deltaData.stage !== SyncStage.delta) {
1642
- throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1643
- }
1644
- if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1645
-
1646
- console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1647
-
1648
- const payloadAddrs = deltaData.payloadAddrs || [];
1649
- const peerRequests = deltaData.requests || [];
1650
- const peerProposesCommit = deltaData.proposeCommit || false;
1651
-
1652
- // 1. Process Received Payload (Ingest)
1653
- const receivedPayloadIbGibs: IbGib_V1[] = [];
1654
- if (payloadAddrs.length > 0) {
1655
- // We use `payloadAddrs` as the manifest.
1656
- // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1657
- // assuming the "Transport" layer put them there implicitly?
1658
- // OR, if we are local-only, we just get them.
1659
- // The `handleDeltaFrame` contract assumes data is reachable in `space`.
1660
-
1661
- const res = await getFromSpace({
1662
- addrs: payloadAddrs,
1663
- space: tempSpace, // Incoming data is in tempSpace
1664
- });
1665
- if (res.ibGibs) {
1666
- receivedPayloadIbGibs.push(...res.ibGibs);
1667
- // Also put them? `getFromSpace` retrieves. If they are in space, they are persisted.
1668
- // If this is a Temp Space, they are safe.
1669
- } else {
1670
- console.warn(`${lc} Failed to retrieve payloads listed in delta: ${payloadAddrs.join(', ')}`);
1671
- }
1672
- }
1660
+ try {
1661
+ if (logalot) { console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`); }
1673
1662
 
1674
- // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1675
- const outgoingPayload: IbGib_V1[] = [];
1676
- const outgoingAddrsSet = new Set<string>(); // Track what we've added
1663
+ const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
1664
+ const deltaData = messageData as SyncSagaMessageDeltaData_V1;
1677
1665
 
1678
- console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${peerRequests.length} peer requests`);
1666
+ // #region validate/sanity
1667
+ if (!deltaData) { throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`); }
1668
+ if (deltaData.stage !== SyncStage.delta) { throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`); }
1669
+ if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1670
+ // #endregion validate/sanity
1679
1671
 
1680
- for (const addr of peerRequests) {
1681
- // Get the requested ibGib
1682
- let ibGib = srcGraph[addr];
1683
- if (!ibGib) {
1684
- const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1685
- if (res.ibGibs && res.ibGibs.length > 0) {
1686
- ibGib = res.ibGibs[0];
1687
- }
1688
- }
1672
+ console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1689
1673
 
1690
- if (ibGib) {
1691
- // Add the requested ibGib itself
1692
- const ibGibAddr = getIbGibAddr({ ibGib });
1693
- if (!outgoingAddrsSet.has(ibGibAddr)) {
1694
- outgoingPayload.push(ibGib);
1695
- outgoingAddrsSet.add(ibGibAddr);
1696
- }
1674
+ const peerProposesCommit = deltaData.proposeCommit || false;
1697
1675
 
1698
- // Expand to include full dependency graph for this ibGib
1699
- // (Receiver needs all deps to properly process/merge)
1700
- try {
1701
- const deps = await getDependencyGraph({
1702
- ibGib,
1703
- space: destSpace,
1704
- });
1676
+ /**
1677
+ * these are already in the local temp space
1678
+ */
1679
+ const receivedPayloadIbGibs: IbGib_V1[] = sagaContext.payloadIbGibsDomain ?? [];
1705
1680
 
1706
- if (deps) {
1707
- Object.values(deps).forEach(depIbGib => {
1708
- const depAddr = getIbGibAddr({ ibGib: depIbGib });
1709
- if (!outgoingAddrsSet.has(depAddr)) {
1710
- outgoingPayload.push(depIbGib);
1711
- outgoingAddrsSet.add(depAddr);
1681
+ // 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
1682
+ console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`);
1683
+ const outgoingPayload = await this.getPayloadsForRequestedInfos({
1684
+ deltaRequestAddrInfos: deltaData.deltaRequestAddrInfos || [],
1685
+ mySpace,
1686
+ });
1687
+ console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1688
+
1689
+ // 3. Execute Merges (If applicable)
1690
+ // Check if we have pending conflicts that we CAN resolve now that we have data.
1691
+ // We look at the Saga History (Ack Frame) to find conflicts.
1692
+ // Optimization: Do this only if we received payloads.
1693
+ const mergeResultIbGibs: IbGib_V1[] = [];
1694
+
1695
+ console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1696
+
1697
+ if (receivedPayloadIbGibs.length > 0) {
1698
+ console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1699
+ // Find the Ack frame in history to get conflicts
1700
+ // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1701
+ // V1 timelines carry full history in `past`.
1702
+ const pastAddrs = sagaIbGib.rel8ns?.past || [];
1703
+ console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1704
+ let ackData: SyncSagaMessageAckData_V1 | undefined;
1705
+
1706
+ if (pastAddrs.length > 0) {
1707
+ // Batch fetch all past frames
1708
+ const resPast = await getFromSpace({ addrs: pastAddrs, space: myTempSpace });
1709
+ if (resPast.success && resPast.ibGibs) {
1710
+ // Iterate backwards (most recent first) to find the latest Ack
1711
+ for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1712
+ const pastFrame = resPast.ibGibs[i];
1713
+ const messageStone = await getSyncSagaMessageFromFrame({
1714
+ frameIbGib: pastFrame,
1715
+ space: myTempSpace
1716
+ });
1717
+ if (messageStone?.data?.stage === SyncStage.ack) {
1718
+ ackData = messageStone.data as SyncSagaMessageAckData_V1;
1719
+ console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1720
+ break;
1712
1721
  }
1713
- });
1714
- }
1715
- } catch (depError) {
1716
- console.warn(`${lc} [CONFLICT DEBUG] Error expanding deps for ${addr}: ${extractErrorMsg(depError)}`);
1717
- }
1718
- } else {
1719
- console.warn(`${lc} Requested addr not found during delta fulfillment: ${addr}`);
1720
- }
1721
- }
1722
-
1723
- console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
1724
-
1725
-
1726
- // 3. Execute Merges (If applicable)
1727
- // Check if we have pending conflicts that we CAN resolve now that we have data.
1728
- // We look at the Saga History (Ack Frame) to find conflicts.
1729
- // Optimization: Do this only if we received payloads.
1730
- const mergeResultIbGibs: IbGib_V1[] = [];
1731
-
1732
- console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
1733
-
1734
- if (receivedPayloadIbGibs.length > 0) {
1735
- console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
1736
- // Find the Ack frame in history to get conflicts
1737
- // Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
1738
- // V1 timelines carry full history in `past`.
1739
- const pastAddrs = sagaIbGib.rel8ns?.past || [];
1740
- console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
1741
- let ackData: SyncSagaMessageAckData_V1 | undefined;
1742
-
1743
- if (pastAddrs.length > 0) {
1744
- // Batch fetch all past frames
1745
- const resPast = await getFromSpace({ addrs: pastAddrs, space: tempSpace });
1746
- if (resPast.success && resPast.ibGibs) {
1747
- // Iterate backwards (most recent first) to find the latest Ack
1748
- for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
1749
- const pastFrame = resPast.ibGibs[i];
1750
- const messageStone = await getSyncSagaMessageFromFrame({
1751
- frameIbGib: pastFrame,
1752
- space: tempSpace
1753
- });
1754
- if (messageStone?.data?.stage === SyncStage.ack) {
1755
- ackData = messageStone.data as SyncSagaMessageAckData_V1;
1756
- console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
1757
- break;
1758
1722
  }
1759
1723
  }
1760
1724
  }
1761
- }
1762
1725
 
1763
- if (ackData && ackData.conflicts) {
1764
- const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1765
- for (const conflict of optimisticConflicts) {
1766
- const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1767
- // We are Sender (usually) here if we are merging.
1768
- // Check if we have the history needed (timelineAddrs).
1769
- // Specifically, we needed the `receiverOnly` parts.
1770
-
1771
- // We blindly attempt merge if we have both tips accessible?
1772
- // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1773
-
1774
- // Check if we have receiverTip in space
1775
- console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1776
- const resRecTip = await getFromSpace({ addr: receiverTip, space: tempSpace }); // Check tempSpace for incoming data
1777
- console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1778
- if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1779
- // We have the tip!
1780
- // Do we have the full history?
1781
- // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1782
- // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1783
-
1784
- // Perform Merge!
1785
- try {
1786
- const mergeResult = await mergeDivergentTimelines({
1787
- tipA: (await getFromSpace({ addr: senderTip, space: destSpace })).ibGibs![0], // Our tip from destSpace
1788
- tipB: resRecTip.ibGibs[0], // Their tip (from tempSpace)
1789
- space: tempSpace, // Merge uses tempSpace
1790
- metaspace,
1791
- });
1792
- if (mergeResult) {
1793
- console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1794
- if (logalot) { console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`); }
1795
- mergeResultIbGibs.push(mergeResult);
1796
- outgoingPayload.push(mergeResult); // Send result to peer
1726
+ if (ackData && ackData.conflicts) {
1727
+ const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
1728
+ for (const conflict of optimisticConflicts) {
1729
+ const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
1730
+ // We are Sender (usually) here if we are merging.
1731
+ // Check if we have the history needed (timelineAddrs).
1732
+ // Specifically, we needed the `receiverOnly` parts.
1733
+
1734
+ // We blindly attempt merge if we have both tips accessible?
1735
+ // We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
1736
+
1737
+ // Check if we have receiverTip in space
1738
+ console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
1739
+ const resRecTip = await getFromSpace({ addr: receiverTip, space: myTempSpace }); // Check myTempSpace for incoming data
1740
+ console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1741
+ if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1742
+ // We have the tip!
1743
+ // Do we have the full history?
1744
+ // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1745
+ // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1746
+
1747
+ // Perform Merge!
1748
+ try {
1749
+ const mergeResult = await mergeDivergentTimelines({
1750
+ tipA: (await getFromSpace({ addr: senderTip, space: mySpace })).ibGibs![0], // Our tip from destSpace
1751
+ tipB: resRecTip.ibGibs[0], // Their tip (from myTempSpace)
1752
+ space: myTempSpace, // Merge uses myTempSpace
1753
+ metaspace,
1754
+ });
1755
+ if (mergeResult) {
1756
+ console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
1757
+ if (logalot) { console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`); }
1758
+ mergeResultIbGibs.push(mergeResult);
1759
+ outgoingPayload.push(mergeResult); // Send result to peer
1760
+ }
1761
+ } catch (e) {
1762
+ console.error(`${lc} Merge failed: ${e}`);
1763
+ // If merge fails, we might Abort or just continue?
1797
1764
  }
1798
- } catch (e) {
1799
- console.error(`${lc} Merge failed: ${e}`);
1800
- // If merge fails, we might Abort or just continue?
1801
1765
  }
1802
1766
  }
1803
1767
  }
1804
1768
  }
1805
- }
1806
1769
 
1807
- // 4. Determine Next Action
1808
- // We have `outgoingPayload` (Requests + Merge Results).
1809
- // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1810
- // Do WE have outstanding requests?
1811
- // We might if `mergeResult` requires further sync? Usually no, result is complete.
1770
+ // 4. Determine Next Action
1771
+ // We have `outgoingPayload` (Requests + Merge Results).
1772
+ // Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
1773
+ // Do WE have outstanding requests?
1774
+ // We might if `mergeResult` requires further sync? Usually no, result is complete.
1812
1775
 
1813
- const myRequests: string[] = []; // If we had more needs (e.g. partial payload), we'd add here.
1776
+ const myRequests: string[] = []; // If we had more needs (e.g. partial payload), we'd add here.
1814
1777
 
1815
- const hasOutgoing = outgoingPayload.length > 0;
1816
- const hasMyRequests = myRequests.length > 0;
1817
-
1818
- if (hasOutgoing || hasMyRequests) {
1819
- // We have business to attend to -> Send Delta
1820
- const responseDeltaData: SyncSagaMessageDeltaData_V1 = {
1821
- sagaId: deltaData.sagaId,
1822
- stage: SyncStage.delta,
1823
- payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1824
- requests: hasMyRequests ? myRequests : undefined,
1825
- proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1826
- // Wait. If we send data, we are NOT committing yet.
1827
- // We are sending data. The OTHER side must ingest it.
1828
- // So proposeCommit = true?
1829
- // "Here is the data. I'm done. If you are good, let's commit."
1830
- // Yes.
1831
- };
1778
+ const hasOutgoing = outgoingPayload.length > 0;
1779
+ const hasMyRequests = myRequests.length > 0;
1832
1780
 
1833
- // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1834
- // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1835
-
1836
- // So yes, proposeCommit = true.
1837
- responseDeltaData.proposeCommit = true;
1838
-
1839
- const deltaStone = await this.createSyncMsgStone({
1840
- data: responseDeltaData,
1841
- localSpace: tempSpace,
1842
- metaspace
1843
- });
1844
-
1845
- const deltaFrame = await this.evolveSyncSagaIbGib({
1846
- prevSagaIbGib: sagaIbGib,
1847
- msgStones: [deltaStone],
1848
- sessionIdentity: identity,
1849
- localSpace: tempSpace,
1850
- metaspace
1851
- });
1852
-
1853
- // Build control payloads: frame + its dependencies (msg stone, identity)
1854
- const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1855
- if (identity) { payloadIbGibsControl.push(identity); }
1856
-
1857
- // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1858
- // return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1859
- throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1860
-
1861
- } else {
1862
- // We have nothing to send.
1863
-
1864
- if (peerProposesCommit) {
1865
- // Peer is done. We are done. -> Commit.
1866
- const commitData: SyncSagaMessageCommitData_V1 = {
1867
- sagaId: deltaData.sagaId,
1868
- stage: SyncStage.commit,
1869
- success: true,
1870
- };
1871
-
1872
- const commitStone = await this.createSyncMsgStone({
1873
- data: commitData,
1874
- localSpace: tempSpace,
1875
- metaspace
1876
- });
1877
-
1878
- const commitFrame = await this.evolveSyncSagaIbGib({
1879
- prevSagaIbGib: sagaIbGib,
1880
- msgStones: [commitStone],
1881
- sessionIdentity: identity,
1882
- localSpace: tempSpace,
1883
- metaspace
1884
- });
1885
-
1886
- // Build control payloads for commit
1887
- const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
1888
- if (identity) { commitCtrlPayloads.push(identity); }
1889
-
1890
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1891
- // return { frame: commitFrame, };
1892
- throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
1893
-
1894
- } else {
1895
- // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1896
- // But we are empty.
1897
- // So WE propose commit.
1781
+ if (hasOutgoing || hasMyRequests) {
1782
+ // We have business to attend to -> Send Delta
1898
1783
  const responseDeltaData: SyncSagaMessageDeltaData_V1 = {
1899
1784
  sagaId: deltaData.sagaId,
1900
1785
  stage: SyncStage.delta,
1901
- proposeCommit: true,
1902
- payloadAddrs: [], // Always include empty array if sending delta
1786
+ payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1787
+ requests: hasMyRequests ? myRequests : undefined,
1788
+ proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1789
+ // Wait. If we send data, we are NOT committing yet.
1790
+ // We are sending data. The OTHER side must ingest it.
1791
+ // So proposeCommit = true?
1792
+ // "Here is the data. I'm done. If you are good, let's commit."
1793
+ // Yes.
1903
1794
  };
1904
1795
 
1796
+ // BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
1797
+ // We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
1798
+
1799
+ // So yes, proposeCommit = true.
1800
+ responseDeltaData.proposeCommit = true;
1801
+
1905
1802
  const deltaStone = await this.createSyncMsgStone({
1906
1803
  data: responseDeltaData,
1907
- localSpace: tempSpace,
1908
- metaspace
1804
+ localSpace: mySpace,
1805
+ metaspace,
1909
1806
  });
1910
1807
 
1911
1808
  const deltaFrame = await this.evolveSyncSagaIbGib({
1912
1809
  prevSagaIbGib: sagaIbGib,
1913
1810
  msgStones: [deltaStone],
1914
1811
  sessionIdentity: identity,
1915
- localSpace: tempSpace,
1916
- metaspace
1812
+ localSpace: mySpace,
1813
+ metaspace,
1917
1814
  });
1918
1815
 
1919
- // Check if PEER proposed commit
1920
- if (deltaData.proposeCommit) {
1921
- if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
1922
- // Peer wants to commit and has no more requests.
1923
- // We should Commit.
1816
+ // Build control payloads: frame + its dependencies (msg stone, identity)
1817
+ const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
1818
+ if (identity) { payloadIbGibsControl.push(identity); }
1819
+
1820
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1821
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1822
+ // throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1924
1823
 
1824
+ } else {
1825
+ // We have nothing to send.
1826
+
1827
+ if (peerProposesCommit) {
1828
+ // Peer is done. We are done. -> Commit.
1925
1829
  const commitData: SyncSagaMessageCommitData_V1 = {
1926
1830
  sagaId: deltaData.sagaId,
1927
1831
  stage: SyncStage.commit,
@@ -1930,68 +1834,136 @@ export class SyncSagaCoordinator {
1930
1834
 
1931
1835
  const commitStone = await this.createSyncMsgStone({
1932
1836
  data: commitData,
1933
- localSpace: tempSpace,
1837
+ localSpace: mySpace,
1934
1838
  metaspace
1935
1839
  });
1936
1840
 
1937
1841
  const commitFrame = await this.evolveSyncSagaIbGib({
1938
- prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1842
+ prevSagaIbGib: sagaIbGib,
1939
1843
  msgStones: [commitStone],
1940
1844
  sessionIdentity: identity,
1941
- localSpace: tempSpace,
1845
+ localSpace: mySpace,
1942
1846
  metaspace
1943
1847
  });
1944
1848
 
1945
1849
  // Build control payloads for commit
1946
- const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
1947
- if (identity) { commitCtrlPayloads2.push(identity); }
1850
+ const commitCtrlPayloads: IbGib_V1[] = [commitFrame, commitStone];
1851
+ if (identity) { commitCtrlPayloads.push(identity); }
1948
1852
 
1949
- // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1950
- // return { frame: commitFrame, };
1951
- throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
1952
- }
1853
+ return { frame: commitFrame, };
1854
+
1855
+ } else {
1856
+ // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
1857
+ // But we are empty.
1858
+ // So WE propose commit.
1859
+ const responseDeltaData: SyncSagaMessageDeltaData_V1 = {
1860
+ sagaId: deltaData.sagaId,
1861
+ stage: SyncStage.delta,
1862
+ proposeCommit: true,
1863
+ };
1864
+
1865
+ const deltaStone = await this.createSyncMsgStone({
1866
+ data: responseDeltaData,
1867
+ localSpace: mySpace,
1868
+ metaspace
1869
+ });
1870
+
1871
+ const deltaFrame = await this.evolveSyncSagaIbGib({
1872
+ prevSagaIbGib: sagaIbGib,
1873
+ msgStones: [deltaStone],
1874
+ sessionIdentity: identity,
1875
+ localSpace: mySpace,
1876
+ metaspace
1877
+ });
1878
+
1879
+ // Check if PEER proposed commit
1880
+ if (deltaData.proposeCommit) {
1881
+ if (logalot) { console.log(`${lc} Peer proposed commit. Accepting & Committing.`); }
1882
+ // Peer wants to commit and has no more requests.
1883
+ // We should Commit.
1884
+
1885
+ const commitData: SyncSagaMessageCommitData_V1 = {
1886
+ sagaId: deltaData.sagaId,
1887
+ stage: SyncStage.commit,
1888
+ success: true,
1889
+ };
1890
+
1891
+ const commitStone = await this.createSyncMsgStone({
1892
+ data: commitData,
1893
+ localSpace: mySpace,
1894
+ metaspace
1895
+ });
1896
+
1897
+ const commitFrame = await this.evolveSyncSagaIbGib({
1898
+ prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1899
+ msgStones: [commitStone],
1900
+ sessionIdentity: identity,
1901
+ localSpace: mySpace,
1902
+ metaspace
1903
+ });
1904
+
1905
+ // Build control payloads for commit
1906
+ const commitCtrlPayloads2: IbGib_V1[] = [commitFrame, commitStone];
1907
+ if (identity) { commitCtrlPayloads2.push(identity); }
1908
+
1909
+ return { frame: commitFrame, };
1910
+ }
1953
1911
 
1954
- // Build control payloads for delta propose
1955
- const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
1956
- if (identity) { deltaCtrlPayloads.push(identity); }
1912
+ // Build control payloads for delta propose
1913
+ const deltaCtrlPayloads: IbGib_V1[] = [deltaFrame, deltaStone];
1914
+ if (identity) { deltaCtrlPayloads.push(identity); }
1957
1915
 
1958
- // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1959
- // return { frame: deltaFrame, };
1960
- throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
1916
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1917
+ }
1961
1918
  }
1919
+ } catch (error) {
1920
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1921
+ throw error;
1922
+ } finally {
1923
+ if (logalot) { console.log(`${lc} complete.`); }
1962
1924
  }
1925
+
1963
1926
  }
1964
1927
 
1965
1928
 
1966
1929
  protected async handleCommitFrame({
1967
1930
  sagaIbGib,
1968
- destSpace,
1969
- tempSpace,
1931
+ mySpace,
1932
+ myTempSpace,
1970
1933
  metaspace,
1971
1934
  identity,
1972
1935
  }: {
1973
1936
  sagaIbGib: SyncIbGib_V1,
1974
- destSpace: IbGibSpaceAny,
1975
- tempSpace: IbGibSpaceAny,
1937
+ mySpace: IbGibSpaceAny,
1938
+ myTempSpace: IbGibSpaceAny,
1976
1939
  metaspace: MetaspaceService,
1977
1940
  identity?: KeystoneIbGib_V1,
1978
1941
  }): Promise<NextSagaFrameInfo> {
1979
1942
  const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
1980
- if (logalot) { console.log(`${lc} Commit received.`); }
1943
+ try {
1944
+ if (logalot) { console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`); }
1945
+
1946
+
1981
1947
 
1982
- // Sender Logic (Finalizing):
1983
- // If we are here, we received a Commit frame from the Peer.
1984
- // This implies the Peer has successfully committed.
1985
- // We should now:
1986
- // 1. Validate (implicitly done by receiving valid frame)
1987
- // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1988
- // 3. Return null to signal saga completion.
1948
+ // Sender Logic (Finalizing):
1949
+ // If we are here, we received a Commit frame from the Peer.
1950
+ // This implies the Peer has successfully committed.
1951
+ // We should now:
1952
+ // 1. Validate (implicitly done by receiving valid frame)
1953
+ // 2. Perform our own cleanup (Temp -> Dest, if applicable)
1954
+ // 3. Return null to signal saga completion.
1989
1955
 
1990
- // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1956
+ // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1957
+ if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1958
+
1959
+ return { responseWasNull: true };
1960
+ } catch (error) {
1961
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1962
+ throw error;
1963
+ } finally {
1964
+ if (logalot) { console.log(`${lc} complete.`); }
1965
+ }
1991
1966
 
1992
- if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1993
- // return { responseWasNull: true };
1994
- throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
1995
1967
  }
1996
1968
 
1997
1969
  // #endregion Handlers
@@ -2045,6 +2017,10 @@ export class SyncSagaCoordinator {
2045
2017
  msgStones: IbGib_V1[],
2046
2018
  localSpace: IbGibSpaceAny,
2047
2019
  metaspace: MetaspaceService,
2020
+ /**
2021
+ * does NOT evolve the keystone. this should be done by the caller.
2022
+ * (NOT IMPLEMENTED YET ANYWAY)
2023
+ */
2048
2024
  sessionIdentity?: KeystoneIbGib_V1,
2049
2025
  }): Promise<SyncIbGib_V1> {
2050
2026
  const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;