@ibgib/space-gib 0.0.1 → 0.0.3

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 (33) hide show
  1. package/dist/client/bootstrap.mjs +1 -1
  2. package/dist/client/bootstrap.mjs.map +1 -1
  3. package/dist/client/chunk-2KJC5XKE.mjs +31 -0
  4. package/dist/client/chunk-2KJC5XKE.mjs.map +7 -0
  5. package/dist/client/chunk-QNIXTRFO.mjs +235 -0
  6. package/dist/client/chunk-QNIXTRFO.mjs.map +7 -0
  7. package/dist/client/index.html +47 -9
  8. package/dist/client/index.mjs +1 -1
  9. package/dist/client/script.mjs +1 -1
  10. package/dist/client/style.css +26 -0
  11. package/dist/server/server.mjs +5752 -1010
  12. package/dist/server/server.mjs.map +4 -4
  13. package/package.json +1 -1
  14. package/src/client/AUTO-GENERATED-version.mts +1 -1
  15. package/src/client/api/space-gib-api-bridge.mts +119 -8
  16. package/src/client/dev-tools.mts +717 -31
  17. package/src/client/index.html +47 -9
  18. package/src/client/style.css +26 -0
  19. package/src/common/keystone-policies.json +64 -0
  20. package/src/common/keystone-policies.mts +39 -86
  21. package/src/server/serve-gib/handlers/api/debug/ws-echo.handler.mts +13 -12
  22. package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +14 -167
  23. package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +6 -6
  24. package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +3 -3
  25. package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +10 -25
  26. package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +205 -0
  27. package/src/server/serve-gib/handlers/ws/sync-upgrade.handler.mts +13 -487
  28. package/src/server/serve-gib/handlers/ws/ws-helper.mts +80 -3
  29. package/dist/client/chunk-CT47Z5WU.mjs +0 -21
  30. package/dist/client/chunk-CT47Z5WU.mjs.map +0 -7
  31. package/dist/client/chunk-RHEDTRKF.mjs +0 -235
  32. package/dist/client/chunk-RHEDTRKF.mjs.map +0 -7
  33. package/dist/respec-gib.node.mjs +0 -5
@@ -10,21 +10,27 @@
10
10
  * working end-to-end.
11
11
  */
12
12
 
13
- import { extractErrorMsg, getTimestamp, getUUID, HashAlgorithm } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
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 { KeystoneChallengeType, KeystoneIbGib_V1, KeystoneReplenishStrategy } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
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/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";
26
+ import { graphsAreEquivalent } from '@ibgib/core-gib/dist/common/other/graph-helper.mjs';
22
27
 
23
28
  import { GLOBAL_LOG_A_LOT } from './constants.mjs';
24
- import { SESSION_KEYSTONE_POLICY, getHandshakeChallenge } from "../common/keystone-policies.mjs";
25
- import { KeystoneStrategyFactory } from '@ibgib/core-gib/dist/keystone/strategy/keystone-strategy-factory.mjs';
29
+ import {
30
+ SESSION_KEYSTONE_POLICY, getConnectChallenge, getSpaceGibPoolConfig
31
+ } from "../common/keystone-policies.mjs";
26
32
  import { SpaceGibApiBridge } from './api/space-gib-api-bridge.mjs';
27
- import { KeystoneVerb } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
33
+ import { SyncConflictStrategy } from '@ibgib/core-gib/dist/sync/sync-constants.mjs';
28
34
 
29
35
  const logalot = GLOBAL_LOG_A_LOT
30
36
  const lc = '[dev-tools]';
@@ -35,6 +41,8 @@ interface DebugState {
35
41
  targetX?: IbGib_V1;
36
42
  sessionS?: KeystoneIbGib_V1;
37
43
  sessionSecret?: string;
44
+ senderPeer?: SyncPeerWebSocketSender_V1;
45
+ sagaId?: string;
38
46
  }
39
47
 
40
48
  export const debugState: DebugState = {};
@@ -114,7 +122,7 @@ function initWsTestButton(): void {
114
122
 
115
123
  function initPrintStateButton(): void {
116
124
  const btn = document.getElementById('btn-print-dev-state') as HTMLButtonElement | null;
117
- if (!btn) return;
125
+ if (!btn) { return; }
118
126
  btn.addEventListener('click', () => {
119
127
  devLog(`Printing debug state to console...`);
120
128
  console.dir(debugState);
@@ -128,7 +136,7 @@ function initPrintStateButton(): void {
128
136
  function initCreateDomainKeystoneButton(): void {
129
137
  const lc_fn = `${lc}[initCreateDomainKeystoneButton]`;
130
138
  const btn = document.getElementById('btn-create-domain-keystone') as HTMLButtonElement | null;
131
- if (!btn) return;
139
+ if (!btn) { return; }
132
140
 
133
141
  btn.addEventListener('click', async () => {
134
142
  try {
@@ -177,7 +185,7 @@ function initCreateDomainKeystoneButton(): void {
177
185
 
178
186
  // Enable next step
179
187
  const nextBtn = document.getElementById('btn-create-test-ibgib') as HTMLButtonElement;
180
- if (nextBtn) nextBtn.disabled = false;
188
+ if (nextBtn) { nextBtn.disabled = false; }
181
189
 
182
190
  } catch (error) {
183
191
  devLog(`✗ ${extractErrorMsg(error)}`);
@@ -219,7 +227,7 @@ function initCreateTestIbGibButton(): void {
219
227
 
220
228
  // Enable next step
221
229
  const nextBtn = document.getElementById('btn-create-session-keystone') as HTMLButtonElement;
222
- if (nextBtn) nextBtn.disabled = false;
230
+ if (nextBtn) { nextBtn.disabled = false; }
223
231
  } catch (error) {
224
232
  devLog(`✗ ${extractErrorMsg(error)}`);
225
233
  console.error(`${lc_fn} ${extractErrorMsg(error)}`);
@@ -276,19 +284,19 @@ function initCreateSessionKeystoneButton(): void {
276
284
  allowedVerbs: [],
277
285
  },
278
286
  {
279
- id: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID,
287
+ id: SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID,
280
288
  salt: await getUUID(),
281
289
  type: SESSION_KEYSTONE_POLICY.COMMON.TYPE,
282
290
  algo: SESSION_KEYSTONE_POLICY.COMMON.ALGO,
283
291
  rounds: SESSION_KEYSTONE_POLICY.COMMON.ROUNDS,
284
292
  behavior: {
285
- size: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.SIZE,
293
+ size: SESSION_KEYSTONE_POLICY.CONNECT_POOL.SIZE,
286
294
  replenish: SESSION_KEYSTONE_POLICY.COMMON.REPLENISH,
287
- selectSequentially: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.SELECT_SEQUENTIALLY,
288
- selectRandomly: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.SELECT_RANDOMLY,
289
- targetBindingChars: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.TARGET_BINDING_CHARS,
295
+ selectSequentially: SESSION_KEYSTONE_POLICY.CONNECT_POOL.SELECT_SEQUENTIALLY,
296
+ selectRandomly: SESSION_KEYSTONE_POLICY.CONNECT_POOL.SELECT_RANDOMLY,
297
+ targetBindingChars: SESSION_KEYSTONE_POLICY.CONNECT_POOL.TARGET_BINDING_CHARS,
290
298
  },
291
- allowedVerbs: [SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.VERB],
299
+ allowedVerbs: [SESSION_KEYSTONE_POLICY.CONNECT_POOL.VERB],
292
300
  }
293
301
  ],
294
302
  metaspace,
@@ -309,7 +317,7 @@ function initCreateSessionKeystoneButton(): void {
309
317
 
310
318
  // Enable next step
311
319
  const nextBtn = document.getElementById('btn-evolve-domain-keystone') as HTMLButtonElement;
312
- if (nextBtn) nextBtn.disabled = false;
320
+ if (nextBtn) { nextBtn.disabled = false; }
313
321
  } catch (error) {
314
322
  devLog(`✗ ${extractErrorMsg(error)}`);
315
323
  console.error(`${lc_fn} ${extractErrorMsg(error)}`);
@@ -383,7 +391,7 @@ function initEvolveDomainKeystoneButton(): void {
383
391
 
384
392
  // Enable next step
385
393
  const nextBtn = document.getElementById('btn-perform-sync') as HTMLButtonElement;
386
- if (nextBtn) nextBtn.disabled = false;
394
+ if (nextBtn) { nextBtn.disabled = false; }
387
395
 
388
396
  } catch (error) {
389
397
  devLog(`✗ ${extractErrorMsg(error)}`);
@@ -394,19 +402,19 @@ function initEvolveDomainKeystoneButton(): void {
394
402
  }
395
403
 
396
404
  // ---------------------------------------------------------------------------
397
- // Perform Sync Handshake (S) button
405
+ // Perform Sync Connect (S) button
398
406
  // ---------------------------------------------------------------------------
399
407
 
400
408
  function initPerformSyncButton(): void {
401
409
  const lc_fn = `${lc}[initPerformSyncButton]`;
402
410
  const btn = document.getElementById('btn-perform-sync') as HTMLButtonElement | null;
403
- if (!btn) return;
411
+ if (!btn) { return; }
404
412
 
405
413
  btn.addEventListener('click', async () => {
406
414
  try {
407
415
  debugger; // walk through sync
408
416
  btn.disabled = true;
409
- devLog('Initiating Sync Handshake via WebSocket...');
417
+ devLog('Initiating Sync Connect via WebSocket...');
410
418
 
411
419
  const sessionS = debugState.sessionS;
412
420
  const sessionSecret = debugState.sessionSecret;
@@ -423,17 +431,17 @@ function initPerformSyncButton(): void {
423
431
  if (!space) { throw new Error("No space."); }
424
432
 
425
433
  devLog('Pre-solving upfront picket-fence challenge...');
426
- const handshakePool = (sessionS.data?.challengePools ?? []).find(p => p.id === SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID);
427
- if (!handshakePool) {
428
- throw new Error(`Session keystone missing "${SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID}" pool`);
434
+ const connectPool = (sessionS.data?.challengePools ?? []).find(p => p.id === SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID);
435
+ if (!connectPool) {
436
+ throw new Error(`Session keystone missing "${SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID}" pool`);
429
437
  }
430
438
 
431
- const { challengeId } = getHandshakeChallenge(sessionS);
432
- const strategy = KeystoneStrategyFactory.create({ config: handshakePool.config });
439
+ const { challengeId } = getConnectChallenge(sessionS);
440
+ const strategy = KeystoneStrategyFactory.create({ config: connectPool.config });
433
441
  const poolSecret = await strategy.derivePoolSecret({ masterSecret: sessionSecret });
434
442
  const solution = await strategy.generateSolution({
435
443
  poolSecret,
436
- poolId: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID,
444
+ poolId: SESSION_KEYSTONE_POLICY.CONNECT_POOL.ID,
437
445
  challengeId
438
446
  });
439
447
 
@@ -464,16 +472,16 @@ function initPerformSyncButton(): void {
464
472
  }));
465
473
  } else if (msg.type === 'auth-challenge') {
466
474
  const { challengeUuid, demandedIds } = msg;
467
- devLog(`→ server demanded ${demandedIds.length} ids. Signing handshake proof...`);
475
+ devLog(`→ server demanded ${demandedIds.length} ids. Signing connect proof...`);
468
476
 
469
477
  const keystoneService = new KeystoneService_V1();
470
478
  const proofFrame = await keystoneService.sign({
471
479
  latestKeystone: sessionS,
472
480
  masterSecret: sessionSecret,
473
- poolId: 'handshake',
481
+ poolId: POOL_ID_CONNECT,
474
482
  requiredChallengeIds: demandedIds,
475
483
  claim: {
476
- verb: 'handshake',
484
+ verb: KEYSTONE_VERB_CONNECT,
477
485
  target: challengeUuid
478
486
  },
479
487
  metaspace,
@@ -486,10 +494,10 @@ function initPerformSyncButton(): void {
486
494
  proofFrame
487
495
  }));
488
496
  } else if (msg.type === 'auth-ok') {
489
- devLog('✓ Handshake SUCCESS! Sync session authorized.');
497
+ devLog('✓ Connect SUCCESS! Sync session authorized.');
490
498
  btn.textContent = '✓ Sync Authorized';
491
499
  } else if (msg.type === 'auth-fail') {
492
- devLog(`✗ Handshake FAILED: ${msg.message}`);
500
+ devLog(`✗ Connect FAILED: ${msg.message}`);
493
501
  btn.disabled = false;
494
502
  }
495
503
  } catch (error) {
@@ -517,6 +525,675 @@ function initPerformSyncButton(): void {
517
525
  });
518
526
  }
519
527
 
528
+ // ---------------------------------------------------------------------------
529
+ // Phase 1B: Standardized Setup / Sync / Check testing loop
530
+ // ---------------------------------------------------------------------------
531
+
532
+ interface PhaseSetupOptions {
533
+ phaseText: '1B' | '2B' | '3B';
534
+ btnId: string;
535
+ nextBtnId: string;
536
+ masterSecret: string;
537
+ syncSalt: string;
538
+ manageSalt: string;
539
+ ib: string;
540
+ data: any;
541
+ }
542
+
543
+ async function performPhaseSetup(opts: PhaseSetupOptions): Promise<void> {
544
+ const lc_fn = `${lc}[performPhaseSetup(${opts.phaseText})]`;
545
+ const btn = document.getElementById(opts.btnId) as HTMLButtonElement | null;
546
+ if (!btn) { return; }
547
+
548
+ try {
549
+ btn.disabled = true;
550
+ devLog(`${opts.phaseText} Setup: Generating Alice's long-lived Domain Keystone (I) and Target (X)...`);
551
+
552
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
553
+ const space = await metaspace.getLocalUserSpace({});
554
+ if (!space) { throw new Error("No default space found in metaspace."); }
555
+
556
+ const keystoneService = new KeystoneService_V1();
557
+
558
+ // 1. Create Genesis Domain Keystone (I^Itjp) in local space
559
+ const domainI = await keystoneService.genesis({
560
+ masterSecret: opts.masterSecret,
561
+ configs: [
562
+ getSpaceGibPoolConfig('sync', opts.syncSalt),
563
+ getSpaceGibPoolConfig('manage', opts.manageSalt),
564
+ ],
565
+ metaspace,
566
+ space,
567
+ frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
568
+ });
569
+
570
+ debugState.domainI = domainI;
571
+ debugState.domainIMasterSecret = opts.masterSecret;
572
+
573
+ devLog(`${opts.phaseText} Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
574
+ devLog(`${opts.phaseText} Setup: Starting postGenesisKeystone...`);
575
+
576
+ // 2. Post Genesis Domain Keystone to receiver/server
577
+ const apiBridge = new SpaceGibApiBridge();
578
+ const resGenesis = await apiBridge.postGenesisKeystone(domainI);
579
+ if (resGenesis.success) {
580
+ devLog(`${opts.phaseText} Setup: ✓ Domain Keystone registered and stored on receiver (server).`);
581
+ } else {
582
+ devLog(`${opts.phaseText} Setup: X postGenesisKeystone failed: ${resGenesis.message}.`);
583
+ throw new Error(`${opts.phaseText} Setup: Server rejected genesis domain keystone: ${resGenesis.message}`);
584
+ }
585
+
586
+ // 3. Create Test IbGib (X) locally
587
+ const resX = await factory.firstGen({
588
+ parentIbGib: ROOT,
589
+ ib: opts.ib,
590
+ data: opts.data,
591
+ dna: true,
592
+ nCounter: true,
593
+ tjp: { uuid: true, timestamp: true }
594
+ });
595
+ const xIbGib = resX.newIbGib;
596
+
597
+ // Persist target X to local space so coordinator can build the sync history
598
+ await metaspace.persistTransformResult({ resTransform: resX, space });
599
+ await metaspace.registerNewIbGib({ ibGib: xIbGib });
600
+
601
+ debugState.targetX = xIbGib;
602
+ devLog(`${opts.phaseText} Setup: ✓ Target (X) created and persisted locally: ${getIbGibAddr({ ibGib: xIbGib })}`);
603
+
604
+ devLog(`✓ ${opts.phaseText} Setup Complete! Ready for ${opts.phaseText} Sync.`);
605
+ btn.textContent = `✓ ${opts.phaseText} Setup Complete`;
606
+
607
+ // Enable next button in the phase flow
608
+ const syncBtn = document.getElementById(opts.nextBtnId) as HTMLButtonElement | null;
609
+ if (syncBtn) { syncBtn.disabled = false; }
610
+
611
+ } catch (error) {
612
+ devLog(`✗ ${opts.phaseText} Setup FAILED: ${extractErrorMsg(error)}`);
613
+ console.error(`${lc_fn} ${opts.phaseText} Setup error:`, error);
614
+ btn.disabled = false;
615
+ }
616
+ }
617
+
618
+ interface PhaseSyncOptions {
619
+ phaseText: '1B' | '2B' | '3B';
620
+ btnId: string;
621
+ nextBtnId: string;
622
+ expectSyncFailure?: boolean;
623
+ failureMessageSuffix?: string;
624
+ }
625
+
626
+ async function performPhaseSync(opts: PhaseSyncOptions): Promise<void> {
627
+ const lc_fn = `${lc}[performPhaseSync(${opts.phaseText})]`;
628
+ const btn = document.getElementById(opts.btnId) as HTMLButtonElement | null;
629
+ if (!btn) { return; }
630
+
631
+ try {
632
+ btn.disabled = true;
633
+ devLog(`${opts.phaseText} Sync: Executing senderCoordinator.sync(...) using WebSocket Peer...`);
634
+
635
+ const domainI = debugState.domainI;
636
+ const targetX = debugState.targetX;
637
+ if (!domainI || !targetX) {
638
+ devLog(`⚠ ${opts.phaseText} Sync: Missing setup state. Please run Setup first.`);
639
+ btn.disabled = false;
640
+ return;
641
+ }
642
+
643
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
644
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
645
+ const space = await metaspace.getLocalUserSpace({});
646
+ if (!space) { throw new Error("No default space."); }
647
+
648
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
649
+ const senderPeer = new SyncPeerWebSocketSender_V1({
650
+ classname: 'SyncPeerWebSocketSender_V1',
651
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
652
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
653
+ });
654
+
655
+ const coordinator = new SyncSagaCoordinator();
656
+ const sagaId = await getUUID();
657
+ debugState.sagaId = sagaId;
658
+ debugState.senderPeer = senderPeer;
659
+
660
+ await senderPeer.initializeOpts({
661
+ localMetaspace: metaspace,
662
+ localSpace: space,
663
+ senderIdentity: domainI,
664
+ fnSenderSecret: async () => debugState.domainIMasterSecret!,
665
+ sagaId,
666
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
667
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
668
+ targetAddrs: [domainAddr]
669
+ });
670
+
671
+ try {
672
+ const syncSaga = await coordinator.sync({
673
+ domainIbGibs: [targetX],
674
+ senderIdentity: domainI,
675
+ fnSenderSecret: async () => debugState.domainIMasterSecret!,
676
+ peer: senderPeer,
677
+ localSpace: space,
678
+ metaspace,
679
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
680
+ });
681
+ await syncSaga.done;
682
+ } catch (err) {
683
+ if (opts.expectSyncFailure) {
684
+ devLog(`${opts.phaseText} Sync: Coordinator sync executed (${opts.failureMessageSuffix ?? 'errors expected'}): ${extractErrorMsg(err)}`);
685
+ } else {
686
+ throw err;
687
+ }
688
+ }
689
+
690
+ devLog(`✓ ${opts.phaseText} Sync Execution Complete! Ready for State Checks.`);
691
+ btn.textContent = `✓ ${opts.phaseText} Sync Run`;
692
+
693
+ const checkBtn = document.getElementById(opts.nextBtnId) as HTMLButtonElement | null;
694
+ if (checkBtn) { checkBtn.disabled = false; }
695
+
696
+ } catch (error) {
697
+ devLog(`✗ ${opts.phaseText} Sync FAILED: ${extractErrorMsg(error)}`);
698
+ console.error(`${lc_fn} ${opts.phaseText} Sync error:`, error);
699
+ btn.disabled = false;
700
+ }
701
+ }
702
+
703
+ function init1bSetupButton(): void {
704
+ const btn = document.getElementById('btn-1b-setup') as HTMLButtonElement | null;
705
+ if (!btn) { return; }
706
+ btn.addEventListener('click', () => {
707
+ performPhaseSetup({
708
+ phaseText: '1B',
709
+ btnId: 'btn-1b-setup',
710
+ nextBtnId: 'btn-1b-sync',
711
+ masterSecret: 'test-sender-secret-phase1',
712
+ syncSalt: 'senderidentitysyncsaltphase1',
713
+ manageSalt: 'senderidentitymanagesaltphase1',
714
+ ib: 'test data',
715
+ data: { hello: 'world', random: Math.random() }
716
+ });
717
+ });
718
+ }
719
+
720
+ function init1bSyncButton(): void {
721
+ const btn = document.getElementById('btn-1b-sync') as HTMLButtonElement | null;
722
+ if (!btn) { return; }
723
+ btn.addEventListener('click', () => {
724
+ performPhaseSync({
725
+ phaseText: '1B',
726
+ btnId: 'btn-1b-sync',
727
+ nextBtnId: 'btn-1b-check',
728
+ expectSyncFailure: true,
729
+ failureMessageSuffix: 'establish phase successfully run; connection/sync errors expected in 1B'
730
+ });
731
+ });
732
+ }
733
+
734
+ function init1bCheckButton(): void {
735
+ const lc_fn = `${lc}[init1bCheckButton]`;
736
+ const btn = document.getElementById('btn-1b-check') as HTMLButtonElement | null;
737
+ if (!btn) { return; }
738
+
739
+ btn.addEventListener('click', async () => {
740
+ try {
741
+ devLog('1B Check: Asserting cryptographic and durable state expectations from respec...');
742
+
743
+ debugger; // allow for walkthrough 1B check
744
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
745
+ const space = await metaspace.getLocalUserSpace({});
746
+ if (!space) { throw new Error("No default space."); }
747
+
748
+ const domainI = debugState.domainI!;
749
+ const targetX = debugState.targetX!;
750
+ const xAddr = getIbGibAddr({ ibGib: targetX });
751
+
752
+ // 1. Resolve evolved sender identity I1 locally
753
+ const newSenderIdentityAddr = await metaspace.getLatestAddr({
754
+ addr: getIbGibAddr({ ibGib: domainI }),
755
+ space
756
+ });
757
+
758
+ if (!newSenderIdentityAddr) {
759
+ devLog('✗ Check: Evolved domain keystone tip I1 NOT found in local space!');
760
+ return;
761
+ }
762
+ devLog(`✓ Check: Evolved Domain Keystone (I1) created and stored locally.`);
763
+
764
+ const getRes = await metaspace.get({ addrs: [newSenderIdentityAddr], space });
765
+ const newSenderIdentity = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
766
+
767
+ if (!newSenderIdentity) {
768
+ devLog('✗ Check: Failed to load I1 from local space.');
769
+ return;
770
+ }
771
+
772
+ const syncProof = newSenderIdentity.data?.proofs?.find(p => p.claim?.verb === 'sync');
773
+ if (!syncProof) {
774
+ devLog('✗ Check: I1 is missing sync-verb claim proof!');
775
+ } else {
776
+ devLog('✓ Check: I1 contains valid sync-verb claim proof.');
777
+ }
778
+
779
+ const sessionIdentityTjpAddr = syncProof?.claim?.target;
780
+ if (!sessionIdentityTjpAddr) {
781
+ devLog('✗ Check: I1 sync claim does not target a session keystone.');
782
+ return;
783
+ }
784
+ devLog(`✓ Check: Sync claim targets session keystone: ${sessionIdentityTjpAddr}`);
785
+
786
+ // 2. Load Session Keystone (S) locally
787
+ const sRes = await metaspace.get({ addrs: [sessionIdentityTjpAddr], space });
788
+ const sessionIdentity = sRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
789
+
790
+ if (!sessionIdentity) {
791
+ devLog('✗ Check: Session Keystone (S) NOT found in local space.');
792
+ return;
793
+ }
794
+ devLog('✓ Check: Session Keystone (S) successfully created locally.');
795
+
796
+ const sPools = sessionIdentity.data?.challengePools ?? [];
797
+ const hasConnectPool = sPools.some(p => p.id === POOL_ID_CONNECT);
798
+ const hasSyncPool = sPools.some(p => p.id === POOL_ID_SYNC);
799
+
800
+ if (hasConnectPool && hasSyncPool) {
801
+ devLog('✓ Check: S contains both connect and sync challenge pools.');
802
+ } else {
803
+ devLog(`✗ Check: S is missing challenge pools! (connect: ${hasConnectPool}, sync: ${hasSyncPool})`);
804
+ }
805
+
806
+ // we don't check this yet until sync ping pong
807
+ // const targetAddrs = sessionIdentity.data?.frameDetails?.targetAddrs ?? [];
808
+ // if (targetAddrs.includes(xAddr)) {
809
+ // devLog('✓ Check: S is securely bound to target domain X in frameDetails.');
810
+ // } else {
811
+ // devLog('✗ Check: S is not bound to target domain X!');
812
+ // }
813
+
814
+ // #region 3. Verify Server-Side (receiver) registrations via GET REST API
815
+ devLog('Check: Verifying registrations on server (receiver metaspace)...');
816
+ const apiBridge = new SpaceGibApiBridge();
817
+
818
+ // Check if I, evolved I1 and session S exist on server
819
+ let hasI = false;
820
+ let hasI1 = false;
821
+ let hasS = false;
822
+
823
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
824
+ const resGetDomainIGraphFromServer = await apiBridge.getKeystoneGraph(domainAddr);
825
+ if (resGetDomainIGraphFromServer.success && resGetDomainIGraphFromServer.graph) {
826
+ const domainGraph = resGetDomainIGraphFromServer.graph;
827
+ hasI = domainGraph[domainAddr] !== undefined;
828
+ hasI1 = domainGraph[newSenderIdentityAddr] !== undefined;
829
+ } else {
830
+ devLog(`✗ Check Server: Failed to fetch keystone graph from server: ${resGetDomainIGraphFromServer.message}`);
831
+ }
832
+
833
+ const resGetSessionSFromServer = await apiBridge.getIbGib(domainAddr, sessionIdentityTjpAddr);
834
+ if (resGetSessionSFromServer.success && resGetSessionSFromServer.ibGib) {
835
+ hasS = true;
836
+ } else {
837
+ devLog(`✗ Check Server: Failed to fetch session keystone from server: ${resGetSessionSFromServer.message}`);
838
+ }
839
+
840
+ if (hasI) { devLog('✓ Check Server: Genesis Domain Keystone (I) registered on server.'); }
841
+ else devLog('✗ Check Server: Genesis Domain Keystone (I) missing on server!');
842
+ if (hasI1) { devLog('✓ Check Server: Evolved Domain Keystone (I1) accepted and stored on server.'); }
843
+ else devLog('✗ Check Server: Evolved Domain Keystone (I1) missing on server!');
844
+ if (hasS) { devLog('✓ Check Server: Session Keystone (S) accepted and stored on server.'); }
845
+ else devLog('✗ Check Server: Session Keystone (S) missing on server!');
846
+ if (hasI && hasI1 && hasS) {
847
+ devLog('🎉 ALL PHASE 1B ESTABLISH CHECKS PASSED FLAWLESSLY! ✓');
848
+ btn.textContent = '✓ 1B All Passed';
849
+
850
+ const btn2bSetup = document.getElementById('btn-2b-setup') as HTMLButtonElement | null;
851
+ if (btn2bSetup) { btn2bSetup.disabled = false; }
852
+ }
853
+
854
+ // #endregion 3. Verify Server-Side (receiver) registrations via GET REST API
855
+
856
+ } catch (error) {
857
+ devLog(`✗ 1B Check FAILED: ${extractErrorMsg(error)}`);
858
+ console.error(`${lc_fn} 1B Check error:`, error);
859
+ }
860
+ });
861
+ }
862
+
863
+ // ---------------------------------------------------------------------------
864
+ // Phase 2B Implementation
865
+ // ---------------------------------------------------------------------------
866
+
867
+ function init2bSetupButton(): void {
868
+ const btn = document.getElementById('btn-2b-setup') as HTMLButtonElement | null;
869
+ if (!btn) { return; }
870
+ btn.addEventListener('click', () => {
871
+ performPhaseSetup({
872
+ phaseText: '2B',
873
+ btnId: 'btn-2b-setup',
874
+ nextBtnId: 'btn-2b-sync',
875
+ masterSecret: 'test-sender-secret-phase2',
876
+ syncSalt: 'senderidentitysyncsaltphase2',
877
+ manageSalt: 'senderidentitymanagesaltphase2',
878
+ ib: 'test data',
879
+ data: { hello: 'world2', random: Math.random() }
880
+ });
881
+ });
882
+ }
883
+
884
+ function init2bSyncButton(): void {
885
+ const btn = document.getElementById('btn-2b-sync') as HTMLButtonElement | null;
886
+ if (!btn) { return; }
887
+ btn.addEventListener('click', () => {
888
+ performPhaseSync({
889
+ phaseText: '2B',
890
+ btnId: 'btn-2b-sync',
891
+ nextBtnId: 'btn-2b-check',
892
+ expectSyncFailure: true,
893
+ failureMessageSuffix: 'connect phase successfully run; subsequent sync errors expected in 2B'
894
+ });
895
+ });
896
+ }
897
+
898
+ function init2bCheckButton(): void {
899
+ const lc_fn = `${lc}[init2bCheckButton]`;
900
+ const btn = document.getElementById('btn-2b-check') as HTMLButtonElement | null;
901
+ if (!btn) { return; }
902
+
903
+ btn.addEventListener('click', async () => {
904
+ try {
905
+ debugger; // allow for walkthrough 2B check
906
+ devLog('2B Check: Asserting cryptographic and WebSocket state expectations...');
907
+
908
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
909
+ const space = await metaspace.getLocalUserSpace({});
910
+ if (!space) { throw new Error("No default space."); }
911
+
912
+ const domainI = debugState.domainI!;
913
+ const targetX = debugState.targetX!;
914
+ const xAddr = getIbGibAddr({ ibGib: targetX });
915
+
916
+ // 1. Resolve evolved sender identity I1 locally
917
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
918
+ const newSenderIdentityAddr = await metaspace.getLatestAddr({
919
+ addr: domainAddr,
920
+ space
921
+ });
922
+
923
+ if (!newSenderIdentityAddr) {
924
+ devLog('✗ Check: Evolved domain keystone tip I1 NOT found in local space!');
925
+ return;
926
+ }
927
+ devLog(`✓ Check: Evolved Domain Keystone (I1) created and stored locally.`);
928
+
929
+ const getRes = await metaspace.get({ addrs: [newSenderIdentityAddr], space });
930
+ const newSenderIdentity = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
931
+
932
+ if (!newSenderIdentity) {
933
+ devLog('✗ Check: Failed to load I1 from local space.');
934
+ return;
935
+ }
936
+
937
+ const syncProof = newSenderIdentity.data?.proofs?.find(p => p.claim?.verb === 'sync');
938
+ const sessionIdentityTjpAddr = syncProof?.claim?.target;
939
+ if (!sessionIdentityTjpAddr) {
940
+ devLog('✗ Check: I1 sync claim does not target a session keystone.');
941
+ return;
942
+ }
943
+ devLog(`✓ Check: Sync claim targets session keystone: ${sessionIdentityTjpAddr}`);
944
+
945
+ // 2. Load Session Keystone (S) locally and get latest tip
946
+ const latestSessionSAddr = await metaspace.getLatestAddr({
947
+ addr: sessionIdentityTjpAddr,
948
+ space
949
+ });
950
+ if (!latestSessionSAddr) {
951
+ devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
952
+ return;
953
+ }
954
+ devLog(`✓ Check: Found latest session keystone (S) locally.`);
955
+
956
+ const sRes = await metaspace.get({ addrs: [latestSessionSAddr], space });
957
+ const sessionIdentity = sRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
958
+
959
+ if (!sessionIdentity) {
960
+ devLog('✗ Check: Failed to load latest Session Keystone (S).');
961
+ return;
962
+ }
963
+
964
+ // Verify S contains connect and sync pools
965
+ const sPools = sessionIdentity.data?.challengePools ?? [];
966
+ const connectPool = sPools.find(p => p.id === POOL_ID_CONNECT);
967
+ const syncPool = sPools.find(p => p.id === POOL_ID_SYNC);
968
+
969
+ if (connectPool && syncPool) {
970
+ devLog('✓ Check: S contains both connect and sync challenge pools.');
971
+ } else {
972
+ devLog(`✗ Check: S is missing challenge pools! (connect: ${!!connectPool}, sync: ${!!syncPool})`);
973
+ return;
974
+ }
975
+
976
+ // Verify S was successfully evolved (depleted connect pool)
977
+ const remainingChallenges = Object.keys(connectPool.challenges).length;
978
+ if (remainingChallenges === 0) {
979
+ devLog('✓ Check: S connect pool successfully depleted (0 challenges remaining).');
980
+ } else {
981
+ devLog(`✗ Check: S connect pool not depleted! (${remainingChallenges} challenges remaining)`);
982
+ return;
983
+ }
984
+
985
+ // we don't check this yet until sync ping pong
986
+ // const targetAddrs = sessionIdentity.data?.frameDetails?.targetAddrs ?? [];
987
+ // if (targetAddrs.includes(xAddr)) {
988
+ // devLog('✓ Check: S is securely bound to target domain X in frameDetails.');
989
+ // } else {
990
+ // devLog('✗ Check: S is not bound to target domain X!');
991
+ // }
992
+
993
+ // 3. Verify WebSocket Connection is open and active
994
+ if (debugState.senderPeer && debugState.senderPeer.isSocketOpen) {
995
+ devLog('✓ Check: WebSocket connection is active and OPEN.');
996
+ } else {
997
+ devLog('✗ Check: WebSocket connection is NOT open or peer is missing!');
998
+ return;
999
+ }
1000
+
1001
+ devLog('🎉 ALL PHASE 2B CONNECT CHECKS PASSED FLAWLESSLY! ✓');
1002
+ btn.textContent = '✓ 2B All Passed';
1003
+
1004
+ // Enable Phase 3B setup
1005
+ const p3SetupBtn = document.getElementById('btn-3b-setup') as HTMLButtonElement | null;
1006
+ if (p3SetupBtn) { p3SetupBtn.disabled = false; }
1007
+
1008
+ } catch (error) {
1009
+ devLog(`✗ 2B Check FAILED: ${extractErrorMsg(error)}`);
1010
+ console.error(`${lc_fn} 2B Check error:`, error);
1011
+ }
1012
+ });
1013
+ }
1014
+
1015
+ function init3bSetupButton(): void {
1016
+ const btn = document.getElementById('btn-3b-setup') as HTMLButtonElement | null;
1017
+ if (!btn) { return; }
1018
+ btn.addEventListener('click', () => {
1019
+ performPhaseSetup({
1020
+ phaseText: '3B',
1021
+ btnId: 'btn-3b-setup',
1022
+ nextBtnId: 'btn-3b-sync',
1023
+ masterSecret: 'test-sender-secret-phase3',
1024
+ syncSalt: 'senderidentitysyncsaltphase3',
1025
+ manageSalt: 'senderidentitymanagesaltphase3',
1026
+ ib: 'test data 3b',
1027
+ data: { hello: 'world3', random: Math.random() }
1028
+ });
1029
+ });
1030
+ }
1031
+
1032
+ function init3bSyncButton(): void {
1033
+ const btn = document.getElementById('btn-3b-sync') as HTMLButtonElement | null;
1034
+ if (!btn) { return; }
1035
+ btn.addEventListener('click', () => {
1036
+ performPhaseSync({
1037
+ phaseText: '3B',
1038
+ btnId: 'btn-3b-sync',
1039
+ nextBtnId: 'btn-3b-check'
1040
+ });
1041
+ });
1042
+ }
1043
+
1044
+ function init3bCheckButton(): void {
1045
+ const lc_fn = `${lc}[init3bCheckButton]`;
1046
+ const btn = document.getElementById('btn-3b-check') as HTMLButtonElement | null;
1047
+ if (!btn) { return; }
1048
+
1049
+ btn.addEventListener('click', async () => {
1050
+ try {
1051
+ debugger; // allow for walkthrough 3B check
1052
+ devLog('3B Check: Asserting cryptographic and WebSocket state expectations...');
1053
+
1054
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
1055
+ const space = await metaspace.getLocalUserSpace({});
1056
+ if (!space) { throw new Error("No default space."); }
1057
+
1058
+ const domainI = debugState.domainI!;
1059
+ const targetX = debugState.targetX!;
1060
+ const xAddr = getIbGibAddr({ ibGib: targetX });
1061
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
1062
+
1063
+ // 1. Assert Target X exists locally (with evolved timeline) and get latest local graph
1064
+ const latestLocalXAddr = await metaspace.getLatestAddr({
1065
+ addr: xAddr,
1066
+ space
1067
+ });
1068
+ if (!latestLocalXAddr) {
1069
+ devLog('✗ Check: Evolved target X NOT found in local space!');
1070
+ return;
1071
+ }
1072
+ devLog(`✓ Check: Evolved target X found locally at: ${latestLocalXAddr}`);
1073
+
1074
+ const localXGraph = await metaspace.getDependencyGraph({
1075
+ ibGibAddr: latestLocalXAddr,
1076
+ space
1077
+ });
1078
+ if (!localXGraph || Object.keys(localXGraph).length === 0) {
1079
+ devLog('✗ Check: Failed to load target X dependency graph locally.');
1080
+ return;
1081
+ }
1082
+ devLog(`✓ Check: Target X local graph loaded (${Object.keys(localXGraph).length} nodes).`);
1083
+
1084
+ // 2. Fetch server's Target X graph via API bridge
1085
+ const apiBridge = new SpaceGibApiBridge();
1086
+ const serverGetRes = await apiBridge.getIbGibGraph(domainAddr, xAddr, true);
1087
+ if (!serverGetRes.success || !serverGetRes.graph) {
1088
+ devLog(`✗ Check: Failed to fetch target X graph from server: ${serverGetRes.message}`);
1089
+ return;
1090
+ }
1091
+ const serverXGraph = serverGetRes.graph;
1092
+ devLog(`✓ Check: Target X server graph loaded (${Object.keys(serverXGraph).length} nodes).`);
1093
+
1094
+ // 3. Verify graphs are equivalent using graphsAreEquivalent
1095
+ const equal = graphsAreEquivalent({ graphA: localXGraph, graphB: serverXGraph });
1096
+ if (equal) {
1097
+ devLog('✓ Check: Target X dependency graphs are equivalent on client and server.');
1098
+ } else {
1099
+ devLog('✗ Check: Target X dependency graphs mismatch between client and server!');
1100
+ return;
1101
+ }
1102
+
1103
+ // 4. Assert evolved sender identity I1 exists locally and on server
1104
+ const latestLocalIAddr = await metaspace.getLatestAddr({
1105
+ addr: domainAddr,
1106
+ space
1107
+ });
1108
+ if (!latestLocalIAddr) {
1109
+ devLog('✗ Check: Evolved domain keystone tip I1 NOT found in local space!');
1110
+ return;
1111
+ }
1112
+ devLog(`✓ Check: Evolved Domain Keystone (I1) exists locally.`);
1113
+
1114
+ const getRes = await metaspace.get({ addrs: [latestLocalIAddr], space });
1115
+ const localI = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
1116
+ if (!localI) {
1117
+ devLog('✗ Check: Failed to load I1 from local space.');
1118
+ return;
1119
+ }
1120
+
1121
+ const syncProof = localI.data?.proofs?.find(p => p.claim?.verb === 'sync');
1122
+ const sessionIdentityTjpAddr = syncProof?.claim?.target;
1123
+ if (!sessionIdentityTjpAddr) {
1124
+ devLog('✗ Check: I1 sync claim does not target a session keystone.');
1125
+ return;
1126
+ }
1127
+
1128
+ const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
1129
+ if (!serverIGetRes.success || !serverIGetRes.ibGib) {
1130
+ devLog(`✗ Check: Evolved Domain Keystone (I1) NOT found on server!`);
1131
+ return;
1132
+ }
1133
+ devLog(`✓ Check: Evolved Domain Keystone (I1) exists on server.`);
1134
+
1135
+ // 5. Assert session identity S tip exists locally and on server
1136
+ const latestLocalSAddr = await metaspace.getLatestAddr({
1137
+ addr: sessionIdentityTjpAddr,
1138
+ space
1139
+ });
1140
+ if (!latestLocalSAddr) {
1141
+ devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
1142
+ return;
1143
+ }
1144
+ devLog(`✓ Check: Found latest session keystone (S) locally: ${latestLocalSAddr}`);
1145
+
1146
+ const localSRes = await metaspace.get({ addrs: [latestLocalSAddr], space });
1147
+ const sessionS = localSRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
1148
+ if (!sessionS) {
1149
+ devLog('✗ Check: Failed to load latest Session Keystone (S) locally.');
1150
+ return;
1151
+ }
1152
+
1153
+ // Verify S evolved to correct n (n === 3 or 4)
1154
+ const n = sessionS.data?.n;
1155
+ devLog(`✓ Check: S tip evolved to n = ${n}.`);
1156
+ if (n !== 3 && n !== 4) {
1157
+ devLog(`⚠ Check: Expected S to evolve to n = 3 or 4, got n = ${n}. Continuing anyway...`);
1158
+ }
1159
+
1160
+ const serverSGetRes = await apiBridge.getIbGib(domainAddr, latestLocalSAddr);
1161
+ if (!serverSGetRes.success || !serverSGetRes.ibGib) {
1162
+ devLog(`✗ Check: Latest Session Keystone (S) NOT found on server!`);
1163
+ return;
1164
+ }
1165
+ devLog(`✓ Check: Latest Session Keystone (S) exists on server.`);
1166
+
1167
+ // 6. Assert S graph is identical on client and server
1168
+ const localSGraph = await metaspace.getDependencyGraph({
1169
+ ibGibAddr: latestLocalSAddr,
1170
+ space
1171
+ });
1172
+ const serverSGetGraphRes = await apiBridge.getIbGibGraph(domainAddr, sessionIdentityTjpAddr, true);
1173
+ if (!serverSGetGraphRes.success || !serverSGetGraphRes.graph) {
1174
+ devLog(`✗ Check: Failed to fetch session S graph from server: ${serverSGetGraphRes.message}`);
1175
+ return;
1176
+ }
1177
+ const serverSGraph = serverSGetGraphRes.graph;
1178
+
1179
+ const sGraphsEqual = graphsAreEquivalent({ graphA: localSGraph, graphB: serverSGraph });
1180
+ if (sGraphsEqual) {
1181
+ devLog('✓ Check: Session Keystone (S) dependency graphs are equivalent on client and server.');
1182
+ } else {
1183
+ devLog('✗ Check: Session Keystone (S) dependency graphs mismatch between client and server!');
1184
+ return;
1185
+ }
1186
+
1187
+ devLog('🎉 ALL PHASE 3B TRANSACTION SYNC CHECKS PASSED FLAWLESSLY! ✓');
1188
+ btn.textContent = '✓ 3B All Passed';
1189
+
1190
+ } catch (error) {
1191
+ devLog(`✗ 3B Check FAILED: ${extractErrorMsg(error)}`);
1192
+ console.error(`${lc_fn} 3B Check error:`, error);
1193
+ }
1194
+ });
1195
+ }
1196
+
520
1197
  // ---------------------------------------------------------------------------
521
1198
  // Dev Tools UI Init
522
1199
  // ---------------------------------------------------------------------------
@@ -534,6 +1211,15 @@ export function initDevTools(): void {
534
1211
  initCreateSessionKeystoneButton();
535
1212
  initEvolveDomainKeystoneButton();
536
1213
  initPerformSyncButton();
1214
+ init1bSetupButton();
1215
+ init1bSyncButton();
1216
+ init1bCheckButton();
1217
+ init2bSetupButton();
1218
+ init2bSyncButton();
1219
+ init2bCheckButton();
1220
+ init3bSetupButton();
1221
+ init3bSyncButton();
1222
+ init3bCheckButton();
537
1223
  } catch (error) {
538
1224
  console.error(`${lc_fn} ${extractErrorMsg(error)}`);
539
1225
  }