import { extractErrorMsg, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs'; import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/factory.mjs'; import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs'; import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs'; import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs'; import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs"; import { mut8Timeline, appendToTimeline } from '@ibgib/core-gib/dist/timeline/timeline-api.mjs'; import { getTjpAddr } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs'; 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'; import { SpaceGibApiBridge } from '../api/space-gib-api-bridge.mjs'; import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordinator.mjs'; import { debugState, devLog, lc, setupDomainAndDelegateIdentities, runSyncPass, runSyncPassFromSpace, copyIdentitiesToSpace } from './common.mjs'; interface Phase48State { domainI_latest?: KeystoneIbGib_V1; testRoot?: any; targetAlphaV3a?: any; targetBetaV0?: any; } const state: Phase48State = {}; export function init4_8bSetupButton(): void { const btn = document.getElementById('btn-4-8b-setup') as HTMLButtonElement | null; if (!btn) { return; } btn.addEventListener('click', async () => { try { btn.disabled = true; devLog('4.8B Setup: Setting up identities, seeding history, and creating divergent branches in client and remote...'); const metaspace = await getGlobalMetaspace_waitIfNeeded(); const space = await metaspace.getLocalUserSpace({}) as any; if (!space) { throw new Error("No default space."); } const apiBridge = new SpaceGibApiBridge(); // 1. Create domain + delegate identities (DRY helper) await setupDomainAndDelegateIdentities({ masterSecret: 'test-sender-secret-phase4-8b', syncSalt: 'senderidentitysyncsaltphase4-8b', manageSalt: 'senderidentitymanagesaltphase4-8b', metaspace, space, apiBridge, phaseText: '4.8B' }); const domainI = debugState.domainI!; state.domainI_latest = domainI; const domainAddr = getIbGibAddr({ ibGib: domainI }); // 2. Create the divergent "remote" space devLog('4.8B Setup: Creating temporary remote space...'); const remoteSpace = await metaspace.createNewLocalSpace({ opts: { allowCancel: false, spaceName: 'remote_space_4_8b', getFnPrompt: metaspace.getFnPrompt! } }) as any; await remoteSpace.initialized; // Copy user identity and delegate to remote space await copyIdentitiesToSpace({ domainI, metaspace, fromSpace: space, toSpace: remoteSpace }); // 3. Seed common history root -> v1 locally const resRoot = await factory.firstGen({ parentIbGib: ROOT, ib: 'timeline_root_conflict_4_8b', data: { type: 'root', label: 'CommonRoot', random: Math.random() }, dna: true, nCounter: true, tjp: { uuid: true, timestamp: true } }); const testRoot = resRoot.newIbGib; await metaspace.persistTransformResult({ resTransform: resRoot, space }); await metaspace.registerNewIbGib({ ibGib: testRoot, space }); state.testRoot = testRoot; const v1_common = await mut8Timeline({ timeline: testRoot, mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1Common' } }, metaspace, space, }); // 4. Copy common history to remote space const graphRoot = await metaspace.getDependencyGraph({ ibGibAddr: getIbGibAddr({ ibGib: testRoot }), space }); const graphV1 = await metaspace.getDependencyGraph({ ibGibAddr: getIbGibAddr({ ibGib: v1_common }), space }); const commonDepGraph = { ...graphRoot, ...graphV1 }; await metaspace.put({ ibGibs: Object.values(commonDepGraph), space: remoteSpace }); await metaspace.registerNewIbGib({ ibGib: testRoot, space: remoteSpace }); await metaspace.registerNewIbGib({ ibGib: v1_common, space: remoteSpace }); // 5. Initial sync of v1_common to the server so server has it devLog('4.8B Setup: Syncing v1_common to server...'); await runSyncPass({ domainIbGibs: [v1_common], passLabel: 'Seed v1_common', metaspace, space }); devLog('4.8B Setup: ✓ Seeding complete on server.'); // Copy evolved delegate and domain tip to remoteSpace await copyIdentitiesToSpace({ domainI: debugState.domainI!, metaspace, fromSpace: space, toSpace: remoteSpace }); // 6. Create Divergence: // Client (default space) gets V2a (fieldA edit + relates newly created independent timeline Beta) const resBeta = await factory.firstGen({ parentIbGib: ROOT, ib: 'beta_multitimelines_withid_4_8b', data: { fieldA: 'beta field A created on source' }, dna: true, nCounter: true, tjp: { uuid: true, timestamp: true } }); const beta_v0 = resBeta.newIbGib; await metaspace.persistTransformResult({ resTransform: resBeta, space }); await metaspace.registerNewIbGib({ ibGib: beta_v0, space }); state.targetBetaV0 = beta_v0; devLog(`4.8B Setup: ✓ Created client independent related timeline Beta: ${getIbGibAddr({ ibGib: beta_v0 })}`); const alpha_v2_source_sansBeta = await mut8Timeline({ timeline: v1_common, mut8Opts: { dataToAddOrPatch: { fieldA: 'source_edit' } }, metaspace, space, }); const alpha_v3_source_rel8beta = await appendToTimeline({ timeline: alpha_v2_source_sansBeta, metaspace, space, rel8nInfos: [ { rel8nName: 'testrel8n', ibGibs: [beta_v0] } ], }); state.targetAlphaV3a = alpha_v3_source_rel8beta; devLog(`4.8B Setup: ✓ Created client divergent edit V3a: ${getIbGibAddr({ ibGib: alpha_v3_source_rel8beta })}`); // Remote Space gets V2b (fieldB edit) const v2b = await mut8Timeline({ timeline: v1_common, mut8Opts: { dataToAddOrPatch: { fieldB: 'dest_edit' } }, metaspace, space: remoteSpace, }); devLog(`4.8B Setup: ✓ Created remote divergent edit V2b: ${getIbGibAddr({ ibGib: v2b })}`); // 7. Sync V2b from remote space to server, so server tip becomes V2b devLog('4.8B Setup: Pushing V2b from remote space to server...'); await runSyncPassFromSpace({ domainIbGibs: [v2b], passLabel: 'Push V2b from remoteSpace', metaspace, space: remoteSpace, primarySpace: space, domainI: debugState.domainI!, masterSecret: 'test-sender-secret-phase4-8b' }); state.domainI_latest = debugState.domainI; devLog('4.8B Setup: ✓ Server tip is now V2b. Evolved identity copied back.'); devLog('✓ 4.8B Setup Complete! Ready for 4.8B Sync.'); btn.textContent = '✓ 4.8B Setup Complete'; const syncBtn = document.getElementById('btn-4-8b-sync') as HTMLButtonElement | null; if (syncBtn) { syncBtn.disabled = false; } } catch (error) { devLog(`✗ 4.8B Setup FAILED: ${extractErrorMsg(error)}`); console.error(error); btn.disabled = false; } }); } export function init4_8bSyncButton(): void { const btn = document.getElementById('btn-4-8b-sync') as HTMLButtonElement | null; if (!btn) { return; } btn.addEventListener('click', async () => { try { btn.disabled = true; devLog('4.8B Sync: Initiating WebSocket Sync for divergent edit V3a (with related timeline Beta)...'); const domainI = state.domainI_latest; const targetAlphaV3a = state.targetAlphaV3a; if (!domainI || !targetAlphaV3a) { devLog('⚠ 4.8B Sync: Missing setup state. Please run Setup first.'); btn.disabled = false; return; } const metaspace = await getGlobalMetaspace_waitIfNeeded(); const space = await metaspace.getLocalUserSpace({}) as any; if (!space) { throw new Error("No default space."); } await runSyncPass({ domainIbGibs: [targetAlphaV3a], passLabel: 'Sync V3a and merge', metaspace, space }); state.domainI_latest = debugState.domainI; devLog('✓ 4.8B Sync Execution Complete! Ready for 4.8B Check.'); btn.textContent = '✓ 4.8B Sync Run'; const checkBtn = document.getElementById('btn-4-8b-check') as HTMLButtonElement | null; if (checkBtn) { checkBtn.disabled = false; } } catch (error) { devLog(`✗ 4.8B Sync FAILED: ${extractErrorMsg(error)}`); console.error(error); btn.disabled = false; } }); } export function init4_8bCheckButton(): void { const btn = document.getElementById('btn-4-8b-check') as HTMLButtonElement | null; if (!btn) { return; } btn.addEventListener('click', async () => { try { btn.disabled = true; devLog('4.8B Check: Verifying merged tip (V4) contains both edits on client and server...'); const domainI = state.domainI_latest; const testRoot = state.testRoot; const targetAlphaV3a = state.targetAlphaV3a; const targetBetaV0 = state.targetBetaV0; if (!domainI || !testRoot || !targetAlphaV3a || !targetBetaV0) { devLog('⚠ 4.8B Check: Missing state. Did Setup and Sync run?'); btn.disabled = false; return; } const domainAddr = getIbGibAddr({ ibGib: domainI }); const tjpAddr = getTjpAddr({ ibGib: testRoot, defaultIfNone: 'incomingAddr' }) ?? getIbGibAddr({ ibGib: testRoot }); const betaAddr = getIbGibAddr({ ibGib: targetBetaV0 }); const metaspace = await getGlobalMetaspace_waitIfNeeded(); const space = await metaspace.getLocalUserSpace({}) as any; if (!space) { throw new Error("No default space."); } // 1. Check client default space tip const clientKV = await metaspace.getLocalUserSpace({}).then((space: any) => { return new SyncSagaCoordinator().getKnowledgeMap({ space, metaspace, domainIbGibs: [testRoot] }); }); const clientTipAddr = clientKV[tjpAddr]; devLog(`4.8B Check: Client tip address: ${clientTipAddr}`); if (!clientTipAddr) { throw new Error("Client default space is missing the timeline tip."); } const resTip = await metaspace.get({ addr: clientTipAddr, space }); if (!resTip.success || !resTip.ibGibs || resTip.ibGibs.length === 0) { throw new Error("Client tip not found in default local space."); } const clientTip = resTip.ibGibs[0]; if (!clientTip.data || !clientTip.data.fieldA || !clientTip.data.fieldB) { throw new Error(`Merged client tip is missing expected data edits: fieldA: ${clientTip.data?.fieldA}, fieldB: ${clientTip.data?.fieldB}`); } devLog('4.8B Check: ✓ Client tip has both fieldA and fieldB edits.'); // Verify graftinfo rel8n exists const graftInfoRel = clientTip.rel8ns?.[GRAFT_INFO_REL8N_NAME]; if (!graftInfoRel?.[0]) { throw new Error("Client tip is missing graftinfo relations."); } const resGraft = await metaspace.get({ addr: graftInfoRel[0], space }); if (!resGraft.success || !resGraft.ibGibs || resGraft.ibGibs.length === 0) { throw new Error("GraftInfo not found in local space."); } const graftInfo = resGraft.ibGibs[0]; const baseRel = graftInfo.rel8ns?.[GRAFT_BASE_REL8N_NAME]; const orphanRel = graftInfo.rel8ns?.[GRAFT_ORPHAN_REL8N_NAME]; if (!baseRel || !orphanRel) { throw new Error("GraftInfo is missing graftbase or graftorphan relations."); } devLog('4.8B Check: ✓ Client graft base and orphan relations are correctly set.'); // 2. Check server tip const apiBridge = new SpaceGibApiBridge(); devLog('4.8B Check: Fetching server timeline graph...'); const resServerGraph = await apiBridge.getIbGibGraph(domainAddr, clientTipAddr, true); if (!resServerGraph.success || !resServerGraph.graph) { throw new Error(`Failed to fetch merged graph from server: ${resServerGraph.message}`); } const serverTip = resServerGraph.graph[clientTipAddr]; if (!serverTip) { throw new Error("Merged V4 tip is not present on the server!"); } if (serverTip.data?.fieldA !== 'source_edit' || serverTip.data?.fieldB !== 'dest_edit') { throw new Error("Server tip data edits do not match merged values."); } devLog('4.8B Check: ✓ Server tip has matching merged edits.'); // 3. Verify related timeline Beta exists on the server! devLog('4.8B Check: Verifying related timeline Beta is present on the server...'); const resBetaGraph = await apiBridge.getIbGibGraph(domainAddr, betaAddr, true); if (!resBetaGraph.success || !resBetaGraph.graph || !resBetaGraph.graph[betaAddr]) { throw new Error(`Related timeline Beta (${betaAddr}) is missing from the server!`); } devLog('4.8B Check: ✓ Related timeline Beta successfully verified on the server.'); devLog('✓ 4.8B Check SUCCESS: Divergent branches merged successfully and related timeline synced on server!'); btn.textContent = '✓ 4.8B Check Success'; } catch (error) { devLog(`✗ 4.8B Check FAILED: ${extractErrorMsg(error)}`); console.error(error); btn.disabled = false; } }); }