@ibgib/space-gib 0.0.1 → 0.0.2
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.
- package/dist/client/bootstrap.mjs +1 -1
- package/dist/client/bootstrap.mjs.map +1 -1
- package/dist/client/chunk-BL2SGXS4.mjs +18994 -0
- package/dist/client/chunk-RDTAT5G4.mjs +235 -0
- package/dist/client/chunk-RDTAT5G4.mjs.map +7 -0
- package/dist/client/chunk-RE7XSMHH.mjs +31 -0
- package/dist/client/chunk-RE7XSMHH.mjs.map +7 -0
- package/dist/client/chunk-YUSGN3J4.mjs +23119 -0
- package/dist/client/index.html +44 -8
- package/dist/client/index.mjs +1 -1
- package/dist/client/script.mjs +1 -1
- package/dist/client/style.css +26 -0
- package/dist/server/server.mjs +5323 -1011
- package/dist/server/server.mjs.map +4 -4
- package/package.json +1 -1
- package/src/client/AUTO-GENERATED-version.mts +1 -1
- package/src/client/api/space-gib-api-bridge.mts +84 -8
- package/src/client/dev-tools.mts +609 -24
- package/src/client/index.html +44 -8
- package/src/client/style.css +26 -0
- package/src/common/keystone-policies.json +64 -0
- package/src/common/keystone-policies.mts +39 -86
- package/src/server/serve-gib/handlers/api/debug/ws-echo.handler.mts +13 -12
- package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +14 -167
- package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +6 -6
- package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +10 -25
- package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +201 -0
- package/src/server/serve-gib/handlers/ws/sync-upgrade.handler.mts +13 -487
- package/src/server/serve-gib/handlers/ws/ws-helper.mts +80 -3
- package/dist/client/chunk-CT47Z5WU.mjs +0 -21
- package/dist/client/chunk-CT47Z5WU.mjs.map +0 -7
- package/dist/client/chunk-RHEDTRKF.mjs +0 -235
- package/dist/client/chunk-RHEDTRKF.mjs.map +0 -7
- package/dist/respec-gib.node.mjs +0 -5
package/src/client/dev-tools.mts
CHANGED
|
@@ -10,21 +10,25 @@
|
|
|
10
10
|
* working end-to-end.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { extractErrorMsg, getTimestamp, getUUID,
|
|
13
|
+
import { extractErrorMsg, getTimestamp, getUUID, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
14
|
import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/factory.mjs';
|
|
15
15
|
import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
|
|
16
16
|
import { getIbGibAddr, } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
17
17
|
import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
18
18
|
import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
|
|
19
|
-
import {
|
|
19
|
+
import { KeystoneIbGib_V1, KeystoneReplenishStrategy } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
|
|
20
20
|
import { createManagePoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
|
|
21
|
+
import { KeystoneStrategyFactory } from '@ibgib/core-gib/dist/keystone/strategy/keystone-strategy-factory.mjs';
|
|
22
|
+
import { SyncPeerWebSocketSender_V1 } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs';
|
|
23
|
+
import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordinator.mjs';
|
|
24
|
+
import { KEYSTONE_VERB_CONNECT, POOL_ID_CONNECT, POOL_ID_SYNC } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
|
|
21
25
|
import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
|
|
22
26
|
|
|
23
27
|
import { GLOBAL_LOG_A_LOT } from './constants.mjs';
|
|
24
|
-
import {
|
|
25
|
-
|
|
28
|
+
import {
|
|
29
|
+
SESSION_KEYSTONE_POLICY, getConnectChallenge, getSpaceGibPoolConfig
|
|
30
|
+
} from "../common/keystone-policies.mjs";
|
|
26
31
|
import { SpaceGibApiBridge } from './api/space-gib-api-bridge.mjs';
|
|
27
|
-
import { KeystoneVerb } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
|
|
28
32
|
|
|
29
33
|
const logalot = GLOBAL_LOG_A_LOT
|
|
30
34
|
const lc = '[dev-tools]';
|
|
@@ -35,6 +39,8 @@ interface DebugState {
|
|
|
35
39
|
targetX?: IbGib_V1;
|
|
36
40
|
sessionS?: KeystoneIbGib_V1;
|
|
37
41
|
sessionSecret?: string;
|
|
42
|
+
senderPeer?: SyncPeerWebSocketSender_V1;
|
|
43
|
+
sagaId?: string;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
export const debugState: DebugState = {};
|
|
@@ -276,19 +282,19 @@ function initCreateSessionKeystoneButton(): void {
|
|
|
276
282
|
allowedVerbs: [],
|
|
277
283
|
},
|
|
278
284
|
{
|
|
279
|
-
id: SESSION_KEYSTONE_POLICY.
|
|
285
|
+
id: SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID,
|
|
280
286
|
salt: await getUUID(),
|
|
281
287
|
type: SESSION_KEYSTONE_POLICY.COMMON.TYPE,
|
|
282
288
|
algo: SESSION_KEYSTONE_POLICY.COMMON.ALGO,
|
|
283
289
|
rounds: SESSION_KEYSTONE_POLICY.COMMON.ROUNDS,
|
|
284
290
|
behavior: {
|
|
285
|
-
size: SESSION_KEYSTONE_POLICY.
|
|
291
|
+
size: SESSION_KEYSTONE_POLICY.CONNECT_POOL.SIZE,
|
|
286
292
|
replenish: SESSION_KEYSTONE_POLICY.COMMON.REPLENISH,
|
|
287
|
-
selectSequentially: SESSION_KEYSTONE_POLICY.
|
|
288
|
-
selectRandomly: SESSION_KEYSTONE_POLICY.
|
|
289
|
-
targetBindingChars: SESSION_KEYSTONE_POLICY.
|
|
293
|
+
selectSequentially: SESSION_KEYSTONE_POLICY.CONNECT_POOL.SELECT_SEQUENTIALLY,
|
|
294
|
+
selectRandomly: SESSION_KEYSTONE_POLICY.CONNECT_POOL.SELECT_RANDOMLY,
|
|
295
|
+
targetBindingChars: SESSION_KEYSTONE_POLICY.CONNECT_POOL.TARGET_BINDING_CHARS,
|
|
290
296
|
},
|
|
291
|
-
allowedVerbs: [SESSION_KEYSTONE_POLICY.
|
|
297
|
+
allowedVerbs: [SESSION_KEYSTONE_POLICY.CONNECT_POOL.VERB],
|
|
292
298
|
}
|
|
293
299
|
],
|
|
294
300
|
metaspace,
|
|
@@ -394,7 +400,7 @@ function initEvolveDomainKeystoneButton(): void {
|
|
|
394
400
|
}
|
|
395
401
|
|
|
396
402
|
// ---------------------------------------------------------------------------
|
|
397
|
-
// Perform Sync
|
|
403
|
+
// Perform Sync Connect (S) button
|
|
398
404
|
// ---------------------------------------------------------------------------
|
|
399
405
|
|
|
400
406
|
function initPerformSyncButton(): void {
|
|
@@ -406,7 +412,7 @@ function initPerformSyncButton(): void {
|
|
|
406
412
|
try {
|
|
407
413
|
debugger; // walk through sync
|
|
408
414
|
btn.disabled = true;
|
|
409
|
-
devLog('Initiating Sync
|
|
415
|
+
devLog('Initiating Sync Connect via WebSocket...');
|
|
410
416
|
|
|
411
417
|
const sessionS = debugState.sessionS;
|
|
412
418
|
const sessionSecret = debugState.sessionSecret;
|
|
@@ -423,17 +429,17 @@ function initPerformSyncButton(): void {
|
|
|
423
429
|
if (!space) { throw new Error("No space."); }
|
|
424
430
|
|
|
425
431
|
devLog('Pre-solving upfront picket-fence challenge...');
|
|
426
|
-
const
|
|
427
|
-
if (!
|
|
428
|
-
throw new Error(`Session keystone missing "${SESSION_KEYSTONE_POLICY.
|
|
432
|
+
const connectPool = (sessionS.data?.challengePools ?? []).find(p => p.id === SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID);
|
|
433
|
+
if (!connectPool) {
|
|
434
|
+
throw new Error(`Session keystone missing "${SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID}" pool`);
|
|
429
435
|
}
|
|
430
436
|
|
|
431
|
-
const { challengeId } =
|
|
432
|
-
const strategy = KeystoneStrategyFactory.create({ config:
|
|
437
|
+
const { challengeId } = getConnectChallenge(sessionS);
|
|
438
|
+
const strategy = KeystoneStrategyFactory.create({ config: connectPool.config });
|
|
433
439
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret: sessionSecret });
|
|
434
440
|
const solution = await strategy.generateSolution({
|
|
435
441
|
poolSecret,
|
|
436
|
-
poolId: SESSION_KEYSTONE_POLICY.
|
|
442
|
+
poolId: SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID,
|
|
437
443
|
challengeId
|
|
438
444
|
});
|
|
439
445
|
|
|
@@ -464,16 +470,16 @@ function initPerformSyncButton(): void {
|
|
|
464
470
|
}));
|
|
465
471
|
} else if (msg.type === 'auth-challenge') {
|
|
466
472
|
const { challengeUuid, demandedIds } = msg;
|
|
467
|
-
devLog(`→ server demanded ${demandedIds.length} ids. Signing
|
|
473
|
+
devLog(`→ server demanded ${demandedIds.length} ids. Signing connect proof...`);
|
|
468
474
|
|
|
469
475
|
const keystoneService = new KeystoneService_V1();
|
|
470
476
|
const proofFrame = await keystoneService.sign({
|
|
471
477
|
latestKeystone: sessionS,
|
|
472
478
|
masterSecret: sessionSecret,
|
|
473
|
-
poolId:
|
|
479
|
+
poolId: POOL_ID_CONNECT,
|
|
474
480
|
requiredChallengeIds: demandedIds,
|
|
475
481
|
claim: {
|
|
476
|
-
verb:
|
|
482
|
+
verb: KEYSTONE_VERB_CONNECT,
|
|
477
483
|
target: challengeUuid
|
|
478
484
|
},
|
|
479
485
|
metaspace,
|
|
@@ -486,10 +492,10 @@ function initPerformSyncButton(): void {
|
|
|
486
492
|
proofFrame
|
|
487
493
|
}));
|
|
488
494
|
} else if (msg.type === 'auth-ok') {
|
|
489
|
-
devLog('✓
|
|
495
|
+
devLog('✓ Connect SUCCESS! Sync session authorized.');
|
|
490
496
|
btn.textContent = '✓ Sync Authorized';
|
|
491
497
|
} else if (msg.type === 'auth-fail') {
|
|
492
|
-
devLog(`✗
|
|
498
|
+
devLog(`✗ Connect FAILED: ${msg.message}`);
|
|
493
499
|
btn.disabled = false;
|
|
494
500
|
}
|
|
495
501
|
} catch (error) {
|
|
@@ -517,6 +523,578 @@ function initPerformSyncButton(): void {
|
|
|
517
523
|
});
|
|
518
524
|
}
|
|
519
525
|
|
|
526
|
+
// ---------------------------------------------------------------------------
|
|
527
|
+
// Phase 1B: Standardized Setup / Sync / Check testing loop
|
|
528
|
+
// ---------------------------------------------------------------------------
|
|
529
|
+
|
|
530
|
+
function init1bSetupButton(): void {
|
|
531
|
+
const lc_fn = `${lc}[init1bSetupButton]`;
|
|
532
|
+
const btn = document.getElementById('btn-1b-setup') as HTMLButtonElement | null;
|
|
533
|
+
if (!btn) return;
|
|
534
|
+
|
|
535
|
+
btn.addEventListener('click', async () => {
|
|
536
|
+
try {
|
|
537
|
+
btn.disabled = true;
|
|
538
|
+
devLog('1B Setup: Generating Alice\'s long-lived Domain Keystone (I) and Target (X)...');
|
|
539
|
+
|
|
540
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
541
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
542
|
+
if (!space) { throw new Error("No default space."); }
|
|
543
|
+
|
|
544
|
+
const masterSecret = 'test-sender-secret-phase1';
|
|
545
|
+
const keystoneService = new KeystoneService_V1();
|
|
546
|
+
|
|
547
|
+
// 1. Create Genesis Domain Keystone (I^Itjp) in local sourceSpace
|
|
548
|
+
const domainI = await keystoneService.genesis({
|
|
549
|
+
masterSecret,
|
|
550
|
+
configs: [
|
|
551
|
+
getSpaceGibPoolConfig('sync', 'senderidentitysyncsaltphase1'),
|
|
552
|
+
getSpaceGibPoolConfig('manage', 'senderidentitymanagesaltphase1'),
|
|
553
|
+
],
|
|
554
|
+
metaspace,
|
|
555
|
+
space,
|
|
556
|
+
frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
debugState.domainI = domainI;
|
|
560
|
+
debugState.domainIMasterSecret = masterSecret;
|
|
561
|
+
|
|
562
|
+
devLog(`1B Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
|
|
563
|
+
devLog(`1B Setup: Starting postGenesisKeystone...`);
|
|
564
|
+
|
|
565
|
+
// 2. Post Genesis Domain Keystone to receiver/server
|
|
566
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
567
|
+
const resGenesis = await apiBridge.postGenesisKeystone(domainI);
|
|
568
|
+
if (resGenesis.success) {
|
|
569
|
+
devLog(`1B Setup: ✓ Domain Keystone registered and stored on receiver (server).`);
|
|
570
|
+
} else {
|
|
571
|
+
devLog(`1B Setup: X postGenesisKeystone failed: ${resGenesis.message}.`);
|
|
572
|
+
throw new Error(`1B Setup: Server rejected genesis domain keystone: ${resGenesis.message}`);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// 3. Create Test IbGib (X) locally
|
|
576
|
+
const resX = await factory.firstGen({
|
|
577
|
+
parentIbGib: ROOT,
|
|
578
|
+
ib: 'test data',
|
|
579
|
+
data: { hello: 'world', random: Math.random() },
|
|
580
|
+
dna: true,
|
|
581
|
+
nCounter: true,
|
|
582
|
+
tjp: { uuid: true, timestamp: true }
|
|
583
|
+
});
|
|
584
|
+
debugState.targetX = resX.newIbGib;
|
|
585
|
+
devLog(`1B Setup: ✓ Target (X) created locally: ${getIbGibAddr({ ibGib: resX.newIbGib })}`);
|
|
586
|
+
|
|
587
|
+
devLog('✓ 1B Setup Complete! Ready for 1B Sync.');
|
|
588
|
+
btn.textContent = '✓ 1B Setup Complete';
|
|
589
|
+
|
|
590
|
+
// Enable next button in the phase flow
|
|
591
|
+
const syncBtn = document.getElementById('btn-1b-sync') as HTMLButtonElement | null;
|
|
592
|
+
if (syncBtn) syncBtn.disabled = false;
|
|
593
|
+
|
|
594
|
+
} catch (error) {
|
|
595
|
+
devLog(`✗ 1B Setup FAILED: ${extractErrorMsg(error)}`);
|
|
596
|
+
console.error(`${lc_fn} 1B Setup error:`, error);
|
|
597
|
+
btn.disabled = false;
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function init1bSyncButton(): void {
|
|
603
|
+
const lc_fn = `${lc}[init1bSyncButton]`;
|
|
604
|
+
const btn = document.getElementById('btn-1b-sync') as HTMLButtonElement | null;
|
|
605
|
+
if (!btn) return;
|
|
606
|
+
|
|
607
|
+
btn.addEventListener('click', async () => {
|
|
608
|
+
try {
|
|
609
|
+
btn.disabled = true;
|
|
610
|
+
devLog('1B Sync: Executing senderCoordinator.sync(...) using WebSocket Peer...');
|
|
611
|
+
|
|
612
|
+
const domainI = debugState.domainI;
|
|
613
|
+
const targetX = debugState.targetX;
|
|
614
|
+
if (!domainI || !targetX) {
|
|
615
|
+
devLog('⚠ 1B Sync: Missing setup state. Please run Setup first.');
|
|
616
|
+
btn.disabled = false;
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
621
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
622
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
623
|
+
if (!space) { throw new Error("No default space."); }
|
|
624
|
+
|
|
625
|
+
// Instantiate WebSocket sender peer
|
|
626
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
627
|
+
const senderPeer = new SyncPeerWebSocketSender_V1({
|
|
628
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
629
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
630
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const coordinator = new SyncSagaCoordinator();
|
|
634
|
+
const sagaId = await getUUID();
|
|
635
|
+
debugState.sagaId = sagaId;
|
|
636
|
+
debugState.senderPeer = senderPeer;
|
|
637
|
+
|
|
638
|
+
await senderPeer.initializeOpts({
|
|
639
|
+
localMetaspace: metaspace,
|
|
640
|
+
localSpace: space,
|
|
641
|
+
senderIdentity: domainI,
|
|
642
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
643
|
+
sagaId,
|
|
644
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
645
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
646
|
+
targetAddrs: [domainAddr]
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
try {
|
|
650
|
+
// Execute sync saga — triggers establishSessionIdentity internally!
|
|
651
|
+
const syncSaga = await coordinator.sync({
|
|
652
|
+
domainIbGibs: [targetX],
|
|
653
|
+
senderIdentity: domainI,
|
|
654
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
655
|
+
peer: senderPeer,
|
|
656
|
+
localSpace: space,
|
|
657
|
+
metaspace,
|
|
658
|
+
conflictStrategy: 'optimisticWithLCS' as any
|
|
659
|
+
});
|
|
660
|
+
await syncSaga.done;
|
|
661
|
+
} catch (err) {
|
|
662
|
+
// connection/sync error is fine/expected right now since connection is in Phase 2
|
|
663
|
+
devLog(`1B Sync: Coordinator sync executed (establish phase successfully run; connection/sync errors expected in 1B): ${extractErrorMsg(err)}`);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
devLog('✓ 1B Sync Execution Complete! Ready for State Checks.');
|
|
667
|
+
btn.textContent = '✓ 1B Sync Run';
|
|
668
|
+
|
|
669
|
+
// Enable check button
|
|
670
|
+
const checkBtn = document.getElementById('btn-1b-check') as HTMLButtonElement | null;
|
|
671
|
+
if (checkBtn) checkBtn.disabled = false;
|
|
672
|
+
|
|
673
|
+
} catch (error) {
|
|
674
|
+
devLog(`✗ 1B Sync FAILED: ${extractErrorMsg(error)}`);
|
|
675
|
+
console.error(`${lc_fn} 1B Sync error:`, error);
|
|
676
|
+
btn.disabled = false;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function init1bCheckButton(): void {
|
|
682
|
+
const lc_fn = `${lc}[init1bCheckButton]`;
|
|
683
|
+
const btn = document.getElementById('btn-1b-check') as HTMLButtonElement | null;
|
|
684
|
+
if (!btn) return;
|
|
685
|
+
|
|
686
|
+
btn.addEventListener('click', async () => {
|
|
687
|
+
try {
|
|
688
|
+
devLog('1B Check: Asserting cryptographic and durable state expectations from respec...');
|
|
689
|
+
|
|
690
|
+
debugger; // allow for walkthrough 1B check
|
|
691
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
692
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
693
|
+
if (!space) { throw new Error("No default space."); }
|
|
694
|
+
|
|
695
|
+
const domainI = debugState.domainI!;
|
|
696
|
+
const targetX = debugState.targetX!;
|
|
697
|
+
const xAddr = getIbGibAddr({ ibGib: targetX });
|
|
698
|
+
|
|
699
|
+
// 1. Resolve evolved sender identity I1 locally
|
|
700
|
+
const newSenderIdentityAddr = await metaspace.getLatestAddr({
|
|
701
|
+
addr: getIbGibAddr({ ibGib: domainI }),
|
|
702
|
+
space
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
if (!newSenderIdentityAddr) {
|
|
706
|
+
devLog('✗ Check: Evolved domain keystone tip I1 NOT found in local space!');
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
devLog(`✓ Check: Evolved Domain Keystone (I1) created and stored locally.`);
|
|
710
|
+
|
|
711
|
+
const getRes = await metaspace.get({ addrs: [newSenderIdentityAddr], space });
|
|
712
|
+
const newSenderIdentity = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
713
|
+
|
|
714
|
+
if (!newSenderIdentity) {
|
|
715
|
+
devLog('✗ Check: Failed to load I1 from local space.');
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const syncProof = newSenderIdentity.data?.proofs?.find(p => p.claim?.verb === 'sync');
|
|
720
|
+
if (!syncProof) {
|
|
721
|
+
devLog('✗ Check: I1 is missing sync-verb claim proof!');
|
|
722
|
+
} else {
|
|
723
|
+
devLog('✓ Check: I1 contains valid sync-verb claim proof.');
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const sessionIdentityTjpAddr = syncProof?.claim?.target;
|
|
727
|
+
if (!sessionIdentityTjpAddr) {
|
|
728
|
+
devLog('✗ Check: I1 sync claim does not target a session keystone.');
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
devLog(`✓ Check: Sync claim targets session keystone: ${sessionIdentityTjpAddr}`);
|
|
732
|
+
|
|
733
|
+
// 2. Load Session Keystone (S) locally
|
|
734
|
+
const sRes = await metaspace.get({ addrs: [sessionIdentityTjpAddr], space });
|
|
735
|
+
const sessionIdentity = sRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
736
|
+
|
|
737
|
+
if (!sessionIdentity) {
|
|
738
|
+
devLog('✗ Check: Session Keystone (S) NOT found in local space.');
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
devLog('✓ Check: Session Keystone (S) successfully created locally.');
|
|
742
|
+
|
|
743
|
+
const sPools = sessionIdentity.data?.challengePools ?? [];
|
|
744
|
+
const hasConnectPool = sPools.some(p => p.id === POOL_ID_CONNECT);
|
|
745
|
+
const hasSyncPool = sPools.some(p => p.id === POOL_ID_SYNC);
|
|
746
|
+
|
|
747
|
+
if (hasConnectPool && hasSyncPool) {
|
|
748
|
+
devLog('✓ Check: S contains both connect and sync challenge pools.');
|
|
749
|
+
} else {
|
|
750
|
+
devLog(`✗ Check: S is missing challenge pools! (connect: ${hasConnectPool}, sync: ${hasSyncPool})`);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// we don't check this yet until sync ping pong
|
|
754
|
+
// const targetAddrs = sessionIdentity.data?.frameDetails?.targetAddrs ?? [];
|
|
755
|
+
// if (targetAddrs.includes(xAddr)) {
|
|
756
|
+
// devLog('✓ Check: S is securely bound to target domain X in frameDetails.');
|
|
757
|
+
// } else {
|
|
758
|
+
// devLog('✗ Check: S is not bound to target domain X!');
|
|
759
|
+
// }
|
|
760
|
+
|
|
761
|
+
// #region 3. Verify Server-Side (receiver) registrations via GET REST API
|
|
762
|
+
devLog('Check: Verifying registrations on server (receiver metaspace)...');
|
|
763
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
764
|
+
|
|
765
|
+
// Check if I, evolved I1 and session S exist on server
|
|
766
|
+
let hasI = false;
|
|
767
|
+
let hasI1 = false;
|
|
768
|
+
let hasS = false;
|
|
769
|
+
|
|
770
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
771
|
+
const resGetDomainIGraphFromServer = await apiBridge.getKeystoneGraph(domainAddr);
|
|
772
|
+
if (resGetDomainIGraphFromServer.success && resGetDomainIGraphFromServer.graph) {
|
|
773
|
+
const domainGraph = resGetDomainIGraphFromServer.graph;
|
|
774
|
+
hasI = domainGraph[domainAddr] !== undefined;
|
|
775
|
+
hasI1 = domainGraph[newSenderIdentityAddr] !== undefined;
|
|
776
|
+
} else {
|
|
777
|
+
devLog(`✗ Check Server: Failed to fetch keystone graph from server: ${resGetDomainIGraphFromServer.message}`);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const resGetSessionSFromServer = await apiBridge.getIbGib(domainAddr, sessionIdentityTjpAddr);
|
|
781
|
+
if (resGetSessionSFromServer.success && resGetSessionSFromServer.ibGib) {
|
|
782
|
+
hasS = true;
|
|
783
|
+
} else {
|
|
784
|
+
devLog(`✗ Check Server: Failed to fetch session keystone from server: ${resGetSessionSFromServer.message}`);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (hasI) devLog('✓ Check Server: Genesis Domain Keystone (I) registered on server.');
|
|
788
|
+
else devLog('✗ Check Server: Genesis Domain Keystone (I) missing on server!');
|
|
789
|
+
if (hasI1) devLog('✓ Check Server: Evolved Domain Keystone (I1) accepted and stored on server.');
|
|
790
|
+
else devLog('✗ Check Server: Evolved Domain Keystone (I1) missing on server!');
|
|
791
|
+
if (hasS) devLog('✓ Check Server: Session Keystone (S) accepted and stored on server.');
|
|
792
|
+
else devLog('✗ Check Server: Session Keystone (S) missing on server!');
|
|
793
|
+
if (hasI && hasI1 && hasS) {
|
|
794
|
+
devLog('🎉 ALL PHASE 1B ESTABLISH CHECKS PASSED FLAWLESSLY! ✓');
|
|
795
|
+
btn.textContent = '✓ 1B All Passed';
|
|
796
|
+
|
|
797
|
+
const btn2bSetup = document.getElementById('btn-2b-setup') as HTMLButtonElement | null;
|
|
798
|
+
if (btn2bSetup) btn2bSetup.disabled = false;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// #endregion 3. Verify Server-Side (receiver) registrations via GET REST API
|
|
802
|
+
|
|
803
|
+
} catch (error) {
|
|
804
|
+
devLog(`✗ 1B Check FAILED: ${extractErrorMsg(error)}`);
|
|
805
|
+
console.error(`${lc_fn} 1B Check error:`, error);
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// ---------------------------------------------------------------------------
|
|
811
|
+
// Phase 2B Implementation
|
|
812
|
+
// ---------------------------------------------------------------------------
|
|
813
|
+
|
|
814
|
+
function init2bSetupButton(): void {
|
|
815
|
+
const lc_fn = `${lc}[init2bSetupButton]`;
|
|
816
|
+
const btn = document.getElementById('btn-2b-setup') as HTMLButtonElement | null;
|
|
817
|
+
if (!btn) return;
|
|
818
|
+
|
|
819
|
+
btn.addEventListener('click', async () => {
|
|
820
|
+
try {
|
|
821
|
+
btn.disabled = true;
|
|
822
|
+
devLog('2B Setup: Generating Alice\'s long-lived Domain Keystone (I) and Target (X)...');
|
|
823
|
+
|
|
824
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
825
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
826
|
+
if (!space) { throw new Error("No default space."); }
|
|
827
|
+
|
|
828
|
+
const masterSecret = 'test-sender-secret-phase2';
|
|
829
|
+
const keystoneService = new KeystoneService_V1();
|
|
830
|
+
|
|
831
|
+
// 1. Create Genesis Domain Keystone (I^Itjp) in local sourceSpace
|
|
832
|
+
const domainI = await keystoneService.genesis({
|
|
833
|
+
masterSecret,
|
|
834
|
+
configs: [
|
|
835
|
+
getSpaceGibPoolConfig('sync', 'senderidentitysyncsaltphase2'),
|
|
836
|
+
getSpaceGibPoolConfig('manage', 'senderidentitymanagesaltphase2'),
|
|
837
|
+
],
|
|
838
|
+
metaspace,
|
|
839
|
+
space,
|
|
840
|
+
frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
debugState.domainI = domainI;
|
|
844
|
+
debugState.domainIMasterSecret = masterSecret;
|
|
845
|
+
|
|
846
|
+
devLog(`2B Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
|
|
847
|
+
devLog(`2B Setup: Starting postGenesisKeystone...`);
|
|
848
|
+
|
|
849
|
+
// 2. Post Genesis Domain Keystone to receiver/server
|
|
850
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
851
|
+
const resGenesis = await apiBridge.postGenesisKeystone(domainI);
|
|
852
|
+
if (resGenesis.success) {
|
|
853
|
+
devLog(`2B Setup: ✓ Domain Keystone registered and stored on receiver (server).`);
|
|
854
|
+
} else {
|
|
855
|
+
devLog(`2B Setup: X postGenesisKeystone failed: ${resGenesis.message}.`);
|
|
856
|
+
throw new Error(`2B Setup: Server rejected genesis domain keystone: ${resGenesis.message}`);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// 3. Create Test IbGib (X) locally
|
|
860
|
+
const resX = await factory.firstGen({
|
|
861
|
+
parentIbGib: ROOT,
|
|
862
|
+
ib: 'test data',
|
|
863
|
+
data: { hello: 'world2', random: Math.random() },
|
|
864
|
+
dna: true,
|
|
865
|
+
nCounter: true,
|
|
866
|
+
tjp: { uuid: true, timestamp: true }
|
|
867
|
+
});
|
|
868
|
+
debugState.targetX = resX.newIbGib;
|
|
869
|
+
devLog(`2B Setup: ✓ Target (X) created locally: ${getIbGibAddr({ ibGib: resX.newIbGib })}`);
|
|
870
|
+
|
|
871
|
+
devLog('✓ 2B Setup Complete! Ready for 2B Sync.');
|
|
872
|
+
btn.textContent = '✓ 2B Setup Complete';
|
|
873
|
+
|
|
874
|
+
// Enable next button in the phase flow
|
|
875
|
+
const syncBtn = document.getElementById('btn-2b-sync') as HTMLButtonElement | null;
|
|
876
|
+
if (syncBtn) syncBtn.disabled = false;
|
|
877
|
+
|
|
878
|
+
} catch (error) {
|
|
879
|
+
devLog(`✗ 2B Setup FAILED: ${extractErrorMsg(error)}`);
|
|
880
|
+
console.error(`${lc_fn} 2B Setup error:`, error);
|
|
881
|
+
btn.disabled = false;
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function init2bSyncButton(): void {
|
|
887
|
+
const lc_fn = `${lc}[init2bSyncButton]`;
|
|
888
|
+
const btn = document.getElementById('btn-2b-sync') as HTMLButtonElement | null;
|
|
889
|
+
if (!btn) return;
|
|
890
|
+
|
|
891
|
+
btn.addEventListener('click', async () => {
|
|
892
|
+
try {
|
|
893
|
+
btn.disabled = true;
|
|
894
|
+
devLog('2B Sync: Executing senderCoordinator.sync(...) using WebSocket Peer...');
|
|
895
|
+
|
|
896
|
+
const domainI = debugState.domainI;
|
|
897
|
+
const targetX = debugState.targetX;
|
|
898
|
+
if (!domainI || !targetX) {
|
|
899
|
+
devLog('⚠ 2B Sync: Missing setup state. Please run Setup first.');
|
|
900
|
+
btn.disabled = false;
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
905
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
906
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
907
|
+
if (!space) { throw new Error("No default space."); }
|
|
908
|
+
|
|
909
|
+
// Instantiate WebSocket sender peer
|
|
910
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
911
|
+
const senderPeer = new SyncPeerWebSocketSender_V1({
|
|
912
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
913
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
914
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
const coordinator = new SyncSagaCoordinator();
|
|
918
|
+
const sagaId = await getUUID();
|
|
919
|
+
debugState.sagaId = sagaId;
|
|
920
|
+
debugState.senderPeer = senderPeer;
|
|
921
|
+
|
|
922
|
+
await senderPeer.initializeOpts({
|
|
923
|
+
localMetaspace: metaspace,
|
|
924
|
+
localSpace: space,
|
|
925
|
+
senderIdentity: domainI,
|
|
926
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
927
|
+
sagaId,
|
|
928
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
929
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
930
|
+
targetAddrs: [domainAddr]
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
try {
|
|
934
|
+
// Execute sync saga — triggers establishSessionIdentity and connectImpl internally!
|
|
935
|
+
const syncSaga = await coordinator.sync({
|
|
936
|
+
domainIbGibs: [targetX],
|
|
937
|
+
senderIdentity: domainI,
|
|
938
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
939
|
+
peer: senderPeer,
|
|
940
|
+
localSpace: space,
|
|
941
|
+
metaspace,
|
|
942
|
+
conflictStrategy: 'optimisticWithLCS' as any
|
|
943
|
+
});
|
|
944
|
+
await syncSaga.done;
|
|
945
|
+
} catch (err) {
|
|
946
|
+
// connection/sync error is fine/expected right now since sync protocol (Phase 3) might be incomplete
|
|
947
|
+
devLog(`2B Sync: Coordinator sync executed (connect phase successfully run; subsequent sync errors expected in 2B): ${extractErrorMsg(err)}`);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
devLog('✓ 2B Sync Execution Complete! Ready for State Checks.');
|
|
951
|
+
btn.textContent = '✓ 2B Sync Run';
|
|
952
|
+
|
|
953
|
+
// Enable check button
|
|
954
|
+
const checkBtn = document.getElementById('btn-2b-check') as HTMLButtonElement | null;
|
|
955
|
+
if (checkBtn) checkBtn.disabled = false;
|
|
956
|
+
|
|
957
|
+
} catch (error) {
|
|
958
|
+
devLog(`✗ 2B Sync FAILED: ${extractErrorMsg(error)}`);
|
|
959
|
+
console.error(`${lc_fn} 2B Sync error:`, error);
|
|
960
|
+
btn.disabled = false;
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function init2bCheckButton(): void {
|
|
966
|
+
const lc_fn = `${lc}[init2bCheckButton]`;
|
|
967
|
+
const btn = document.getElementById('btn-2b-check') as HTMLButtonElement | null;
|
|
968
|
+
if (!btn) return;
|
|
969
|
+
|
|
970
|
+
btn.addEventListener('click', async () => {
|
|
971
|
+
try {
|
|
972
|
+
debugger; // allow for walkthrough 2B check
|
|
973
|
+
devLog('2B Check: Asserting cryptographic and WebSocket state expectations...');
|
|
974
|
+
|
|
975
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
976
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
977
|
+
if (!space) { throw new Error("No default space."); }
|
|
978
|
+
|
|
979
|
+
const domainI = debugState.domainI!;
|
|
980
|
+
const targetX = debugState.targetX!;
|
|
981
|
+
const xAddr = getIbGibAddr({ ibGib: targetX });
|
|
982
|
+
|
|
983
|
+
// 1. Resolve evolved sender identity I1 locally
|
|
984
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
985
|
+
const newSenderIdentityAddr = await metaspace.getLatestAddr({
|
|
986
|
+
addr: domainAddr,
|
|
987
|
+
space
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
if (!newSenderIdentityAddr) {
|
|
991
|
+
devLog('✗ Check: Evolved domain keystone tip I1 NOT found in local space!');
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
devLog(`✓ Check: Evolved Domain Keystone (I1) created and stored locally.`);
|
|
995
|
+
|
|
996
|
+
const getRes = await metaspace.get({ addrs: [newSenderIdentityAddr], space });
|
|
997
|
+
const newSenderIdentity = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
998
|
+
|
|
999
|
+
if (!newSenderIdentity) {
|
|
1000
|
+
devLog('✗ Check: Failed to load I1 from local space.');
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const syncProof = newSenderIdentity.data?.proofs?.find(p => p.claim?.verb === 'sync');
|
|
1005
|
+
const sessionIdentityTjpAddr = syncProof?.claim?.target;
|
|
1006
|
+
if (!sessionIdentityTjpAddr) {
|
|
1007
|
+
devLog('✗ Check: I1 sync claim does not target a session keystone.');
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
devLog(`✓ Check: Sync claim targets session keystone: ${sessionIdentityTjpAddr}`);
|
|
1011
|
+
|
|
1012
|
+
// 2. Load Session Keystone (S) locally and get latest tip
|
|
1013
|
+
const latestSessionSAddr = await metaspace.getLatestAddr({
|
|
1014
|
+
addr: sessionIdentityTjpAddr,
|
|
1015
|
+
space
|
|
1016
|
+
});
|
|
1017
|
+
if (!latestSessionSAddr) {
|
|
1018
|
+
devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
devLog(`✓ Check: Found latest session keystone (S) locally.`);
|
|
1022
|
+
|
|
1023
|
+
const sRes = await metaspace.get({ addrs: [latestSessionSAddr], space });
|
|
1024
|
+
const sessionIdentity = sRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
1025
|
+
|
|
1026
|
+
if (!sessionIdentity) {
|
|
1027
|
+
devLog('✗ Check: Failed to load latest Session Keystone (S).');
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Verify S contains connect and sync pools
|
|
1032
|
+
const sPools = sessionIdentity.data?.challengePools ?? [];
|
|
1033
|
+
const connectPool = sPools.find(p => p.id === POOL_ID_CONNECT);
|
|
1034
|
+
const syncPool = sPools.find(p => p.id === POOL_ID_SYNC);
|
|
1035
|
+
|
|
1036
|
+
if (connectPool && syncPool) {
|
|
1037
|
+
devLog('✓ Check: S contains both connect and sync challenge pools.');
|
|
1038
|
+
} else {
|
|
1039
|
+
devLog(`✗ Check: S is missing challenge pools! (connect: ${!!connectPool}, sync: ${!!syncPool})`);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// Verify S was successfully evolved (depleted connect pool)
|
|
1044
|
+
const remainingChallenges = Object.keys(connectPool.challenges).length;
|
|
1045
|
+
if (remainingChallenges === 0) {
|
|
1046
|
+
devLog('✓ Check: S connect pool successfully depleted (0 challenges remaining).');
|
|
1047
|
+
} else {
|
|
1048
|
+
devLog(`✗ Check: S connect pool not depleted! (${remainingChallenges} challenges remaining)`);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// we don't check this yet until sync ping pong
|
|
1053
|
+
// const targetAddrs = sessionIdentity.data?.frameDetails?.targetAddrs ?? [];
|
|
1054
|
+
// if (targetAddrs.includes(xAddr)) {
|
|
1055
|
+
// devLog('✓ Check: S is securely bound to target domain X in frameDetails.');
|
|
1056
|
+
// } else {
|
|
1057
|
+
// devLog('✗ Check: S is not bound to target domain X!');
|
|
1058
|
+
// }
|
|
1059
|
+
|
|
1060
|
+
// 3. Verify WebSocket Connection is open and active
|
|
1061
|
+
if (debugState.senderPeer && debugState.senderPeer.isSocketOpen) {
|
|
1062
|
+
devLog('✓ Check: WebSocket connection is active and OPEN.');
|
|
1063
|
+
} else {
|
|
1064
|
+
devLog('✗ Check: WebSocket connection is NOT open or peer is missing!');
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
devLog('🎉 ALL PHASE 2B CONNECT CHECKS PASSED FLAWLESSLY! ✓');
|
|
1069
|
+
btn.textContent = '✓ 2B All Passed';
|
|
1070
|
+
|
|
1071
|
+
// Enable Phase 3B setup
|
|
1072
|
+
const p3SetupBtn = document.getElementById('btn-3b-setup') as HTMLButtonElement | null;
|
|
1073
|
+
if (p3SetupBtn) p3SetupBtn.disabled = false;
|
|
1074
|
+
|
|
1075
|
+
} catch (error) {
|
|
1076
|
+
devLog(`✗ 2B Check FAILED: ${extractErrorMsg(error)}`);
|
|
1077
|
+
console.error(`${lc_fn} 2B Check error:`, error);
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function init3bPlaceholder(): void {
|
|
1083
|
+
const btnSetup = document.getElementById('btn-3b-setup') as HTMLButtonElement | null;
|
|
1084
|
+
const btnSync = document.getElementById('btn-3b-sync') as HTMLButtonElement | null;
|
|
1085
|
+
const btnCheck = document.getElementById('btn-3b-check') as HTMLButtonElement | null;
|
|
1086
|
+
|
|
1087
|
+
if (btnSetup) {
|
|
1088
|
+
btnSetup.addEventListener('click', () => devLog('3B Setup: Placeholders ready for Phase 3B (Transactional Sync).'));
|
|
1089
|
+
}
|
|
1090
|
+
if (btnSync) {
|
|
1091
|
+
btnSync.addEventListener('click', () => devLog('3B Sync: Placeholders ready for Phase 3B (Transactional Sync).'));
|
|
1092
|
+
}
|
|
1093
|
+
if (btnCheck) {
|
|
1094
|
+
btnCheck.addEventListener('click', () => devLog('3B Check: Placeholders ready for Phase 3B (Transactional Sync).'));
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
520
1098
|
// ---------------------------------------------------------------------------
|
|
521
1099
|
// Dev Tools UI Init
|
|
522
1100
|
// ---------------------------------------------------------------------------
|
|
@@ -534,6 +1112,13 @@ export function initDevTools(): void {
|
|
|
534
1112
|
initCreateSessionKeystoneButton();
|
|
535
1113
|
initEvolveDomainKeystoneButton();
|
|
536
1114
|
initPerformSyncButton();
|
|
1115
|
+
init1bSetupButton();
|
|
1116
|
+
init1bSyncButton();
|
|
1117
|
+
init1bCheckButton();
|
|
1118
|
+
init2bSetupButton();
|
|
1119
|
+
init2bSyncButton();
|
|
1120
|
+
init2bCheckButton();
|
|
1121
|
+
init3bPlaceholder();
|
|
537
1122
|
} catch (error) {
|
|
538
1123
|
console.error(`${lc_fn} ${extractErrorMsg(error)}`);
|
|
539
1124
|
}
|