@tatchi-xyz/sdk 0.17.0 → 0.18.0
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/cjs/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
- package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/cjs/core/EmailRecovery/index.js +32 -20
- package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/index.js +1 -0
- package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/relay.js +23 -1
- package/dist/cjs/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/cjs/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js +6 -2
- package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/core/rpcCalls.js +8 -0
- package/dist/cjs/core/rpcCalls.js.map +1 -1
- package/dist/cjs/index.js +6 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
- package/dist/cjs/react/hooks/usePreconnectWalletAssets.js +27 -32
- package/dist/cjs/react/hooks/usePreconnectWalletAssets.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +32 -20
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +1 -0
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +6 -2
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/rpcCalls.js +8 -0
- package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
- package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/esm/core/EmailRecovery/index.js +28 -21
- package/dist/esm/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/relay.js +23 -1
- package/dist/esm/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/esm/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/router.js +7 -3
- package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/core/rpcCalls.js +8 -1
- package/dist/esm/core/rpcCalls.js.map +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
- package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
- package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
- package/dist/esm/react/hooks/usePreconnectWalletAssets.js +27 -32
- package/dist/esm/react/hooks/usePreconnectWalletAssets.js.map +1 -1
- package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +28 -21
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +7 -3
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js +8 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{router-BLFegW7J.js → router-DuGYOd3G.js} +6 -9
- package/dist/esm/sdk/{rpcCalls-DEv9x5-f.js → rpcCalls-BQrJMTdg.js} +2 -2
- package/dist/esm/sdk/{rpcCalls-OhgEeFig.js → rpcCalls-YVeUVMk2.js} +8 -1
- package/dist/esm/sdk/wallet-iframe-host.js +624 -471
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts +25 -0
- package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts.map +1 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts +1 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +35 -6
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts +2 -2
- package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/relay.d.ts +2 -1
- package/dist/types/src/core/TatchiPasskey/relay.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/IframeTransport.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts +3 -3
- package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
- package/dist/types/src/core/rpcCalls.d.ts +9 -0
- package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/react/hooks/usePreconnectWalletAssets.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IframeTransport.js","names":["isObject","WebAuthnBridgeMessage","timeout: number | undefined","pub: PublicKeyCredentialCreationOptions","serializeRegistrationCredentialWithPRF","pub: PublicKeyCredentialRequestOptions","serializeAuthenticationCredentialWithPRF"],"sources":["../../../../../../../../src/core/WalletIframe/client/IframeTransport.ts"],"sourcesContent":["/**\n * IframeTransport - Client-Side Communication Layer\n *\n * This module handles the low-level iframe management and connection establishment.\n * It encapsulates all the complex browser-specific logic for safely creating and\n * connecting to the wallet service iframe.\n *\n * Key Responsibilities:\n * - Iframe Creation: Creates and mounts the iframe element with proper security attributes\n * - Security Hardening: Sets appropriate allow/sandbox attributes for WebAuthn and clipboard access\n * - Load Event Handling: Waits for iframe load to avoid postMessage races\n * - Connection Handshake: Performs robust CONNECT → READY handshake using MessageChannel\n * - Boot Latency Handling: Manages cross-origin boot delays with SERVICE_HOST_BOOTED hints\n * - Connection Deduplication: Prevents multiple concurrent connection attempts\n * - Error Handling: Provides clear error messages for connection failures\n *\n * Security Model:\n * - Uses explicit allow attributes for WebAuthn and clipboard permissions\n * - Avoids sandboxing for cross-origin deployments (prevents MessagePort transfer issues)\n * - Validates wallet origin URLs to prevent security issues\n * - Uses MessageChannel for secure, bidirectional communication\n *\n * Browser Compatibility:\n * - Handles various browser quirks around iframe loading and MessagePort transfer\n * - Provides fallback behavior for different browser implementations\n * - Manages timing issues with cross-origin iframe boot sequences\n */\n\nimport type { ChildToParentEnvelope } from '../shared/messages';\nimport { isObject } from '../validation';\nimport { serializeRegistrationCredentialWithPRF, serializeAuthenticationCredentialWithPRF } from '../../WebAuthnManager/credentialsHelpers';\nimport { ensureOverlayBase } from './overlay-styles';\nimport { WebAuthnBridgeMessage } from '../../WebAuthnManager/WebAuthnFallbacks';\n\n// Message constants (typed string literals, tree‑shake friendly)\nexport const IframeMessage = {\n Connect: 'CONNECT',\n Ready: 'READY',\n HostBooted: 'SERVICE_HOST_BOOTED',\n HostDebugOrigin: 'SERVICE_HOST_DEBUG_ORIGIN',\n HostLog: 'SERVICE_HOST_LOG',\n} as const;\n\n// Bridge request payloads\ntype CreateReq = { requestId?: string; publicKey?: PublicKeyCredentialCreationOptions };\ntype GetReq = { requestId?: string; publicKey?: PublicKeyCredentialRequestOptions };\n\nexport interface IframeTransportOptions {\n walletOrigin: string; // e.g., https://wallet.example.com\n servicePath?: string; // default '/wallet-service'\n connectTimeoutMs?: number; // total budget for handshake retries\n testOptions?: {\n routerId?: string; // identity tag for the iframe element\n ownerTag?: string; // e.g., 'app' | 'tests'\n };\n}\n\nexport class IframeTransport {\n private readonly opts: Required<IframeTransportOptions>;\n private iframeEl: HTMLIFrameElement | null = null;\n private serviceBooted = false; // set when wallet host sends SERVICE_HOST_BOOTED (best-effort only)\n private connectInFlight: Promise<MessagePort> | null = null;\n private readonly walletServiceUrl: URL;\n private readonly walletOrigin: string;\n private readonly testOptions: { routerId?: string; ownerTag?: string };\n\n constructor(options: IframeTransportOptions) {\n this.opts = {\n servicePath: '/wallet-service',\n connectTimeoutMs: 8000,\n ...options,\n } as Required<IframeTransportOptions>;\n\n // Chrome extension resources require a recognized file extension to be treated as HTML.\n // A no-extension path like `/wallet-service` is treated as a download (or non-HTML),\n // so we alias the default path to `/wallet-service.html` for extension origins.\n //\n // This keeps the *config* stable (`walletServicePath` can remain `/wallet-service`)\n // while making the embedded service page actually load/execute in an iframe.\n try {\n const walletUrl = new URL(this.opts.walletOrigin);\n if (walletUrl.protocol === 'chrome-extension:') {\n const trimmed = (this.opts.servicePath || '/wallet-service').replace(/\\/+$/, '');\n if (trimmed === '/wallet-service') {\n this.opts.servicePath = '/wallet-service.html';\n }\n }\n } catch {}\n\n try {\n this.walletServiceUrl = new URL(this.opts.servicePath, this.opts.walletOrigin);\n } catch (err) {\n throw new Error(`[IframeTransport] Invalid wallet origin (${options.walletOrigin}) or servicePath (${options.servicePath || '/wallet-service'})`);\n }\n this.walletOrigin = this.walletServiceUrl.origin;\n this.testOptions = {\n routerId: options.testOptions?.routerId,\n ownerTag: options.testOptions?.ownerTag,\n };\n\n // Listen for a best-effort boot hint from the wallet host. Not required for correctness,\n // but helps reduce redundant CONNECT posts while the host script is still booting.\n try {\n window.addEventListener('message', (e) => {\n const data = e.data as unknown;\n if (!isObject(data)) return;\n const type = (data as { type?: unknown }).type;\n if (type === IframeMessage.HostBooted && e.origin === this.walletOrigin) {\n this.serviceBooted = true;\n return;\n }\n if (type === IframeMessage.HostDebugOrigin) {\n }\n if (type === IframeMessage.HostLog) {\n // Only surface wallet logs when debug is enabled\n console.debug('[IframeTransport][wallet-log]', (data as { payload?: unknown }).payload);\n }\n // Parent‑performed WebAuthn bridge for Safari cross‑origin scenarios.\n // Only accept requests originating from the wallet iframe origin.\n if (e.origin === this.walletOrigin) {\n if (type === WebAuthnBridgeMessage.Create || type === WebAuthnBridgeMessage.Get) {\n // Delegate to common bridge handler\n this.performWebAuthnBridge(type as typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get, data, e);\n return;\n }\n }\n });\n } catch {}\n }\n\n /** Returns the underlying iframe element if it exists. */\n getIframeEl(): HTMLIFrameElement | null { return this.iframeEl; }\n\n /**\n * Guardrail: prevent multiple overlay iframes accumulating when apps accidentally\n * create more than one WalletIframeRouter/TatchiPasskey instance.\n */\n private removeExistingOverlaysForOrigin(): void {\n try {\n const isDev = (() => {\n const env = (globalThis as any)?.process?.env?.NODE_ENV;\n if (env && env !== 'production') return true;\n const h = window.location.hostname || '';\n if (/localhost|127\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)|\\.local(?:host)?$/i.test(h)) return true;\n return false;\n })();\n\n const existing = Array.from(document.querySelectorAll('iframe.w3a-wallet-overlay')) as HTMLIFrameElement[];\n const matches = existing.filter((el) => {\n const dsOrigin = (el as any)?.dataset?.w3aOrigin as string | undefined;\n if (dsOrigin) return dsOrigin === this.walletOrigin;\n try { return new URL(el.src).origin === this.walletOrigin; } catch { return false; }\n });\n\n if (!matches.length) return;\n\n if (isDev) {\n const routerIds = matches\n .map((el) => (el as any)?.dataset?.w3aRouterId)\n .filter((v): v is string => typeof v === 'string' && v.length > 0);\n console.warn(\n `[IframeTransport] Found existing wallet overlay iframe(s) for ${this.walletOrigin}. This usually indicates multiple SDK instances. Removing old iframe(s) to avoid duplicates.`,\n { count: matches.length, routerIds }\n );\n }\n\n for (const el of matches) {\n try { el.remove(); } catch {}\n }\n } catch {}\n }\n\n /** Ensure the iframe element exists and is appended to the DOM. Idempotent. */\n ensureIframeMounted(): HTMLIFrameElement {\n if (this.iframeEl) {\n return this.iframeEl;\n }\n\n this.removeExistingOverlaysForOrigin();\n\n const iframe = document.createElement('iframe');\n // Hidden by default via CSS classes; higher layers toggle state using overlay-styles.\n iframe.classList.add('w3a-wallet-overlay', 'is-hidden');\n // Ensure the base overlay stylesheet is installed early so computed styles\n // (opacity/pointer-events) reflect the hidden state immediately after mount.\n try { ensureOverlayBase(iframe); } catch {}\n // Ensure no initial footprint even before stylesheet attaches\n iframe.setAttribute('width', '0');\n iframe.setAttribute('height', '0');\n iframe.setAttribute('aria-hidden', 'true');\n iframe.setAttribute('tabindex', '-1');\n // Hint higher priority fetch for the iframe document on supporting browsers\n iframe.setAttribute('loading', 'eager');\n iframe.setAttribute('fetchpriority', 'high');\n\n iframe.dataset.w3aRouterId = this.testOptions?.routerId || '';\n if (this.testOptions?.ownerTag) iframe.dataset.w3aOwner = this.testOptions.ownerTag;\n iframe.dataset.w3aOrigin = this.walletOrigin;\n\n // Delegate WebAuthn + clipboard capabilities to the wallet origin frame\n try {\n iframe.setAttribute('allow', this.buildAllowAttr(this.walletOrigin));\n } catch {\n iframe.setAttribute('allow', \"publickey-credentials-get 'self'; publickey-credentials-create 'self'; clipboard-read; clipboard-write\");\n }\n\n // Track load state to guard against races where we post before content is listening\n iframe._svc_loaded = false;\n iframe.addEventListener('load', () => { iframe._svc_loaded = true; }, { once: true });\n\n const src = this.walletServiceUrl.toString();\n console.debug('[IframeTransport] mount: external origin', src);\n iframe.src = src;\n\n document.body.appendChild(iframe);\n console.debug('[IframeTransport] mount: iframe appended');\n this.iframeEl = iframe;\n return iframe;\n }\n\n /**\n * Connect to the wallet iframe using a MessageChannel handshake.\n * - Repeatedly posts {type:'CONNECT'} with a fresh port until a 'READY' message arrives\n * - Times out after connectTimeoutMs\n * - Deduplicates concurrent calls and returns the same MessagePort promise\n */\n async connect(): Promise<MessagePort> {\n if (this.connectInFlight) return this.connectInFlight;\n this.connectInFlight = (async () => {\n const iframe = this.ensureIframeMounted();\n\n // Ensure load fired at least once so the host script can attach listeners\n await this.waitForLoad(iframe);\n\n // For cross-origin pages, give the host only a very brief moment to boot its script\n // Keep this low to avoid adding noticeable latency to the first CONNECT attempt.\n // The handshake will continue retrying regardless, so a shorter wait improves TTFB.\n const bootWaitMs = Math.min(this.opts.connectTimeoutMs / 12, 300);\n const startBoot = Date.now();\n while (!this.serviceBooted && (Date.now() - startBoot) < bootWaitMs) {\n await new Promise(r => setTimeout(r, 50));\n }\n\n let resolved = false;\n let attempt = 0;\n let warnedNullOrigin = false;\n const start = Date.now();\n const overallTimeout = this.opts.connectTimeoutMs;\n\n const port = await new Promise<MessagePort>((resolve, reject) => {\n const tick = () => {\n if (resolved) return;\n const elapsed = Date.now() - start;\n if (elapsed >= overallTimeout) {\n console.debug('[IframeTransport] handshake timeout after %d ms', elapsed);\n return reject(new Error('Wallet iframe READY timeout'));\n }\n\n attempt += 1;\n const channel = new MessageChannel();\n const port1 = channel.port1;\n const port2 = channel.port2;\n const cleanup = () => { try { port1.onmessage = null; } catch {} };\n\n port1.onmessage = (e: MessageEvent<ChildToParentEnvelope>) => {\n const data = e.data;\n if (data.type === IframeMessage.Ready) {\n resolved = true;\n cleanup();\n port1.start?.();\n return resolve(port1);\n }\n };\n\n // Ensure the receiving side is actively listening before we post the CONNECT\n try { port1.start?.(); } catch {}\n\n const cw = iframe.contentWindow;\n if (!cw) {\n cleanup();\n return reject(new Error('Wallet iframe window missing'));\n }\n // Explicitly target the wallet origin so Chromium delivers the MessagePort\n // transfer across origins. Using '*' can silently drop the transferable\n // port in stricter environments, preventing the host from ever adopting it.\n //\n // However, some dev setups (e.g., mDNS/.local + reverse proxy ports) can\n // result in the iframe document resolving to a slightly different serialized\n // origin (e.g., host without the expected port). In those cases, the strict\n // target will never deliver. As a pragmatic fallback for development, we\n // periodically attempt with '*' so the wallet host can adopt the port and\n // reply with READY. Subsequent communication uses MessagePort, not window.postMessage.\n // Try strict origin first, but fall back to '*' more frequently in dev to\n // avoid stalls when local origins serialize differently (e.g., iOS + mDNS).\n // Using '*' here only affects this CONNECT; subsequent traffic uses MessagePort.\n //\n // Prefer wildcard target until we have observed SERVICE_HOST_BOOTED,\n // which indicates the iframe has a stable, non-opaque origin and is\n // ready to adopt a MessagePort. This avoids noisy 'null' origin\n // warnings while still allowing strict-origin delivery as soon as\n // the host is booted.\n const targetOrigin = this.serviceBooted ? this.walletOrigin : '*';\n ({ warnedNullOrigin } = this.postConnectMessage(\n cw,\n { type: IframeMessage.Connect },\n port2,\n targetOrigin,\n warnedNullOrigin,\n elapsed,\n attempt,\n ));\n\n // Schedule next tick if not resolved yet (light backoff to reduce spam)\n const interval = attempt < 10 ? 200 : attempt < 20 ? 400 : 800;\n setTimeout(() => { if (!resolved) tick(); }, interval);\n };\n\n tick();\n });\n\n return port;\n })();\n\n try {\n return await this.connectInFlight;\n } finally {\n this.connectInFlight = null;\n }\n }\n\n private postConnectMessage(\n cw: Window,\n data: unknown,\n port2: MessagePort,\n targetOrigin: string,\n warnedNullOrigin: boolean,\n elapsed: number,\n attempt: number,\n ): { warnedNullOrigin: boolean } {\n try {\n cw.postMessage(data, targetOrigin, [port2]);\n return { warnedNullOrigin };\n } catch (e) {\n const message = e instanceof Error ? e.message ?? String(e) : String(e);\n if (!warnedNullOrigin && message.includes(\"'null'\")) {\n warnedNullOrigin = true;\n console.warn('[IframeTransport] CONNECT blocked; iframe origin appears to be null. Check that %s is reachable and responds with Cross-Origin-Resource-Policy: cross-origin.', this.walletServiceUrl.toString());\n }\n // Attempt wildcard fallback and continue retries\n try { cw.postMessage(data, '*', [port2]); } catch {}\n console.debug('[IframeTransport] CONNECT attempt %d threw after %d ms; retrying.', attempt, elapsed);\n return { warnedNullOrigin };\n }\n }\n\n /** Guard against posting to the iframe before it has fired load. */\n private async waitForLoad(iframe: HTMLIFrameElement): Promise<void> {\n if (iframe._svc_loaded) return;\n await new Promise<void>((resolve) => {\n try {\n let timeout: number | undefined;\n iframe.addEventListener?.('load', () => {\n if (typeof timeout === 'number') {\n clearTimeout(timeout);\n }\n resolve();\n }, { once: true });\n // Safety net: resolve shortly even if load listener fails to attach\n timeout = window.setTimeout(() => {\n console.debug('[IframeTransport] waitForLoad did not observe load event within 150ms; continuing');\n resolve();\n }, 150);\n } catch {\n resolve();\n }\n });\n }\n\n private buildAllowAttr(walletOrigin: string): string {\n return `publickey-credentials-get 'self' ${walletOrigin}; publickey-credentials-create 'self' ${walletOrigin}; clipboard-read; clipboard-write`;\n }\n\n private postBridgeResult(\n source: WindowProxy | null,\n type: 'WALLET_WEBAUTHN_CREATE_RESULT' | 'WALLET_WEBAUTHN_GET_RESULT',\n requestId: string,\n ok: boolean,\n payload: { credential?: unknown; error?: string },\n ): void {\n // Reply directly to the requesting window; wildcard target avoids transient\n // 'null' origin warnings during early navigation while remaining safe since\n // we already validated the sender's origin before bridging.\n source?.postMessage({ type, requestId, ok, ...payload }, '*');\n }\n\n private performWebAuthnBridge(\n kind: typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get,\n raw: unknown,\n e: MessageEvent,\n ): void {\n if (kind === WebAuthnBridgeMessage.Create) {\n void this.handleWebAuthnCreate(raw as CreateReq, e);\n return;\n }\n // kind === 'WALLET_WEBAUTHN_GET'\n void this.handleWebAuthnGet(raw as GetReq, e);\n }\n\n private async handleWebAuthnCreate(req: CreateReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialCreationOptions);\n const rpName = src.rp?.name || 'WebAuthn';\n const rpId = src.rp?.id || window.location.hostname;\n const pub: PublicKeyCredentialCreationOptions = { ...src, rp: { name: rpName, id: rpId } };\n console.debug('[IframeTransport][bridge] CREATE received', { requestId, from: e.origin, rpId: pub.rp?.id });\n const cred = await navigator.credentials.create({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeRegistrationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] CREATE ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] CREATE failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n\n private async handleWebAuthnGet(req: GetReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialRequestOptions);\n const rpId = src.rpId || window.location.hostname;\n const pub: PublicKeyCredentialRequestOptions = { ...src, rpId };\n console.debug('[IframeTransport][bridge] GET received', { requestId, from: e.origin, rpId: pub.rpId });\n const cred = await navigator.credentials.get({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeAuthenticationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] GET ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] GET failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IframeTransport.js","names":["isObject","WebAuthnBridgeMessage","timeout: number | undefined","pub: PublicKeyCredentialCreationOptions","serializeRegistrationCredentialWithPRF","pub: PublicKeyCredentialRequestOptions","serializeAuthenticationCredentialWithPRF"],"sources":["../../../../../../../../src/core/WalletIframe/client/IframeTransport.ts"],"sourcesContent":["/**\n * IframeTransport - Client-Side Communication Layer\n *\n * This module handles the low-level iframe management and connection establishment.\n * It encapsulates all the complex browser-specific logic for safely creating and\n * connecting to the wallet service iframe.\n *\n * Key Responsibilities:\n * - Iframe Creation: Creates and mounts the iframe element with proper security attributes\n * - Security Hardening: Sets appropriate allow/sandbox attributes for WebAuthn and clipboard access\n * - Load Event Handling: Waits for iframe load to avoid postMessage races\n * - Connection Handshake: Performs robust CONNECT → READY handshake using MessageChannel\n * - Boot Latency Handling: Manages cross-origin boot delays with SERVICE_HOST_BOOTED hints\n * - Connection Deduplication: Prevents multiple concurrent connection attempts\n * - Error Handling: Provides clear error messages for connection failures\n *\n * Security Model:\n * - Uses explicit allow attributes for WebAuthn and clipboard permissions\n * - Avoids sandboxing for cross-origin deployments (prevents MessagePort transfer issues)\n * - Validates wallet origin URLs to prevent security issues\n * - Uses MessageChannel for secure, bidirectional communication\n *\n * Browser Compatibility:\n * - Handles various browser quirks around iframe loading and MessagePort transfer\n * - Provides fallback behavior for different browser implementations\n * - Manages timing issues with cross-origin iframe boot sequences\n */\n\nimport type { ChildToParentEnvelope } from '../shared/messages';\nimport { isObject } from '../validation';\nimport { serializeRegistrationCredentialWithPRF, serializeAuthenticationCredentialWithPRF } from '../../WebAuthnManager/credentialsHelpers';\nimport { ensureOverlayBase } from './overlay-styles';\nimport { WebAuthnBridgeMessage } from '../../WebAuthnManager/WebAuthnFallbacks';\n\n// Message constants (typed string literals, tree‑shake friendly)\nexport const IframeMessage = {\n Connect: 'CONNECT',\n Ready: 'READY',\n HostBooted: 'SERVICE_HOST_BOOTED',\n HostDebugOrigin: 'SERVICE_HOST_DEBUG_ORIGIN',\n HostLog: 'SERVICE_HOST_LOG',\n} as const;\n\n// Bridge request payloads\ntype CreateReq = { requestId?: string; publicKey?: PublicKeyCredentialCreationOptions };\ntype GetReq = { requestId?: string; publicKey?: PublicKeyCredentialRequestOptions };\n\nexport interface IframeTransportOptions {\n walletOrigin: string; // e.g., https://wallet.example.com\n servicePath?: string; // default '/wallet-service'\n connectTimeoutMs?: number; // total budget for handshake retries\n testOptions?: {\n routerId?: string; // identity tag for the iframe element\n ownerTag?: string; // e.g., 'app' | 'tests'\n };\n}\n\nexport class IframeTransport {\n private readonly opts: Required<IframeTransportOptions>;\n private iframeEl: HTMLIFrameElement | null = null;\n private serviceBooted = false; // set when wallet host sends SERVICE_HOST_BOOTED (best-effort only)\n private connectInFlight: Promise<MessagePort> | null = null;\n private readonly walletServiceUrl: URL;\n private readonly walletOrigin: string;\n private readonly testOptions: { routerId?: string; ownerTag?: string };\n\n constructor(options: IframeTransportOptions) {\n this.opts = {\n servicePath: '/wallet-service',\n connectTimeoutMs: 8000,\n ...options,\n } as Required<IframeTransportOptions>;\n\n try {\n this.walletServiceUrl = new URL(this.opts.servicePath, this.opts.walletOrigin);\n } catch (err) {\n throw new Error(`[IframeTransport] Invalid wallet origin (${options.walletOrigin}) or servicePath (${options.servicePath || '/wallet-service'})`);\n }\n this.walletOrigin = this.walletServiceUrl.origin;\n this.testOptions = {\n routerId: options.testOptions?.routerId,\n ownerTag: options.testOptions?.ownerTag,\n };\n\n // Listen for a best-effort boot hint from the wallet host. Not required for correctness,\n // but helps reduce redundant CONNECT posts while the host script is still booting.\n try {\n window.addEventListener('message', (e) => {\n const data = e.data as unknown;\n if (!isObject(data)) return;\n const type = (data as { type?: unknown }).type;\n if (type === IframeMessage.HostBooted && e.origin === this.walletOrigin) {\n this.serviceBooted = true;\n return;\n }\n if (type === IframeMessage.HostDebugOrigin) {\n }\n if (type === IframeMessage.HostLog) {\n // Only surface wallet logs when debug is enabled\n console.debug('[IframeTransport][wallet-log]', (data as { payload?: unknown }).payload);\n }\n // Parent‑performed WebAuthn bridge for Safari cross‑origin scenarios.\n // Only accept requests originating from the wallet iframe origin.\n if (e.origin === this.walletOrigin) {\n if (type === WebAuthnBridgeMessage.Create || type === WebAuthnBridgeMessage.Get) {\n // Delegate to common bridge handler\n this.performWebAuthnBridge(type as typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get, data, e);\n return;\n }\n }\n });\n } catch {}\n }\n\n /** Returns the underlying iframe element if it exists. */\n getIframeEl(): HTMLIFrameElement | null { return this.iframeEl; }\n\n /**\n * Guardrail: prevent multiple overlay iframes accumulating when apps accidentally\n * create more than one WalletIframeRouter/TatchiPasskey instance.\n */\n private removeExistingOverlaysForOrigin(): void {\n try {\n const isDev = (() => {\n const env = (globalThis as any)?.process?.env?.NODE_ENV;\n if (env && env !== 'production') return true;\n const h = window.location.hostname || '';\n if (/localhost|127\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)|\\.local(?:host)?$/i.test(h)) return true;\n return false;\n })();\n\n const existing = Array.from(document.querySelectorAll('iframe.w3a-wallet-overlay')) as HTMLIFrameElement[];\n const matches = existing.filter((el) => {\n const dsOrigin = (el as any)?.dataset?.w3aOrigin as string | undefined;\n if (dsOrigin) return dsOrigin === this.walletOrigin;\n try { return new URL(el.src).origin === this.walletOrigin; } catch { return false; }\n });\n\n if (!matches.length) return;\n\n if (isDev) {\n const routerIds = matches\n .map((el) => (el as any)?.dataset?.w3aRouterId)\n .filter((v): v is string => typeof v === 'string' && v.length > 0);\n console.warn(\n `[IframeTransport] Found existing wallet overlay iframe(s) for ${this.walletOrigin}. This usually indicates multiple SDK instances. Removing old iframe(s) to avoid duplicates.`,\n { count: matches.length, routerIds }\n );\n }\n\n for (const el of matches) {\n try { el.remove(); } catch {}\n }\n } catch {}\n }\n\n /** Ensure the iframe element exists and is appended to the DOM. Idempotent. */\n ensureIframeMounted(): HTMLIFrameElement {\n if (this.iframeEl) {\n return this.iframeEl;\n }\n\n this.removeExistingOverlaysForOrigin();\n\n const iframe = document.createElement('iframe');\n // Hidden by default via CSS classes; higher layers toggle state using overlay-styles.\n iframe.classList.add('w3a-wallet-overlay', 'is-hidden');\n // Ensure the base overlay stylesheet is installed early so computed styles\n // (opacity/pointer-events) reflect the hidden state immediately after mount.\n try { ensureOverlayBase(iframe); } catch {}\n // Ensure no initial footprint even before stylesheet attaches\n iframe.setAttribute('width', '0');\n iframe.setAttribute('height', '0');\n iframe.setAttribute('aria-hidden', 'true');\n iframe.setAttribute('tabindex', '-1');\n // Hint higher priority fetch for the iframe document on supporting browsers\n iframe.setAttribute('loading', 'eager');\n iframe.setAttribute('fetchpriority', 'high');\n\n iframe.dataset.w3aRouterId = this.testOptions?.routerId || '';\n if (this.testOptions?.ownerTag) iframe.dataset.w3aOwner = this.testOptions.ownerTag;\n iframe.dataset.w3aOrigin = this.walletOrigin;\n\n // Delegate WebAuthn + clipboard capabilities to the wallet origin frame\n try {\n iframe.setAttribute('allow', this.buildAllowAttr(this.walletOrigin));\n } catch {\n iframe.setAttribute('allow', \"publickey-credentials-get 'self'; publickey-credentials-create 'self'; clipboard-read; clipboard-write\");\n }\n\n // Track load state to guard against races where we post before content is listening\n iframe._svc_loaded = false;\n iframe.addEventListener('load', () => { iframe._svc_loaded = true; }, { once: true });\n\n const src = this.walletServiceUrl.toString();\n console.debug('[IframeTransport] mount: external origin', src);\n iframe.src = src;\n\n document.body.appendChild(iframe);\n console.debug('[IframeTransport] mount: iframe appended');\n this.iframeEl = iframe;\n return iframe;\n }\n\n /**\n * Connect to the wallet iframe using a MessageChannel handshake.\n * - Repeatedly posts {type:'CONNECT'} with a fresh port until a 'READY' message arrives\n * - Times out after connectTimeoutMs\n * - Deduplicates concurrent calls and returns the same MessagePort promise\n */\n async connect(): Promise<MessagePort> {\n if (this.connectInFlight) return this.connectInFlight;\n this.connectInFlight = (async () => {\n const iframe = this.ensureIframeMounted();\n\n // Ensure load fired at least once so the host script can attach listeners\n await this.waitForLoad(iframe);\n\n // For cross-origin pages, give the host only a very brief moment to boot its script\n // Keep this low to avoid adding noticeable latency to the first CONNECT attempt.\n // The handshake will continue retrying regardless, so a shorter wait improves TTFB.\n const bootWaitMs = Math.min(this.opts.connectTimeoutMs / 12, 300);\n const startBoot = Date.now();\n while (!this.serviceBooted && (Date.now() - startBoot) < bootWaitMs) {\n await new Promise(r => setTimeout(r, 50));\n }\n\n let resolved = false;\n let attempt = 0;\n let warnedNullOrigin = false;\n const start = Date.now();\n const overallTimeout = this.opts.connectTimeoutMs;\n\n const port = await new Promise<MessagePort>((resolve, reject) => {\n const tick = () => {\n if (resolved) return;\n const elapsed = Date.now() - start;\n if (elapsed >= overallTimeout) {\n console.debug('[IframeTransport] handshake timeout after %d ms', elapsed);\n return reject(new Error('Wallet iframe READY timeout'));\n }\n\n attempt += 1;\n const channel = new MessageChannel();\n const port1 = channel.port1;\n const port2 = channel.port2;\n const cleanup = () => { try { port1.onmessage = null; } catch {} };\n\n port1.onmessage = (e: MessageEvent<ChildToParentEnvelope>) => {\n const data = e.data;\n if (data.type === IframeMessage.Ready) {\n resolved = true;\n cleanup();\n port1.start?.();\n return resolve(port1);\n }\n };\n\n // Ensure the receiving side is actively listening before we post the CONNECT\n try { port1.start?.(); } catch {}\n\n const cw = iframe.contentWindow;\n if (!cw) {\n cleanup();\n return reject(new Error('Wallet iframe window missing'));\n }\n // Explicitly target the wallet origin so Chromium delivers the MessagePort\n // transfer across origins. Using '*' can silently drop the transferable\n // port in stricter environments, preventing the host from ever adopting it.\n //\n // However, some dev setups (e.g., mDNS/.local + reverse proxy ports) can\n // result in the iframe document resolving to a slightly different serialized\n // origin (e.g., host without the expected port). In those cases, the strict\n // target will never deliver. As a pragmatic fallback for development, we\n // periodically attempt with '*' so the wallet host can adopt the port and\n // reply with READY. Subsequent communication uses MessagePort, not window.postMessage.\n // Try strict origin first, but fall back to '*' more frequently in dev to\n // avoid stalls when local origins serialize differently (e.g., iOS + mDNS).\n // Using '*' here only affects this CONNECT; subsequent traffic uses MessagePort.\n //\n // Prefer wildcard target until we have observed SERVICE_HOST_BOOTED,\n // which indicates the iframe has a stable, non-opaque origin and is\n // ready to adopt a MessagePort. This avoids noisy 'null' origin\n // warnings while still allowing strict-origin delivery as soon as\n // the host is booted.\n const targetOrigin = this.serviceBooted ? this.walletOrigin : '*';\n ({ warnedNullOrigin } = this.postConnectMessage(\n cw,\n { type: IframeMessage.Connect },\n port2,\n targetOrigin,\n warnedNullOrigin,\n elapsed,\n attempt,\n ));\n\n // Schedule next tick if not resolved yet (light backoff to reduce spam)\n const interval = attempt < 10 ? 200 : attempt < 20 ? 400 : 800;\n setTimeout(() => { if (!resolved) tick(); }, interval);\n };\n\n tick();\n });\n\n return port;\n })();\n\n try {\n return await this.connectInFlight;\n } finally {\n this.connectInFlight = null;\n }\n }\n\n private postConnectMessage(\n cw: Window,\n data: unknown,\n port2: MessagePort,\n targetOrigin: string,\n warnedNullOrigin: boolean,\n elapsed: number,\n attempt: number,\n ): { warnedNullOrigin: boolean } {\n try {\n cw.postMessage(data, targetOrigin, [port2]);\n return { warnedNullOrigin };\n } catch (e) {\n const message = e instanceof Error ? e.message ?? String(e) : String(e);\n if (!warnedNullOrigin && message.includes(\"'null'\")) {\n warnedNullOrigin = true;\n console.warn('[IframeTransport] CONNECT blocked; iframe origin appears to be null. Check that %s is reachable and responds with Cross-Origin-Resource-Policy: cross-origin.', this.walletServiceUrl.toString());\n }\n // Attempt wildcard fallback and continue retries\n try { cw.postMessage(data, '*', [port2]); } catch {}\n console.debug('[IframeTransport] CONNECT attempt %d threw after %d ms; retrying.', attempt, elapsed);\n return { warnedNullOrigin };\n }\n }\n\n /** Guard against posting to the iframe before it has fired load. */\n private async waitForLoad(iframe: HTMLIFrameElement): Promise<void> {\n if (iframe._svc_loaded) return;\n await new Promise<void>((resolve) => {\n try {\n let timeout: number | undefined;\n iframe.addEventListener?.('load', () => {\n if (typeof timeout === 'number') {\n clearTimeout(timeout);\n }\n resolve();\n }, { once: true });\n // Safety net: resolve shortly even if load listener fails to attach\n timeout = window.setTimeout(() => {\n console.debug('[IframeTransport] waitForLoad did not observe load event within 150ms; continuing');\n resolve();\n }, 150);\n } catch {\n resolve();\n }\n });\n }\n\n private buildAllowAttr(walletOrigin: string): string {\n return `publickey-credentials-get 'self' ${walletOrigin}; publickey-credentials-create 'self' ${walletOrigin}; clipboard-read; clipboard-write`;\n }\n\n private postBridgeResult(\n source: WindowProxy | null,\n type: 'WALLET_WEBAUTHN_CREATE_RESULT' | 'WALLET_WEBAUTHN_GET_RESULT',\n requestId: string,\n ok: boolean,\n payload: { credential?: unknown; error?: string },\n ): void {\n // Reply directly to the requesting window; wildcard target avoids transient\n // 'null' origin warnings during early navigation while remaining safe since\n // we already validated the sender's origin before bridging.\n source?.postMessage({ type, requestId, ok, ...payload }, '*');\n }\n\n private performWebAuthnBridge(\n kind: typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get,\n raw: unknown,\n e: MessageEvent,\n ): void {\n if (kind === WebAuthnBridgeMessage.Create) {\n void this.handleWebAuthnCreate(raw as CreateReq, e);\n return;\n }\n // kind === 'WALLET_WEBAUTHN_GET'\n void this.handleWebAuthnGet(raw as GetReq, e);\n }\n\n private async handleWebAuthnCreate(req: CreateReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialCreationOptions);\n const rpName = src.rp?.name || 'WebAuthn';\n const rpId = src.rp?.id || window.location.hostname;\n const pub: PublicKeyCredentialCreationOptions = { ...src, rp: { name: rpName, id: rpId } };\n console.debug('[IframeTransport][bridge] CREATE received', { requestId, from: e.origin, rpId: pub.rp?.id });\n const cred = await navigator.credentials.create({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeRegistrationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] CREATE ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] CREATE failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n\n private async handleWebAuthnGet(req: GetReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialRequestOptions);\n const rpId = src.rpId || window.location.hostname;\n const pub: PublicKeyCredentialRequestOptions = { ...src, rpId };\n console.debug('[IframeTransport][bridge] GET received', { requestId, from: e.origin, rpId: pub.rpId });\n const cred = await navigator.credentials.get({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeAuthenticationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] GET ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] GET failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -662,7 +662,7 @@ var WalletIframeRouter = class {
|
|
|
662
662
|
recoveryEmail: payload.recoveryEmail,
|
|
663
663
|
options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : void 0
|
|
664
664
|
},
|
|
665
|
-
options: { onProgress: payload.onEvent }
|
|
665
|
+
options: { onProgress: this.wrapOnEvent(payload.onEvent, isEmailRecoverySSEEvent) }
|
|
666
666
|
});
|
|
667
667
|
return res.result;
|
|
668
668
|
}
|
|
@@ -673,7 +673,7 @@ var WalletIframeRouter = class {
|
|
|
673
673
|
accountId: payload.accountId,
|
|
674
674
|
nearPublicKey: payload.nearPublicKey
|
|
675
675
|
},
|
|
676
|
-
options: { onProgress: payload.onEvent }
|
|
676
|
+
options: { onProgress: this.wrapOnEvent(payload.onEvent, isEmailRecoverySSEEvent) }
|
|
677
677
|
});
|
|
678
678
|
}
|
|
679
679
|
async stopEmailRecovery(payload) {
|
|
@@ -1013,6 +1013,7 @@ const LOGIN_PHASES = new Set(Object.values(require_sdkSentEvents.LoginPhase));
|
|
|
1013
1013
|
const ACTION_PHASES = new Set(Object.values(require_sdkSentEvents.ActionPhase));
|
|
1014
1014
|
const DEVICE_LINKING_PHASES = new Set(Object.values(require_sdkSentEvents.DeviceLinkingPhase));
|
|
1015
1015
|
const ACCOUNT_RECOVERY_PHASES = new Set(Object.values(require_sdkSentEvents.AccountRecoveryPhase));
|
|
1016
|
+
const EMAIL_RECOVERY_PHASES = new Set(Object.values(require_sdkSentEvents.EmailRecoveryPhase));
|
|
1016
1017
|
function phaseOf(progress) {
|
|
1017
1018
|
return String(progress?.phase ?? "");
|
|
1018
1019
|
}
|
|
@@ -1031,6 +1032,9 @@ function isDeviceLinkingSSEEvent(p) {
|
|
|
1031
1032
|
function isAccountRecoverySSEEvent(p) {
|
|
1032
1033
|
return ACCOUNT_RECOVERY_PHASES.has(phaseOf(p));
|
|
1033
1034
|
}
|
|
1035
|
+
function isEmailRecoverySSEEvent(p) {
|
|
1036
|
+
return EMAIL_RECOVERY_PHASES.has(phaseOf(p));
|
|
1037
|
+
}
|
|
1034
1038
|
/**
|
|
1035
1039
|
* Strips out class functions as they cannot be sent over postMessage to iframe
|
|
1036
1040
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","names":["parsedOrigin: URL","IframeTransport","OverlayController","OnEventsProgressBus","defaultPhaseHeuristics","err: Error & { code?: string; details?: unknown }","full: ParentToChildEnvelope","isObject","isBoolean","toError","cancelEnvelope: ParentToChildEnvelope","RegistrationPhase","LoginPhase","ActionPhase","DeviceLinkingPhase","AccountRecoveryPhase","isPlainSignedTransactionLike","SignedTransaction","extractBorshBytesFromPlainSignedTx","stripFunctionsShallow"],"sources":["../../../../../../../../src/core/WalletIframe/client/router.ts"],"sourcesContent":["/*\n * WalletIframeRouter - Client-Side Communication Layer\n *\n * Owns all iframe overlay show/hide behavior for WebAuthn activation. It is the\n * single place that decides *how* the wallet iframe is displayed (fullscreen vs\n * anchored, sticky mode, force-fullscreen during registration, etc.).\n *\n * Responsibilities:\n * - Request/Response Correlation: Tracks pending requests with unique IDs.\n * - Progress Event Bridging: Receives PROGRESS from the wallet iframe and forwards\n * them to app `onEvent` handlers.\n * - Overlay Ownership:\n * - Delegates *when* to show/hide to OnEventsProgressBus (based on progress events).\n * - Executes *how* to show/hide via OverlayController (DOM / CSS / ARIA).\n * - Also reacts to wallet-host UI messages (e.g., WALLET_UI_CLOSED) and export flows.\n * - Timeout Handling: Manages request timeouts and cleanup.\n * - Message Serialization: Strips non-serializable functions from messages.\n * - Error Handling: Converts iframe errors to parent-appropriate errors.\n *\n * High-level flow:\n *\n * Step legend\n * -----------\n * (1) App calls a router RPC (executeAction, registerPasskey, etc).\n * (2) Router posts request to iframe and tracks a pending entry.\n * (3) Wallet iframe sends PROGRESS messages back to the router.\n * (4) Router forwards ProgressPayloads into OnEventsProgressBus.\n * (5) OnEventsProgressBus decides 'show' | 'hide' and calls router adapters.\n * (6) Router delegates to OverlayController to show|hide the iframe.\n * (7) Router receives final result, resolves the pending promise, unregisters,\n * and may hide the overlay if no other request still needs it.\n *\n * +-----------+ +--------------------+ +----------------------+ +----------------------+\n * | App | | WalletIframeRouter | | OnEventsProgressBus | | OverlayController |\n * +-----+-----+ +---------+----------+ +----------+-----------+ +----------+-----------+\n * | (1) RPC call (executeAction, etc.) | |\n * |---------------------->|---------------------------->| |\n * | | |\n * | (2) post(): send request to iframe |\n * | | |\n * | (3) PROGRESS from iframe via onPortMessage() |\n * |<----------------------------------------------------| |\n * | | |\n * | (4) ProgressPayload → heuristic |\n * | |---(5) 'show'|'hide' intent-->|\n * | | |\n * | (6) showFrameForActivation() | hideFrameForActivation() |\n * | | |\n * | | (6) show()|hide() |\n * | |----------------------------->|\n * | | |\n * | (7) PM_RESULT/ERROR → resolve pending, maybe hide overlay |\n * |<----------------------------------------------------| |\n *\n * Communication Flow (requests):\n * 1. Parent calls RPC method (e.g., registerPasskey).\n * 2. Router creates unique request ID and pending entry.\n * 3. Message sent to iframe via MessagePort.\n * 4. Progress events bridged back to parent callbacks and fed into OnEventsProgressBus.\n * 5. OnEventsProgressBus emits show/hide intents; router invokes OverlayController.\n * 6. Final result resolves the pending promise; router unregisters and may hide overlay.\n */\n\nimport {\n type ParentToChildEnvelope,\n type ChildToParentEnvelope,\n type ProgressPayload,\n type PreferencesChangedPayload,\n} from '../shared/messages';\nimport { SignedTransaction } from '../../NearClient';\nimport { OnEventsProgressBus, defaultPhaseHeuristics } from './on-events-progress-bus';\nimport type {\n ActionSSEEvent,\n ActionHooksOptions,\n AfterCall,\n AccountRecoverySSEEvent,\n DelegateActionSSEEvent,\n DeviceLinkingSSEEvent,\n LoginSSEvent,\n RegistrationSSEEvent,\n SendTransactionHooksOptions,\n SignAndSendTransactionHooksOptions,\n} from '../../types/sdkSentEvents';\nimport {\n RegistrationPhase,\n LoginPhase,\n ActionPhase,\n DeviceLinkingPhase,\n AccountRecoveryPhase,\n} from '../../types/sdkSentEvents';\nimport type {\n ActionResult,\n GetRecentLoginsResult,\n LoginAndCreateSessionResult,\n LoginSession,\n RegistrationResult,\n SignTransactionResult,\n} from '../../types/tatchi';\nimport {\n ActionArgs,\n TransactionInput,\n TxExecutionStatus\n} from '../../types';\nimport type { DelegateActionInput } from '../../types/delegate';\nimport { IframeTransport } from './IframeTransport';\nimport OverlayController, { type DOMRectLike } from './overlay-controller';\nimport { isObject, isPlainSignedTransactionLike, extractBorshBytesFromPlainSignedTx, isBoolean } from '../validation';\nimport type { WalletUIRegistry } from '../host/iframe-lit-element-registry';\nimport { toError } from '../../../utils/errors';\nimport {\n DeviceLinkingQRData,\n LinkDeviceResult,\n StartDevice2LinkingFlowArgs,\n StartDevice2LinkingFlowResults,\n} from '../../types/linkDevice'\nimport type { AuthenticatorOptions } from '../../types/authenticatorOptions';\nimport type { ConfirmationConfig } from '../../types/signer-worker';\nimport type { AccessKeyList } from '../../NearClient';\nimport type { SignNEP413MessageResult } from '../../TatchiPasskey/signNEP413';\nimport type { RecoveryResult } from '../../TatchiPasskey';\nimport { openOfflineExportWindow } from '../../OfflineExport/index.js';\nimport type { DerivedAddressRecord } from '../../IndexedDBManager';\nimport type { EmailRecoveryContracts } from '../../types/tatchi';\n\n// Simple, framework-agnostic service iframe client.\n// Responsibilities split:\n// - IframeTransport: low-level mount + load + CONNECT/READY handshake (MessagePort)\n// - WalletIframeRouter (this): request/response correlation, progress events,\n// overlay display, and high-level wallet RPC helpers\n\nexport interface WalletIframeRouterOptions {\n walletOrigin: string; // e.g., https://wallet.example.com\n servicePath?: string; // default '/wallet-service'\n connectTimeoutMs?: number; // default 8000\n requestTimeoutMs?: number; // default 20000\n theme?: 'dark' | 'light';\n // Enable verbose client-side logging for debugging\n debug?: boolean;\n // Test-only/diagnostic options (not part of the public API contract for apps)\n testOptions?: {\n // Optional identity/ownership tags for the iframe instance (useful for tests/tools)\n routerId?: string;\n ownerTag?: string; // e.g., 'app' | 'tests'\n // Lazy mounting: when false, do not auto-connect/mount during init(); connect on first use\n autoMount?: boolean;\n };\n // Optional config forwarded to wallet host\n nearRpcUrl?: string;\n nearNetwork?: 'testnet' | 'mainnet';\n contractId?: string;\n relayer?: {\n url: string;\n };\n vrfWorkerConfigs?: Record<string, unknown>;\n rpIdOverride?: string;\n authenticatorOptions?: AuthenticatorOptions;\n emailRecoveryContracts?: Partial<EmailRecoveryContracts>;\n // SDK asset base path for embedded bundles when mounting same‑origin via srcdoc\n // Must serve dist/esm under this base path. Defaults to '/sdk'.\n sdkBasePath?: string;\n // Optional: pre-register UI components in wallet host\n uiRegistry?: Record<string, unknown>;\n // Optional: explorer base URL for TxTree links\n nearExplorerUrl?: string;\n}\n\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (reason?: unknown) => void;\n timer: number | undefined;\n onProgress?: (payload: ProgressPayload) => void;\n onTimeout: () => Error;\n};\n\ntype PostResult<T> = {\n ok: boolean,\n result: T\n}\n\nexport class WalletIframeRouter {\n private opts: Required<WalletIframeRouterOptions>;\n // Low-level transport handling iframe mount + handshake\n private transport: IframeTransport;\n private port: MessagePort | null = null;\n private ready = false;\n // Deduplicate concurrent init() calls and avoid race conditions\n private initInFlight: Promise<void> | null = null;\n private pending = new Map<string, Pending>();\n private reqCounter = 0;\n private readyListeners: Set<() => void> = new Set();\n private vrfStatusListeners: Set<(status: { active: boolean; nearAccountId: string | null; sessionDuration?: number }) => void> = new Set();\n private preferencesChangedListeners: Set<(payload: PreferencesChangedPayload) => void> = new Set();\n // Coalesce duplicate Device2 start calls (e.g., React StrictMode double-effects)\n private device2StartPromise: Promise<{ qrData: DeviceLinkingQRData; qrCodeDataURL: string }> | null = null;\n private progressBus: OnEventsProgressBus;\n private debug = false;\n private readonly walletOriginUrl: URL;\n private readonly walletOriginOrigin: string;\n private overlay: OverlayController;\n // Force the overlay to remain fullscreen during critical flows (e.g., registration)\n // and ignore anchored rect updates from helper hooks.\n private overlayForceFullscreen = false;\n // Overlay register button window-message bridging (wallet-host UI → parent)\n private readonly registerOverlayResultListeners = new Set<(\n payload: { ok: boolean; result?: RegistrationResult; cancelled?: boolean; error?: string }\n ) => void>();\n private readonly registerOverlaySubmitListeners = new Set<() => void>();\n private windowMsgHandlerBound?: (ev: MessageEvent) => void;\n\n constructor(options: WalletIframeRouterOptions) {\n if (!options?.walletOrigin) {\n throw new Error('[WalletIframeRouter] walletOrigin is required when using the wallet iframe');\n }\n\n let parsedOrigin: URL;\n try {\n parsedOrigin = new URL(options.walletOrigin);\n } catch (err) {\n throw new Error(`[WalletIframeRouter] Invalid walletOrigin: ${options.walletOrigin}`);\n }\n\n if (typeof window !== 'undefined') {\n const parentOrigin = window.location.origin;\n if (parsedOrigin.origin === parentOrigin) {\n console.warn('[WalletIframeRouter] walletOrigin matches the host origin. Isolation safeguards rely on the parent; consider moving the wallet to a dedicated origin.');\n }\n }\n\n const defaultRouterId = `w3a-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const testOptions = {\n routerId: defaultRouterId,\n ownerTag: undefined as string | undefined,\n autoMount: true,\n ...(options?.testOptions || {}),\n };\n this.opts = {\n connectTimeoutMs: 8000,\n requestTimeoutMs: 20000,\n servicePath: '/wallet-service',\n sdkBasePath: '/sdk',\n testOptions,\n ...options,\n } as Required<WalletIframeRouterOptions>;\n this.walletOriginUrl = parsedOrigin;\n this.walletOriginOrigin = parsedOrigin.origin;\n this.debug = !!this.opts.debug;\n // Encapsulate iframe mount + handshake logic in transport\n this.transport = new IframeTransport({\n walletOrigin: this.opts.walletOrigin,\n servicePath: this.opts.servicePath,\n connectTimeoutMs: this.opts.connectTimeoutMs,\n testOptions: {\n routerId: this.opts.testOptions.routerId,\n ownerTag: this.opts.testOptions.ownerTag,\n },\n });\n\n // Centralize overlay sizing/visibility. The router is the single owner of\n // \"how\" the iframe is shown/hidden (fullscreen vs anchored, sticky, etc).\n this.overlay = new OverlayController({ ensureIframe: () => this.transport.ensureIframeMounted() });\n\n // Initialize progress router with overlay control and phase heuristics.\n // OnEventsProgressBus only decides *when* to show/hide based on events; it calls\n // these adapter functions, and the router delegates to OverlayController.\n this.progressBus = new OnEventsProgressBus(\n {\n show: () => this.showFrameForActivation(),\n hide: () => this.hideFrameForActivation()\n },\n defaultPhaseHeuristics,\n this.debug\n ? (msg: string, data?: Record<string, unknown>) => {\n console.debug('[WalletIframeRouter][OnEventsProgressBus]', msg, data || {});\n }\n : undefined\n );\n\n // Bridge wallet-host overlay UI messages into router callbacks\n this.windowMsgHandlerBound = (ev: MessageEvent) => {\n if (ev.origin !== this.walletOriginOrigin) return;\n const data = ev.data as unknown;\n if (!data || typeof data !== 'object') return;\n const type = (data as { type?: unknown }).type;\n if (type === 'REGISTER_BUTTON_SUBMIT') {\n // User clicked the register arrow inside the wallet-anchored UI\n // Force the overlay to fullscreen immediately so the TxConfirmer\n // can mount and capture activation in Safari/iOS/mobile.\n this.overlayForceFullscreen = true;\n this.overlay.setSticky(true);\n this.overlay.showFullscreen();\n for (const cb of Array.from(this.registerOverlaySubmitListeners)) {\n try { cb(); } catch {}\n }\n return;\n }\n if (type === 'REGISTER_BUTTON_RESULT') {\n const payload = (data as { payload?: unknown }).payload as\n | { ok?: boolean; result?: RegistrationResult; cancelled?: boolean; error?: string }\n | undefined;\n const ok = !!payload?.ok;\n for (const cb of Array.from(this.registerOverlayResultListeners)) {\n cb({ ok, result: payload?.result, cancelled: payload?.cancelled, error: payload?.error });\n }\n // Release overlay lock after result\n this.overlayForceFullscreen = false;\n this.overlay.setSticky(false);\n // Progress bus will hide after completion; hide defensively here\n this.hideFrameForActivation();\n if (ok) {\n const acct = payload?.result?.nearAccountId;\n void this.getLoginSession(acct)\n .then(({ login: st }) => {\n this.emitVrfStatusChanged({ active: !!st.vrfActive, nearAccountId: st.nearAccountId, sessionDuration: st.vrfSessionDuration });\n })\n .catch(() => {});\n }\n return;\n }\n };\n globalThis.addEventListener?.('message', this.windowMsgHandlerBound);\n }\n\n private attachExportUiClosedListener = (walletOrigin: string): (() => void) => {\n const onUiClosed = (ev: MessageEvent) => {\n if (ev.origin !== walletOrigin) return;\n const data = ev.data as unknown;\n if (!data || (data as any).type !== 'WALLET_UI_CLOSED') return;\n this.overlay.setSticky(false);\n this.hideFrameForActivation();\n globalThis.removeEventListener?.('message', onUiClosed);\n };\n globalThis.addEventListener?.('message', onUiClosed);\n return () => { globalThis.removeEventListener?.('message', onUiClosed) };\n }\n\n private attachExportUiFallbackListener = (walletOrigin: string, accountId: string): (() => void) => {\n const onFallback = async (ev: MessageEvent) => {\n if (ev.origin !== walletOrigin) return;\n const data = ev.data as any;\n if (!data || data.type !== 'OFFLINE_EXPORT_FALLBACK') return;\n globalThis.removeEventListener?.('message', onFallback);\n this.overlay.setSticky(false);\n this.hideFrameForActivation();\n await this.openOfflineExport({ accountId });\n };\n globalThis.addEventListener?.('message', onFallback);\n return () => { globalThis.removeEventListener?.('message', onFallback) };\n }\n\n /**\n * Subscribe to service-ready event. Returns an unsubscribe function.\n * If already ready, the listener is invoked on next microtask.\n */\n onReady(listener: () => void): () => void {\n if (this.ready) {\n Promise.resolve().then(() => { listener(); });\n return () => {};\n }\n this.readyListeners.add(listener);\n return () => { this.readyListeners.delete(listener); };\n }\n\n private emitReady(): void {\n if (!this.readyListeners.size) return;\n for (const cb of Array.from(this.readyListeners)) { cb(); }\n // Keep listeners registered; callers can unsubscribe if desired.\n }\n\n /**\n * Initialize the transport and configure the wallet host.\n * Safe to call multiple times; concurrent calls deduplicate via initInFlight.\n */\n async init(): Promise<void> {\n if (this.ready) return;\n if (this.initInFlight) { return this.initInFlight; }\n this.initInFlight = (async () => {\n // Respect autoMount=false by deferring connect until first use\n if (this.opts.testOptions.autoMount !== false) {\n this.port = await this.transport.connect();\n this.port.onmessage = (ev) => this.onPortMessage(ev);\n this.port.start?.();\n this.ready = true;\n }\n console.debug('[WalletIframeRouter] init: %s', this.ready ? 'connected' : 'deferred (autoMount=false)');\n await this.post({\n type: 'PM_SET_CONFIG',\n payload: {\n theme: this.opts.theme,\n nearRpcUrl: this.opts.nearRpcUrl,\n nearNetwork: this.opts.nearNetwork,\n // Align with PMSetConfigPayload which expects `contractId`\n // while keeping RouterOptions field name `contractId` for external API.\n contractId: this.opts.contractId,\n nearExplorerUrl: this.opts.nearExplorerUrl,\n relayer: this.opts.relayer,\n vrfWorkerConfigs: this.opts.vrfWorkerConfigs,\n rpIdOverride: this.opts.rpIdOverride,\n authenticatorOptions: this.opts.authenticatorOptions,\n emailRecoveryContracts: this.opts.emailRecoveryContracts,\n uiRegistry: this.opts.uiRegistry,\n // for embedded Lit components\n assetsBaseUrl: (() => {\n try {\n const base = new URL(this.opts.sdkBasePath, this.walletOriginUrl).toString();\n return base.endsWith('/') ? base : `${base}/`;\n } catch {\n const fallback = new URL('/sdk/', this.walletOriginUrl).toString();\n return fallback.endsWith('/') ? fallback : `${fallback}/`;\n }\n })(),\n }\n });\n this.emitReady();\n })();\n\n try {\n await this.initInFlight;\n } finally {\n this.initInFlight = null;\n }\n }\n\n isReady(): boolean { return this.ready; }\n\n // ===== UI registry/window-message helpers (generic mounting) =====\n registerUiTypes(registry: WalletUIRegistry): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_REGISTER_TYPES', payload: registry }, target);\n }\n\n mountUiComponent(params: { key: string; props?: Record<string, unknown>; targetSelector?: string; id?: string }): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_MOUNT', payload: params }, target);\n }\n\n updateUiComponent(params: { id: string; props?: Record<string, unknown> }): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_UPDATE', payload: params }, target);\n }\n\n unmountUiComponent(id: string): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_UNMOUNT', payload: { id } }, target);\n }\n\n // ===== Public RPC helpers =====\n\n // Subscribe to VRF status changes observed by this client\n onVrfStatusChanged(listener: (status: {\n active: boolean;\n nearAccountId: string | null;\n sessionDuration?: number\n }) => void): () => void {\n this.vrfStatusListeners.add(listener);\n return () => { this.vrfStatusListeners.delete(listener); };\n }\n\n // Subscribe to wallet-host preference changes (authoritative in wallet-iframe mode).\n onPreferencesChanged(listener: (payload: PreferencesChangedPayload) => void): () => void {\n this.preferencesChangedListeners.add(listener);\n return () => { this.preferencesChangedListeners.delete(listener); };\n }\n\n private emitVrfStatusChanged(status: {\n active: boolean;\n nearAccountId: string | null;\n sessionDuration?: number\n }): void {\n for (const cb of Array.from(this.vrfStatusListeners)) {\n try { cb(status); } catch {}\n }\n }\n\n private emitPreferencesChanged(payload: PreferencesChangedPayload): void {\n if (!this.preferencesChangedListeners.size) return;\n for (const cb of Array.from(this.preferencesChangedListeners)) {\n try { cb(payload); } catch {}\n }\n }\n\n // Overlay register button events (optional convenience API)\n onRegisterOverlayResult(listener: (payload: { ok: boolean; result?: RegistrationResult; cancelled?: boolean; error?: string }) => void): () => void {\n this.registerOverlayResultListeners.add(listener);\n return () => { this.registerOverlayResultListeners.delete(listener); };\n }\n\n onRegisterOverlaySubmit(listener: () => void): () => void {\n this.registerOverlaySubmitListeners.add(listener);\n return () => { this.registerOverlaySubmitListeners.delete(listener); };\n }\n\n // ===== TatchiPasskey RPCs =====\n\n async signTransactionsWithActions(payload: {\n nearAccountId: string;\n transactions: TransactionInput[];\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<SignTransactionResult[]>;\n // Allow minimal overrides (e.g., { uiMode: 'drawer' })\n confirmationConfig?: Partial<ConfirmationConfig>;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<SignTransactionResult[]> {\n // Do not forward non-cloneable functions in options; host emits its own PROGRESS messages\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n }\n : undefined;\n const res = await this.post<SignTransactionResult>({\n type: 'PM_SIGN_TXS_WITH_ACTIONS',\n payload: {\n nearAccountId: payload.nearAccountId,\n transactions: payload.transactions,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isActionSSEEvent) }\n });\n return normalizeSignedTransactionObject(res.result)\n }\n\n async signDelegateAction(payload: {\n nearAccountId: string;\n delegate: DelegateActionInput;\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<any>;\n confirmationConfig?: Partial<ConfirmationConfig>;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<unknown> {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n }\n : undefined;\n const res = await this.post<unknown>({\n type: 'PM_SIGN_DELEGATE_ACTION',\n payload: {\n nearAccountId: payload.nearAccountId,\n delegate: payload.delegate,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async registerPasskey(payload: {\n nearAccountId: string;\n confirmationConfig?: Partial<ConfirmationConfig>;\n options?: {\n onEvent?: (ev: RegistrationSSEEvent) => void;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<RegistrationResult> {\n // Step 1: For registration, force fullscreen overlay (not anchored to CTA)\n // so the TxConfirmer (drawer/modal) has space to render and capture activation.\n // Lock overlay to fullscreen for the duration of registration\n this.overlayForceFullscreen = true;\n this.overlay.setSticky(true);\n this.overlay.showFullscreen();\n\n try {\n // Optional one-time confirmation override (non-persistent)\n if (payload.confirmationConfig) {\n const base = await this.getConfirmationConfig();\n await this.setConfirmationConfig({ ...base, ...payload.confirmationConfig });\n }\n\n // Step 2: Strip non-serializable functions from options (functions can't cross iframe boundary)\n const safeOptions = removeFunctionsFromOptions(payload.options);\n\n // Step 3: Send PM_REGISTER message to iframe and wait for response\n const res = await this.post<RegistrationResult>({\n type: 'PM_REGISTER',\n payload: {\n nearAccountId: payload.nearAccountId,\n options: safeOptions,\n ...(payload.confirmationConfig ? { confirmationConfig: payload.confirmationConfig as unknown as Record<string, unknown> } : {})\n },\n // Bridge progress events from iframe back to parent callback\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isRegistrationSSEEvent) }\n });\n\n // Step 4: Update VRF status after successful registration\n const { login: st } = await this.getLoginSession(payload.nearAccountId);\n this.emitVrfStatusChanged({\n active: !!st.vrfActive,\n nearAccountId: st.nearAccountId,\n sessionDuration: st.vrfSessionDuration\n });\n\n return res?.result;\n } finally {\n // Step 5: Always release overlay lock and hide when done (success or error)\n this.overlayForceFullscreen = false;\n this.overlay.setSticky(false);\n this.hideFrameForActivation();\n }\n }\n\n async loginAndCreateSession(payload: {\n nearAccountId: string;\n options?: {\n onEvent?: (ev: LoginSSEvent) => void;\n // Forward session config so host can mint JWT/cookie\n session?: {\n kind: 'jwt' | 'cookie';\n relayUrl?: string;\n route?: string;\n };\n // Warm signing session policy override during login\n signingSession?: {\n ttlMs?: number;\n remainingUses?: number;\n };\n }\n }): Promise<LoginAndCreateSessionResult> {\n this.showFrameForActivation();\n try {\n const safeOptions = removeFunctionsFromOptions(payload.options);\n const res = await this.post<LoginAndCreateSessionResult>({\n type: 'PM_LOGIN',\n payload: {\n nearAccountId: payload.nearAccountId,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isLoginSSEEvent) }\n });\n const { login: st } = await this.getLoginSession(payload.nearAccountId);\n this.emitVrfStatusChanged({ active: !!st.vrfActive, nearAccountId: st.nearAccountId, sessionDuration: st.vrfSessionDuration });\n return res?.result;\n } finally {\n this.hideFrameForActivation();\n }\n }\n\n async getLoginSession(nearAccountId?: string): Promise<LoginSession> {\n const res = await this.post<LoginSession>({\n type: 'PM_GET_LOGIN_SESSION',\n payload: nearAccountId ? { nearAccountId } : undefined\n });\n return res.result;\n }\n\n async checkVrfStatus(): Promise<PostResult<{ active: boolean; nearAccountId: string | null; sessionDuration?: number }>> {\n const { login: st } = await this.getLoginSession();\n return {\n ok: true,\n result: {\n active: !!st.vrfActive,\n nearAccountId: st.nearAccountId,\n sessionDuration: st.vrfSessionDuration\n }\n };\n }\n\n async clearVrfSession(): Promise<PostResult<void>> {\n await this.post<void>({ type: 'PM_LOGOUT' });\n this.emitVrfStatusChanged({ active: false, nearAccountId: null });\n return { ok: true, result: undefined };\n }\n\n async signNep413Message(payload: {\n nearAccountId: string;\n message: string;\n recipient: string;\n state?: string;\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void;\n confirmerText?: { title?: string; body?: string };\n confirmationConfig?: Partial<ConfirmationConfig>;\n }\n }): Promise<SignNEP413MessageResult> {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n }\n : undefined;\n const res = await this.post<SignNEP413MessageResult>({\n type: 'PM_SIGN_NEP413',\n payload: {\n nearAccountId: payload.nearAccountId,\n params: {\n message: payload.message,\n recipient: payload.recipient,\n state: payload.state\n },\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isActionSSEEvent) }\n });\n return res.result\n }\n\n async signTransactionWithKeyPair(payload: {\n signedTransaction: SignedTransaction;\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void\n }\n }): Promise<ActionResult> {\n // Strip non-cloneable functions from options; host emits PROGRESS events\n const { options } = payload;\n const res = await this.post<ActionResult>( {\n type: 'PM_SEND_TRANSACTION',\n payload: {\n signedTransaction: payload.signedTransaction,\n options: options\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async executeAction(payload: {\n nearAccountId: string;\n receiverId: string;\n actionArgs: ActionArgs | ActionArgs[];\n options?: ActionHooksOptions\n }): Promise<ActionResult> {\n // Strip non-cloneable functions from options; host emits PROGRESS events\n const { options } = payload;\n const safeOptions = options\n ? {\n waitUntil: options.waitUntil,\n confirmationConfig: options.confirmationConfig,\n ...(options.confirmerText ? { confirmerText: options.confirmerText } : {}),\n }\n : undefined;\n\n const res = await this.post<ActionResult>({\n type: 'PM_EXECUTE_ACTION',\n payload: {\n ...payload,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async setConfirmBehavior(behavior: 'requireClick' | 'autoProceed'): Promise<void> {\n let { nearAccountId } = (await this.getLoginSession()).login;\n await this.post<void>({\n type: 'PM_SET_CONFIRM_BEHAVIOR',\n payload: { behavior, nearAccountId }\n });\n }\n\n async setConfirmationConfig(config: ConfirmationConfig): Promise<void> {\n let { nearAccountId } = (await this.getLoginSession()).login;\n await this.post<void>({\n type: 'PM_SET_CONFIRMATION_CONFIG',\n payload: { config, nearAccountId }\n });\n }\n\n async getConfirmationConfig(): Promise<ConfirmationConfig> {\n const res = await this.post<ConfirmationConfig>({ type: 'PM_GET_CONFIRMATION_CONFIG' });\n return res.result\n }\n\n async setTheme(theme: 'dark' | 'light'): Promise<void> {\n await this.post<void>({ type: 'PM_SET_THEME', payload: { theme } });\n }\n\n async prefetchBlockheight(): Promise<void> {\n await this.post<void>({ type: 'PM_PREFETCH_BLOCKHEIGHT' } );\n }\n\n async getRecentLogins(): Promise<GetRecentLoginsResult> {\n const res = await this.post<GetRecentLoginsResult>({ type: 'PM_GET_RECENT_LOGINS' } );\n return res.result;\n }\n\n // === Local persistence helpers (wallet-origin IndexedDB) ===\n\n async setDerivedAddress(payload: {\n nearAccountId: string;\n args: { contractId: string; path: string; address: string };\n }): Promise<void> {\n await this.post<void>({\n type: 'PM_SET_DERIVED_ADDRESS',\n payload,\n });\n }\n\n async getDerivedAddressRecord(payload: {\n nearAccountId: string;\n args: { contractId: string; path: string };\n }): Promise<DerivedAddressRecord | null> {\n const res = await this.post<DerivedAddressRecord | null>({\n type: 'PM_GET_DERIVED_ADDRESS_RECORD',\n payload,\n });\n return (res.result as DerivedAddressRecord | null) || null;\n }\n\n async getDerivedAddress(payload: {\n nearAccountId: string;\n args: { contractId: string; path: string };\n }): Promise<string | null> {\n const res = await this.post<string | null>({\n type: 'PM_GET_DERIVED_ADDRESS',\n payload,\n });\n return (res.result as string | null) || null;\n }\n\n async getRecoveryEmails(\n nearAccountId: string,\n ): Promise<Array<{ hashHex: string; email: string }>> {\n const res = await this.post<Array<{ hashHex: string; email: string }>>({\n type: 'PM_GET_RECOVERY_EMAILS',\n payload: { nearAccountId },\n });\n return (res.result as Array<{ hashHex: string; email: string }>) || [];\n }\n\n async setRecoveryEmails(payload: {\n nearAccountId: string;\n recoveryEmails: string[];\n options?: ActionHooksOptions;\n }): Promise<ActionResult> {\n const { options } = payload;\n const safeOptions = options\n ? {\n waitUntil: options.waitUntil,\n confirmationConfig: options.confirmationConfig,\n }\n : undefined;\n\n const res = await this.post<ActionResult>({\n type: 'PM_SET_RECOVERY_EMAILS',\n payload: {\n nearAccountId: payload.nearAccountId,\n recoveryEmails: payload.recoveryEmails,\n options: safeOptions,\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) },\n });\n return res.result;\n }\n\n // Bridge typed public onEvent callbacks to the transport's onProgress callback.\n // - onEvent: consumer's strongly-typed event handler (e.g., ActionSSEEvent)\n // - isExpectedEvent: runtime type guard that validates a ProgressPayload as that event type\n // Returns an onProgress handler that safely narrows before invoking onEvent.\n private wrapOnEvent<TEvent extends ProgressPayload>(\n onEvent: ((event: TEvent) => void) | undefined,\n isExpectedEvent: (progress: ProgressPayload) => progress is TEvent\n ): ((progress: ProgressPayload) => void) | undefined {\n if (!onEvent) return undefined;\n return (progress: ProgressPayload) => {\n try {\n if (isExpectedEvent(progress)) onEvent(progress);\n } catch {}\n };\n }\n\n async signAndSendTransactions(payload: {\n nearAccountId: string;\n transactions: TransactionInput[];\n options?: SignAndSendTransactionHooksOptions\n }): Promise<ActionResult[]> {\n\n const { options } = payload;\n // cannot send objects/functions through postMessage(), clean options first\n const safeOptions = options\n ? {\n waitUntil: options.waitUntil,\n executionWait: options.executionWait,\n confirmationConfig: options.confirmationConfig,\n ...(options.confirmerText ? { confirmerText: options.confirmerText } : {}),\n }\n : undefined;\n\n const res = await this.post<ActionResult[]>({\n type: 'PM_SIGN_AND_SEND_TXS',\n payload: {\n nearAccountId: payload.nearAccountId,\n transactions: payload.transactions,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async hasPasskeyCredential(nearAccountId: string): Promise<boolean> {\n const res = await this.post<boolean>({\n type: 'PM_HAS_PASSKEY',\n payload: { nearAccountId }\n });\n return !!res?.result;\n }\n\n async viewAccessKeyList(accountId: string): Promise<AccessKeyList> {\n const res = await this.post<AccessKeyList>({\n type: 'PM_VIEW_ACCESS_KEYS',\n payload: { accountId }\n });\n return res.result\n }\n\n async deleteDeviceKey(\n accountId: string,\n publicKeyToDelete: string,\n options?: { onEvent?: (ev: ActionSSEEvent) => void }\n ) : Promise<ActionResult> {\n const res = await this.post<ActionResult>({\n type: 'PM_DELETE_DEVICE_KEY',\n payload: {\n accountId,\n publicKeyToDelete\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result\n }\n\n async sendTransaction(args: {\n signedTransaction: SignedTransaction;\n options?: SendTransactionHooksOptions;\n }): Promise<ActionResult> {\n // Strip non-cloneable functions from options; host emits PROGRESS events\n const { options } = args;\n const safeOptions = options\n ? { waitUntil: options.waitUntil }\n : undefined;\n\n const res = await this.post<ActionResult>({\n type: 'PM_SEND_TRANSACTION',\n payload: {\n signedTransaction: args.signedTransaction,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result\n }\n\n async exportNearKeypairWithUI(\n nearAccountId: string,\n options?: { variant?: 'drawer' | 'modal'; theme?: 'dark' | 'light' }\n ): Promise<void> {\n try {\n // Make the wallet iframe visible while the export viewer is open.\n // Unlike request/response flows, the wallet host renders UI and manages\n // its own lifecycle; it will notify us when to hide via window message.\n this.showFrameForActivation();\n const walletOrigin = this.walletOriginOrigin;\n const detachClosed = this.attachExportUiClosedListener(walletOrigin);\n const detachFallback = this.attachExportUiFallbackListener(walletOrigin, nearAccountId);\n await this.post<void>({\n type: 'PM_EXPORT_NEAR_KEYPAIR_UI',\n payload: { nearAccountId, variant: options?.variant, theme: options?.theme },\n options: { sticky: true }\n });\n // Cleanup once posted (handlers will remove themselves on events)\n void detachClosed;\n void detachFallback;\n return;\n } catch (e) {\n // Fallback to offline-export route (new tab) if wallet host is unreachable or errors out\n await this.openOfflineExport({ accountId: nearAccountId });\n }\n }\n\n /**\n * Open the offline-export route as a full-screen overlay iframe and instruct it via postMessage\n * to begin the export flow for the given account. Cleans up when the viewer closes or on error.\n */\n async openOfflineExport({ accountId, timeoutMs = 20000 }: { accountId: string; timeoutMs?: number }): Promise<void> {\n const walletOrigin = this.opts.walletOrigin || window.location.origin;\n // Default: open a new tab/window for clarity\n openOfflineExportWindow({ walletOrigin, target: '_blank', accountId });\n return Promise.resolve();\n }\n\n // ===== Account Recovery (single-endpoint flow) =====\n async recoverAccountFlow(payload: {\n accountId?: string;\n onEvent?: (ev: AccountRecoverySSEEvent) => void\n }): Promise<RecoveryResult> {\n const res = await this.post<RecoveryResult>({\n type: 'PM_RECOVER_ACCOUNT_FLOW',\n payload: { accountId: payload.accountId },\n options: {\n onProgress: this.wrapOnEvent(payload.onEvent, isAccountRecoverySSEEvent)\n }\n });\n return res.result\n }\n\n // ===== Email Recovery (wallet-hosted) =====\n async startEmailRecovery(payload: {\n accountId: string;\n recoveryEmail: string;\n onEvent?: (ev: ProgressPayload) => void;\n options?: {\n confirmerText?: { title?: string; body?: string };\n confirmationConfig?: Partial<ConfirmationConfig>;\n }\n }): Promise<{ mailtoUrl: string; nearPublicKey: string }> {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n }\n : undefined;\n const res = await this.post<{ mailtoUrl: string; nearPublicKey: string }>({\n type: 'PM_START_EMAIL_RECOVERY',\n payload: {\n accountId: payload.accountId,\n recoveryEmail: payload.recoveryEmail,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: {\n onProgress: payload.onEvent\n }\n });\n return res.result;\n }\n\n async finalizeEmailRecovery(payload: {\n accountId: string;\n nearPublicKey?: string;\n onEvent?: (ev: ProgressPayload) => void\n }): Promise<void> {\n await this.post<void>({\n type: 'PM_FINALIZE_EMAIL_RECOVERY',\n payload: { accountId: payload.accountId, nearPublicKey: payload.nearPublicKey },\n options: {\n onProgress: payload.onEvent\n }\n });\n }\n\n async stopEmailRecovery(payload?: { accountId?: string; nearPublicKey?: string }): Promise<void> {\n await this.post<void>({\n type: 'PM_STOP_EMAIL_RECOVERY',\n payload: { accountId: payload?.accountId, nearPublicKey: payload?.nearPublicKey },\n });\n }\n\n // ===== Device Linking (iframe-hosted) =====\n async linkDeviceWithScannedQRData(payload: {\n qrData: DeviceLinkingQRData;\n fundingAmount: string;\n options?: {\n onEvent?: (ev: DeviceLinkingSSEEvent) => void;\n confirmationConfig?: Partial<ConfirmationConfig>;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<LinkDeviceResult> {\n // TouchID required within host\n this.showFrameForActivation();\n try {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n }\n : undefined;\n const res = await this.post<LinkDeviceResult>({\n type: 'PM_LINK_DEVICE_WITH_SCANNED_QR_DATA',\n payload: {\n qrData: payload.qrData,\n fundingAmount: payload.fundingAmount,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: {\n onProgress: this.wrapOnEvent(payload.options?.onEvent, isDeviceLinkingSSEEvent)\n }\n });\n return res.result\n } finally {\n this.hideFrameForActivation();\n }\n }\n\n async startDevice2LinkingFlow(payload?: StartDevice2LinkingFlowArgs): Promise<StartDevice2LinkingFlowResults> {\n if (this.device2StartPromise) {\n return this.device2StartPromise\n }\n const options = payload?.options;\n const safeOptions = options\n ? {\n ...(options.confirmationConfig\n ? { confirmationConfig: options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(options.confirmerText ? { confirmerText: options.confirmerText } : {}),\n }\n : undefined;\n const p = this.post<StartDevice2LinkingFlowResults>({\n type: 'PM_START_DEVICE2_LINKING_FLOW',\n payload: {\n ui: payload?.ui,\n cameraId: payload?.cameraId,\n options: safeOptions\n },\n options: {\n onProgress: this.wrapOnEvent(options?.onEvent, isDeviceLinkingSSEEvent),\n sticky: true\n }\n }).then((res) => res.result)\n .finally(() => { this.device2StartPromise = null; });\n\n this.device2StartPromise = p;\n return p;\n }\n\n async stopDevice2LinkingFlow(): Promise<void> {\n await this.post<void>({ type: 'PM_STOP_DEVICE2_LINKING_FLOW' });\n this.progressBus.clearAll();\n }\n\n // ===== Control APIs =====\n async cancelRequest(requestId: string): Promise<void> {\n // Best-effort cancel. Host will attempt to close open modals and mark the request as cancelled.\n await this.post<void>({ type: 'PM_CANCEL', payload: { requestId } }).catch(() => {});\n // Always clear local progress + hide overlay even if the host didn't receive the message\n this.progressBus.unregister(requestId);\n this.hideFrameForActivation();\n }\n\n async cancelAll(): Promise<void> {\n // Try to cancel all requests on the host, but don't depend on READY/port availability\n await this.post<void>({ type: 'PM_CANCEL', payload: {} }).catch(() => {});\n // Clear all local progress listeners and force-hide the overlay\n this.progressBus.clearAll();\n this.hideFrameForActivation();\n }\n\n private onPortMessage(e: MessageEvent<ChildToParentEnvelope>) {\n const msg = e.data as ChildToParentEnvelope;\n // Some wallet-host messages are push-style and are not correlated to a requestId.\n if (msg.type === 'PREFERENCES_CHANGED') {\n this.emitPreferencesChanged(msg.payload as PreferencesChangedPayload);\n return;\n }\n const requestId = msg.requestId;\n if (!requestId) return;\n\n // Bridge PROGRESS events to caller-provided onEvent callback via pending registry\n if (msg.type === 'PROGRESS') {\n const payload = (msg.payload as ProgressPayload);\n // Route via ProgressBus (handles overlay + sticky delivery)\n this.progressBus.dispatch({ requestId: requestId, payload: payload });\n // Refresh timeout for long-running operations whenever progress is received\n const pend = this.pending.get(requestId);\n if (pend) {\n if (pend.timer) window.clearTimeout(pend.timer);\n pend.timer = window.setTimeout(() => {\n const err = pend.onTimeout();\n pend.reject(err);\n }, this.opts.requestTimeoutMs);\n }\n return;\n }\n\n const pending = this.pending.get(requestId);\n // Hide overlay on completion only if no other requests still need it,\n // and this request wasn't marked sticky (UI-managed lifecycle).\n if (!this.progressBus.isSticky(requestId)) {\n if (!this.progressBus.wantsVisible()) {\n this.hideFrameForActivation();\n }\n }\n if (!pending) {\n // Even if no pending exists (e.g., early cancel or pre-resolved),\n // ensure any lingering progress subscriber is removed.\n if (this.debug) {\n console.debug('[WalletIframeRouter] Non-PROGRESS without pending → hide + unregister', {\n requestId,\n type: (msg as unknown as { type?: unknown })?.type || 'unknown'\n });\n }\n this.progressBus.unregister(requestId);\n return;\n }\n this.pending.delete(requestId);\n if (pending.timer) window.clearTimeout(pending.timer);\n\n if (msg.type === 'ERROR') {\n const err: Error & { code?: string; details?: unknown } = new Error(msg.payload?.message || 'Wallet error');\n err.code = msg.payload?.code;\n err.details = msg.payload?.details;\n // Deliver to pending promise if present\n pending.reject(err);\n // Also notify all progress subscribers for this requestId\n this.progressBus.dispatch({\n requestId: requestId,\n payload: {\n step: 0,\n phase: 'error',\n status: 'error',\n message: msg.payload?.message\n }\n });\n this.progressBus.unregister(requestId);\n return;\n }\n\n pending.resolve(msg.payload);\n if (!this.progressBus.isSticky(requestId)) {\n this.progressBus.unregister(requestId);\n }\n }\n\n /**\n * Post a typed envelope over the MessagePort with robust readiness handling.\n * This is the core method that handles all communication with the iframe.\n *\n * Flow:\n * 1. Ensure iframe is ready (lazy initialization)\n * 2. Generate unique request ID for correlation\n * 3. Set up timeout and progress handling\n * 4. Send message to iframe via MessagePort\n * 5. Wait for response (PM_RESULT or ERROR)\n * 6. Clean up on completion or timeout\n */\n private async post<T>(\n envelope: Omit<ParentToChildEnvelope, 'requestId'>,\n ): Promise<PostResult<T>> {\n\n // Step 1: Lazily initialize the iframe/client if not ready yet\n if (!this.ready || !this.port) {\n await this.init();\n }\n\n // Step 2: Generate unique request ID for correlation\n const requestId = `${Date.now()}-${++this.reqCounter}`;\n const full: ParentToChildEnvelope = { ...(envelope as ParentToChildEnvelope), requestId };\n const { options } = full;\n\n return new Promise<PostResult<T>>((resolve, reject) => {\n const onTimeout = () => {\n const pending = this.pending.get(requestId);\n if (pending?.timer !== undefined) window.clearTimeout(pending.timer);\n this.pending.delete(requestId);\n this.progressBus.unregister(requestId);\n this.overlay.setSticky(false);\n if (!this.progressBus.wantsVisible()) {\n this.hideFrameForActivation();\n }\n this.sendBestEffortCancel(requestId);\n return new Error(`Wallet request timeout for ${envelope.type}`);\n };\n\n // Step 3: Set up timeout handler for request\n const timer = window.setTimeout(() => {\n const err = onTimeout();\n reject(err);\n }, this.opts.requestTimeoutMs);\n\n // Step 4: Register pending request for correlation\n this.pending.set(requestId, {\n resolve: (v) => resolve(v as PostResult<T>),\n reject,\n timer,\n onProgress: options?.onProgress,\n onTimeout,\n });\n\n // Step 5: Register progress handler for real-time updates\n this.progressBus.register({\n requestId: requestId,\n sticky: !!options?.sticky, // Some flows need to persist after completion\n onProgress: (payload: ProgressPayload) => {\n // Bridge progress events from iframe back to parent callback\n try {\n options?.onProgress?.(payload);\n } catch {}\n },\n });\n\n try {\n // Step 6: Strip non-cloneable fields (functions) from envelope options before posting\n const wireOptions = (options && isObject(options))\n ? (() => {\n const stickyVal = (options as { sticky?: unknown }).sticky;\n return isBoolean(stickyVal) ? { sticky: stickyVal } : undefined;\n })()\n : undefined;\n const serializableFull = wireOptions ? { ...full, options: wireOptions } : { ...full, options: undefined };\n\n // Align overlay stickiness with request options (phase 2 will use intents)\n this.overlay.setSticky(!!(wireOptions && (wireOptions as { sticky?: boolean }).sticky));\n\n // Step 7: Apply overlay intent (conservative) if not already visible, then post\n if (!this.overlay.getState().visible) {\n const intent = this.computeOverlayIntent(serializableFull.type);\n if (intent.mode === 'fullscreen') {\n this.overlay.setSticky(!!(wireOptions && (wireOptions as { sticky?: boolean }).sticky));\n this.overlay.showFullscreen();\n }\n }\n\n // Send message to iframe via MessagePort\n this.port!.postMessage(serializableFull as ParentToChildEnvelope);\n } catch (err) {\n // Step 8: Handle send errors - clean up and reject\n this.pending.delete(requestId);\n window.clearTimeout(timer);\n this.progressBus.unregister(requestId);\n reject(toError(err));\n }\n });\n }\n\n /**\n * computeOverlayIntent - Preflight \"Show\" Decision\n *\n * This method makes the initial decision about whether to show the overlay\n * BEFORE sending the request to the iframe. It's a conservative preflight\n * check that ensures the iframe is visible in time for user activation.\n *\n * Key Responsibilities:\n * - Preflight Decision: Determines overlay visibility before request is sent\n * - User Activation Timing: Ensures iframe is visible when WebAuthn prompts appear\n * - Conservative Approach: Only shows overlay if not already visible\n * - Request Type Mapping: Maps message types to overlay requirements\n *\n * How it differs from other components:\n *\n * vs OnEventsProgressBus (lifecycle and close decision):\n * - computeOverlayIntent: \"SHOW\" decision - runs before sending request\n * - OnEventsProgressBus: \"CLOSE\" decision - runs during operation lifecycle\n * - OnEventsProgressBus drives ongoing UI phases and manages sticky behavior\n * - OnEventsProgressBus handles PM_RESULT/ERROR and decides when to hide overlay\n *\n * vs OverlayController (single executor):\n * - computeOverlayIntent: DECIDES what to do (show/hide decision logic)\n * - OverlayController: EXECUTES the decision (actual CSS manipulation)\n * - OverlayController receives commands from both intent and ProgressBus\n * - OverlayController keeps all style mutations in one place\n *\n * Architecture Flow:\n * 1. computeOverlayIntent() → decides to show overlay\n * 2. OverlayController.showFullscreen() → executes the decision\n * 3. Request sent to iframe → operation begins\n * 4. OnEventsProgressBus manages lifecycle → handles progress events\n * 5. OnEventsProgressBus decides to hide → when operation completes\n * 6. OverlayController.hide() → executes the hide decision\n *\n * Special Cases:\n * - Anchored flows (UI registry with viewportRect) are message-driven\n * - Parent sets bounds and sticky via registry messages\n * - computeOverlayIntent returns 'hidden' for these (don't pre-show)\n * - Some legacy paths still call showFrameForActivation() directly\n *\n * Future Evolution:\n * - If host always emits early PROGRESS for a type, this can be reduced\n * - Intent is to move toward OnEventsProgressBus-driven lifecycle management\n * - This provides predictable, glitch-free activation without hardcoding\n */\n private computeOverlayIntent(type: ParentToChildEnvelope['type']): { mode: 'hidden' | 'fullscreen' } {\n switch (type) {\n // Operations that require fullscreen overlay for WebAuthn activation\n case 'PM_EXPORT_NEAR_KEYPAIR_UI':\n case 'PM_REGISTER':\n case 'PM_LOGIN':\n case 'PM_LINK_DEVICE_WITH_SCANNED_QR_DATA':\n case 'PM_SIGN_AND_SEND_TXS':\n case 'PM_EXECUTE_ACTION':\n case 'PM_SEND_TRANSACTION':\n case 'PM_SIGN_TXS_WITH_ACTIONS':\n return { mode: 'fullscreen' };\n\n // All other operations (background/read-only) don't need overlay\n default:\n return { mode: 'hidden' };\n }\n }\n\n // Temporarily show the service iframe to capture user activation\n private showFrameForActivation(): void {\n // Ensure iframe exists so overlay can be applied immediately\n this.transport.ensureIframeMounted();\n if (this.overlayForceFullscreen) {\n this.overlay.showFullscreen();\n } else {\n // Prefer fullscreen by default\n this.overlay.showFullscreen();\n }\n }\n\n private hideFrameForActivation(): void {\n if (!this.overlay.getState().visible) return;\n this.overlay.hide();\n }\n\n private sendBestEffortCancel(targetRequestId?: string): void {\n const port = this.port;\n if (!port) return;\n const cancelEnvelope: ParentToChildEnvelope = {\n type: 'PM_CANCEL',\n requestId: `cancel-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n payload: targetRequestId ? { requestId: targetRequestId } : {}\n };\n port.postMessage(cancelEnvelope);\n }\n\n /**\n * Public toggle to surface the wallet iframe for user activation or hide it.\n * Useful when mounting inline UI components that require direct user clicks.\n */\n setOverlayVisible(visible: boolean): void {\n if (visible) {\n // Respect fullscreen lock when present\n if (this.overlayForceFullscreen) {\n this.overlay.showFullscreen();\n } else {\n this.showFrameForActivation();\n }\n } else {\n this.hideFrameForActivation();\n }\n }\n\n /** Public helper for tests/tools: get the underlying iframe element. */\n getIframeEl(): HTMLIFrameElement | null {\n return this.transport.getIframeEl();\n }\n\n /** Public helper for tests/tools: inspect current overlay state. */\n getOverlayState(): { visible: boolean; mode: 'hidden' | 'fullscreen' | 'anchored'; sticky: boolean; rect?: DOMRectLike } {\n return this.overlay.getState();\n }\n\n /**\n * Position and show the wallet iframe as an anchored overlay matching a DOMRect.\n * Accepts viewport-relative coordinates (from getBoundingClientRect()).\n *\n * Important: Some apps apply CSS transforms (or filters/perspective) on html/body,\n * which changes the containing block for position: fixed. In those cases a fixed\n * iframe will be offset by the page scroll. To avoid that mismatch, anchor the\n * overlay using absolute positioning in document coordinates.\n */\n setOverlayBounds(rect: { top: number; left: number; width: number; height: number }): void {\n if (this.overlayForceFullscreen) return; // ignore anchored bounds while locked to fullscreen\n this.transport.ensureIframeMounted();\n this.overlay.showAnchored(rect as DOMRectLike);\n }\n\n // setAnchoredOverlayBounds/clearAnchoredOverlay removed with Arrow overlay deprecation\n\n // Post a window message and surface errors in debug mode instead of silently swallowing them\n private postWindowMessage(w: Window, data: unknown, target: string): void {\n try {\n w.postMessage(data, target);\n } catch (err) {\n if (this.debug) {\n console.error('[WalletIframeRouter] window.postMessage failed', { error: err, data });\n }\n }\n }\n\n}\n\n// ===== Runtime type guards to safely bridge ProgressPayload → typed SSE events =====\nconst REGISTRATION_PHASES = new Set<string>(Object.values(RegistrationPhase) as string[]);\nconst LOGIN_PHASES = new Set<string>(Object.values(LoginPhase) as string[]);\nconst ACTION_PHASES = new Set<string>(Object.values(ActionPhase) as string[]);\nconst DEVICE_LINKING_PHASES = new Set<string>(Object.values(DeviceLinkingPhase) as string[]);\nconst ACCOUNT_RECOVERY_PHASES = new Set<string>(Object.values(AccountRecoveryPhase) as string[]);\n\nfunction phaseOf(progress: ProgressPayload): string {\n return String((progress as { phase?: unknown })?.phase ?? '');\n}\n\nfunction isRegistrationSSEEvent(progress: ProgressPayload): progress is RegistrationSSEEvent {\n return REGISTRATION_PHASES.has(phaseOf(progress));\n}\n\nfunction isLoginSSEEvent(p: ProgressPayload): p is LoginSSEvent {\n return LOGIN_PHASES.has(phaseOf(p));\n}\n\nfunction isActionSSEEvent(p: ProgressPayload): p is ActionSSEEvent {\n return ACTION_PHASES.has(phaseOf(p));\n}\n\nexport function isDelegateSSEEvent(p: ProgressPayload): p is DelegateActionSSEEvent {\n if (!isActionSSEEvent(p)) return false;\n const data = (p as any).data;\n return !!data && typeof data === 'object' && (data as any).context === 'delegate';\n}\n\nfunction isDeviceLinkingSSEEvent(p: ProgressPayload): p is DeviceLinkingSSEEvent {\n return DEVICE_LINKING_PHASES.has(phaseOf(p));\n}\n\nfunction isAccountRecoverySSEEvent(p: ProgressPayload): p is AccountRecoverySSEEvent {\n return ACCOUNT_RECOVERY_PHASES.has(phaseOf(p));\n}\n\n/**\n * Strips out class functions as they cannot be sent over postMessage to iframe\n */\n function normalizeSignedTransactionObject(result: SignTransactionResult) {\n const arr = Array.isArray(result) ? result : [];\n const normalized = arr.map(entry => {\n if (entry?.signedTransaction) {\n const st = entry.signedTransaction as unknown;\n if (isPlainSignedTransactionLike(st)) {\n entry.signedTransaction = SignedTransaction.fromPlain({\n transaction: (st as { transaction: unknown }).transaction,\n signature: (st as { signature: unknown }).signature,\n borsh_bytes: extractBorshBytesFromPlainSignedTx(st),\n });\n }\n }\n return entry;\n });\n return normalized\n }\n\n/**\n * Strips out functions as they cannot be sent over postMessage to iframe\n */\nimport { stripFunctionsShallow } from '../validation';\n\nfunction removeFunctionsFromOptions(options?: object): object | undefined {\n return stripFunctionsShallow(options as Record<string, unknown>);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.js","names":["parsedOrigin: URL","IframeTransport","OverlayController","OnEventsProgressBus","defaultPhaseHeuristics","err: Error & { code?: string; details?: unknown }","full: ParentToChildEnvelope","isObject","isBoolean","toError","cancelEnvelope: ParentToChildEnvelope","RegistrationPhase","LoginPhase","ActionPhase","DeviceLinkingPhase","AccountRecoveryPhase","EmailRecoveryPhase","isPlainSignedTransactionLike","SignedTransaction","extractBorshBytesFromPlainSignedTx","stripFunctionsShallow"],"sources":["../../../../../../../../src/core/WalletIframe/client/router.ts"],"sourcesContent":["/*\n * WalletIframeRouter - Client-Side Communication Layer\n *\n * Owns all iframe overlay show/hide behavior for WebAuthn activation. It is the\n * single place that decides *how* the wallet iframe is displayed (fullscreen vs\n * anchored, sticky mode, force-fullscreen during registration, etc.).\n *\n * Responsibilities:\n * - Request/Response Correlation: Tracks pending requests with unique IDs.\n * - Progress Event Bridging: Receives PROGRESS from the wallet iframe and forwards\n * them to app `onEvent` handlers.\n * - Overlay Ownership:\n * - Delegates *when* to show/hide to OnEventsProgressBus (based on progress events).\n * - Executes *how* to show/hide via OverlayController (DOM / CSS / ARIA).\n * - Also reacts to wallet-host UI messages (e.g., WALLET_UI_CLOSED) and export flows.\n * - Timeout Handling: Manages request timeouts and cleanup.\n * - Message Serialization: Strips non-serializable functions from messages.\n * - Error Handling: Converts iframe errors to parent-appropriate errors.\n *\n * High-level flow:\n *\n * Step legend\n * -----------\n * (1) App calls a router RPC (executeAction, registerPasskey, etc).\n * (2) Router posts request to iframe and tracks a pending entry.\n * (3) Wallet iframe sends PROGRESS messages back to the router.\n * (4) Router forwards ProgressPayloads into OnEventsProgressBus.\n * (5) OnEventsProgressBus decides 'show' | 'hide' and calls router adapters.\n * (6) Router delegates to OverlayController to show|hide the iframe.\n * (7) Router receives final result, resolves the pending promise, unregisters,\n * and may hide the overlay if no other request still needs it.\n *\n * +-----------+ +--------------------+ +----------------------+ +----------------------+\n * | App | | WalletIframeRouter | | OnEventsProgressBus | | OverlayController |\n * +-----+-----+ +---------+----------+ +----------+-----------+ +----------+-----------+\n * | (1) RPC call (executeAction, etc.) | |\n * |---------------------->|---------------------------->| |\n * | | |\n * | (2) post(): send request to iframe |\n * | | |\n * | (3) PROGRESS from iframe via onPortMessage() |\n * |<----------------------------------------------------| |\n * | | |\n * | (4) ProgressPayload → heuristic |\n * | |---(5) 'show'|'hide' intent-->|\n * | | |\n * | (6) showFrameForActivation() | hideFrameForActivation() |\n * | | |\n * | | (6) show()|hide() |\n * | |----------------------------->|\n * | | |\n * | (7) PM_RESULT/ERROR → resolve pending, maybe hide overlay |\n * |<----------------------------------------------------| |\n *\n * Communication Flow (requests):\n * 1. Parent calls RPC method (e.g., registerPasskey).\n * 2. Router creates unique request ID and pending entry.\n * 3. Message sent to iframe via MessagePort.\n * 4. Progress events bridged back to parent callbacks and fed into OnEventsProgressBus.\n * 5. OnEventsProgressBus emits show/hide intents; router invokes OverlayController.\n * 6. Final result resolves the pending promise; router unregisters and may hide overlay.\n */\n\nimport {\n type ParentToChildEnvelope,\n type ChildToParentEnvelope,\n type ProgressPayload,\n type PreferencesChangedPayload,\n} from '../shared/messages';\nimport { SignedTransaction } from '../../NearClient';\nimport { OnEventsProgressBus, defaultPhaseHeuristics } from './on-events-progress-bus';\nimport type {\n ActionSSEEvent,\n ActionHooksOptions,\n AfterCall,\n AccountRecoverySSEEvent,\n DelegateActionSSEEvent,\n DeviceLinkingSSEEvent,\n EmailRecoverySSEEvent,\n LoginSSEvent,\n RegistrationSSEEvent,\n SendTransactionHooksOptions,\n SignAndSendTransactionHooksOptions,\n} from '../../types/sdkSentEvents';\nimport {\n RegistrationPhase,\n LoginPhase,\n ActionPhase,\n DeviceLinkingPhase,\n AccountRecoveryPhase,\n EmailRecoveryPhase,\n} from '../../types/sdkSentEvents';\nimport type {\n ActionResult,\n GetRecentLoginsResult,\n LoginAndCreateSessionResult,\n LoginSession,\n RegistrationResult,\n SignTransactionResult,\n} from '../../types/tatchi';\nimport {\n ActionArgs,\n TransactionInput,\n TxExecutionStatus\n} from '../../types';\nimport type { DelegateActionInput } from '../../types/delegate';\nimport { IframeTransport } from './IframeTransport';\nimport OverlayController, { type DOMRectLike } from './overlay-controller';\nimport { isObject, isPlainSignedTransactionLike, extractBorshBytesFromPlainSignedTx, isBoolean } from '../validation';\nimport type { WalletUIRegistry } from '../host/iframe-lit-element-registry';\nimport { toError } from '../../../utils/errors';\nimport {\n DeviceLinkingQRData,\n LinkDeviceResult,\n StartDevice2LinkingFlowArgs,\n StartDevice2LinkingFlowResults,\n} from '../../types/linkDevice'\nimport type { AuthenticatorOptions } from '../../types/authenticatorOptions';\nimport type { ConfirmationConfig } from '../../types/signer-worker';\nimport type { AccessKeyList } from '../../NearClient';\nimport type { SignNEP413MessageResult } from '../../TatchiPasskey/signNEP413';\nimport type { RecoveryResult } from '../../TatchiPasskey';\nimport { openOfflineExportWindow } from '../../OfflineExport/index.js';\nimport type { DerivedAddressRecord } from '../../IndexedDBManager';\nimport type { EmailRecoveryContracts } from '../../types/tatchi';\n\n// Simple, framework-agnostic service iframe client.\n// Responsibilities split:\n// - IframeTransport: low-level mount + load + CONNECT/READY handshake (MessagePort)\n// - WalletIframeRouter (this): request/response correlation, progress events,\n// overlay display, and high-level wallet RPC helpers\n\nexport interface WalletIframeRouterOptions {\n walletOrigin: string; // e.g., https://wallet.example.com\n servicePath?: string; // default '/wallet-service'\n connectTimeoutMs?: number; // default 8000\n requestTimeoutMs?: number; // default 20000\n theme?: 'dark' | 'light';\n // Enable verbose client-side logging for debugging\n debug?: boolean;\n // Test-only/diagnostic options (not part of the public API contract for apps)\n testOptions?: {\n // Optional identity/ownership tags for the iframe instance (useful for tests/tools)\n routerId?: string;\n ownerTag?: string; // e.g., 'app' | 'tests'\n // Lazy mounting: when false, do not auto-connect/mount during init(); connect on first use\n autoMount?: boolean;\n };\n // Optional config forwarded to wallet host\n nearRpcUrl?: string;\n nearNetwork?: 'testnet' | 'mainnet';\n contractId?: string;\n relayer?: {\n url: string;\n };\n vrfWorkerConfigs?: Record<string, unknown>;\n rpIdOverride?: string;\n authenticatorOptions?: AuthenticatorOptions;\n emailRecoveryContracts?: Partial<EmailRecoveryContracts>;\n // SDK asset base path for embedded bundles when mounting same‑origin via srcdoc\n // Must serve dist/esm under this base path. Defaults to '/sdk'.\n sdkBasePath?: string;\n // Optional: pre-register UI components in wallet host\n uiRegistry?: Record<string, unknown>;\n // Optional: explorer base URL for TxTree links\n nearExplorerUrl?: string;\n}\n\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (reason?: unknown) => void;\n timer: number | undefined;\n onProgress?: (payload: ProgressPayload) => void;\n onTimeout: () => Error;\n};\n\ntype PostResult<T> = {\n ok: boolean,\n result: T\n}\n\nexport class WalletIframeRouter {\n private opts: Required<WalletIframeRouterOptions>;\n // Low-level transport handling iframe mount + handshake\n private transport: IframeTransport;\n private port: MessagePort | null = null;\n private ready = false;\n // Deduplicate concurrent init() calls and avoid race conditions\n private initInFlight: Promise<void> | null = null;\n private pending = new Map<string, Pending>();\n private reqCounter = 0;\n private readyListeners: Set<() => void> = new Set();\n private vrfStatusListeners: Set<(status: { active: boolean; nearAccountId: string | null; sessionDuration?: number }) => void> = new Set();\n private preferencesChangedListeners: Set<(payload: PreferencesChangedPayload) => void> = new Set();\n // Coalesce duplicate Device2 start calls (e.g., React StrictMode double-effects)\n private device2StartPromise: Promise<{ qrData: DeviceLinkingQRData; qrCodeDataURL: string }> | null = null;\n private progressBus: OnEventsProgressBus;\n private debug = false;\n private readonly walletOriginUrl: URL;\n private readonly walletOriginOrigin: string;\n private overlay: OverlayController;\n // Force the overlay to remain fullscreen during critical flows (e.g., registration)\n // and ignore anchored rect updates from helper hooks.\n private overlayForceFullscreen = false;\n // Overlay register button window-message bridging (wallet-host UI → parent)\n private readonly registerOverlayResultListeners = new Set<(\n payload: { ok: boolean; result?: RegistrationResult; cancelled?: boolean; error?: string }\n ) => void>();\n private readonly registerOverlaySubmitListeners = new Set<() => void>();\n private windowMsgHandlerBound?: (ev: MessageEvent) => void;\n\n constructor(options: WalletIframeRouterOptions) {\n if (!options?.walletOrigin) {\n throw new Error('[WalletIframeRouter] walletOrigin is required when using the wallet iframe');\n }\n\n let parsedOrigin: URL;\n try {\n parsedOrigin = new URL(options.walletOrigin);\n } catch (err) {\n throw new Error(`[WalletIframeRouter] Invalid walletOrigin: ${options.walletOrigin}`);\n }\n\n if (typeof window !== 'undefined') {\n const parentOrigin = window.location.origin;\n if (parsedOrigin.origin === parentOrigin) {\n console.warn('[WalletIframeRouter] walletOrigin matches the host origin. Isolation safeguards rely on the parent; consider moving the wallet to a dedicated origin.');\n }\n }\n\n const defaultRouterId = `w3a-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const testOptions = {\n routerId: defaultRouterId,\n ownerTag: undefined as string | undefined,\n autoMount: true,\n ...(options?.testOptions || {}),\n };\n this.opts = {\n connectTimeoutMs: 8000,\n requestTimeoutMs: 20000,\n servicePath: '/wallet-service',\n sdkBasePath: '/sdk',\n testOptions,\n ...options,\n } as Required<WalletIframeRouterOptions>;\n this.walletOriginUrl = parsedOrigin;\n this.walletOriginOrigin = parsedOrigin.origin;\n this.debug = !!this.opts.debug;\n // Encapsulate iframe mount + handshake logic in transport\n this.transport = new IframeTransport({\n walletOrigin: this.opts.walletOrigin,\n servicePath: this.opts.servicePath,\n connectTimeoutMs: this.opts.connectTimeoutMs,\n testOptions: {\n routerId: this.opts.testOptions.routerId,\n ownerTag: this.opts.testOptions.ownerTag,\n },\n });\n\n // Centralize overlay sizing/visibility. The router is the single owner of\n // \"how\" the iframe is shown/hidden (fullscreen vs anchored, sticky, etc).\n this.overlay = new OverlayController({ ensureIframe: () => this.transport.ensureIframeMounted() });\n\n // Initialize progress router with overlay control and phase heuristics.\n // OnEventsProgressBus only decides *when* to show/hide based on events; it calls\n // these adapter functions, and the router delegates to OverlayController.\n this.progressBus = new OnEventsProgressBus(\n {\n show: () => this.showFrameForActivation(),\n hide: () => this.hideFrameForActivation()\n },\n defaultPhaseHeuristics,\n this.debug\n ? (msg: string, data?: Record<string, unknown>) => {\n console.debug('[WalletIframeRouter][OnEventsProgressBus]', msg, data || {});\n }\n : undefined\n );\n\n // Bridge wallet-host overlay UI messages into router callbacks\n this.windowMsgHandlerBound = (ev: MessageEvent) => {\n if (ev.origin !== this.walletOriginOrigin) return;\n const data = ev.data as unknown;\n if (!data || typeof data !== 'object') return;\n const type = (data as { type?: unknown }).type;\n if (type === 'REGISTER_BUTTON_SUBMIT') {\n // User clicked the register arrow inside the wallet-anchored UI\n // Force the overlay to fullscreen immediately so the TxConfirmer\n // can mount and capture activation in Safari/iOS/mobile.\n this.overlayForceFullscreen = true;\n this.overlay.setSticky(true);\n this.overlay.showFullscreen();\n for (const cb of Array.from(this.registerOverlaySubmitListeners)) {\n try { cb(); } catch {}\n }\n return;\n }\n if (type === 'REGISTER_BUTTON_RESULT') {\n const payload = (data as { payload?: unknown }).payload as\n | { ok?: boolean; result?: RegistrationResult; cancelled?: boolean; error?: string }\n | undefined;\n const ok = !!payload?.ok;\n for (const cb of Array.from(this.registerOverlayResultListeners)) {\n cb({ ok, result: payload?.result, cancelled: payload?.cancelled, error: payload?.error });\n }\n // Release overlay lock after result\n this.overlayForceFullscreen = false;\n this.overlay.setSticky(false);\n // Progress bus will hide after completion; hide defensively here\n this.hideFrameForActivation();\n if (ok) {\n const acct = payload?.result?.nearAccountId;\n void this.getLoginSession(acct)\n .then(({ login: st }) => {\n this.emitVrfStatusChanged({ active: !!st.vrfActive, nearAccountId: st.nearAccountId, sessionDuration: st.vrfSessionDuration });\n })\n .catch(() => {});\n }\n return;\n }\n };\n globalThis.addEventListener?.('message', this.windowMsgHandlerBound);\n }\n\n private attachExportUiClosedListener = (walletOrigin: string): (() => void) => {\n const onUiClosed = (ev: MessageEvent) => {\n if (ev.origin !== walletOrigin) return;\n const data = ev.data as unknown;\n if (!data || (data as any).type !== 'WALLET_UI_CLOSED') return;\n this.overlay.setSticky(false);\n this.hideFrameForActivation();\n globalThis.removeEventListener?.('message', onUiClosed);\n };\n globalThis.addEventListener?.('message', onUiClosed);\n return () => { globalThis.removeEventListener?.('message', onUiClosed) };\n }\n\n private attachExportUiFallbackListener = (walletOrigin: string, accountId: string): (() => void) => {\n const onFallback = async (ev: MessageEvent) => {\n if (ev.origin !== walletOrigin) return;\n const data = ev.data as any;\n if (!data || data.type !== 'OFFLINE_EXPORT_FALLBACK') return;\n globalThis.removeEventListener?.('message', onFallback);\n this.overlay.setSticky(false);\n this.hideFrameForActivation();\n await this.openOfflineExport({ accountId });\n };\n globalThis.addEventListener?.('message', onFallback);\n return () => { globalThis.removeEventListener?.('message', onFallback) };\n }\n\n /**\n * Subscribe to service-ready event. Returns an unsubscribe function.\n * If already ready, the listener is invoked on next microtask.\n */\n onReady(listener: () => void): () => void {\n if (this.ready) {\n Promise.resolve().then(() => { listener(); });\n return () => {};\n }\n this.readyListeners.add(listener);\n return () => { this.readyListeners.delete(listener); };\n }\n\n private emitReady(): void {\n if (!this.readyListeners.size) return;\n for (const cb of Array.from(this.readyListeners)) { cb(); }\n // Keep listeners registered; callers can unsubscribe if desired.\n }\n\n /**\n * Initialize the transport and configure the wallet host.\n * Safe to call multiple times; concurrent calls deduplicate via initInFlight.\n */\n async init(): Promise<void> {\n if (this.ready) return;\n if (this.initInFlight) { return this.initInFlight; }\n this.initInFlight = (async () => {\n // Respect autoMount=false by deferring connect until first use\n if (this.opts.testOptions.autoMount !== false) {\n this.port = await this.transport.connect();\n this.port.onmessage = (ev) => this.onPortMessage(ev);\n this.port.start?.();\n this.ready = true;\n }\n console.debug('[WalletIframeRouter] init: %s', this.ready ? 'connected' : 'deferred (autoMount=false)');\n await this.post({\n type: 'PM_SET_CONFIG',\n payload: {\n theme: this.opts.theme,\n nearRpcUrl: this.opts.nearRpcUrl,\n nearNetwork: this.opts.nearNetwork,\n // Align with PMSetConfigPayload which expects `contractId`\n // while keeping RouterOptions field name `contractId` for external API.\n contractId: this.opts.contractId,\n nearExplorerUrl: this.opts.nearExplorerUrl,\n relayer: this.opts.relayer,\n vrfWorkerConfigs: this.opts.vrfWorkerConfigs,\n rpIdOverride: this.opts.rpIdOverride,\n authenticatorOptions: this.opts.authenticatorOptions,\n emailRecoveryContracts: this.opts.emailRecoveryContracts,\n uiRegistry: this.opts.uiRegistry,\n // for embedded Lit components\n assetsBaseUrl: (() => {\n try {\n const base = new URL(this.opts.sdkBasePath, this.walletOriginUrl).toString();\n return base.endsWith('/') ? base : `${base}/`;\n } catch {\n const fallback = new URL('/sdk/', this.walletOriginUrl).toString();\n return fallback.endsWith('/') ? fallback : `${fallback}/`;\n }\n })(),\n }\n });\n this.emitReady();\n })();\n\n try {\n await this.initInFlight;\n } finally {\n this.initInFlight = null;\n }\n }\n\n isReady(): boolean { return this.ready; }\n\n // ===== UI registry/window-message helpers (generic mounting) =====\n registerUiTypes(registry: WalletUIRegistry): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_REGISTER_TYPES', payload: registry }, target);\n }\n\n mountUiComponent(params: { key: string; props?: Record<string, unknown>; targetSelector?: string; id?: string }): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_MOUNT', payload: params }, target);\n }\n\n updateUiComponent(params: { id: string; props?: Record<string, unknown> }): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_UPDATE', payload: params }, target);\n }\n\n unmountUiComponent(id: string): void {\n const iframe = this.transport.ensureIframeMounted();\n const w = iframe.contentWindow;\n if (!w) return;\n const target = this.walletOriginOrigin;\n this.postWindowMessage(w, { type: 'WALLET_UI_UNMOUNT', payload: { id } }, target);\n }\n\n // ===== Public RPC helpers =====\n\n // Subscribe to VRF status changes observed by this client\n onVrfStatusChanged(listener: (status: {\n active: boolean;\n nearAccountId: string | null;\n sessionDuration?: number\n }) => void): () => void {\n this.vrfStatusListeners.add(listener);\n return () => { this.vrfStatusListeners.delete(listener); };\n }\n\n // Subscribe to wallet-host preference changes (authoritative in wallet-iframe mode).\n onPreferencesChanged(listener: (payload: PreferencesChangedPayload) => void): () => void {\n this.preferencesChangedListeners.add(listener);\n return () => { this.preferencesChangedListeners.delete(listener); };\n }\n\n private emitVrfStatusChanged(status: {\n active: boolean;\n nearAccountId: string | null;\n sessionDuration?: number\n }): void {\n for (const cb of Array.from(this.vrfStatusListeners)) {\n try { cb(status); } catch {}\n }\n }\n\n private emitPreferencesChanged(payload: PreferencesChangedPayload): void {\n if (!this.preferencesChangedListeners.size) return;\n for (const cb of Array.from(this.preferencesChangedListeners)) {\n try { cb(payload); } catch {}\n }\n }\n\n // Overlay register button events (optional convenience API)\n onRegisterOverlayResult(listener: (payload: { ok: boolean; result?: RegistrationResult; cancelled?: boolean; error?: string }) => void): () => void {\n this.registerOverlayResultListeners.add(listener);\n return () => { this.registerOverlayResultListeners.delete(listener); };\n }\n\n onRegisterOverlaySubmit(listener: () => void): () => void {\n this.registerOverlaySubmitListeners.add(listener);\n return () => { this.registerOverlaySubmitListeners.delete(listener); };\n }\n\n // ===== TatchiPasskey RPCs =====\n\n async signTransactionsWithActions(payload: {\n nearAccountId: string;\n transactions: TransactionInput[];\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<SignTransactionResult[]>;\n // Allow minimal overrides (e.g., { uiMode: 'drawer' })\n confirmationConfig?: Partial<ConfirmationConfig>;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<SignTransactionResult[]> {\n // Do not forward non-cloneable functions in options; host emits its own PROGRESS messages\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n }\n : undefined;\n const res = await this.post<SignTransactionResult>({\n type: 'PM_SIGN_TXS_WITH_ACTIONS',\n payload: {\n nearAccountId: payload.nearAccountId,\n transactions: payload.transactions,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isActionSSEEvent) }\n });\n return normalizeSignedTransactionObject(res.result)\n }\n\n async signDelegateAction(payload: {\n nearAccountId: string;\n delegate: DelegateActionInput;\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<any>;\n confirmationConfig?: Partial<ConfirmationConfig>;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<unknown> {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n }\n : undefined;\n const res = await this.post<unknown>({\n type: 'PM_SIGN_DELEGATE_ACTION',\n payload: {\n nearAccountId: payload.nearAccountId,\n delegate: payload.delegate,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async registerPasskey(payload: {\n nearAccountId: string;\n confirmationConfig?: Partial<ConfirmationConfig>;\n options?: {\n onEvent?: (ev: RegistrationSSEEvent) => void;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<RegistrationResult> {\n // Step 1: For registration, force fullscreen overlay (not anchored to CTA)\n // so the TxConfirmer (drawer/modal) has space to render and capture activation.\n // Lock overlay to fullscreen for the duration of registration\n this.overlayForceFullscreen = true;\n this.overlay.setSticky(true);\n this.overlay.showFullscreen();\n\n try {\n // Optional one-time confirmation override (non-persistent)\n if (payload.confirmationConfig) {\n const base = await this.getConfirmationConfig();\n await this.setConfirmationConfig({ ...base, ...payload.confirmationConfig });\n }\n\n // Step 2: Strip non-serializable functions from options (functions can't cross iframe boundary)\n const safeOptions = removeFunctionsFromOptions(payload.options);\n\n // Step 3: Send PM_REGISTER message to iframe and wait for response\n const res = await this.post<RegistrationResult>({\n type: 'PM_REGISTER',\n payload: {\n nearAccountId: payload.nearAccountId,\n options: safeOptions,\n ...(payload.confirmationConfig ? { confirmationConfig: payload.confirmationConfig as unknown as Record<string, unknown> } : {})\n },\n // Bridge progress events from iframe back to parent callback\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isRegistrationSSEEvent) }\n });\n\n // Step 4: Update VRF status after successful registration\n const { login: st } = await this.getLoginSession(payload.nearAccountId);\n this.emitVrfStatusChanged({\n active: !!st.vrfActive,\n nearAccountId: st.nearAccountId,\n sessionDuration: st.vrfSessionDuration\n });\n\n return res?.result;\n } finally {\n // Step 5: Always release overlay lock and hide when done (success or error)\n this.overlayForceFullscreen = false;\n this.overlay.setSticky(false);\n this.hideFrameForActivation();\n }\n }\n\n async loginAndCreateSession(payload: {\n nearAccountId: string;\n options?: {\n onEvent?: (ev: LoginSSEvent) => void;\n // Forward session config so host can mint JWT/cookie\n session?: {\n kind: 'jwt' | 'cookie';\n relayUrl?: string;\n route?: string;\n };\n // Warm signing session policy override during login\n signingSession?: {\n ttlMs?: number;\n remainingUses?: number;\n };\n }\n }): Promise<LoginAndCreateSessionResult> {\n this.showFrameForActivation();\n try {\n const safeOptions = removeFunctionsFromOptions(payload.options);\n const res = await this.post<LoginAndCreateSessionResult>({\n type: 'PM_LOGIN',\n payload: {\n nearAccountId: payload.nearAccountId,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isLoginSSEEvent) }\n });\n const { login: st } = await this.getLoginSession(payload.nearAccountId);\n this.emitVrfStatusChanged({ active: !!st.vrfActive, nearAccountId: st.nearAccountId, sessionDuration: st.vrfSessionDuration });\n return res?.result;\n } finally {\n this.hideFrameForActivation();\n }\n }\n\n async getLoginSession(nearAccountId?: string): Promise<LoginSession> {\n const res = await this.post<LoginSession>({\n type: 'PM_GET_LOGIN_SESSION',\n payload: nearAccountId ? { nearAccountId } : undefined\n });\n return res.result;\n }\n\n async checkVrfStatus(): Promise<PostResult<{ active: boolean; nearAccountId: string | null; sessionDuration?: number }>> {\n const { login: st } = await this.getLoginSession();\n return {\n ok: true,\n result: {\n active: !!st.vrfActive,\n nearAccountId: st.nearAccountId,\n sessionDuration: st.vrfSessionDuration\n }\n };\n }\n\n async clearVrfSession(): Promise<PostResult<void>> {\n await this.post<void>({ type: 'PM_LOGOUT' });\n this.emitVrfStatusChanged({ active: false, nearAccountId: null });\n return { ok: true, result: undefined };\n }\n\n async signNep413Message(payload: {\n nearAccountId: string;\n message: string;\n recipient: string;\n state?: string;\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void;\n confirmerText?: { title?: string; body?: string };\n confirmationConfig?: Partial<ConfirmationConfig>;\n }\n }): Promise<SignNEP413MessageResult> {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n }\n : undefined;\n const res = await this.post<SignNEP413MessageResult>({\n type: 'PM_SIGN_NEP413',\n payload: {\n nearAccountId: payload.nearAccountId,\n params: {\n message: payload.message,\n recipient: payload.recipient,\n state: payload.state\n },\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: { onProgress: this.wrapOnEvent(payload.options?.onEvent, isActionSSEEvent) }\n });\n return res.result\n }\n\n async signTransactionWithKeyPair(payload: {\n signedTransaction: SignedTransaction;\n options?: {\n onEvent?: (ev: ActionSSEEvent) => void\n }\n }): Promise<ActionResult> {\n // Strip non-cloneable functions from options; host emits PROGRESS events\n const { options } = payload;\n const res = await this.post<ActionResult>( {\n type: 'PM_SEND_TRANSACTION',\n payload: {\n signedTransaction: payload.signedTransaction,\n options: options\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async executeAction(payload: {\n nearAccountId: string;\n receiverId: string;\n actionArgs: ActionArgs | ActionArgs[];\n options?: ActionHooksOptions\n }): Promise<ActionResult> {\n // Strip non-cloneable functions from options; host emits PROGRESS events\n const { options } = payload;\n const safeOptions = options\n ? {\n waitUntil: options.waitUntil,\n confirmationConfig: options.confirmationConfig,\n ...(options.confirmerText ? { confirmerText: options.confirmerText } : {}),\n }\n : undefined;\n\n const res = await this.post<ActionResult>({\n type: 'PM_EXECUTE_ACTION',\n payload: {\n ...payload,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async setConfirmBehavior(behavior: 'requireClick' | 'autoProceed'): Promise<void> {\n let { nearAccountId } = (await this.getLoginSession()).login;\n await this.post<void>({\n type: 'PM_SET_CONFIRM_BEHAVIOR',\n payload: { behavior, nearAccountId }\n });\n }\n\n async setConfirmationConfig(config: ConfirmationConfig): Promise<void> {\n let { nearAccountId } = (await this.getLoginSession()).login;\n await this.post<void>({\n type: 'PM_SET_CONFIRMATION_CONFIG',\n payload: { config, nearAccountId }\n });\n }\n\n async getConfirmationConfig(): Promise<ConfirmationConfig> {\n const res = await this.post<ConfirmationConfig>({ type: 'PM_GET_CONFIRMATION_CONFIG' });\n return res.result\n }\n\n async setTheme(theme: 'dark' | 'light'): Promise<void> {\n await this.post<void>({ type: 'PM_SET_THEME', payload: { theme } });\n }\n\n async prefetchBlockheight(): Promise<void> {\n await this.post<void>({ type: 'PM_PREFETCH_BLOCKHEIGHT' } );\n }\n\n async getRecentLogins(): Promise<GetRecentLoginsResult> {\n const res = await this.post<GetRecentLoginsResult>({ type: 'PM_GET_RECENT_LOGINS' } );\n return res.result;\n }\n\n // === Local persistence helpers (wallet-origin IndexedDB) ===\n\n async setDerivedAddress(payload: {\n nearAccountId: string;\n args: { contractId: string; path: string; address: string };\n }): Promise<void> {\n await this.post<void>({\n type: 'PM_SET_DERIVED_ADDRESS',\n payload,\n });\n }\n\n async getDerivedAddressRecord(payload: {\n nearAccountId: string;\n args: { contractId: string; path: string };\n }): Promise<DerivedAddressRecord | null> {\n const res = await this.post<DerivedAddressRecord | null>({\n type: 'PM_GET_DERIVED_ADDRESS_RECORD',\n payload,\n });\n return (res.result as DerivedAddressRecord | null) || null;\n }\n\n async getDerivedAddress(payload: {\n nearAccountId: string;\n args: { contractId: string; path: string };\n }): Promise<string | null> {\n const res = await this.post<string | null>({\n type: 'PM_GET_DERIVED_ADDRESS',\n payload,\n });\n return (res.result as string | null) || null;\n }\n\n async getRecoveryEmails(\n nearAccountId: string,\n ): Promise<Array<{ hashHex: string; email: string }>> {\n const res = await this.post<Array<{ hashHex: string; email: string }>>({\n type: 'PM_GET_RECOVERY_EMAILS',\n payload: { nearAccountId },\n });\n return (res.result as Array<{ hashHex: string; email: string }>) || [];\n }\n\n async setRecoveryEmails(payload: {\n nearAccountId: string;\n recoveryEmails: string[];\n options?: ActionHooksOptions;\n }): Promise<ActionResult> {\n const { options } = payload;\n const safeOptions = options\n ? {\n waitUntil: options.waitUntil,\n confirmationConfig: options.confirmationConfig,\n }\n : undefined;\n\n const res = await this.post<ActionResult>({\n type: 'PM_SET_RECOVERY_EMAILS',\n payload: {\n nearAccountId: payload.nearAccountId,\n recoveryEmails: payload.recoveryEmails,\n options: safeOptions,\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) },\n });\n return res.result;\n }\n\n // Bridge typed public onEvent callbacks to the transport's onProgress callback.\n // - onEvent: consumer's strongly-typed event handler (e.g., ActionSSEEvent)\n // - isExpectedEvent: runtime type guard that validates a ProgressPayload as that event type\n // Returns an onProgress handler that safely narrows before invoking onEvent.\n private wrapOnEvent<TEvent extends ProgressPayload>(\n onEvent: ((event: TEvent) => void) | undefined,\n isExpectedEvent: (progress: ProgressPayload) => progress is TEvent\n ): ((progress: ProgressPayload) => void) | undefined {\n if (!onEvent) return undefined;\n return (progress: ProgressPayload) => {\n try {\n if (isExpectedEvent(progress)) onEvent(progress);\n } catch {}\n };\n }\n\n async signAndSendTransactions(payload: {\n nearAccountId: string;\n transactions: TransactionInput[];\n options?: SignAndSendTransactionHooksOptions\n }): Promise<ActionResult[]> {\n\n const { options } = payload;\n // cannot send objects/functions through postMessage(), clean options first\n const safeOptions = options\n ? {\n waitUntil: options.waitUntil,\n executionWait: options.executionWait,\n confirmationConfig: options.confirmationConfig,\n ...(options.confirmerText ? { confirmerText: options.confirmerText } : {}),\n }\n : undefined;\n\n const res = await this.post<ActionResult[]>({\n type: 'PM_SIGN_AND_SEND_TXS',\n payload: {\n nearAccountId: payload.nearAccountId,\n transactions: payload.transactions,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result;\n }\n\n async hasPasskeyCredential(nearAccountId: string): Promise<boolean> {\n const res = await this.post<boolean>({\n type: 'PM_HAS_PASSKEY',\n payload: { nearAccountId }\n });\n return !!res?.result;\n }\n\n async viewAccessKeyList(accountId: string): Promise<AccessKeyList> {\n const res = await this.post<AccessKeyList>({\n type: 'PM_VIEW_ACCESS_KEYS',\n payload: { accountId }\n });\n return res.result\n }\n\n async deleteDeviceKey(\n accountId: string,\n publicKeyToDelete: string,\n options?: { onEvent?: (ev: ActionSSEEvent) => void }\n ) : Promise<ActionResult> {\n const res = await this.post<ActionResult>({\n type: 'PM_DELETE_DEVICE_KEY',\n payload: {\n accountId,\n publicKeyToDelete\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result\n }\n\n async sendTransaction(args: {\n signedTransaction: SignedTransaction;\n options?: SendTransactionHooksOptions;\n }): Promise<ActionResult> {\n // Strip non-cloneable functions from options; host emits PROGRESS events\n const { options } = args;\n const safeOptions = options\n ? { waitUntil: options.waitUntil }\n : undefined;\n\n const res = await this.post<ActionResult>({\n type: 'PM_SEND_TRANSACTION',\n payload: {\n signedTransaction: args.signedTransaction,\n options: safeOptions\n },\n options: { onProgress: this.wrapOnEvent(options?.onEvent, isActionSSEEvent) }\n });\n return res.result\n }\n\n async exportNearKeypairWithUI(\n nearAccountId: string,\n options?: { variant?: 'drawer' | 'modal'; theme?: 'dark' | 'light' }\n ): Promise<void> {\n try {\n // Make the wallet iframe visible while the export viewer is open.\n // Unlike request/response flows, the wallet host renders UI and manages\n // its own lifecycle; it will notify us when to hide via window message.\n this.showFrameForActivation();\n const walletOrigin = this.walletOriginOrigin;\n const detachClosed = this.attachExportUiClosedListener(walletOrigin);\n const detachFallback = this.attachExportUiFallbackListener(walletOrigin, nearAccountId);\n await this.post<void>({\n type: 'PM_EXPORT_NEAR_KEYPAIR_UI',\n payload: { nearAccountId, variant: options?.variant, theme: options?.theme },\n options: { sticky: true }\n });\n // Cleanup once posted (handlers will remove themselves on events)\n void detachClosed;\n void detachFallback;\n return;\n } catch (e) {\n // Fallback to offline-export route (new tab) if wallet host is unreachable or errors out\n await this.openOfflineExport({ accountId: nearAccountId });\n }\n }\n\n /**\n * Open the offline-export route as a full-screen overlay iframe and instruct it via postMessage\n * to begin the export flow for the given account. Cleans up when the viewer closes or on error.\n */\n async openOfflineExport({ accountId, timeoutMs = 20000 }: { accountId: string; timeoutMs?: number }): Promise<void> {\n const walletOrigin = this.opts.walletOrigin || window.location.origin;\n // Default: open a new tab/window for clarity\n openOfflineExportWindow({ walletOrigin, target: '_blank', accountId });\n return Promise.resolve();\n }\n\n // ===== Account Recovery (single-endpoint flow) =====\n async recoverAccountFlow(payload: {\n accountId?: string;\n onEvent?: (ev: AccountRecoverySSEEvent) => void\n }): Promise<RecoveryResult> {\n const res = await this.post<RecoveryResult>({\n type: 'PM_RECOVER_ACCOUNT_FLOW',\n payload: { accountId: payload.accountId },\n options: {\n onProgress: this.wrapOnEvent(payload.onEvent, isAccountRecoverySSEEvent)\n }\n });\n return res.result\n }\n\n // ===== Email Recovery (wallet-hosted) =====\n async startEmailRecovery(payload: {\n accountId: string;\n recoveryEmail: string;\n onEvent?: (ev: EmailRecoverySSEEvent) => void;\n options?: {\n confirmerText?: { title?: string; body?: string };\n confirmationConfig?: Partial<ConfirmationConfig>;\n }\n }): Promise<{ mailtoUrl: string; nearPublicKey: string }> {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n }\n : undefined;\n const res = await this.post<{ mailtoUrl: string; nearPublicKey: string }>({\n type: 'PM_START_EMAIL_RECOVERY',\n payload: {\n accountId: payload.accountId,\n recoveryEmail: payload.recoveryEmail,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: {\n onProgress: this.wrapOnEvent(payload.onEvent, isEmailRecoverySSEEvent)\n }\n });\n return res.result;\n }\n\n async finalizeEmailRecovery(payload: {\n accountId: string;\n nearPublicKey?: string;\n onEvent?: (ev: EmailRecoverySSEEvent) => void\n }): Promise<void> {\n await this.post<void>({\n type: 'PM_FINALIZE_EMAIL_RECOVERY',\n payload: { accountId: payload.accountId, nearPublicKey: payload.nearPublicKey },\n options: {\n onProgress: this.wrapOnEvent(payload.onEvent, isEmailRecoverySSEEvent)\n }\n });\n }\n\n async stopEmailRecovery(payload?: { accountId?: string; nearPublicKey?: string }): Promise<void> {\n await this.post<void>({\n type: 'PM_STOP_EMAIL_RECOVERY',\n payload: { accountId: payload?.accountId, nearPublicKey: payload?.nearPublicKey },\n });\n }\n\n // ===== Device Linking (iframe-hosted) =====\n async linkDeviceWithScannedQRData(payload: {\n qrData: DeviceLinkingQRData;\n fundingAmount: string;\n options?: {\n onEvent?: (ev: DeviceLinkingSSEEvent) => void;\n confirmationConfig?: Partial<ConfirmationConfig>;\n confirmerText?: { title?: string; body?: string };\n }\n }): Promise<LinkDeviceResult> {\n // TouchID required within host\n this.showFrameForActivation();\n try {\n const safeOptions = payload.options\n ? {\n ...(payload.options.confirmationConfig\n ? { confirmationConfig: payload.options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(payload.options.confirmerText ? { confirmerText: payload.options.confirmerText } : {}),\n }\n : undefined;\n const res = await this.post<LinkDeviceResult>({\n type: 'PM_LINK_DEVICE_WITH_SCANNED_QR_DATA',\n payload: {\n qrData: payload.qrData,\n fundingAmount: payload.fundingAmount,\n options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : undefined\n },\n options: {\n onProgress: this.wrapOnEvent(payload.options?.onEvent, isDeviceLinkingSSEEvent)\n }\n });\n return res.result\n } finally {\n this.hideFrameForActivation();\n }\n }\n\n async startDevice2LinkingFlow(payload?: StartDevice2LinkingFlowArgs): Promise<StartDevice2LinkingFlowResults> {\n if (this.device2StartPromise) {\n return this.device2StartPromise\n }\n const options = payload?.options;\n const safeOptions = options\n ? {\n ...(options.confirmationConfig\n ? { confirmationConfig: options.confirmationConfig as unknown as Record<string, unknown> }\n : {}),\n ...(options.confirmerText ? { confirmerText: options.confirmerText } : {}),\n }\n : undefined;\n const p = this.post<StartDevice2LinkingFlowResults>({\n type: 'PM_START_DEVICE2_LINKING_FLOW',\n payload: {\n ui: payload?.ui,\n cameraId: payload?.cameraId,\n options: safeOptions\n },\n options: {\n onProgress: this.wrapOnEvent(options?.onEvent, isDeviceLinkingSSEEvent),\n sticky: true\n }\n }).then((res) => res.result)\n .finally(() => { this.device2StartPromise = null; });\n\n this.device2StartPromise = p;\n return p;\n }\n\n async stopDevice2LinkingFlow(): Promise<void> {\n await this.post<void>({ type: 'PM_STOP_DEVICE2_LINKING_FLOW' });\n this.progressBus.clearAll();\n }\n\n // ===== Control APIs =====\n async cancelRequest(requestId: string): Promise<void> {\n // Best-effort cancel. Host will attempt to close open modals and mark the request as cancelled.\n await this.post<void>({ type: 'PM_CANCEL', payload: { requestId } }).catch(() => {});\n // Always clear local progress + hide overlay even if the host didn't receive the message\n this.progressBus.unregister(requestId);\n this.hideFrameForActivation();\n }\n\n async cancelAll(): Promise<void> {\n // Try to cancel all requests on the host, but don't depend on READY/port availability\n await this.post<void>({ type: 'PM_CANCEL', payload: {} }).catch(() => {});\n // Clear all local progress listeners and force-hide the overlay\n this.progressBus.clearAll();\n this.hideFrameForActivation();\n }\n\n private onPortMessage(e: MessageEvent<ChildToParentEnvelope>) {\n const msg = e.data as ChildToParentEnvelope;\n // Some wallet-host messages are push-style and are not correlated to a requestId.\n if (msg.type === 'PREFERENCES_CHANGED') {\n this.emitPreferencesChanged(msg.payload as PreferencesChangedPayload);\n return;\n }\n const requestId = msg.requestId;\n if (!requestId) return;\n\n // Bridge PROGRESS events to caller-provided onEvent callback via pending registry\n if (msg.type === 'PROGRESS') {\n const payload = (msg.payload as ProgressPayload);\n // Route via ProgressBus (handles overlay + sticky delivery)\n this.progressBus.dispatch({ requestId: requestId, payload: payload });\n // Refresh timeout for long-running operations whenever progress is received\n const pend = this.pending.get(requestId);\n if (pend) {\n if (pend.timer) window.clearTimeout(pend.timer);\n pend.timer = window.setTimeout(() => {\n const err = pend.onTimeout();\n pend.reject(err);\n }, this.opts.requestTimeoutMs);\n }\n return;\n }\n\n const pending = this.pending.get(requestId);\n // Hide overlay on completion only if no other requests still need it,\n // and this request wasn't marked sticky (UI-managed lifecycle).\n if (!this.progressBus.isSticky(requestId)) {\n if (!this.progressBus.wantsVisible()) {\n this.hideFrameForActivation();\n }\n }\n if (!pending) {\n // Even if no pending exists (e.g., early cancel or pre-resolved),\n // ensure any lingering progress subscriber is removed.\n if (this.debug) {\n console.debug('[WalletIframeRouter] Non-PROGRESS without pending → hide + unregister', {\n requestId,\n type: (msg as unknown as { type?: unknown })?.type || 'unknown'\n });\n }\n this.progressBus.unregister(requestId);\n return;\n }\n this.pending.delete(requestId);\n if (pending.timer) window.clearTimeout(pending.timer);\n\n if (msg.type === 'ERROR') {\n const err: Error & { code?: string; details?: unknown } = new Error(msg.payload?.message || 'Wallet error');\n err.code = msg.payload?.code;\n err.details = msg.payload?.details;\n // Deliver to pending promise if present\n pending.reject(err);\n // Also notify all progress subscribers for this requestId\n this.progressBus.dispatch({\n requestId: requestId,\n payload: {\n step: 0,\n phase: 'error',\n status: 'error',\n message: msg.payload?.message\n }\n });\n this.progressBus.unregister(requestId);\n return;\n }\n\n pending.resolve(msg.payload);\n if (!this.progressBus.isSticky(requestId)) {\n this.progressBus.unregister(requestId);\n }\n }\n\n /**\n * Post a typed envelope over the MessagePort with robust readiness handling.\n * This is the core method that handles all communication with the iframe.\n *\n * Flow:\n * 1. Ensure iframe is ready (lazy initialization)\n * 2. Generate unique request ID for correlation\n * 3. Set up timeout and progress handling\n * 4. Send message to iframe via MessagePort\n * 5. Wait for response (PM_RESULT or ERROR)\n * 6. Clean up on completion or timeout\n */\n private async post<T>(\n envelope: Omit<ParentToChildEnvelope, 'requestId'>,\n ): Promise<PostResult<T>> {\n\n // Step 1: Lazily initialize the iframe/client if not ready yet\n if (!this.ready || !this.port) {\n await this.init();\n }\n\n // Step 2: Generate unique request ID for correlation\n const requestId = `${Date.now()}-${++this.reqCounter}`;\n const full: ParentToChildEnvelope = { ...(envelope as ParentToChildEnvelope), requestId };\n const { options } = full;\n\n return new Promise<PostResult<T>>((resolve, reject) => {\n const onTimeout = () => {\n const pending = this.pending.get(requestId);\n if (pending?.timer !== undefined) window.clearTimeout(pending.timer);\n this.pending.delete(requestId);\n this.progressBus.unregister(requestId);\n this.overlay.setSticky(false);\n if (!this.progressBus.wantsVisible()) {\n this.hideFrameForActivation();\n }\n this.sendBestEffortCancel(requestId);\n return new Error(`Wallet request timeout for ${envelope.type}`);\n };\n\n // Step 3: Set up timeout handler for request\n const timer = window.setTimeout(() => {\n const err = onTimeout();\n reject(err);\n }, this.opts.requestTimeoutMs);\n\n // Step 4: Register pending request for correlation\n this.pending.set(requestId, {\n resolve: (v) => resolve(v as PostResult<T>),\n reject,\n timer,\n onProgress: options?.onProgress,\n onTimeout,\n });\n\n // Step 5: Register progress handler for real-time updates\n this.progressBus.register({\n requestId: requestId,\n sticky: !!options?.sticky, // Some flows need to persist after completion\n onProgress: (payload: ProgressPayload) => {\n // Bridge progress events from iframe back to parent callback\n try {\n options?.onProgress?.(payload);\n } catch {}\n },\n });\n\n try {\n // Step 6: Strip non-cloneable fields (functions) from envelope options before posting\n const wireOptions = (options && isObject(options))\n ? (() => {\n const stickyVal = (options as { sticky?: unknown }).sticky;\n return isBoolean(stickyVal) ? { sticky: stickyVal } : undefined;\n })()\n : undefined;\n const serializableFull = wireOptions ? { ...full, options: wireOptions } : { ...full, options: undefined };\n\n // Align overlay stickiness with request options (phase 2 will use intents)\n this.overlay.setSticky(!!(wireOptions && (wireOptions as { sticky?: boolean }).sticky));\n\n // Step 7: Apply overlay intent (conservative) if not already visible, then post\n if (!this.overlay.getState().visible) {\n const intent = this.computeOverlayIntent(serializableFull.type);\n if (intent.mode === 'fullscreen') {\n this.overlay.setSticky(!!(wireOptions && (wireOptions as { sticky?: boolean }).sticky));\n this.overlay.showFullscreen();\n }\n }\n\n // Send message to iframe via MessagePort\n this.port!.postMessage(serializableFull as ParentToChildEnvelope);\n } catch (err) {\n // Step 8: Handle send errors - clean up and reject\n this.pending.delete(requestId);\n window.clearTimeout(timer);\n this.progressBus.unregister(requestId);\n reject(toError(err));\n }\n });\n }\n\n /**\n * computeOverlayIntent - Preflight \"Show\" Decision\n *\n * This method makes the initial decision about whether to show the overlay\n * BEFORE sending the request to the iframe. It's a conservative preflight\n * check that ensures the iframe is visible in time for user activation.\n *\n * Key Responsibilities:\n * - Preflight Decision: Determines overlay visibility before request is sent\n * - User Activation Timing: Ensures iframe is visible when WebAuthn prompts appear\n * - Conservative Approach: Only shows overlay if not already visible\n * - Request Type Mapping: Maps message types to overlay requirements\n *\n * How it differs from other components:\n *\n * vs OnEventsProgressBus (lifecycle and close decision):\n * - computeOverlayIntent: \"SHOW\" decision - runs before sending request\n * - OnEventsProgressBus: \"CLOSE\" decision - runs during operation lifecycle\n * - OnEventsProgressBus drives ongoing UI phases and manages sticky behavior\n * - OnEventsProgressBus handles PM_RESULT/ERROR and decides when to hide overlay\n *\n * vs OverlayController (single executor):\n * - computeOverlayIntent: DECIDES what to do (show/hide decision logic)\n * - OverlayController: EXECUTES the decision (actual CSS manipulation)\n * - OverlayController receives commands from both intent and ProgressBus\n * - OverlayController keeps all style mutations in one place\n *\n * Architecture Flow:\n * 1. computeOverlayIntent() → decides to show overlay\n * 2. OverlayController.showFullscreen() → executes the decision\n * 3. Request sent to iframe → operation begins\n * 4. OnEventsProgressBus manages lifecycle → handles progress events\n * 5. OnEventsProgressBus decides to hide → when operation completes\n * 6. OverlayController.hide() → executes the hide decision\n *\n * Special Cases:\n * - Anchored flows (UI registry with viewportRect) are message-driven\n * - Parent sets bounds and sticky via registry messages\n * - computeOverlayIntent returns 'hidden' for these (don't pre-show)\n * - Some legacy paths still call showFrameForActivation() directly\n *\n * Future Evolution:\n * - If host always emits early PROGRESS for a type, this can be reduced\n * - Intent is to move toward OnEventsProgressBus-driven lifecycle management\n * - This provides predictable, glitch-free activation without hardcoding\n */\n private computeOverlayIntent(type: ParentToChildEnvelope['type']): { mode: 'hidden' | 'fullscreen' } {\n switch (type) {\n // Operations that require fullscreen overlay for WebAuthn activation\n case 'PM_EXPORT_NEAR_KEYPAIR_UI':\n case 'PM_REGISTER':\n case 'PM_LOGIN':\n case 'PM_LINK_DEVICE_WITH_SCANNED_QR_DATA':\n case 'PM_SIGN_AND_SEND_TXS':\n case 'PM_EXECUTE_ACTION':\n case 'PM_SEND_TRANSACTION':\n case 'PM_SIGN_TXS_WITH_ACTIONS':\n return { mode: 'fullscreen' };\n\n // All other operations (background/read-only) don't need overlay\n default:\n return { mode: 'hidden' };\n }\n }\n\n // Temporarily show the service iframe to capture user activation\n private showFrameForActivation(): void {\n // Ensure iframe exists so overlay can be applied immediately\n this.transport.ensureIframeMounted();\n if (this.overlayForceFullscreen) {\n this.overlay.showFullscreen();\n } else {\n // Prefer fullscreen by default\n this.overlay.showFullscreen();\n }\n }\n\n private hideFrameForActivation(): void {\n if (!this.overlay.getState().visible) return;\n this.overlay.hide();\n }\n\n private sendBestEffortCancel(targetRequestId?: string): void {\n const port = this.port;\n if (!port) return;\n const cancelEnvelope: ParentToChildEnvelope = {\n type: 'PM_CANCEL',\n requestId: `cancel-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n payload: targetRequestId ? { requestId: targetRequestId } : {}\n };\n port.postMessage(cancelEnvelope);\n }\n\n /**\n * Public toggle to surface the wallet iframe for user activation or hide it.\n * Useful when mounting inline UI components that require direct user clicks.\n */\n setOverlayVisible(visible: boolean): void {\n if (visible) {\n // Respect fullscreen lock when present\n if (this.overlayForceFullscreen) {\n this.overlay.showFullscreen();\n } else {\n this.showFrameForActivation();\n }\n } else {\n this.hideFrameForActivation();\n }\n }\n\n /** Public helper for tests/tools: get the underlying iframe element. */\n getIframeEl(): HTMLIFrameElement | null {\n return this.transport.getIframeEl();\n }\n\n /** Public helper for tests/tools: inspect current overlay state. */\n getOverlayState(): { visible: boolean; mode: 'hidden' | 'fullscreen' | 'anchored'; sticky: boolean; rect?: DOMRectLike } {\n return this.overlay.getState();\n }\n\n /**\n * Position and show the wallet iframe as an anchored overlay matching a DOMRect.\n * Accepts viewport-relative coordinates (from getBoundingClientRect()).\n *\n * Important: Some apps apply CSS transforms (or filters/perspective) on html/body,\n * which changes the containing block for position: fixed. In those cases a fixed\n * iframe will be offset by the page scroll. To avoid that mismatch, anchor the\n * overlay using absolute positioning in document coordinates.\n */\n setOverlayBounds(rect: { top: number; left: number; width: number; height: number }): void {\n if (this.overlayForceFullscreen) return; // ignore anchored bounds while locked to fullscreen\n this.transport.ensureIframeMounted();\n this.overlay.showAnchored(rect as DOMRectLike);\n }\n\n // setAnchoredOverlayBounds/clearAnchoredOverlay removed with Arrow overlay deprecation\n\n // Post a window message and surface errors in debug mode instead of silently swallowing them\n private postWindowMessage(w: Window, data: unknown, target: string): void {\n try {\n w.postMessage(data, target);\n } catch (err) {\n if (this.debug) {\n console.error('[WalletIframeRouter] window.postMessage failed', { error: err, data });\n }\n }\n }\n\n}\n\n// ===== Runtime type guards to safely bridge ProgressPayload → typed SSE events =====\nconst REGISTRATION_PHASES = new Set<string>(Object.values(RegistrationPhase) as string[]);\nconst LOGIN_PHASES = new Set<string>(Object.values(LoginPhase) as string[]);\nconst ACTION_PHASES = new Set<string>(Object.values(ActionPhase) as string[]);\nconst DEVICE_LINKING_PHASES = new Set<string>(Object.values(DeviceLinkingPhase) as string[]);\nconst ACCOUNT_RECOVERY_PHASES = new Set<string>(Object.values(AccountRecoveryPhase) as string[]);\nconst EMAIL_RECOVERY_PHASES = new Set<string>(Object.values(EmailRecoveryPhase) as string[]);\n\nfunction phaseOf(progress: ProgressPayload): string {\n return String((progress as { phase?: unknown })?.phase ?? '');\n}\n\nfunction isRegistrationSSEEvent(progress: ProgressPayload): progress is RegistrationSSEEvent {\n return REGISTRATION_PHASES.has(phaseOf(progress));\n}\n\nfunction isLoginSSEEvent(p: ProgressPayload): p is LoginSSEvent {\n return LOGIN_PHASES.has(phaseOf(p));\n}\n\nfunction isActionSSEEvent(p: ProgressPayload): p is ActionSSEEvent {\n return ACTION_PHASES.has(phaseOf(p));\n}\n\nexport function isDelegateSSEEvent(p: ProgressPayload): p is DelegateActionSSEEvent {\n if (!isActionSSEEvent(p)) return false;\n const data = (p as any).data;\n return !!data && typeof data === 'object' && (data as any).context === 'delegate';\n}\n\nfunction isDeviceLinkingSSEEvent(p: ProgressPayload): p is DeviceLinkingSSEEvent {\n return DEVICE_LINKING_PHASES.has(phaseOf(p));\n}\n\nfunction isAccountRecoverySSEEvent(p: ProgressPayload): p is AccountRecoverySSEEvent {\n return ACCOUNT_RECOVERY_PHASES.has(phaseOf(p));\n}\n\nfunction isEmailRecoverySSEEvent(p: ProgressPayload): p is EmailRecoverySSEEvent {\n return EMAIL_RECOVERY_PHASES.has(phaseOf(p));\n}\n\n/**\n * Strips out class functions as they cannot be sent over postMessage to iframe\n */\n function normalizeSignedTransactionObject(result: SignTransactionResult) {\n const arr = Array.isArray(result) ? result : [];\n const normalized = arr.map(entry => {\n if (entry?.signedTransaction) {\n const st = entry.signedTransaction as unknown;\n if (isPlainSignedTransactionLike(st)) {\n entry.signedTransaction = SignedTransaction.fromPlain({\n transaction: (st as { transaction: unknown }).transaction,\n signature: (st as { signature: unknown }).signature,\n borsh_bytes: extractBorshBytesFromPlainSignedTx(st),\n });\n }\n }\n return entry;\n });\n return normalized\n }\n\n/**\n * Strips out functions as they cannot be sent over postMessage to iframe\n */\nimport { stripFunctionsShallow } from '../validation';\n\nfunction removeFunctionsFromOptions(options?: object): object | undefined {\n return stripFunctionsShallow(options as Record<string, unknown>);\n}\n"],"mappings":"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"}
|
|
@@ -8,6 +8,13 @@ const require_actions = require('./types/actions.js');
|
|
|
8
8
|
const require_rpc = require('./types/rpc.js');
|
|
9
9
|
|
|
10
10
|
//#region src/core/rpcCalls.ts
|
|
11
|
+
async function getEmailRecoveryVerificationResult(nearClient, dkimVerifierAccountId, verificationViewMethod, requestId) {
|
|
12
|
+
return await nearClient.view({
|
|
13
|
+
account: dkimVerifierAccountId,
|
|
14
|
+
method: verificationViewMethod,
|
|
15
|
+
args: { request_id: requestId }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
11
18
|
/**
|
|
12
19
|
* Query the contract to get the account linked to a device public key
|
|
13
20
|
* Used in device linking flow to check if a device key has been added
|
|
@@ -396,6 +403,7 @@ exports.executeDeviceLinkingContractCalls = executeDeviceLinkingContractCalls;
|
|
|
396
403
|
exports.getAuthenticatorsByUser = getAuthenticatorsByUser;
|
|
397
404
|
exports.getCredentialIdsContractCall = getCredentialIdsContractCall;
|
|
398
405
|
exports.getDeviceLinkingAccountContractCall = getDeviceLinkingAccountContractCall;
|
|
406
|
+
exports.getEmailRecoveryVerificationResult = getEmailRecoveryVerificationResult;
|
|
399
407
|
exports.getRecoveryEmailHashesContractCall = getRecoveryEmailHashesContractCall;
|
|
400
408
|
Object.defineProperty(exports, 'init_rpcCalls', {
|
|
401
409
|
enumerable: true,
|