@ibgib/core-gib 0.1.18 → 0.1.20

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 (83) hide show
  1. package/dist/common/other/ibgib-helper.d.mts +13 -0
  2. package/dist/common/other/ibgib-helper.d.mts.map +1 -1
  3. package/dist/common/other/ibgib-helper.mjs +44 -0
  4. package/dist/common/other/ibgib-helper.mjs.map +1 -1
  5. package/dist/sync/merge-info/merge-info-constants.d.mts +2 -0
  6. package/dist/sync/merge-info/merge-info-constants.d.mts.map +1 -0
  7. package/dist/sync/merge-info/merge-info-constants.mjs +2 -0
  8. package/dist/sync/merge-info/merge-info-constants.mjs.map +1 -0
  9. package/dist/sync/merge-info/merge-info-helpers.d.mts +51 -0
  10. package/dist/sync/merge-info/merge-info-helpers.d.mts.map +1 -0
  11. package/dist/sync/merge-info/merge-info-helpers.mjs +92 -0
  12. package/dist/sync/merge-info/merge-info-helpers.mjs.map +1 -0
  13. package/dist/sync/merge-info/merge-info-helpers.respec.d.mts +2 -0
  14. package/dist/sync/merge-info/merge-info-helpers.respec.d.mts.map +1 -0
  15. package/dist/sync/merge-info/merge-info-helpers.respec.mjs +32 -0
  16. package/dist/sync/merge-info/merge-info-helpers.respec.mjs.map +1 -0
  17. package/dist/sync/merge-info/merge-info-types.d.mts +26 -0
  18. package/dist/sync/merge-info/merge-info-types.d.mts.map +1 -0
  19. package/dist/sync/merge-info/merge-info-types.mjs +2 -0
  20. package/dist/sync/merge-info/merge-info-types.mjs.map +1 -0
  21. package/dist/sync/strategies/conflict-optimistic.d.mts +37 -0
  22. package/dist/sync/strategies/conflict-optimistic.d.mts.map +1 -0
  23. package/dist/sync/strategies/conflict-optimistic.mjs +162 -0
  24. package/dist/sync/strategies/conflict-optimistic.mjs.map +1 -0
  25. package/dist/sync/sync-conflict.respec.d.mts +8 -0
  26. package/dist/sync/sync-conflict.respec.d.mts.map +1 -0
  27. package/dist/sync/sync-conflict.respec.mjs +158 -0
  28. package/dist/sync/sync-conflict.respec.mjs.map +1 -0
  29. package/dist/sync/sync-innerspace-constants.respec.d.mts +8 -0
  30. package/dist/sync/sync-innerspace-constants.respec.d.mts.map +1 -0
  31. package/dist/sync/sync-innerspace-constants.respec.mjs +116 -0
  32. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -0
  33. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +2 -3
  34. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  35. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +3 -3
  36. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  37. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
  38. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  39. package/dist/sync/sync-innerspace-partial-update.respec.d.mts +7 -0
  40. package/dist/sync/sync-innerspace-partial-update.respec.d.mts.map +1 -0
  41. package/dist/sync/sync-innerspace-partial-update.respec.mjs +116 -0
  42. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -0
  43. package/dist/sync/sync-innerspace.respec.mjs +5 -5
  44. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  45. package/dist/sync/sync-saga-coordinator.d.mts +23 -12
  46. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  47. package/dist/sync/sync-saga-coordinator.mjs +612 -95
  48. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  49. package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts +11 -0
  50. package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts.map +1 -1
  51. package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs +24 -0
  52. package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs.map +1 -1
  53. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +30 -0
  54. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  55. package/dist/sync/sync-types.d.mts +31 -4
  56. package/dist/sync/sync-types.d.mts.map +1 -1
  57. package/dist/sync/sync-types.mjs.map +1 -1
  58. package/ibgib-foundations.md +129 -0
  59. package/package.json +1 -1
  60. package/roadmap.md +59 -0
  61. package/src/common/other/ibgib-helper.mts +52 -0
  62. package/src/keystone/README.md +13 -155
  63. package/src/keystone/docs/architecture.md +55 -0
  64. package/src/sync/README.md +37 -42
  65. package/src/sync/docs/architecture.md +69 -0
  66. package/src/sync/docs/verification.md +43 -0
  67. package/src/sync/merge-info/merge-info-constants.mts +1 -0
  68. package/src/sync/merge-info/merge-info-helpers.mts +134 -0
  69. package/src/sync/merge-info/merge-info-helpers.respec.mts +41 -0
  70. package/src/sync/merge-info/merge-info-types.mts +28 -0
  71. package/src/sync/strategies/conflict-optimistic.mts +208 -0
  72. package/src/sync/sync-conflict.respec.mts +194 -0
  73. package/src/sync/sync-innerspace-constants.respec.mts +133 -0
  74. package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -2
  75. package/src/sync/sync-innerspace-dest-ahead.respec.mts +2 -2
  76. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
  77. package/src/sync/sync-innerspace-partial-update.respec.mts +150 -0
  78. package/src/sync/sync-innerspace.respec.mts +4 -4
  79. package/src/sync/sync-saga-coordinator.mts +673 -103
  80. package/src/sync/sync-saga-message/sync-saga-message-helpers.mts +41 -0
  81. package/src/sync/sync-saga-message/sync-saga-message-types.mts +28 -0
  82. package/src/sync/sync-types.mts +33 -4
  83. package/tmp.md +2 -374
@@ -57,3 +57,44 @@ export async function parseSyncSagaMessageIb({
57
57
  throw error;
58
58
  }
59
59
  }
60
+
61
+ /**
62
+ * Helper to retrieve the Message Stone associated with a Saga Frame.
63
+ *
64
+ * Saga Frames link to their Message Stone via the `sync-msg` relationship.
65
+ */
66
+ import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
67
+ import { IbGibSpaceAny } from "../../witness/space/space-base-v1.mjs";
68
+ import { getRel8dIbGibs } from "../../common/other/ibgib-helper.mjs";
69
+ import { SYNC_MSG_REL8N_NAME } from "../sync-constants.mjs";
70
+
71
+ export async function getSyncSagaMessageFromFrame({
72
+ frameIbGib,
73
+ space,
74
+ }: {
75
+ frameIbGib: IbGib_V1,
76
+ space: IbGibSpaceAny,
77
+ }): Promise<IbGib_V1<SyncSagaMessageData_V1>> {
78
+ const lc = `[${getSyncSagaMessageFromFrame.name}]`;
79
+ try {
80
+ const rel8d = await getRel8dIbGibs({
81
+ ibGib: frameIbGib,
82
+ rel8nNames: [SYNC_MSG_REL8N_NAME],
83
+ space
84
+ });
85
+ const msgs = rel8d[SYNC_MSG_REL8N_NAME];
86
+
87
+ if (!msgs || msgs.length === 0) {
88
+ throw new Error(`(UNEXPECTED) No sync message stone found found for frame: ${frameIbGib.ib} (E: 8a4b2c1d3e5f6a9b7c8d9e0f1a2b3c4d)`);
89
+ }
90
+
91
+ if (msgs.length > 1) {
92
+ throw new Error(`(UNEXPECTED) Multiple sync message stones found (${msgs.length}) for frame: ${frameIbGib.ib}. logic not currently equipped to handle multiple messages per frame. (E: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d)`);
93
+ }
94
+
95
+ return msgs[0] as IbGib_V1<SyncSagaMessageData_V1>;
96
+ } catch (error) {
97
+ console.error(`${lc} ${extractErrorMsg(error)}`);
98
+ throw error;
99
+ }
100
+ }
@@ -42,11 +42,39 @@ export interface SyncSagaMessageAckData_V1 extends SyncSagaMessageData_V1 {
42
42
  stage: typeof SyncStage.ack;
43
43
  deltaReqAddrs: string[];
44
44
  pushOfferAddrs: string[];
45
+ /**
46
+ * Map of group keys (TJP or Constant Addr) to list of known addresses (Full History).
47
+ * Used by Sender to calculate differential payloads.
48
+ */
49
+ knowledgeVector?: { [groupKey: string]: string[] };
50
+ /**
51
+ * List of identified conflicts.
52
+ *
53
+ * If present, the Sender should use the `timelineAddrs` (Receiver's history)
54
+ * to compute the LCA and delta requirements for merging.
55
+ */
56
+ conflicts?: {
57
+ tjpAddr: string;
58
+ localAddr: string;
59
+ remoteAddr: string;
60
+ /**
61
+ * Full history of the timeline from the Receiver's perspective.
62
+ * Used by Sender to find LCA.
63
+ */
64
+ timelineAddrs: string[];
65
+ reason: string;
66
+ terminal: boolean;
67
+ }[];
45
68
  }
46
69
 
47
70
  export interface SyncSagaMessageDeltaData_V1 extends SyncSagaMessageData_V1, SyncDeltaData {
48
71
  stage: typeof SyncStage.delta;
49
72
  requests?: string[];
73
+ /**
74
+ * Flag indicating the sender has no further requests or payloads
75
+ * and is ready to commit the transaction.
76
+ */
77
+ proposeCommit?: boolean;
50
78
  }
51
79
 
52
80
  export interface SyncSagaMessageCommitData_V1 extends SyncSagaMessageData_V1, SyncCommitData {
@@ -4,6 +4,7 @@ import { KeystoneIbGib_V1, KeystoneProof } from "../keystone/keystone-types.mjs"
4
4
  import { SYNC_ATOM, SyncStage } from "./sync-constants.mjs";
5
5
  import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
6
6
  import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
7
+ import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
7
8
 
8
9
 
9
10
  // #region SyncMode
@@ -45,6 +46,10 @@ export function isValidSyncConflictStrategy(strategy: string): strategy is SyncC
45
46
  // #endregion SyncConflictStrategy
46
47
 
47
48
  export interface SyncOptions {
49
+ /**
50
+ * The peer we are syncing with.
51
+ */
52
+ peer: SyncPeerWitness;
48
53
  /**
49
54
  * The ibgibs we wish to sync.
50
55
  *
@@ -54,11 +59,15 @@ export interface SyncOptions {
54
59
  /**
55
60
  * The space containing the data we want to send.
56
61
  */
57
- source: IbGibSpaceAny;
62
+ source?: IbGibSpaceAny;
63
+ /**
64
+ * Alias for {@link source}. Used in some contexts.
65
+ */
66
+ localSpace?: IbGibSpaceAny;
58
67
  /**
59
68
  * The space receiving the data.
60
69
  */
61
- dest: IbGibSpaceAny;
70
+ dest?: IbGibSpaceAny;
62
71
  /**
63
72
  * The metaspace context (for registering sync sagas locally).
64
73
  */
@@ -66,16 +75,22 @@ export interface SyncOptions {
66
75
  /**
67
76
  * The identity authorizing this sync.
68
77
  */
69
- identity: KeystoneIbGib_V1;
78
+ identity?: KeystoneIbGib_V1;
70
79
  /**
71
80
  * The secret for the identity (to sign the commit).
72
81
  */
73
- identitySecret: string;
82
+ identitySecret?: string;
74
83
  /**
75
84
  * How to handle conflicts when both Source and Dest have diverged on the same timeline.
76
85
  * @default 'abort'
77
86
  */
78
87
  conflictStrategy?: SyncConflictStrategy;
88
+ /**
89
+ * @deprecated Use `identity` instead if you have a specific identity.
90
+ * If true, creates an ephemeral session identity.
91
+ * @default true
92
+ */
93
+ useSessionIdentity?: boolean;
79
94
  }
80
95
 
81
96
  // ===========================================================================
@@ -168,6 +183,20 @@ export interface SyncData_V1 extends IbGibData_V1 {
168
183
  */
169
184
  uuid: string;
170
185
 
186
+ /**
187
+ * Optional verification metadata (e.g. success count).
188
+ */
189
+ successCount?: number;
190
+ /**
191
+ * If failed, the reasons why.
192
+ */
193
+ errors?: string[];
194
+
195
+ /**
196
+ * Strategy used for resolving conflicts in this saga.
197
+ */
198
+ conflictStrategy?: SyncConflictStrategy;
199
+
171
200
  /**
172
201
  * Optional absolute timestamp (Unix ms) after which this frame should be considered void.
173
202
  * Useful for:
package/tmp.md CHANGED
@@ -1,375 +1,3 @@
1
- /**
2
- * @module sync-innerspace-complex.respec
3
- *
4
- * Verifies Complex Sync Scenarios using InnerSpace.
5
- */
1
+ Clarifying Merge Context
6
2
 
7
- import {
8
- respecfully, lastOfAll, ifWe, iReckon,
9
- ifWeMight
10
- } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
11
- const maam = `[${import.meta.url}]`, sir = maam;
12
- import { delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
13
- import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
14
- import { getDependencyGraph } from '../common/other/graph-helper.mjs';
15
-
16
- import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
17
- import { putInSpace, getFromSpace } from '../witness/space/space-helper.mjs';
18
- import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
19
- import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
20
- import { createTimelineRootHelper, getTestKeystoneServiceHelper } from '../agent-helpers.mjs';
21
- import { mut8Timeline } from '../timeline/timeline-api.mjs';
22
- import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace-v1.mjs';
23
- import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
24
- import { toDto } from '../common/other/ibgib-helper.mjs';
25
-
26
- const logalot = true;
27
- const lc = `[sync-innerspace-complex.respec]`;
28
-
29
- await respecfully(sir, `Sync InnerSpaces (Complex)`, async () => {
30
-
31
- let metaspace: Metaspace_Innerspace;
32
- let sourceSpace: InnerSpace_V1;
33
- let destSpace: InnerSpace_V1;
34
-
35
- await respecfully(sir, `Multiple Timelines (Push Source -> Dest)`, async () => {
36
- // 1. Setup Spaces
37
- metaspace = new Metaspace_Innerspace(undefined);
38
- await metaspace.initialize({
39
- getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
40
- getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
41
- getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
42
- });
43
- while (!metaspace.initialized) { await delay(10); }
44
-
45
- const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
46
- await defaultLocalUserSpace!.initialized;
47
-
48
- sourceSpace = new InnerSpace_V1({
49
- ...DEFAULT_INNER_SPACE_DATA_V1,
50
- name: 'source',
51
- uuid: 'source_uuid',
52
- description: 'source test space',
53
- });
54
- await sourceSpace.initialized;
55
-
56
- destSpace = new InnerSpace_V1({
57
- ...DEFAULT_INNER_SPACE_DATA_V1,
58
- name: 'dest',
59
- uuid: 'dest_uuid',
60
- description: 'dest test space',
61
- });
62
- await destSpace.initialized;
63
-
64
- // 2. Seed Source Data (Multiple Independent Timelines)
65
- console.log(`${lc} Creating data in Source...`);
66
-
67
- // Timeline A
68
- const rootA = await createTimelineRootHelper({
69
- ib: 'timeline_root_A',
70
- data: { type: 'root', label: 'A' },
71
- space: sourceSpace,
72
- });
73
- const childA = await mut8Timeline({
74
- timeline: rootA,
75
- mut8Opts: { dataToAddOrPatch: { type: 'child', label: 'A1' } },
76
- metaspace,
77
- space: sourceSpace,
78
- });
79
- const addrA = getIbGibAddr({ ibGib: childA });
80
-
81
- // Timeline B
82
- const rootB = await createTimelineRootHelper({
83
- ib: 'timeline_root_B',
84
- data: { type: 'root', label: 'B' },
85
- space: sourceSpace,
86
- });
87
- // B has deeper history
88
- const childB1 = await mut8Timeline({
89
- timeline: rootB,
90
- mut8Opts: { dataToAddOrPatch: { type: 'child', label: 'B1' } },
91
- metaspace,
92
- space: sourceSpace,
93
- });
94
- const childB2 = await mut8Timeline({
95
- timeline: childB1,
96
- mut8Opts: { dataToAddOrPatch: { type: 'child', label: 'B2' } },
97
- metaspace,
98
- space: sourceSpace,
99
- });
100
- const addrB = getIbGibAddr({ ibGib: childB2 });
101
-
102
- // 3. Setup Coordinators & Peer
103
- const mockKeystone = await getTestKeystoneServiceHelper();
104
- const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
105
- const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
106
-
107
- const peer = new SyncPeerInnerspace_V1({
108
- senderSpace: sourceSpace,
109
- receiverSpace: destSpace,
110
- receiverCoordinator: receiverCoordinator,
111
- receiverMetaspace: metaspace,
112
- });
113
-
114
- // 4. Run Sync (Push Both)
115
- console.log(`${lc} Running Sync...`);
116
- const resSync = await senderCoordinator.sync({
117
- peer: peer,
118
- localSpace: sourceSpace,
119
- metaspace: metaspace,
120
- domainIbGibs: [childA, childB2], // Sync both tips
121
- useSessionIdentity: false,
122
- });
123
-
124
- await resSync.done;
125
- // await delay(100); // Wait for flush?
126
-
127
- // 5. Verify Dest
128
- console.log(`${lc} Verifying Destination...`);
129
-
130
- await ifWeMight(sir, `verify timelines present`, async () => {
131
- // Verify A
132
- const getA = await getFromSpace({ space: destSpace, addr: addrA });
133
- iReckon(sir, getA.success).asTo('Timeline A present').isGonnaBeTrue();
134
- iReckon(sir, getA.ibGibs?.[0]?.data?.label).asTo('Label A match').isGonnaBe('A1');
135
-
136
- // Verify B
137
- const getB = await getFromSpace({ space: destSpace, addr: addrB });
138
- iReckon(sir, getB.success).asTo('Timeline B present').isGonnaBeTrue();
139
- iReckon(sir, getB.ibGibs?.[0]?.data?.label).asTo('Label B match').isGonnaBe('B2');
140
- });
141
-
142
- });
143
-
144
- interface TestData {
145
- type: string;
146
- label?: string;
147
- uuid?: string;
148
- n?: number;
149
- }
150
-
151
- await respecfully(sir, `Deep Updates (Push Source -> Dest)`, async () => {
152
- // 1. Setup Spaces
153
- metaspace = new Metaspace_Innerspace(undefined);
154
- await metaspace.initialize({
155
- getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
156
- getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
157
- getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
158
- });
159
- while (!metaspace.initialized) { await delay(10); }
160
-
161
- const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
162
- await defaultLocalUserSpace!.initialized;
163
-
164
- sourceSpace = new InnerSpace_V1({
165
- ...DEFAULT_INNER_SPACE_DATA_V1,
166
- name: 'source',
167
- uuid: 'source_uuid',
168
- description: 'source test space',
169
- });
170
- await sourceSpace.initialized;
171
-
172
- destSpace = new InnerSpace_V1({
173
- ...DEFAULT_INNER_SPACE_DATA_V1,
174
- name: 'dest',
175
- uuid: 'dest_uuid',
176
- description: 'dest test space',
177
- });
178
- await destSpace.initialized;
179
-
180
- // 2. Seed Source Data (Deep History)
181
- console.log(`${lc} Creating deep data in Source...`);
182
-
183
- // Root -> Comment (Child) -> Edit Comment (Child v2)
184
- const root = await createTimelineRootHelper<TestData>({
185
- ib: 'timeline_root_deep',
186
- data: { type: 'root', label: 'Root' },
187
- space: sourceSpace,
188
- });
189
-
190
- // n=1
191
- const commentV1 = await mut8Timeline<TestData>({
192
- timeline: root,
193
- mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'Hello World' } },
194
- metaspace,
195
- space: sourceSpace,
196
- });
197
-
198
- // n=2 (Edit)
199
- const commentV2 = await mut8Timeline<TestData>({
200
- timeline: commentV1,
201
- mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'Hello Sync' } },
202
- metaspace,
203
- space: sourceSpace,
204
- });
205
- const addrV2 = getIbGibAddr({ ibGib: commentV2 });
206
-
207
- // 3. Setup
208
- const mockKeystone = await getTestKeystoneServiceHelper();
209
- const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
210
- const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
211
-
212
- const peer = new SyncPeerInnerspace_V1({
213
- senderSpace: sourceSpace,
214
- receiverSpace: destSpace,
215
- receiverCoordinator: receiverCoordinator,
216
- receiverMetaspace: metaspace,
217
- });
218
-
219
- // 4. Run Sync
220
- console.log(`${lc} Running Sync...`);
221
- // Debug Source Graph
222
- const sourceGraph = await getDependencyGraph({ ibGib: commentV2, space: sourceSpace });
223
- console.log(`${lc} Source Graph count: ${sourceGraph ? Object.keys(sourceGraph).length : 'null'}`);
224
-
225
- const resSync = await senderCoordinator.sync({
226
- peer: peer,
227
- localSpace: sourceSpace,
228
- metaspace: metaspace,
229
- domainIbGibs: [commentV2], // Sync the TIP
230
- useSessionIdentity: false,
231
- });
232
-
233
- await resSync.done;
234
-
235
- // 5. Verify Dest
236
- console.log(`${lc} Verifying Destination...`);
237
-
238
- await ifWeMight(sir, `verify deep timeline present`, async () => {
239
- // Verify Tip (V2)
240
- const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
241
- iReckon(sir, getV2.success).asTo('Tip V2 present').isGonnaBeTrue();
242
- const v2IbGib = getV2.ibGibs![0];
243
- iReckon(sir, v2IbGib.data!.label).asTo('Label V2 match').isGonnaBe('Hello Sync');
244
- // iReckon(sir, v2IbGib.data!.n).asTo('n=2').isGonnaBe(2); // Relaxing n check for now
245
-
246
- // Verify Past Frame (V1) availability
247
- // Sync should transfer dependencies (past frames)
248
- // Rel8ns are accretive, so we check the LAST one.
249
- const pastAddr = v2IbGib.rel8ns?.past?.at(-1);
250
- if (pastAddr) {
251
- const getV1 = await getFromSpace({ space: destSpace, addr: pastAddr });
252
- iReckon(sir, getV1.success).asTo('Past Frame (V1) present').isGonnaBeTrue();
253
- iReckon(sir, getV1.ibGibs?.[0]?.data?.label).asTo('Label V1 match').isGonnaBe('Hello World');
254
- } else {
255
- iReckon(sir, false).asTo('Past relation present').isGonnaBeTrue();
256
- }
257
-
258
- // Verify Full Graph Integrity (DNA, Roots)
259
- const graph = await getDependencyGraph({ ibGib: v2IbGib, space: destSpace });
260
- // const debugGraph = ... (removed)
261
-
262
- iReckon(sir, !!graph).asTo('Dependency Graph resolvable in Dest').isGonnaBeTrue();
263
- // Should contain Root, V1, V2
264
- // We can check counts or specific existence.
265
- iReckon(sir, Object.keys(graph!).length > 2).asTo('Graph size > 2').isGonnaBeTrue();
266
- });
267
- });
268
-
269
- await respecfully(sir, `Dest Ahead (Remote Newer)`, async () => {
270
- // 1. Setup Spaces
271
- metaspace = new Metaspace_Innerspace(undefined);
272
- await metaspace.initialize({
273
- getFnAlert: () => async ({ title, msg }) => { },
274
- getFnPrompt: () => async ({ title, msg }) => { return ''; },
275
- getFnPromptPassword: () => async (title, msg) => { return null; },
276
- });
277
- while (!metaspace.initialized) { await delay(10); }
278
-
279
- const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
280
- await defaultLocalUserSpace!.initialized;
281
-
282
- sourceSpace = new InnerSpace_V1({
283
- ...DEFAULT_INNER_SPACE_DATA_V1,
284
- name: 'source',
285
- uuid: 'source_uuid',
286
- description: 'source test space',
287
- });
288
- await sourceSpace.initialized;
289
-
290
- destSpace = new InnerSpace_V1({
291
- ...DEFAULT_INNER_SPACE_DATA_V1,
292
- name: 'dest',
293
- uuid: 'dest_uuid',
294
- description: 'dest test space',
295
- });
296
- await destSpace.initialized;
297
-
298
- // 2. Seed Data
299
- // Root -> V1 (Shared) -> V2 (Dest has New)
300
- // Source only has V1.
301
-
302
- const root = await createTimelineRootHelper<TestData>({
303
- ib: 'timeline_root_ff',
304
- data: { type: 'root', label: 'Root' },
305
- space: sourceSpace,
306
- });
307
-
308
- // V1 (Both have it, but we create in source and copy to dest)
309
- const v1 = await mut8Timeline<TestData>({
310
- timeline: root,
311
- mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1' } },
312
- metaspace,
313
- space: sourceSpace,
314
- });
315
-
316
- // Transfer Root & V1 to Dest
317
- await putInSpace({ space: destSpace, ibGibs: [root, v1] }); // Naive seeding
318
-
319
- // V2 (Created in Dest ONLY)
320
- const v2 = await mut8Timeline<TestData>({
321
- timeline: v1, // v1 is in memory, linked to source, but we want to Mutate IN DEST SPACE
322
- mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V2' } },
323
- metaspace,
324
- space: destSpace, // Mutate in Dest
325
- });
326
- const addrV2 = getIbGibAddr({ ibGib: v2 });
327
-
328
- await ifWe(sir, 'verify setup', async () => {
329
- // Ensure V2 is ONLY in Dest (it is, per `space: destSpace`)
330
- // Ensure Source does NOT have V2
331
- const checkV2InSource = await getFromSpace({ space: sourceSpace, addr: addrV2 });
332
- iReckon(sir, checkV2InSource.success && !!checkV2InSource.ibGibs?.length).asTo('Source has V2').isGonnaBeFalse();
333
- });
334
-
335
- // 3. Setup Sync
336
- const mockKeystone = await getTestKeystoneServiceHelper();
337
- const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
338
- const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
339
-
340
- const peer = new SyncPeerInnerspace_V1({
341
- senderSpace: sourceSpace,
342
- receiverSpace: destSpace,
343
- receiverCoordinator: receiverCoordinator,
344
- receiverMetaspace: metaspace,
345
- });
346
-
347
- // 4. Run Sync (Source Pushes V1)
348
- console.log(`${lc} Running Sync...`);
349
- const resSync = await senderCoordinator.sync({
350
- peer: peer,
351
- localSpace: sourceSpace,
352
- metaspace: metaspace,
353
- domainIbGibs: [v1], // Source tries to push V1
354
- useSessionIdentity: false,
355
- });
356
-
357
- await resSync.done;
358
-
359
- // 5. Verify Dest (Should still have V2 as latest)
360
- console.log(`${lc} Verifying Destination...`);
361
-
362
- await ifWeMight(sir, `verify dest stays ahead`, async () => {
363
- // Verify Tip (V2)
364
- const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
365
- iReckon(sir, getV2.success).asTo('V2 still present in Dest').isGonnaBeTrue();
366
-
367
- // Verify V2 points to V1
368
- const v2IbGib = getV2.ibGibs![0];
369
- const pastAddr = v2IbGib.rel8ns?.past?.at(-1);
370
- iReckon(sir, pastAddr === getIbGibAddr({ ibGib: v1 })).asTo('V2 points to V1').isGonnaBeTrue();
371
- });
372
-
373
- });
374
-
375
- });
3
+ I'm now integrating merge context into handleDeltaFrame. It's crucial that this function knows the history of conflicts. I'm focusing on deriving state from the sagaIbGib and arguments, as the coordinator is mostly stateless. I'm thinking of checking for conflicts in the Ack within the saga history. If conflicts existed, I can then check for available merge data. This will involve inspecting delta changes to determine merge necessity, and if required, triggering merge based on existing conflicts. I'll add the necessary type updates accordingly.