@ibgib/space-gib 0.0.2 → 0.0.4

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 (67) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/IMPLEMENTATION.md +9 -13
  3. package/dist/client/bootstrap.mjs +1 -1
  4. package/dist/client/bootstrap.mjs.map +1 -1
  5. package/dist/client/chunk-NCXKCVYS.mjs +42 -0
  6. package/dist/client/chunk-NCXKCVYS.mjs.map +7 -0
  7. package/dist/client/chunk-ZUEU37Z5.mjs +1920 -0
  8. package/dist/client/chunk-ZUEU37Z5.mjs.map +7 -0
  9. package/dist/client/index.html +108 -8
  10. package/dist/client/index.mjs +1 -1
  11. package/dist/client/script.mjs +1 -1
  12. package/dist/client/style.css +466 -61
  13. package/dist/respec-gib.node.mjs +5 -0
  14. package/dist/server/server.mjs +815 -316
  15. package/dist/server/server.mjs.map +4 -4
  16. package/package.json +6 -6
  17. package/src/client/AUTO-GENERATED-version.mts +1 -1
  18. package/src/client/api/space-gib-api-bridge.mts +35 -0
  19. package/src/client/components/identity-header/IMPLEMENTATION.md +45 -0
  20. package/src/client/components/identity-header/identity-header.css +74 -0
  21. package/src/client/components/identity-header/identity-header.html +10 -0
  22. package/src/client/components/identity-header/identity-header.mts +361 -0
  23. package/src/client/components/identity-manager/IMPLEMENTATION.md +100 -0
  24. package/src/client/components/identity-manager/identity-manager.css +467 -0
  25. package/src/client/components/identity-manager/identity-manager.html +113 -0
  26. package/src/client/components/identity-manager/identity-manager.mts +767 -0
  27. package/src/client/components/keystone-creator/keystone-creator.css +2 -76
  28. package/src/client/components/keystone-creator/keystone-creator.html +41 -26
  29. package/src/client/components/keystone-creator/keystone-creator.mts +178 -41
  30. package/src/client/dev-tools/base-tools.mts +252 -0
  31. package/src/client/dev-tools/common.mts +217 -0
  32. package/src/client/dev-tools/phase-1.mts +156 -0
  33. package/src/client/dev-tools/phase-2.mts +143 -0
  34. package/src/client/dev-tools/phase-3.mts +189 -0
  35. package/src/client/dev-tools/phase-4-1.mts +197 -0
  36. package/src/client/dev-tools/phase-4-10.mts +884 -0
  37. package/src/client/dev-tools/phase-4-2.mts +388 -0
  38. package/src/client/dev-tools/phase-4-3.mts +391 -0
  39. package/src/client/dev-tools/phase-4-4.mts +374 -0
  40. package/src/client/dev-tools/phase-4-5.mts +376 -0
  41. package/src/client/dev-tools/phase-4-6.mts +273 -0
  42. package/src/client/dev-tools/phase-4-7.mts +399 -0
  43. package/src/client/dev-tools/phase-4-8.mts +430 -0
  44. package/src/client/dev-tools/phase-4-9.mts +398 -0
  45. package/src/client/dev-tools/phase-4.mts +1302 -0
  46. package/src/client/dev-tools.mts +55 -1096
  47. package/src/client/index.html +108 -8
  48. package/src/client/style.css +466 -61
  49. package/src/client/ui/shell/space-gib-shell-constants.mts +0 -2
  50. package/src/client/ui/shell/space-gib-shell-service.mts +82 -10
  51. package/src/common/common-constants.mts +0 -0
  52. package/src/common/keystone-policies.json +40 -43
  53. package/src/common/keystone-policies.mts +4 -6
  54. package/src/server/path-helper.respec.mts +99 -94
  55. package/src/server/serve-gib/README.md +9 -0
  56. package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +1 -1
  57. package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +1 -1
  58. package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +4 -4
  59. package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +1 -1
  60. package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +37 -5
  61. package/src/server/serve-gib/handlers/ws/ws-helper.mts +73 -45
  62. package/dist/client/chunk-BL2SGXS4.mjs +0 -18994
  63. package/dist/client/chunk-RDTAT5G4.mjs +0 -235
  64. package/dist/client/chunk-RDTAT5G4.mjs.map +0 -7
  65. package/dist/client/chunk-RE7XSMHH.mjs +0 -31
  66. package/dist/client/chunk-RE7XSMHH.mjs.map +0 -7
  67. package/dist/client/chunk-YUSGN3J4.mjs +0 -23119
@@ -0,0 +1,884 @@
1
+ // #region Imports
2
+ import { extractErrorMsg, getUUID, getTimestamp, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
3
+ import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/factory.mjs';
4
+ import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
5
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
6
+ import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
7
+ import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
8
+ import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
9
+ import { mut8Timeline, appendToTimeline } from '@ibgib/core-gib/dist/timeline/timeline-api.mjs';
10
+ import { getTjpAddr } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
11
+ import { SyncPeerWebSocketSender_V1 } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs';
12
+ import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordinator.mjs';
13
+ import { SyncConflictStrategy } from '@ibgib/core-gib/dist/sync/sync-constants.mjs';
14
+ import { GRAFT_BASE_REL8N_NAME, GRAFT_ORPHAN_REL8N_NAME, GRAFT_INFO_REL8N_NAME } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-constants.mjs';
15
+ import { SESSION_KEYSTONE_POLICY, getSpaceGibPoolConfig } from "../../common/keystone-policies.mjs";
16
+ import { SpaceGibApiBridge } from '../api/space-gib-api-bridge.mjs';
17
+
18
+ import { debugState, devLog } from './common.mjs';
19
+ import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
20
+ // #endregion Imports
21
+
22
+ // #region Module State
23
+ interface Phase410State {
24
+ domainI_initial?: KeystoneIbGib_V1;
25
+ domainI_latest?: KeystoneIbGib_V1;
26
+ remoteSpace?: any;
27
+ alpha_tjpAddr?: string;
28
+ beta_tjpAddr?: string;
29
+ gamma_tjpAddr?: string;
30
+
31
+ alpha_v0?: any;
32
+ r2_alpha_v3_source_rel8beta?: any;
33
+ r2_beta_v0_source?: any;
34
+ }
35
+
36
+ const state: Phase410State = {};
37
+
38
+ const TEST_REL8N_NAME = 'testrel8n';
39
+ // #endregion Module State
40
+
41
+ // #region Phase 4.10B Setup
42
+ export function init4_10bSetupButton(): void {
43
+ const btn = document.getElementById('btn-4-10b-setup') as HTMLButtonElement | null;
44
+ if (!btn) { return; }
45
+ btn.addEventListener('click', async () => {
46
+ try {
47
+ btn.disabled = true;
48
+ devLog('4.10B Setup: Setting up identities and preparing Round 1 & Round 2 divergence...');
49
+
50
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
51
+ const space = await metaspace.getLocalUserSpace({}) as any;
52
+ if (!space) { throw new Error("No default space."); }
53
+
54
+ const keystoneService = new KeystoneService_V1();
55
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
56
+
57
+ // 1. Generate Domain User Identity
58
+ const domainI = await keystoneService.genesis({
59
+ masterSecret: 'test-sender-secret-phase4-10b',
60
+ configs: [
61
+ getSpaceGibPoolConfig('sync', 'senderidentitysyncsaltphase4-10b'),
62
+ getSpaceGibPoolConfig('manage', 'senderidentitymanagesaltphase4-10b'),
63
+ ],
64
+ metaspace,
65
+ space,
66
+ frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
67
+ });
68
+
69
+ state.domainI_initial = domainI;
70
+ state.domainI_latest = domainI;
71
+ let domainI_latest = domainI;
72
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
73
+ devLog(`4.10B Setup: ✓ Created identity: ${domainAddr}`);
74
+
75
+ // Register on server
76
+ const apiBridge = new SpaceGibApiBridge();
77
+ const resGenesis = await apiBridge.postGenesisKeystone(domainI);
78
+ if (!resGenesis.success) {
79
+ throw new Error(`Server rejected genesis domain keystone: ${resGenesis.message}`);
80
+ }
81
+ devLog('4.10B Setup: ✓ Identity registered on server.');
82
+
83
+ // 2. Create the divergent "remote" space
84
+ devLog('4.10B Setup: Creating temporary remote space...');
85
+ const remoteSpace = await metaspace.createNewLocalSpace({
86
+ opts: {
87
+ allowCancel: false,
88
+ spaceName: 'remote_space_4_10b',
89
+ getFnPrompt: metaspace.getFnPrompt!
90
+ }
91
+ }) as any;
92
+ await remoteSpace.initialized;
93
+ state.remoteSpace = remoteSpace;
94
+
95
+ // Copy user identity to remote space
96
+ await metaspace.put({ ibGib: domainI, space: remoteSpace });
97
+ await metaspace.registerNewIbGib({ ibGib: domainI, space: remoteSpace });
98
+
99
+ // 3. Seed common history (Round 1) locally in default space
100
+ devLog('4.10B Setup: Creating common history for alpha...');
101
+ const resRoot = await factory.firstGen({
102
+ parentIbGib: ROOT,
103
+ ib: 'timeline_root_alpha_4_10b',
104
+ data: { commonField: 'common field R1 val', label: 'AlphaRoot', random: Math.random() },
105
+ dna: true,
106
+ nCounter: true,
107
+ tjp: { uuid: true, timestamp: true }
108
+ });
109
+ const alpha_v0 = resRoot.newIbGib;
110
+ state.alpha_v0 = alpha_v0;
111
+ await metaspace.persistTransformResult({ resTransform: resRoot, space });
112
+ await metaspace.registerNewIbGib({ ibGib: alpha_v0, space });
113
+ const alpha_tjpAddr = getTjpAddr({ ibGib: alpha_v0, defaultIfNone: 'incomingAddr' })!;
114
+ state.alpha_tjpAddr = alpha_tjpAddr;
115
+
116
+ // Copy alpha_v0 to remote space before it gets signed by the seeding sync
117
+ await metaspace.persistTransformResult({ resTransform: resRoot, space: remoteSpace });
118
+ await metaspace.registerNewIbGib({ ibGib: alpha_v0, space: remoteSpace });
119
+
120
+ // Sync alpha_v0 to server immediately so server has it
121
+ devLog('4.10B Setup: Seeding alpha_v0 to server...');
122
+ const senderPeerInit = new SyncPeerWebSocketSender_V1({
123
+ classname: 'SyncPeerWebSocketSender_V1',
124
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
125
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
126
+ });
127
+ const coordinatorInit = new SyncSagaCoordinator();
128
+ await senderPeerInit.initializeOpts({
129
+ localMetaspace: metaspace,
130
+ localSpace: space,
131
+ senderIdentity: domainI,
132
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
133
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
134
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
135
+ targetAddrs: [domainAddr]
136
+ });
137
+ const syncSagaInit = await coordinatorInit.sync({
138
+ domainIbGibs: [alpha_v0],
139
+ senderIdentity: domainI,
140
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
141
+ peer: senderPeerInit,
142
+ localSpace: space,
143
+ metaspace,
144
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
145
+ });
146
+ await syncSagaInit.done;
147
+ devLog('4.10B Setup: ✓ alpha_v0 seeded on server.');
148
+
149
+ // Fetch evolved user identity (n=2)
150
+ const domainLatestAddr = await metaspace.getLatestAddr({ ibGib: domainI, space });
151
+ if (!domainLatestAddr) { throw new Error("Could not find latest evolved identity address in local space."); }
152
+ const resGetLatest = await metaspace.get({ addr: domainLatestAddr, space });
153
+ domainI_latest = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
154
+ state.domainI_latest = domainI_latest;
155
+
156
+ // Copy to remote space
157
+ await metaspace.put({ ibGib: domainI_latest, space: remoteSpace });
158
+ await metaspace.registerNewIbGib({ ibGib: domainI_latest, space: remoteSpace });
159
+
160
+ // 4. Create Round 2 Divergence:
161
+ // Client (default space): Create beta, mutate alpha fieldA, relate alpha to beta
162
+ const resBeta = await factory.firstGen({
163
+ parentIbGib: ROOT,
164
+ ib: 'timeline_root_beta_4_10b',
165
+ data: { fieldA: 'beta field A created on source', label: 'BetaRoot', random: Math.random() },
166
+ dna: true,
167
+ nCounter: true,
168
+ tjp: { uuid: true, timestamp: true }
169
+ });
170
+ const beta_v0_source = resBeta.newIbGib;
171
+ await metaspace.persistTransformResult({ resTransform: resBeta, space });
172
+ await metaspace.registerNewIbGib({ ibGib: beta_v0_source, space });
173
+ state.r2_beta_v0_source = beta_v0_source;
174
+ const beta_tjpAddr = getTjpAddr({ ibGib: beta_v0_source, defaultIfNone: 'incomingAddr' })!;
175
+ state.beta_tjpAddr = beta_tjpAddr;
176
+
177
+ const r2_alpha_v1_source_sansBeta = await mut8Timeline({
178
+ timeline: alpha_v0,
179
+ mut8Opts: {
180
+ mut8Ib: alpha_v0.ib + '_r2client',
181
+ dataToAddOrPatch: { fieldA: 'source_edit_r2' }
182
+ },
183
+ metaspace,
184
+ space,
185
+ });
186
+ devLog(`r2_alpha_v1_source_sansBeta.gib: ${r2_alpha_v1_source_sansBeta.gib}`)
187
+
188
+ betaAddr = getIbGibAddr({ ibGib: beta_v0_source });
189
+ const r2_alpha_v2_source_rel8dBeta = await appendToTimeline({
190
+ timeline: r2_alpha_v1_source_sansBeta,
191
+ metaspace,
192
+ space,
193
+ rel8nInfos: [
194
+ { rel8nName: TEST_REL8N_NAME, ibGibs: [beta_v0_source] }
195
+ ]
196
+ });
197
+ state.r2_alpha_v3_source_rel8beta = r2_alpha_v2_source_rel8dBeta;
198
+ devLog(`r2_alpha_v2_source_rel8dBeta.gib: ${r2_alpha_v2_source_rel8dBeta.gib}`)
199
+ devLog('4.10B Setup: ✓ Created client Round 2 edits (alpha fieldA edit + relate beta).');
200
+
201
+ // Remote Space: Mutate alpha fieldB
202
+ const r2_alpha_v1_remote = await mut8Timeline({
203
+ timeline: alpha_v0,
204
+ mut8Opts: {
205
+ mut8Ib: alpha_v0.ib + '_r2remote',
206
+ dataToAddOrPatch: { fieldB: 'remote_edit_r2' }
207
+ },
208
+ metaspace,
209
+ space: remoteSpace,
210
+ });
211
+ console.log(`alpha_v0: ${pretty(alpha_v0)}`)
212
+ console.log(`r2_alpha_v1_remote: ${pretty(r2_alpha_v1_remote)}`)
213
+ devLog(`r2_alpha_v1_remote.gib: ${r2_alpha_v1_remote.gib}`)
214
+
215
+ // Sync remote Round 2 edit to server
216
+ devLog('4.10B Setup: Pushing remote Round 2 edit to server...');
217
+ const senderPeerRemote = new SyncPeerWebSocketSender_V1({
218
+ classname: 'SyncPeerWebSocketSender_V1',
219
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
220
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
221
+ });
222
+ const coordinatorRemote = new SyncSagaCoordinator();
223
+ await senderPeerRemote.initializeOpts({
224
+ localMetaspace: metaspace,
225
+ localSpace: remoteSpace,
226
+ senderIdentity: domainI_latest,
227
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
228
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
229
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
230
+ targetAddrs: [domainAddr]
231
+ });
232
+ const syncSagaRemote = await coordinatorRemote.sync({
233
+ domainIbGibs: [r2_alpha_v1_remote],
234
+ senderIdentity: domainI_latest,
235
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
236
+ peer: senderPeerRemote,
237
+ localSpace: remoteSpace,
238
+ metaspace,
239
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
240
+ });
241
+ await syncSagaRemote.done;
242
+ devLog('4.10B Setup: ✓ Server tip is now remote Round 2 edit.');
243
+
244
+ // Fetch evolved user identity from remote space (n=3)
245
+ const domainLatestAddrRemote = await metaspace.getLatestAddr({ ibGib: domainI, space: remoteSpace });
246
+ if (!domainLatestAddrRemote) { throw new Error("Could not find latest evolved identity address in remote space."); }
247
+ const resGetLatestRemote = await metaspace.get({ addr: domainLatestAddrRemote, space: remoteSpace });
248
+ const domainI_latestRemote = resGetLatestRemote.ibGibs![0] as KeystoneIbGib_V1;
249
+
250
+ // Copy back to default space
251
+ await metaspace.put({ ibGib: domainI_latestRemote, space });
252
+ await metaspace.registerNewIbGib({ ibGib: domainI_latestRemote, space });
253
+ state.domainI_latest = domainI_latestRemote;
254
+
255
+ devLog('✓ 4.10B Setup Complete! Ready for 4.10B Sync (which runs Round 2, 3, and 4 in succession).');
256
+ btn.textContent = '✓ 4.10B Setup Complete';
257
+
258
+ const syncBtn = document.getElementById('btn-4-10b-sync') as HTMLButtonElement | null;
259
+ if (syncBtn) { syncBtn.disabled = false; }
260
+
261
+ } catch (error) {
262
+ devLog(`✗ 4.10B Setup FAILED: ${extractErrorMsg(error)}`);
263
+ console.error(error);
264
+ btn.disabled = false;
265
+ }
266
+ });
267
+ }
268
+ // #endregion Phase 4.10B Setup
269
+
270
+ let gammaAddr: IbGibAddr | undefined = undefined;
271
+ let betaAddr: IbGibAddr | undefined = undefined;
272
+ // #region Phase 4.10B Sync Execution
273
+ export function init4_10bSyncButton(): void {
274
+ const btn = document.getElementById('btn-4-10b-sync') as HTMLButtonElement | null;
275
+ if (!btn) { return; }
276
+ btn.addEventListener('click', async () => {
277
+ try {
278
+ btn.disabled = true;
279
+ devLog('4.10B Sync: Initiating WebSocket Sync for Round 2, 3, and 4 sequential evolution...');
280
+
281
+ let domainI = state.domainI_latest!;
282
+ const remoteSpace = state.remoteSpace;
283
+ const alpha_tjpAddr = state.alpha_tjpAddr!;
284
+ const beta_tjpAddr = state.beta_tjpAddr!;
285
+ const alpha_v0 = state.alpha_v0!;
286
+
287
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
288
+ const space = await metaspace.getLocalUserSpace({}) as any;
289
+ if (!space) { throw new Error("No default space."); }
290
+
291
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
292
+ const domainAddr = getIbGibAddr({ ibGib: state.domainI_initial! });
293
+
294
+ // ----------------------------------------------------
295
+ // #region Round 2 Sync
296
+ // ----------------------------------------------------
297
+ devLog('4.10B Sync: Running Round 2 Sync (merging divergent alpha edits)...');
298
+ const senderPeerR2 = new SyncPeerWebSocketSender_V1({
299
+ classname: 'SyncPeerWebSocketSender_V1',
300
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
301
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
302
+ });
303
+ const coordinatorR2 = new SyncSagaCoordinator();
304
+ await senderPeerR2.initializeOpts({
305
+ localMetaspace: metaspace,
306
+ localSpace: space,
307
+ senderIdentity: domainI,
308
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
309
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
310
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
311
+ targetAddrs: [domainAddr]
312
+ });
313
+ const syncSagaR2 = await coordinatorR2.sync({
314
+ domainIbGibs: [state.r2_alpha_v3_source_rel8beta],
315
+ senderIdentity: domainI,
316
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
317
+ peer: senderPeerR2,
318
+ localSpace: space,
319
+ metaspace,
320
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
321
+ });
322
+ await syncSagaR2.done;
323
+ devLog('4.10B Sync: ✓ Round 2 Sync complete.');
324
+
325
+ // Fetch evolved user identity (n=4)
326
+ let domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space });
327
+ let resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space });
328
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
329
+
330
+ // Copy to remote space
331
+ await metaspace.put({ ibGib: domainI, space: remoteSpace });
332
+ await metaspace.registerNewIbGib({ ibGib: domainI, space: remoteSpace });
333
+ // #endregion Round 2 Sync
334
+
335
+ // ----------------------------------------------------
336
+ // #region Round 3 Divergence & Sync
337
+ // ----------------------------------------------------
338
+ devLog('4.10B Sync: Preparing Round 3 divergence (multiple conflicting timelines)...');
339
+ // Client: Mutate alpha (fieldC), mutate beta (betaFieldA)
340
+ const clientKV_R2 = await coordinatorR2.getKnowledgeMap({
341
+ domainIbGibs: [state.r2_alpha_v3_source_rel8beta, state.r2_beta_v0_source],
342
+ space,
343
+ metaspace,
344
+ });
345
+ const alpha_tipAddr_R2_client = clientKV_R2[alpha_tjpAddr];
346
+ const beta_tipAddr_R2_client = clientKV_R2[beta_tjpAddr];
347
+
348
+ const resGetAlphaClientR2 = await metaspace.get({ addr: alpha_tipAddr_R2_client!, space });
349
+ const resGetBetaClientR2 = await metaspace.get({ addr: beta_tipAddr_R2_client!, space });
350
+
351
+ const r3_alpha_v4_source = await mut8Timeline({
352
+ timeline: resGetAlphaClientR2.ibGibs![0],
353
+ mut8Opts: {
354
+ mut8Ib: alpha_v0.ib + '_r3client',
355
+ dataToAddOrPatch: { fieldC: 'source_edit_r3' }
356
+ },
357
+ metaspace,
358
+ space,
359
+ });
360
+
361
+ const r3_beta_v1_source = await mut8Timeline({
362
+ timeline: resGetBetaClientR2.ibGibs![0],
363
+ mut8Opts: {
364
+ mut8Ib: state.r2_beta_v0_source.ib + '_r3client',
365
+ dataToAddOrPatch: { betaFieldA: 'source_beta_edit_r3' }
366
+ },
367
+ metaspace,
368
+ space,
369
+ });
370
+
371
+ // Remote: Sync remote to latest merged tips, then mutate alpha (fieldD), mutate beta (betaFieldB)
372
+ devLog('4.10B Sync: Updating remote space to latest merged tips...');
373
+ const senderPeerRemoteR3_Init = new SyncPeerWebSocketSender_V1({
374
+ classname: 'SyncPeerWebSocketSender_V1',
375
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
376
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
377
+ });
378
+ const coordinatorRemoteR3_Init = new SyncSagaCoordinator();
379
+ await senderPeerRemoteR3_Init.initializeOpts({
380
+ localMetaspace: metaspace,
381
+ localSpace: remoteSpace,
382
+ senderIdentity: domainI,
383
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
384
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
385
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
386
+ targetAddrs: [domainAddr]
387
+ });
388
+
389
+ // Query remoteSpace for its latest tips to initiate sync
390
+ const alphaLatestAddrRemoteR3 = await metaspace.getLatestAddr({ addr: alpha_tjpAddr, space: remoteSpace });
391
+ const resGetAlphaRemoteTipR3 = await metaspace.get({ addr: alphaLatestAddrRemoteR3!, space: remoteSpace });
392
+ const alphaRemoteTipR3 = resGetAlphaRemoteTipR3.ibGibs![0];
393
+
394
+ const domainIbGibsRemoteR3 = [alphaRemoteTipR3];
395
+
396
+ const betaLatestAddrRemoteR3 = await metaspace.getLatestAddr({ addr: beta_tjpAddr, space: remoteSpace });
397
+ if (betaLatestAddrRemoteR3) {
398
+ const resGetBetaRemoteTipR3 = await metaspace.get({ addr: betaLatestAddrRemoteR3, space: remoteSpace });
399
+ domainIbGibsRemoteR3.push(resGetBetaRemoteTipR3.ibGibs![0]);
400
+ }
401
+
402
+ // Sync remote space to pull merged alpha and beta from server
403
+ const syncSagaRemoteR3_Init = await coordinatorRemoteR3_Init.sync({
404
+ domainIbGibs: domainIbGibsRemoteR3,
405
+ senderIdentity: domainI,
406
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
407
+ peer: senderPeerRemoteR3_Init,
408
+ localSpace: remoteSpace,
409
+ metaspace,
410
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
411
+ });
412
+ await syncSagaRemoteR3_Init.done;
413
+
414
+ // Fetch evolved identity (n=5)
415
+ domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space: remoteSpace });
416
+ resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space: remoteSpace });
417
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
418
+ await metaspace.put({ ibGib: domainI, space });
419
+ await metaspace.registerNewIbGib({ ibGib: domainI, space });
420
+
421
+ // Now mutate alpha (fieldD) and beta (betaFieldB) in remote space
422
+ const remoteKV_R3 = await coordinatorRemoteR3_Init.getKnowledgeMap({ space: remoteSpace, metaspace, domainIbGibs: [alpha_v0, state.r2_beta_v0_source] });
423
+ const alpha_tipAddr_R2_remote = remoteKV_R3[alpha_tjpAddr];
424
+ const beta_tipAddr_R2_remote = remoteKV_R3[beta_tjpAddr];
425
+
426
+ const resGetAlphaRemoteR2 = await metaspace.get({ addr: alpha_tipAddr_R2_remote!, space: remoteSpace });
427
+ const resGetBetaRemoteR2 = await metaspace.get({ addr: beta_tipAddr_R2_remote!, space: remoteSpace });
428
+
429
+ const r3_alpha_v4_remote = await mut8Timeline({
430
+ timeline: resGetAlphaRemoteR2.ibGibs![0],
431
+ mut8Opts: {
432
+ mut8Ib: alpha_v0.ib + '_r3remote',
433
+ dataToAddOrPatch: { fieldD: 'remote_edit_r3' }
434
+ },
435
+ metaspace,
436
+ space: remoteSpace,
437
+ });
438
+
439
+ const r3_beta_v1_remote = await mut8Timeline({
440
+ timeline: resGetBetaRemoteR2.ibGibs![0],
441
+ mut8Opts: {
442
+ mut8Ib: state.r2_beta_v0_source.ib + '_r3remote',
443
+ dataToAddOrPatch: { betaFieldB: 'remote_beta_edit_r3' }
444
+ },
445
+ metaspace,
446
+ space: remoteSpace,
447
+ });
448
+
449
+ // Sync remote Round 3 edits to server
450
+ devLog('4.10B Sync: Pushing remote Round 3 edits to server...');
451
+ const senderPeerRemoteR3 = new SyncPeerWebSocketSender_V1({
452
+ classname: 'SyncPeerWebSocketSender_V1',
453
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
454
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
455
+ });
456
+ const coordinatorRemoteR3 = new SyncSagaCoordinator();
457
+ await senderPeerRemoteR3.initializeOpts({
458
+ localMetaspace: metaspace,
459
+ localSpace: remoteSpace,
460
+ senderIdentity: domainI,
461
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
462
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
463
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
464
+ targetAddrs: [domainAddr]
465
+ });
466
+ const syncSagaRemoteR3 = await coordinatorRemoteR3.sync({
467
+ domainIbGibs: [r3_alpha_v4_remote, r3_beta_v1_remote],
468
+ senderIdentity: domainI,
469
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
470
+ peer: senderPeerRemoteR3,
471
+ localSpace: remoteSpace,
472
+ metaspace,
473
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
474
+ });
475
+ await syncSagaRemoteR3.done;
476
+
477
+ // Fetch evolved identity (n=6)
478
+ domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space: remoteSpace });
479
+ resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space: remoteSpace });
480
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
481
+ await metaspace.put({ ibGib: domainI, space });
482
+ await metaspace.registerNewIbGib({ ibGib: domainI, space });
483
+
484
+ // Sync Client Round 3 edits to server (merges client and server)
485
+ devLog('4.10B Sync: Running Round 3 Client Sync (merging alpha & beta divergence)...');
486
+ const senderPeerR3 = new SyncPeerWebSocketSender_V1({
487
+ classname: 'SyncPeerWebSocketSender_V1',
488
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
489
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
490
+ });
491
+ const coordinatorR3 = new SyncSagaCoordinator();
492
+ await senderPeerR3.initializeOpts({
493
+ localMetaspace: metaspace,
494
+ localSpace: space,
495
+ senderIdentity: domainI,
496
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
497
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
498
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
499
+ targetAddrs: [domainAddr]
500
+ });
501
+ const syncSagaR3 = await coordinatorR3.sync({
502
+ domainIbGibs: [r3_alpha_v4_source, r3_beta_v1_source],
503
+ senderIdentity: domainI,
504
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
505
+ peer: senderPeerR3,
506
+ localSpace: space,
507
+ metaspace,
508
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
509
+ });
510
+ await syncSagaR3.done;
511
+ devLog('4.10B Sync: ✓ Round 3 Sync complete.');
512
+
513
+ // Fetch evolved identity (n=7)
514
+ domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space: space });
515
+ resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space });
516
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
517
+ await metaspace.put({ ibGib: domainI, space: remoteSpace });
518
+ await metaspace.registerNewIbGib({ ibGib: domainI, space: remoteSpace });
519
+ // #endregion Round 3 Divergence & Sync
520
+
521
+ // ----------------------------------------------------
522
+ // #region Round 4 Divergence & Sync
523
+ // ----------------------------------------------------
524
+ devLog('4.10B Sync: Preparing Round 4 divergence (post-graft chain edits & new timeline)...');
525
+ const clientKV_R3 = await coordinatorR3.getKnowledgeMap({ space, metaspace, domainIbGibs: [alpha_v0, state.r2_beta_v0_source] });
526
+ const alpha_tipAddr_R3_client = clientKV_R3[alpha_tjpAddr];
527
+ const beta_tipAddr_R3_client = clientKV_R3[beta_tjpAddr];
528
+
529
+ const resGetAlphaClientR3 = await metaspace.get({ addr: alpha_tipAddr_R3_client!, space });
530
+ const resGetBetaClientR3 = await metaspace.get({ addr: beta_tipAddr_R3_client!, space });
531
+
532
+ // Client: Mutate alpha (fieldE), create gamma, relate alpha to gamma (v6), mutate alpha (fieldF). Mutate beta (betaFieldD).
533
+ const r4_alpha_v5_source = await mut8Timeline({
534
+ timeline: resGetAlphaClientR3.ibGibs![0],
535
+ mut8Opts: {
536
+ mut8Ib: alpha_v0.ib + '_r4client_v5',
537
+ dataToAddOrPatch: { fieldE: 'source_edit_r4_v5' }
538
+ },
539
+ metaspace,
540
+ space,
541
+ });
542
+
543
+ // Create gamma
544
+ const resGamma = await factory.firstGen({
545
+ parentIbGib: ROOT,
546
+ ib: 'timeline_root_gamma_4_10b',
547
+ data: { gammaField: 'gamma created on client', label: 'GammaRoot', random: Math.random() },
548
+ dna: true,
549
+ nCounter: true,
550
+ tjp: { uuid: true, timestamp: true }
551
+ });
552
+ const gamma_v0_source = resGamma.newIbGib;
553
+ await metaspace.persistTransformResult({ resTransform: resGamma, space });
554
+ await metaspace.registerNewIbGib({ ibGib: gamma_v0_source, space });
555
+ const gamma_tjpAddr = getTjpAddr({ ibGib: gamma_v0_source, defaultIfNone: 'incomingAddr' })!;
556
+ state.gamma_tjpAddr = gamma_tjpAddr;
557
+
558
+ // Relate alpha to gamma
559
+ gammaAddr = getIbGibAddr({ ibGib: gamma_v0_source });
560
+ const r4_alpha_v6_source_rel8dGamma = await appendToTimeline({
561
+ timeline: r4_alpha_v5_source,
562
+ metaspace,
563
+ space,
564
+ rel8nInfos: [
565
+ { rel8nName: TEST_REL8N_NAME, ibGibs: [gamma_v0_source] }
566
+ ]
567
+ });
568
+
569
+ // Mutate alpha again (fieldF)
570
+ const r4_alpha_v7_source = await mut8Timeline({
571
+ timeline: r4_alpha_v6_source_rel8dGamma,
572
+ mut8Opts: {
573
+ mut8Ib: alpha_v0.ib + '_r4client_v7',
574
+ dataToAddOrPatch: { fieldF: 'source_edit_r4_v7' }
575
+ },
576
+ metaspace,
577
+ space,
578
+ });
579
+
580
+ // Mutate beta (betaFieldD)
581
+ const r4_beta_v2_source = await mut8Timeline({
582
+ timeline: resGetBetaClientR3.ibGibs![0],
583
+ mut8Opts: {
584
+ mut8Ib: state.r2_beta_v0_source.ib + '_r4client_v2',
585
+ dataToAddOrPatch: { betaFieldD: 'source_beta_edit_r4_v2' }
586
+ },
587
+ metaspace,
588
+ space,
589
+ });
590
+
591
+ // Remote: Sync remote to latest merged tips, then mutate alpha (fieldG), mutate beta (betaFieldC)
592
+ devLog('4.10B Sync: Updating remote space to Round 3 merged tips...');
593
+ const senderPeerRemoteR4_Init = new SyncPeerWebSocketSender_V1({
594
+ classname: 'SyncPeerWebSocketSender_V1',
595
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
596
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
597
+ });
598
+ const coordinatorRemoteR4_Init = new SyncSagaCoordinator();
599
+ await senderPeerRemoteR4_Init.initializeOpts({
600
+ localMetaspace: metaspace,
601
+ localSpace: remoteSpace,
602
+ senderIdentity: domainI,
603
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
604
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
605
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
606
+ targetAddrs: [domainAddr]
607
+ });
608
+
609
+ // Query remoteSpace for its latest tips to initiate sync
610
+ const alphaLatestAddrRemoteR4 = await metaspace.getLatestAddr({ addr: alpha_tjpAddr, space: remoteSpace });
611
+ const resGetAlphaRemoteTipR4 = await metaspace.get({ addr: alphaLatestAddrRemoteR4!, space: remoteSpace });
612
+ const alphaRemoteTipR4 = resGetAlphaRemoteTipR4.ibGibs![0];
613
+
614
+ const domainIbGibsRemoteR4 = [alphaRemoteTipR4];
615
+
616
+ const betaLatestAddrRemoteR4 = await metaspace.getLatestAddr({ addr: beta_tjpAddr, space: remoteSpace });
617
+ if (betaLatestAddrRemoteR4) {
618
+ const resGetBetaRemoteTipR4 = await metaspace.get({ addr: betaLatestAddrRemoteR4, space: remoteSpace });
619
+ domainIbGibsRemoteR4.push(resGetBetaRemoteTipR4.ibGibs![0]);
620
+ }
621
+
622
+ const syncSagaRemoteR4_Init = await coordinatorRemoteR4_Init.sync({
623
+ domainIbGibs: domainIbGibsRemoteR4,
624
+ senderIdentity: domainI,
625
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
626
+ peer: senderPeerRemoteR4_Init,
627
+ localSpace: remoteSpace,
628
+ metaspace,
629
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
630
+ });
631
+ await syncSagaRemoteR4_Init.done;
632
+
633
+
634
+ // Fetch evolved identity (n=8)
635
+ domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space: remoteSpace });
636
+ resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space: remoteSpace });
637
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
638
+ await metaspace.put({ ibGib: domainI, space });
639
+ await metaspace.registerNewIbGib({ ibGib: domainI, space });
640
+
641
+ // Mutate alpha (fieldG) and beta (betaFieldC) in remote space
642
+ const remoteKV_R4 = await coordinatorRemoteR4_Init.getKnowledgeMap({ space: remoteSpace, metaspace, domainIbGibs: [alpha_v0, state.r2_beta_v0_source] });
643
+ const alpha_tipAddr_R3_remote = remoteKV_R4[alpha_tjpAddr];
644
+ const beta_tipAddr_R3_remote = remoteKV_R4[beta_tjpAddr];
645
+
646
+ const resGetAlphaRemoteR3 = await metaspace.get({ addr: alpha_tipAddr_R3_remote!, space: remoteSpace });
647
+ const resGetBetaRemoteR3 = await metaspace.get({ addr: beta_tipAddr_R3_remote!, space: remoteSpace });
648
+
649
+ const r4_alpha_v5_remote = await mut8Timeline({
650
+ timeline: resGetAlphaRemoteR3.ibGibs![0],
651
+ mut8Opts: {
652
+ mut8Ib: alpha_v0.ib + '_r4remote_v5',
653
+ dataToAddOrPatch: { fieldG: 'remote_edit_r4_v5' }
654
+ },
655
+ metaspace,
656
+ space: remoteSpace,
657
+ });
658
+
659
+ const r4_beta_v2_remote = await mut8Timeline({
660
+ timeline: resGetBetaRemoteR3.ibGibs![0],
661
+ mut8Opts: {
662
+ mut8Ib: state.r2_beta_v0_source.ib + '_r4remote_v2',
663
+ dataToAddOrPatch: { betaFieldC: 'remote_beta_edit_r4_v2' }
664
+ },
665
+ metaspace,
666
+ space: remoteSpace,
667
+ });
668
+
669
+ // Sync remote edits to server
670
+ devLog('4.10B Sync: Pushing remote Round 4 edits to server...');
671
+ const senderPeerRemoteR4 = new SyncPeerWebSocketSender_V1({
672
+ classname: 'SyncPeerWebSocketSender_V1',
673
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
674
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
675
+ });
676
+ const coordinatorRemoteR4 = new SyncSagaCoordinator();
677
+ await senderPeerRemoteR4.initializeOpts({
678
+ localMetaspace: metaspace,
679
+ localSpace: remoteSpace,
680
+ senderIdentity: domainI,
681
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
682
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
683
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
684
+ targetAddrs: [domainAddr]
685
+ });
686
+ const syncSagaRemoteR4 = await coordinatorRemoteR4.sync({
687
+ domainIbGibs: [r4_alpha_v5_remote, r4_beta_v2_remote],
688
+ senderIdentity: domainI,
689
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
690
+ peer: senderPeerRemoteR4,
691
+ localSpace: remoteSpace,
692
+ metaspace,
693
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
694
+ });
695
+ await syncSagaRemoteR4.done;
696
+
697
+
698
+ // Fetch evolved identity (n=9)
699
+ domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space: remoteSpace });
700
+ resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space: remoteSpace });
701
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
702
+ await metaspace.put({ ibGib: domainI, space });
703
+ await metaspace.registerNewIbGib({ ibGib: domainI, space });
704
+
705
+ // Sync Client Round 4 edits to server (merges client and server final states)
706
+ devLog('4.10B Sync: Running final Round 4 Client Sync (merging alpha, beta, and propagating gamma)...');
707
+ // debugger; // walk through this, but a breakpoint on createInitFrame
708
+ const senderPeerR4 = new SyncPeerWebSocketSender_V1({
709
+ classname: 'SyncPeerWebSocketSender_V1',
710
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
711
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
712
+ });
713
+ const coordinatorR4 = new SyncSagaCoordinator();
714
+ await senderPeerR4.initializeOpts({
715
+ localMetaspace: metaspace,
716
+ localSpace: space,
717
+ senderIdentity: domainI,
718
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
719
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
720
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
721
+ targetAddrs: [domainAddr]
722
+ });
723
+ const syncSagaR4 = await coordinatorR4.sync({
724
+ domainIbGibs: [r4_alpha_v7_source, r4_beta_v2_source, gamma_v0_source],
725
+ senderIdentity: domainI,
726
+ fnSenderSecret: async () => 'test-sender-secret-phase4-10b',
727
+ peer: senderPeerR4,
728
+ localSpace: space,
729
+ metaspace,
730
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
731
+ });
732
+ await syncSagaR4.done;
733
+
734
+ // Fetch final evolved identity (n=10)
735
+ domainLatestAddr = await metaspace.getLatestAddr({ ibGib: state.domainI_initial!, space: space });
736
+ resGetLatest = await metaspace.get({ addr: domainLatestAddr!, space });
737
+ domainI = resGetLatest.ibGibs![0] as KeystoneIbGib_V1;
738
+ state.domainI_latest = domainI;
739
+
740
+ devLog('✓ 4.10B Sync: All rounds of divergent edits and merges complete!');
741
+ btn.textContent = '✓ 4.10B Sync Complete';
742
+
743
+ const checkBtn = document.getElementById('btn-4-10b-check') as HTMLButtonElement | null;
744
+ if (checkBtn) { checkBtn.disabled = false; }
745
+ // #endregion Round 4 Divergence & Sync
746
+
747
+ } catch (error) {
748
+ devLog(`✗ 4.10B Sync FAILED: ${extractErrorMsg(error)}`);
749
+ console.error(error);
750
+ btn.disabled = false;
751
+ }
752
+ });
753
+ }
754
+ // #endregion Phase 4.10B Sync Execution
755
+
756
+ // #region Phase 4.10B Check/Verification
757
+ export function init4_10bCheckButton(): void {
758
+ const btn = document.getElementById('btn-4-10b-check') as HTMLButtonElement | null;
759
+ if (!btn) { return; }
760
+ btn.addEventListener('click', async () => {
761
+ try {
762
+ btn.disabled = true;
763
+ devLog('4.10B Check: Verifying multi-round merged timelines on client and server...');
764
+
765
+ const domainI = state.domainI_latest!;
766
+ const alpha_tjpAddr = state.alpha_tjpAddr!;
767
+ const beta_tjpAddr = state.beta_tjpAddr!;
768
+ const gamma_tjpAddr = state.gamma_tjpAddr!;
769
+
770
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
771
+ const space = await metaspace.getLocalUserSpace({}) as any;
772
+ if (!space) { throw new Error("No default space."); }
773
+
774
+ const domainAddr = getIbGibAddr({ ibGib: state.domainI_initial! });
775
+
776
+ // 1. Get Client tips
777
+ const clientKV = await new SyncSagaCoordinator().getKnowledgeMap({
778
+ space,
779
+ metaspace,
780
+ domainIbGibs: [state.domainI_initial!] // Just a dummy domain to satisfy API or look up by root address
781
+ });
782
+
783
+ // Get absolute tips from space
784
+ const clientAlphaTipAddr = await metaspace.getLatestAddr({ addr: alpha_tjpAddr, space });
785
+ const clientBetaTipAddr = await metaspace.getLatestAddr({ addr: beta_tjpAddr, space });
786
+ const clientGammaTipAddr = await metaspace.getLatestAddr({ addr: gamma_tjpAddr, space });
787
+
788
+ if (!clientAlphaTipAddr || !clientBetaTipAddr || !clientGammaTipAddr) {
789
+ throw new Error("Client space is missing timeline tips for Alpha, Beta, or Gamma.");
790
+ }
791
+
792
+ const resGetAlpha = await metaspace.get({ addr: clientAlphaTipAddr, space });
793
+ const resGetBeta = await metaspace.get({ addr: clientBetaTipAddr, space });
794
+
795
+ const alphaTip = resGetAlpha.ibGibs![0];
796
+ const betaTip = resGetBeta.ibGibs![0];
797
+
798
+ // Assertions for Alpha data values
799
+ if (alphaTip.data?.fieldA !== 'source_edit_r2') {
800
+ throw new Error(`Alpha fieldA mismatch! Expected 'source_edit_r2', got '${alphaTip.data?.fieldA}'`);
801
+ }
802
+ if (alphaTip.data?.fieldB !== 'remote_edit_r2') {
803
+ throw new Error(`Alpha fieldB mismatch! Expected 'remote_edit_r2', got '${alphaTip.data?.fieldB}'`);
804
+ }
805
+ if (alphaTip.data?.fieldC !== 'source_edit_r3') {
806
+ throw new Error(`Alpha fieldC mismatch! Expected 'source_edit_r3', got '${alphaTip.data?.fieldC}'`);
807
+ }
808
+ if (alphaTip.data?.fieldD !== 'remote_edit_r3') {
809
+ throw new Error(`Alpha fieldD mismatch! Expected 'remote_edit_r3', got '${alphaTip.data?.fieldD}'`);
810
+ }
811
+ if (alphaTip.data?.fieldE !== 'source_edit_r4_v5') {
812
+ throw new Error(`Alpha fieldE mismatch! Expected 'source_edit_r4_v5', got '${alphaTip.data?.fieldE}'`);
813
+ }
814
+ if (alphaTip.data?.fieldF !== 'source_edit_r4_v7') {
815
+ throw new Error(`Alpha fieldF mismatch! Expected 'source_edit_r4_v7', got '${alphaTip.data?.fieldF}'`);
816
+ }
817
+ if (alphaTip.data?.fieldG !== 'remote_edit_r4_v5') {
818
+ throw new Error(`Alpha fieldG mismatch! Expected 'remote_edit_r4_v5', got '${alphaTip.data?.fieldG}'`);
819
+ }
820
+ devLog('4.10B Check: ✓ Client Alpha merged tip contains all Round 2, 3, and 4 edits.');
821
+
822
+ // Assertions for Beta data values
823
+ if (betaTip.data?.betaFieldA !== 'source_beta_edit_r3') {
824
+ throw new Error(`Beta betaFieldA mismatch! Expected 'source_beta_edit_r3', got '${betaTip.data?.betaFieldA}'`);
825
+ }
826
+ if (betaTip.data?.betaFieldB !== 'remote_beta_edit_r3') {
827
+ throw new Error(`Beta betaFieldB mismatch! Expected 'remote_beta_edit_r3', got '${betaTip.data?.betaFieldB}'`);
828
+ }
829
+ if (betaTip.data?.betaFieldC !== 'remote_beta_edit_r4_v2') {
830
+ throw new Error(`Beta betaFieldC mismatch! Expected 'remote_beta_edit_r4_v2', got '${betaTip.data?.betaFieldC}'`);
831
+ }
832
+ if (betaTip.data?.betaFieldD !== 'source_beta_edit_r4_v2') {
833
+ throw new Error(`Beta betaFieldD mismatch! Expected 'source_beta_edit_r4_v2', got '${betaTip.data?.betaFieldD}'`);
834
+ }
835
+ devLog('4.10B Check: ✓ Client Beta merged tip contains all Round 3 and 4 edits.');
836
+
837
+ // Verify alpha's relation to gamma
838
+ const rels = alphaTip.rel8ns?.[TEST_REL8N_NAME] || [];
839
+ if (!betaAddr) { throw new Error(`(UNEXPECTED) betaAddr falsy? (E: 81c032ae7a93bddfd8eed6664d314826)`); }
840
+ if (!rels.includes(betaAddr)) {
841
+ throw new Error("Client Alpha tip is missing relation to Beta timeline.");
842
+ }
843
+ if (!gammaAddr) { throw new Error(`(UNEXPECTED) gammaAddr falsy? (E: 12220a81d3b86385d1683d5a6bae5826)`); }
844
+ if (!rels.includes(gammaAddr)) {
845
+ throw new Error("Client Alpha tip is missing relation to Gamma timeline.");
846
+ }
847
+ devLog('4.10B Check: ✓ Client Alpha tip retains relation to Gamma.');
848
+
849
+ // 2. Check server tips
850
+ const apiBridge = new SpaceGibApiBridge();
851
+ devLog('4.10B Check: Fetching server timeline graphs...');
852
+ const resServerGraphAlpha = await apiBridge.getIbGibGraph(domainAddr, clientAlphaTipAddr, true);
853
+ const resServerGraphBeta = await apiBridge.getIbGibGraph(domainAddr, clientBetaTipAddr, true);
854
+
855
+ if (!resServerGraphAlpha.success || !resServerGraphAlpha.graph || !resServerGraphBeta.success || !resServerGraphBeta.graph) {
856
+ throw new Error("Failed to fetch merged graphs from server.");
857
+ }
858
+
859
+ const serverAlphaTip = resServerGraphAlpha.graph[clientAlphaTipAddr];
860
+ const serverBetaTip = resServerGraphBeta.graph[clientBetaTipAddr];
861
+
862
+ if (!serverAlphaTip || !serverBetaTip) {
863
+ throw new Error("Server is missing the final merged tips for Alpha or Beta.");
864
+ }
865
+
866
+ if (serverAlphaTip.data?.fieldF !== 'source_edit_r4_v7' || serverAlphaTip.data?.fieldG !== 'remote_edit_r4_v5') {
867
+ throw new Error("Server Alpha tip does not match final client merged state.");
868
+ }
869
+ if (serverBetaTip.data?.betaFieldC !== 'remote_beta_edit_r4_v2' || serverBetaTip.data?.betaFieldD !== 'source_beta_edit_r4_v2') {
870
+ throw new Error("Server Beta tip does not match final client merged state.");
871
+ }
872
+ devLog('4.10B Check: ✓ Server tips match client merged states perfectly.');
873
+
874
+ devLog('✓ 4.10B Check SUCCESS: Advanced multi-round/timeline conflict resolved and verified on both client and server!');
875
+ btn.textContent = '✓ 4.10B Check Success';
876
+
877
+ } catch (error) {
878
+ devLog(`✗ 4.10B Check FAILED: ${extractErrorMsg(error)}`);
879
+ console.error(error);
880
+ btn.disabled = false;
881
+ }
882
+ });
883
+ }
884
+ // #endregion Phase 4.10B Check/Verification