{"version":3,"file":"src-DrO48QOo.cjs","names":["getClientId","getBackendUrl","Deferred","sdkConfigStore","Deferred","FrakRpcError","RpcErrorCodes","OpenPanel","getClientId","base64urlDecode","getSupportedCurrency"],"sources":["../src/constants.ts","../src/utils/i18n/detectPageLanguage.ts","../src/clients/ssoUrlListener.ts","../src/utils/browser/deepLinkWithFallback.ts","../src/utils/browser/inAppBrowser.ts","../src/utils/iframe/iframeHelper.ts","../src/clients/transports/iframeLifecycleManager.ts","../src/clients/createIFrameFrakClient.ts","../src/utils/compression/decompress.ts","../src/clients/setupClient.ts","../src/context/mergeAttribution.ts"],"sourcesContent":["/**\n * The backup key for client side backup if needed\n */\nexport const BACKUP_KEY = \"nexus-wallet-backup\";\n\n/**\n * Deep link scheme for Frak Wallet mobile app.\n *\n * Replaced at build time via tsdown/Vite `define`. Defaults to the prod scheme;\n * in-monorepo dev builds (listener at wallet-dev.frak.id) override this with\n * `frakwallet-dev://` so deep links open the dev wallet variant (id.frak.wallet.dev).\n * External integrators consuming the published NPM/CDN bundle always see the prod scheme.\n */\nexport const DEEP_LINK_SCHEME: string =\n    process.env.DEEP_LINK_SCHEME ?? \"frakwallet://\";\n","import type { Language } from \"../../types/config\";\n\n/** Normalize a raw locale string (e.g. `\"fr-FR\"`) to a supported {@link Language}. */\nfunction normalize(raw: string | undefined | null): Language | undefined {\n    const base = raw?.split(\"-\")[0]?.toLowerCase();\n    return base === \"en\" || base === \"fr\" ? base : undefined;\n}\n\n/**\n * Best-effort detection of the page's intended content language for SDK copy.\n *\n * Precedence: the document's declared `<html lang>` (the page author's explicit\n * content language) → the browser UI language (`navigator.language`). Returns\n * `undefined` when neither resolves to a supported language, letting callers\n * apply their own fallback (e.g. `\"en\"`).\n *\n * The `<html lang>` check comes first so a page authored in French renders\n * French SDK copy even when the visitor's browser is set to another language.\n */\nexport function detectPageLanguage(): Language | undefined {\n    const htmlLang =\n        typeof document !== \"undefined\"\n            ? normalize(document.documentElement.lang)\n            : undefined;\n    if (htmlLang) return htmlLang;\n\n    return typeof navigator !== \"undefined\"\n        ? normalize(navigator.language)\n        : undefined;\n}\n","import type { RpcClient } from \"@frak-labs/frame-connector\";\nimport type { FrakLifecycleEvent } from \"../types\";\nimport type { IFrameRpcSchema } from \"../types/rpc\";\n\n/**\n * Listen for SSO redirect with compressed data in URL\n * Forwards compressed data to iframe via lifecycle event\n * Cleans URL immediately after detection\n *\n * Performance: One-shot URL check, no polling, no re-renders\n *\n * @param rpcClient - RPC client instance to send lifecycle events\n * @param waitForConnection - Promise that resolves when iframe is connected\n */\nexport function setupSsoUrlListener(\n    rpcClient: RpcClient<IFrameRpcSchema, FrakLifecycleEvent>,\n    waitForConnection: Promise<boolean>\n): void {\n    if (typeof window === \"undefined\") {\n        return;\n    }\n\n    // One-shot URL check - no need for MutationObserver or polling\n    const url = new URL(window.location.href);\n    const compressedSso = url.searchParams.get(\"sso\");\n\n    // Early return if no SSO parameter\n    if (!compressedSso) {\n        return;\n    }\n\n    // Forward compressed data directly to iframe (no decompression on SDK side)\n    // Iframe will decompress and process\n    waitForConnection\n        .then(() => {\n            // Send lifecycle event with compressed string\n            // This is a one-way notification, no response expected\n            rpcClient.sendLifecycle({\n                clientLifecycle: \"sso-redirect-complete\",\n                data: { compressed: compressedSso },\n            });\n\n            console.log(\n                \"[SSO URL Listener] Forwarded compressed SSO data to iframe\"\n            );\n        })\n        .catch((error) => {\n            console.error(\n                \"[SSO URL Listener] Failed to forward SSO data:\",\n                error\n            );\n        });\n\n    // Clean URL immediately to prevent exposure in browser history\n    // Use replaceState to avoid navigation/re-render\n    url.searchParams.delete(\"sso\");\n    window.history.replaceState({}, \"\", url.toString());\n\n    console.log(\"[SSO URL Listener] SSO parameter detected and URL cleaned\");\n}\n","import { DEEP_LINK_SCHEME } from \"../../constants\";\n\n/**\n * Options for deep link with fallback\n */\nexport type DeepLinkFallbackOptions = {\n    /** Timeout in ms before triggering fallback (default: 2500ms) */\n    timeout?: number;\n    /** Callback invoked when fallback is triggered (app not installed) */\n    onFallback?: () => void;\n};\n\n/**\n * Check if running on a Chromium-based Android browser.\n *\n * On Chrome Android, custom scheme deep links (e.g. frakwallet://) trigger\n * a confirmation bar (\"Continue to Frak Wallet?\"). Using intent:// URLs\n * instead bypasses this for Chromium browsers while keeping custom scheme\n * fallback for non-Chromium browsers (e.g. Firefox) where it works fine.\n */\nexport function isChromiumAndroid(): boolean {\n    const ua = navigator.userAgent;\n    return /Android/i.test(ua) && /Chrome\\/\\d+/i.test(ua);\n}\n\n/**\n * Convert a Frak deep link to an Android intent:// URL.\n *\n * Intent URLs let Chromium browsers open the app directly without\n * showing the \"Continue to app?\" confirmation bar.\n *\n * Note: We intentionally omit the `package` parameter. Including it\n * causes Chrome to redirect to the Play Store when the app is not\n * installed, which breaks the visibility-based fallback detection.\n * Without `package`, Chrome simply does nothing when the app is\n * missing, allowing the fallback mechanism to fire correctly.\n *\n * The scheme is derived from `DEEP_LINK_SCHEME` so the dev variant\n * (`frakwallet-dev://`) routes to the dev app shell, not prod.\n *\n * Format: intent://path#Intent;scheme=<scheme>;end\n */\nconst DEEP_LINK_SCHEME_NAME = /* @__PURE__ */ DEEP_LINK_SCHEME.replace(\n    \"://\",\n    \"\"\n);\n\nexport function toAndroidIntentUrl(deepLink: string): string {\n    // Extract everything after the scheme (e.g. \"frakwallet://\" or \"frakwallet-dev://\")\n    const path = deepLink.slice(DEEP_LINK_SCHEME.length);\n    return `intent://${path}#Intent;scheme=${DEEP_LINK_SCHEME_NAME};end`;\n}\n\n/**\n * Trigger a deep link with visibility-based fallback detection.\n *\n * Uses the Page Visibility API to detect if the app opened (page goes hidden).\n * If the page remains visible after the timeout, assumes app is not installed\n * and invokes the onFallback callback.\n *\n * On Chromium Android, converts custom scheme to intent:// URL to avoid\n * the \"Continue to app?\" confirmation bar.\n *\n * @param deepLink - The deep link URL to trigger (e.g., \"frakwallet://wallet\")\n * @param options - Optional configuration (timeout, onFallback callback)\n */\nexport function triggerDeepLinkWithFallback(\n    deepLink: string,\n    options?: DeepLinkFallbackOptions\n): void {\n    const timeout = options?.timeout ?? 2500;\n\n    // Track if the app opened (page went to background)\n    let appOpened = false;\n\n    const onVisibilityChange = () => {\n        if (document.hidden) {\n            appOpened = true;\n        }\n    };\n\n    // Start listening for visibility changes\n    document.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n    // On Chromium Android, use intent:// to avoid confirmation bar\n    const url =\n        isChromiumAndroid() && isFrakDeepLink(deepLink)\n            ? toAndroidIntentUrl(deepLink)\n            : deepLink;\n\n    // Trigger the deep link\n    window.location.href = url;\n\n    // Check after timeout if app opened\n    setTimeout(() => {\n        // Clean up listener\n        document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\n        if (!appOpened) {\n            // App didn't open - trigger fallback callback\n            options?.onFallback?.();\n        }\n    }, timeout);\n}\n\n/**\n * Check if a URL is a Frak deep link\n */\nexport function isFrakDeepLink(url: string): boolean {\n    return url.startsWith(DEEP_LINK_SCHEME);\n}\n","/**\n * Check if the current device runs iOS (including iPadOS 13+).\n */\nfunction checkIsIOS(): boolean {\n    if (typeof navigator === \"undefined\") return false;\n    const ua = navigator.userAgent;\n    // Standard iOS devices\n    if (/iPhone|iPad|iPod/i.test(ua)) return true;\n    // iPadOS 13+ reports as Macintosh — detect via touch support\n    if (/Macintosh/i.test(ua) && navigator.maxTouchPoints > 1) return true;\n    return false;\n}\n\n/**\n * Whether the current device runs iOS (including iPadOS 13+).\n */\nexport const isIOS: boolean = /* @__PURE__ */ checkIsIOS();\n\n/**\n * Check if the current device is a mobile device (iOS, iPadOS, Android,\n * webOS, BlackBerry, IEMobile, Opera Mini). Reuses {@link isIOS} so the\n * iPadOS-13+ Macintosh heuristic stays in one place.\n */\nexport function isMobile(): boolean {\n    if (typeof navigator === \"undefined\") return false;\n    if (isIOS) return true;\n    return /Android|webOS|BlackBerry|IEMobile|Opera Mini/i.test(\n        navigator.userAgent\n    );\n}\n\n/**\n * Check if the current browser is a social media in-app browser\n * (Instagram, Facebook WebView).\n */\nfunction checkInAppBrowser(): boolean {\n    if (typeof navigator === \"undefined\") return false;\n    const ua = navigator.userAgent.toLowerCase();\n    return (\n        ua.includes(\"instagram\") ||\n        ua.includes(\"fban\") ||\n        ua.includes(\"fbav\") ||\n        ua.includes(\"facebook\")\n    );\n}\n\n/**\n * Whether the current browser is a social media in-app browser\n * (Instagram, Facebook).\n */\nexport const isInAppBrowser: boolean = /* @__PURE__ */ checkInAppBrowser();\n\n/**\n * Redirect to external browser from in-app WebView.\n *\n * - **iOS**: Uses `x-safari-https://` scheme — server-side 302 redirects\n *   to custom URL schemes are silently swallowed by WKWebView.\n *   Direct `window.location.href` assignment works (confirmed iOS 17+).\n *\n * - **Android**: Uses backend `/common/social` endpoint which returns a PDF\n *   Content-Type response, forcing the WebView to hand off to the default browser.\n *\n * @param targetUrl - The URL to open in the external browser\n */\nexport function redirectToExternalBrowser(targetUrl: string): void {\n    if (isIOS && targetUrl.startsWith(\"https://\")) {\n        window.location.href = `x-safari-https://${targetUrl.slice(8)}`;\n    } else if (isIOS && targetUrl.startsWith(\"http://\")) {\n        window.location.href = `x-safari-http://${targetUrl.slice(7)}`;\n    } else {\n        window.location.href = `${process.env.BACKEND_URL}/common/social?u=${encodeURIComponent(targetUrl)}`;\n    }\n}\n","import { getBackendUrl } from \"../../config/backendUrl\";\nimport { getClientId } from \"../../config/clientId\";\nimport type { FrakWalletSdkConfig, ListenerPreloadOption } from \"../../types\";\n\n/**\n * Base props for the iframe\n * @ignore\n */\nexport const baseIframeProps = {\n    id: \"frak-wallet\",\n    name: \"frak-wallet\",\n    title: \"Frak Wallet\",\n    allow: \"publickey-credentials-get *; clipboard-write; web-share *\",\n    style: {\n        width: \"0\",\n        height: \"0\",\n        border: \"0\",\n        position: \"absolute\",\n        zIndex: 2000001,\n        top: \"-1000px\",\n        left: \"-1000px\",\n        colorScheme: \"auto\",\n    },\n};\n\n/**\n * Create the Frak iframe\n * @param args\n * @param args.walletBaseUrl - Use `config.walletUrl` instead. Will be removed in future versions.\n * @param args.config - The configuration object containing iframe options, including the replacement for `walletBaseUrl`.\n */\nexport function createIframe({\n    walletBaseUrl,\n    config,\n}: {\n    walletBaseUrl?: string;\n    config?: FrakWalletSdkConfig;\n}): Promise<HTMLIFrameElement | undefined> {\n    // Check if the iframe is already created\n    const alreadyCreatedIFrame = document.querySelector(\"#frak-wallet\");\n\n    // If the iframe is already created, remove it\n    if (alreadyCreatedIFrame) {\n        alreadyCreatedIFrame.remove();\n    }\n\n    const iframe = document.createElement(\"iframe\");\n\n    // Set the base iframe props\n    iframe.id = baseIframeProps.id;\n    iframe.name = baseIframeProps.name;\n    iframe.allow = baseIframeProps.allow;\n    iframe.style.zIndex = baseIframeProps.style.zIndex.toString();\n\n    changeIframeVisibility({ iframe, isVisible: false });\n\n    // Set src BEFORE appending to DOM to avoid about:blank load event\n    const walletUrl =\n        config?.walletUrl ?? walletBaseUrl ?? \"https://wallet.frak.id\";\n    const clientId = getClientId();\n\n    // Preconnect to the wallet + backend origins so the handshake doesn't pay\n    // for a cold DNS/TLS round-trip on partner sites that didn't warm them.\n    preconnect(walletUrl);\n    preconnect(getBackendUrl(walletUrl));\n\n    iframe.src = buildListenerUrl({\n        walletUrl,\n        clientId,\n        preload: config?.preload,\n    });\n\n    return new Promise((resolve) => {\n        iframe.addEventListener(\"load\", () => resolve(iframe));\n        document.body.appendChild(iframe);\n    });\n}\n\n/**\n * Build the listener iframe URL.\n *\n * Query params:\n *  - `clientId` — anonymous SDK client identifier used for funnel joining.\n *\n * Hash params (consumed by `apps/listener/app/bootstrap.ts#setupPreloadHints`):\n *  - `preload=modal,sharing` — idle-warms the matching Ring 1 + Ring 2 chunks.\n *    Skipped entirely when no preload hints are provided so the listener\n *    doesn't pay for warm-ups that nobody asked for.\n */\nfunction buildListenerUrl({\n    walletUrl,\n    clientId,\n    preload,\n}: {\n    walletUrl: string;\n    clientId: string;\n    preload?: ListenerPreloadOption[];\n}): string {\n    const base = `${walletUrl}/listener?clientId=${encodeURIComponent(clientId)}`;\n    if (!preload || preload.length === 0) return base;\n    return `${base}#preload=${preload.join(\",\")}`;\n}\n\n/**\n * Change the visibility of the given iframe\n * @ignore\n */\nexport function changeIframeVisibility({\n    iframe,\n    isVisible,\n}: {\n    iframe: HTMLIFrameElement;\n    isVisible: boolean;\n}) {\n    if (!isVisible) {\n        iframe.style.width = \"0\";\n        iframe.style.height = \"0\";\n        iframe.style.border = \"0\";\n        iframe.style.position = \"fixed\";\n        iframe.style.top = \"-1000px\";\n        iframe.style.left = \"-1000px\";\n        return;\n    }\n\n    iframe.style.position = \"fixed\";\n    iframe.style.top = \"0\";\n    iframe.style.left = \"0\";\n    iframe.style.width = \"100%\";\n    iframe.style.height = \"100%\";\n    iframe.style.pointerEvents = \"auto\";\n}\n\n/**\n * Find an iframe within window.opener by pathname\n *\n * When a popup is opened via window.open from an iframe, window.opener points to\n * the parent window, not the iframe itself. This utility searches through all frames\n * in window.opener to find an iframe matching the specified pathname.\n *\n * @param pathname - The pathname to search for (default: \"/listener\")\n * @returns The matching iframe window, or null if not found\n *\n * @example\n * ```typescript\n * // Find the default /listener iframe\n * const listenerIframe = findIframeInOpener();\n *\n * // Find a custom iframe\n * const customIframe = findIframeInOpener(\"/my-custom-iframe\");\n * ```\n */\nexport function findIframeInOpener(pathname = \"/listener\"): Window | null {\n    if (!window.opener) return null;\n\n    const frameCheck = (frame: Window) => {\n        try {\n            return (\n                frame.location.origin === window.location.origin &&\n                frame.location.pathname === pathname\n            );\n        } catch {\n            // Cross-origin frame, skip\n            return false;\n        }\n    };\n\n    // Check if the openner window is the right one\n    if (frameCheck(window.opener)) return window.opener;\n\n    // Search through frames in window.opener\n    try {\n        const frames = window.opener.frames;\n        for (let i = 0; i < frames.length; i++) {\n            if (frameCheck(frames[i])) {\n                return frames[i];\n            }\n        }\n        return null;\n    } catch (error) {\n        console.error(\n            `[findIframeInOpener] Error finding iframe with pathname ${pathname}:`,\n            error\n        );\n        return null;\n    }\n}\n\n/**\n * Inject a `<link rel=\"preconnect\">` for the given origin. Idempotent — browsers\n * de-duplicate preconnects per origin automatically, but we also guard on a data\n * attribute to avoid spamming the <head> in SPA re-inits.\n */\nfunction preconnect(url: string): void {\n    if (typeof document === \"undefined\") return;\n    try {\n        const origin = new URL(url).origin;\n        const selector = `link[rel=\"preconnect\"][data-frak-preconnect=\"${origin}\"]`;\n        if (document.head.querySelector(selector)) return;\n        const link = document.createElement(\"link\");\n        link.rel = \"preconnect\";\n        link.href = origin;\n        link.crossOrigin = \"\";\n        link.dataset.frakPreconnect = origin;\n        document.head.appendChild(link);\n    } catch {\n        // Invalid URL — nothing to preconnect.\n    }\n}\n","import { Deferred } from \"@frak-labs/frame-connector\";\nimport { BACKUP_KEY } from \"../../constants\";\nimport type { FrakLifecycleEvent } from \"../../types\";\nimport {\n    isFrakDeepLink,\n    triggerDeepLinkWithFallback,\n} from \"../../utils/browser/deepLinkWithFallback\";\nimport { isInAppBrowser, isIOS } from \"../../utils/browser/inAppBrowser\";\nimport { changeIframeVisibility } from \"../../utils/iframe/iframeHelper\";\n\n// iOS in-app browsers (Instagram, Facebook) silently swallow server-side 302\n// redirects to custom URL schemes (x-safari-https://) in WKWebView; direct\n// window.location.href assignment works.\nconst isIOSInAppBrowser = isIOS && isInAppBrowser;\n\n/** @ignore */\nexport type IframeLifecycleManager = {\n    isConnected: Promise<boolean>;\n    handleEvent: (messageEvent: FrakLifecycleEvent) => void;\n};\n\n/**\n * Handle backup storage\n */\nfunction handleBackup(backup: string | undefined): void {\n    if (backup) {\n        localStorage.setItem(BACKUP_KEY, backup);\n    } else {\n        localStorage.removeItem(BACKUP_KEY);\n    }\n}\n\n/**\n * Compute final redirect URL with parameter substitution\n */\nfunction computeRedirectUrl(\n    baseRedirectUrl: string,\n    mergeToken?: string\n): string {\n    try {\n        const redirectUrl = new URL(baseRedirectUrl);\n        if (!redirectUrl.searchParams.has(\"u\")) {\n            return baseRedirectUrl;\n        }\n\n        // Append merge token to the page URL so it survives\n        // the backend /common/social redirect chain\n        const finalPageUrl = appendMergeToken(window.location.href, mergeToken);\n\n        redirectUrl.searchParams.delete(\"u\");\n        redirectUrl.searchParams.append(\"u\", finalPageUrl);\n\n        return redirectUrl.toString();\n    } catch {\n        return baseRedirectUrl;\n    }\n}\n\n/**\n * Redirect current page to Safari via x-safari-https:// scheme.\n * Used on iOS in-app browsers where backend 302 → custom scheme fails.\n */\nfunction redirectToSafari(mergeToken?: string) {\n    const url = new URL(window.location.href);\n    if (mergeToken) {\n        url.searchParams.set(\"fmt\", mergeToken);\n    }\n    const scheme =\n        url.protocol === \"http:\" ? \"x-safari-http\" : \"x-safari-https\";\n    window.location.href = `${scheme}://${url.host}${url.pathname}${url.search}${url.hash}`;\n}\n\n/**\n * Check if this is a social/in-app-browser escape redirect (contains /common/social)\n */\nfunction isSocialRedirect(url: string): boolean {\n    return url.includes(\"/common/social\");\n}\n\n/**\n * Append merge token to a URL as the `fmt` query parameter.\n */\nfunction appendMergeToken(urlString: string, mergeToken?: string): string {\n    if (!mergeToken) return urlString;\n    try {\n        const url = new URL(urlString);\n        url.searchParams.set(\"fmt\", mergeToken);\n        return url.toString();\n    } catch {\n        const sep = urlString.includes(\"?\") ? \"&\" : \"?\";\n        return `${urlString}${sep}fmt=${encodeURIComponent(mergeToken)}`;\n    }\n}\n\n/**\n * Handle redirect with deep link fallback\n */\nfunction handleRedirect(\n    iframe: HTMLIFrameElement,\n    baseRedirectUrl: string,\n    targetOrigin: string,\n    mergeToken?: string,\n    openInNewTab?: boolean\n): void {\n    // If requested, open in a new tab instead of navigating the current page.\n    // This preserves the merchant page while triggering universal links.\n    // Requires the iframe postMessage to include user activation delegation.\n    if (openInNewTab) {\n        const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);\n        window.open(finalUrl, \"_blank\");\n        return;\n    }\n\n    if (isFrakDeepLink(baseRedirectUrl)) {\n        const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);\n        triggerDeepLinkWithFallback(finalUrl, {\n            onFallback: () => {\n                iframe.contentWindow?.postMessage(\n                    {\n                        clientLifecycle: \"deep-link-failed\",\n                        data: { originalUrl: finalUrl },\n                    },\n                    targetOrigin\n                );\n            },\n        });\n    } else if (isIOSInAppBrowser && isSocialRedirect(baseRedirectUrl)) {\n        // iOS WKWebView silently swallows 302 redirects to custom URL\n        // schemes — bypass the server redirect entirely\n        redirectToSafari(mergeToken);\n    } else {\n        const finalUrl = computeRedirectUrl(baseRedirectUrl, mergeToken);\n        window.location.href = finalUrl;\n    }\n}\n\n/**\n * Create a new iframe lifecycle handler\n * @param args\n * @param args.iframe - The iframe element used for wallet communication\n * @param args.targetOrigin - The wallet URL origin for postMessage security\n * @ignore\n */\nexport function createIFrameLifecycleManager({\n    iframe,\n    targetOrigin,\n}: {\n    iframe: HTMLIFrameElement;\n    targetOrigin: string;\n}): IframeLifecycleManager {\n    // Create the isConnected listener\n    const isConnectedDeferred = new Deferred<boolean>();\n\n    // Build the handler itself\n    const handler = (messageEvent: FrakLifecycleEvent) => {\n        if (!(\"iframeLifecycle\" in messageEvent)) return;\n\n        const { iframeLifecycle: event, data } = messageEvent;\n\n        switch (event) {\n            // Resolve the isConnected promise\n            case \"connected\":\n                isConnectedDeferred.resolve(true);\n                break;\n            // Perform a frak backup\n            case \"do-backup\":\n                handleBackup(data.backup);\n                break;\n            // Remove frak backup\n            case \"remove-backup\":\n                localStorage.removeItem(BACKUP_KEY);\n                break;\n            // Change iframe visibility\n            case \"show\":\n            case \"hide\":\n                changeIframeVisibility({ iframe, isVisible: event === \"show\" });\n                break;\n            // Redirect handling\n            case \"redirect\":\n                handleRedirect(\n                    iframe,\n                    data.baseRedirectUrl,\n                    targetOrigin,\n                    data.mergeToken,\n                    data.openInNewTab\n                );\n                break;\n        }\n    };\n\n    return {\n        handleEvent: handler,\n        isConnected: isConnectedDeferred.promise,\n    };\n}\n","import {\n    createRpcClient,\n    Deferred,\n    FrakRpcError,\n    type RpcClient,\n    RpcErrorCodes,\n} from \"@frak-labs/frame-connector\";\nimport { OpenPanel } from \"@openpanel/web\";\nimport { getClientId } from \"../config/clientId\";\nimport { sdkConfigStore } from \"../config/sdkConfigStore\";\nimport { BACKUP_KEY } from \"../constants\";\nimport type { FrakLifecycleEvent } from \"../types\";\nimport type { FrakClient } from \"../types/client\";\nimport type { FrakWalletSdkConfig } from \"../types/config\";\nimport type { SdkResolvedConfig } from \"../types/resolvedConfig\";\nimport type { IFrameRpcSchema } from \"../types/rpc\";\nimport { clearAllCache } from \"../utils/cache\";\nimport { detectPageLanguage } from \"../utils/i18n/detectPageLanguage\";\nimport { setupSsoUrlListener } from \"./ssoUrlListener\";\nimport {\n    createIFrameLifecycleManager,\n    type IframeLifecycleManager,\n} from \"./transports/iframeLifecycleManager\";\n\ntype SdkRpcClient = RpcClient<IFrameRpcSchema, FrakLifecycleEvent>;\ntype MerchantConfigResult = Awaited<ReturnType<typeof sdkConfigStore.resolve>>;\n\n/**\n * Create a new iframe Frak client\n * @param args\n * @param args.config - The configuration to use for the Frak Wallet SDK.\n *   When `config.domain` is set, it is used to resolve the correct merchant config in tunneled/proxied environments (e.g. Shopify dev with Cloudflare tunnel).\n * @param args.iframe - The iframe to use for the communication\n * @returns The created Frak Client\n *\n * @example\n * const frakConfig: FrakWalletSdkConfig = {\n *     metadata: {\n *         name: \"My app title\",\n *     },\n * }\n * const iframe = await createIframe({ config: frakConfig });\n * const client = createIFrameFrakClient({ config: frakConfig, iframe });\n */\nexport function createIFrameFrakClient({\n    config,\n    iframe,\n}: {\n    config: FrakWalletSdkConfig;\n    iframe: HTMLIFrameElement;\n}): FrakClient {\n    const frakWalletUrl = config?.walletUrl ?? \"https://wallet.frak.id\";\n\n    // Precedence: explicit `metadata.lang` → page `<html lang>` → browser\n    // language. Lets a page authored in a given language drive SDK copy even\n    // when the visitor's browser is set to another language.\n    const detectedLang = config.metadata.lang ?? detectPageLanguage();\n    const targetDomain =\n        config.domain ??\n        (typeof window !== \"undefined\" ? window.location.hostname : \"\");\n    sdkConfigStore.setCacheScope(targetDomain, detectedLang);\n    sdkConfigStore.reset();\n\n    // Skip fetch entirely if cache is fresh, otherwise fetch (SWR)\n    const configPromise = sdkConfigStore.isCacheFresh\n        ? undefined\n        : sdkConfigStore.resolve(config.domain, config.walletUrl, detectedLang);\n\n    // Create lifecycle manager\n    const lifecycleManager = createIFrameLifecycleManager({\n        iframe,\n        targetOrigin: frakWalletUrl,\n    });\n\n    // Resolved after first resolved-config is sent to iframe (prevents RPC before context exists)\n    const contextSent = new Deferred<void>();\n\n    // Handshake timing: measured from client creation until the iframe\n    // lifecycle manager resolves the `isConnected` promise.\n    const handshakeStartedAt = Date.now();\n\n    // Validate iframe\n    if (!iframe.contentWindow) {\n        throw new FrakRpcError(\n            RpcErrorCodes.configError,\n            \"The iframe does not have a content window\"\n        );\n    }\n\n    // Create RPC client with middleware and lifecycle handlers\n    const rpcClient = createRpcClient<IFrameRpcSchema, FrakLifecycleEvent>({\n        emittingTransport: iframe.contentWindow,\n        listeningTransport: window,\n        targetOrigin: frakWalletUrl,\n        middleware: [\n            // Ensure we are connected and context is sent before sending request\n            {\n                async onRequest(_message, ctx) {\n                    const isConnected = await lifecycleManager.isConnected;\n                    if (!isConnected) {\n                        throw new FrakRpcError(\n                            RpcErrorCodes.clientNotConnected,\n                            \"The iframe provider isn't connected yet\"\n                        );\n                    }\n                    await contextSent.promise;\n                    return ctx;\n                },\n            },\n        ],\n        // Add lifecycle handlers to process iframe lifecycle events\n        lifecycleHandlers: {\n            iframeLifecycle: (event, _context) => {\n                // Delegate to lifecycle manager  (cast for type compatibility)\n                lifecycleManager.handleEvent(event);\n            },\n        },\n    });\n\n    // Setup heartbeat\n    const stopHeartbeat = setupHeartbeat(rpcClient, lifecycleManager);\n\n    const destroy = async () => {\n        stopHeartbeat();\n        rpcClient.cleanup();\n        iframe.remove();\n        clearAllCache();\n        sdkConfigStore.clearCache();\n        sdkConfigStore.reset();\n    };\n\n    // Init open panel\n    let openPanel: OpenPanel | undefined;\n    if (\n        process.env.OPEN_PANEL_API_URL &&\n        process.env.OPEN_PANEL_SDK_CLIENT_ID\n    ) {\n        console.log(\"[Frak SDK] Initializing OpenPanel\");\n        openPanel = new OpenPanel({\n            apiUrl: process.env.OPEN_PANEL_API_URL,\n            clientId: process.env.OPEN_PANEL_SDK_CLIENT_ID,\n            trackScreenViews: true,\n            trackOutgoingLinks: true,\n            trackAttributes: false,\n            // We use a filter to ensure we got the open panel instance initialized\n            //  A bit hacky, but this way we are sure that we got everything needed for the first event ever sent\n            filter: ({ type, payload }) => {\n                if (type !== \"track\") return true;\n                if (!payload?.properties) return true;\n\n                // Check if we we got the properties once initialized\n                if (!(\"sdk_version\" in payload.properties)) {\n                    payload.properties = {\n                        ...payload.properties,\n                        sdk_version: process.env.SDK_VERSION,\n                        user_anonymous_client_id: getClientId(),\n                    };\n                }\n\n                return true;\n            },\n        });\n        openPanel.setGlobalProperties({\n            sdk_version: process.env.SDK_VERSION,\n            user_anonymous_client_id: getClientId(),\n        });\n        openPanel.init();\n        openPanel.track(\"sdk_initialized\", {\n            sdk_version: process.env.SDK_VERSION,\n        });\n\n        // Race the connection against the heartbeat timeout so we can\n        // distinguish \"connected\" from \"timeout\" cleanly without touching\n        // the heartbeat plumbing. 30s matches `HEARTBEAT_TIMEOUT`.\n        let settled = false;\n        const timeoutHandle = setTimeout(() => {\n            if (settled) return;\n            settled = true;\n            openPanel?.track(\"sdk_iframe_handshake_failed\", {\n                reason: \"timeout\",\n            });\n        }, 30_000);\n        lifecycleManager.isConnected\n            .then(() => {\n                if (settled) return;\n                settled = true;\n                clearTimeout(timeoutHandle);\n                openPanel?.track(\"sdk_iframe_connected\", {\n                    handshake_duration_ms: Date.now() - handshakeStartedAt,\n                });\n            })\n            .catch(() => {\n                if (settled) return;\n                settled = true;\n                clearTimeout(timeoutHandle);\n                openPanel?.track(\"sdk_iframe_handshake_failed\", {\n                    reason: \"unknown\",\n                });\n            });\n    }\n\n    // Perform the post connection setup\n    const waitForSetup = postConnectionSetup({\n        config,\n        rpcClient,\n        lifecycleManager,\n        configPromise,\n        contextSent,\n        openPanel,\n    })\n        .then(() => {})\n        .catch((err) => {\n            contextSent.reject(err);\n            throw err;\n        });\n\n    return {\n        config,\n        waitForConnection: lifecycleManager.isConnected,\n        waitForSetup,\n        request: rpcClient.request,\n        listenerRequest: rpcClient.listen,\n        destroy,\n        openPanel,\n    };\n}\n\n/**\n * Setup the heartbeat\n * @param rpcClient - RPC client to send lifecycle events\n * @param lifecycleManager - Lifecycle manager to track connection\n */\nfunction setupHeartbeat(\n    rpcClient: SdkRpcClient,\n    lifecycleManager: IframeLifecycleManager\n) {\n    const HEARTBEAT_INTERVAL = 250; // Fallback discovery ping until we are connected\n    const HEARTBEAT_TIMEOUT = 30_000; // 30 seconds timeout\n    let heartbeatInterval: NodeJS.Timeout;\n    let timeoutId: NodeJS.Timeout;\n\n    const sendHeartbeat = () =>\n        rpcClient.sendLifecycle({\n            clientLifecycle: \"heartbeat\",\n        });\n\n    // Start sending heartbeats\n    async function startHeartbeat() {\n        sendHeartbeat(); // Send initial heartbeat\n        heartbeatInterval = setInterval(sendHeartbeat, HEARTBEAT_INTERVAL);\n\n        // Set up timeout\n        timeoutId = setTimeout(() => {\n            stopHeartbeat();\n            console.log(\"Heartbeat timeout: connection failed\");\n        }, HEARTBEAT_TIMEOUT);\n\n        // Once connected, stop it\n        await lifecycleManager.isConnected;\n\n        // We are now connected, stop the heartbeat\n        stopHeartbeat();\n    }\n\n    // Stop sending heartbeats\n    function stopHeartbeat() {\n        if (heartbeatInterval) {\n            clearInterval(heartbeatInterval);\n        }\n        if (timeoutId) {\n            clearTimeout(timeoutId);\n        }\n    }\n\n    startHeartbeat();\n\n    // Return cleanup function\n    return stopHeartbeat;\n}\n\n/**\n * Perform the post connection setup\n * @param config - SDK configuration\n * @param rpcClient - RPC client to send lifecycle events\n * @param lifecycleManager - Lifecycle manager to track connection\n */\nasync function postConnectionSetup({\n    config,\n    rpcClient,\n    lifecycleManager,\n    configPromise,\n    contextSent,\n    openPanel,\n}: {\n    config: FrakWalletSdkConfig;\n    rpcClient: SdkRpcClient;\n    lifecycleManager: IframeLifecycleManager;\n    configPromise: Promise<MerchantConfigResult> | undefined;\n    contextSent: Deferred<void>;\n    openPanel: OpenPanel | undefined;\n}): Promise<void> {\n    await lifecycleManager.isConnected;\n\n    setupSsoUrlListener(rpcClient, lifecycleManager.isConnected);\n\n    // Read and consume the pending merge token from URL (SSO identity merge)\n    const url = new URL(window.location.href);\n    const pendingMergeToken = url.searchParams.get(\"fmt\") ?? undefined;\n    if (pendingMergeToken) {\n        url.searchParams.delete(\"fmt\");\n        window.history.replaceState({}, \"\", url.toString());\n    }\n\n    // Merge a raw backend response with SDK metadata and persist to store\n    const mergeAndSetConfig = (merchantConfig: MerchantConfigResult) => {\n        const merchantId =\n            merchantConfig?.merchantId ?? config.metadata.merchantId ?? \"\";\n        const domain = merchantConfig?.domain ?? \"\";\n        const allowedDomains = merchantConfig?.allowedDomains ?? [];\n        const raw = merchantConfig?.sdkConfig;\n\n        // Per-field merge: backend wins over SDK static config.\n        const mergedAttribution =\n            raw?.attribution || config.attribution\n                ? { ...config.attribution, ...raw?.attribution }\n                : undefined;\n\n        sdkConfigStore.setConfig(\n            raw\n                ? {\n                      isResolved: true,\n                      merchantId,\n                      domain,\n                      allowedDomains,\n                      hasRawSdkConfig: true,\n                      name: raw.name ?? config.metadata.name,\n                      logoUrl: raw.logoUrl ?? config.metadata.logoUrl,\n                      homepageLink:\n                          raw.homepageLink ?? config.metadata.homepageLink,\n                      lang: raw.lang ?? config.metadata.lang,\n                      currency: raw.currency ?? config.metadata.currency,\n                      hidden: raw.hidden,\n                      css: raw.css,\n                      translations: raw.translations,\n                      placements: raw.placements,\n                      components: raw.components,\n                      attribution: mergedAttribution,\n                  }\n                : {\n                      isResolved: true,\n                      merchantId,\n                      domain,\n                      allowedDomains,\n                      name: config.metadata.name,\n                      logoUrl: config.metadata.logoUrl,\n                      homepageLink: config.metadata.homepageLink,\n                      lang: config.metadata.lang,\n                      currency: config.metadata.currency,\n                      attribution: mergedAttribution,\n                  }\n        );\n    };\n\n    // Send the resolved-config lifecycle event to the iframe.\n    // This is where we also update SDK-side OpenPanel global props with\n    // `merchantId` + `domain` (first time they are known) so every\n    // subsequent SDK event is merchant-attributed. We pass\n    // `sdkAnonymousId` through so the listener can join SDK funnels.\n    let mergeTokenConsumed = false;\n    const sendLifecycleConfig = (resolved: SdkResolvedConfig) => {\n        const token = mergeTokenConsumed ? undefined : pendingMergeToken;\n        mergeTokenConsumed = true;\n\n        const sdkConfig = resolved.hasRawSdkConfig\n            ? {\n                  name: resolved.name,\n                  logoUrl: resolved.logoUrl,\n                  homepageLink: resolved.homepageLink,\n                  lang: resolved.lang,\n                  currency: resolved.currency,\n                  hidden: resolved.hidden,\n                  css: resolved.css,\n                  translations: resolved.translations,\n                  placements: resolved.placements,\n                  attribution: resolved.attribution,\n              }\n            : resolved.attribution\n              ? { attribution: resolved.attribution }\n              : undefined;\n\n        const sdkAnonymousId = getClientId();\n\n        if (openPanel) {\n            const current = openPanel.global ?? {};\n            openPanel.setGlobalProperties({\n                ...current,\n                merchant_id: resolved.merchantId,\n                domain: resolved.domain ?? \"\",\n            });\n        }\n\n        rpcClient.sendLifecycle({\n            clientLifecycle: \"resolved-config\",\n            data: {\n                merchantId: resolved.merchantId,\n                domain: resolved.domain ?? \"\",\n                allowedDomains: resolved.allowedDomains ?? [],\n                sourceUrl: window.location.href,\n                ...(sdkAnonymousId && { sdkAnonymousId }),\n                ...(token && { pendingMergeToken: token }),\n                ...(sdkConfig && { sdkConfig }),\n            },\n        });\n    };\n\n    // SWR: if we have cached data, send it to the iframe immediately\n    if (sdkConfigStore.isResolved) {\n        sendLifecycleConfig(sdkConfigStore.getConfig());\n        contextSent.resolve();\n    }\n\n    // If a fetch is running (stale/missing cache), wait for fresh data and update\n    if (configPromise) {\n        const merchantConfig = await configPromise;\n        mergeAndSetConfig(merchantConfig);\n        sendLifecycleConfig(sdkConfigStore.getConfig());\n        contextSent.resolve();\n    }\n\n    // Push raw CSS if needed\n    async function pushCss() {\n        const cssLink = config.customizations?.css;\n        if (!cssLink) return;\n        rpcClient.sendLifecycle({\n            clientLifecycle: \"modal-css\" as const,\n            data: { cssLink },\n        });\n    }\n\n    // Push i18n if needed\n    async function pushI18n() {\n        const i18n = config.customizations?.i18n;\n        if (!i18n) return;\n        rpcClient.sendLifecycle({\n            clientLifecycle: \"modal-i18n\" as const,\n            data: { i18n },\n        });\n    }\n\n    // Push local backup if needed\n    async function pushBackup() {\n        if (typeof window === \"undefined\") return;\n        const backup = window.localStorage.getItem(BACKUP_KEY);\n        if (!backup) return;\n        rpcClient.sendLifecycle({\n            clientLifecycle: \"restore-backup\" as const,\n            data: { backup },\n        });\n    }\n\n    // Inspect each setup result — a failed CSS/i18n/backup push leaves the\n    // partner UI in a broken-but-connected state (iframe reports\n    // `sdk_iframe_connected`, user sees no modal styles / wrong locale).\n    // Surface it as a distinct handshake reason so dashboards can\n    // distinguish timeout vs. asset-push failures.\n    const results = await Promise.allSettled([\n        pushCss(),\n        pushI18n(),\n        pushBackup(),\n    ]);\n    const hasFailedAssetPush = results.some((r) => r.status === \"rejected\");\n    if (hasFailedAssetPush) {\n        openPanel?.track(\"sdk_iframe_handshake_failed\", {\n            reason: \"asset_push\",\n        });\n    }\n}\n","import { jsonDecode } from \"@frak-labs/frame-connector\";\nimport { base64urlDecode } from \"./b64\";\n\n/**\n * Decompress json data\n * @param data\n * @ignore\n */\nexport function decompressJsonFromB64<T>(data: string): T | null {\n    return jsonDecode<T>(base64urlDecode(data));\n}\n","import { createIFrameFrakClient } from \"../clients\";\nimport type { FrakClient, FrakWalletSdkConfig } from \"../types\";\nimport { createIframe, getSupportedCurrency } from \"../utils\";\n\n/**\n * Directly setup the Frak client with an iframe\n * Return when the FrakClient is ready (setup and communication estbalished with the wallet)\n *\n * @param config - The configuration to use for the Frak Wallet SDK\n * @returns a Promise with the Frak Client\n *\n * @example\n * const frakConfig: FrakWalletSdkConfig = {\n *     metadata: {\n *         name: \"My app title\",\n *     },\n * }\n * const client = await setupClient({ config: frakConfig });\n */\nexport async function setupClient({\n    config,\n}: {\n    config: FrakWalletSdkConfig;\n}): Promise<FrakClient | undefined> {\n    // Prepare the config\n    const preparedConfig = prepareConfig(config);\n\n    // Create our iframe\n    const iframe = await createIframe({\n        config: preparedConfig,\n    });\n\n    if (!iframe) {\n        console.error(\"Failed to create iframe\");\n        return;\n    }\n\n    // Create our client\n    const client = createIFrameFrakClient({\n        config: preparedConfig,\n        iframe,\n    });\n\n    // Wait for the client to be all setup\n    await client.waitForSetup;\n\n    // Wait for the connection to be established\n    const waitForConnection = await client.waitForConnection;\n    if (!waitForConnection) {\n        console.error(\"Failed to connect to client\");\n        return;\n    }\n\n    return client;\n}\n\n/**\n * Prepare the config for the Frak Client\n * @param config - The configuration to use for the Frak Wallet SDK\n * @returns The prepared configuration with the supported currency\n */\nfunction prepareConfig(config: FrakWalletSdkConfig): FrakWalletSdkConfig {\n    // Get the supported currency (e.g. \"eur\")\n    const supportedCurrency = getSupportedCurrency(config.metadata?.currency);\n\n    return {\n        ...config,\n        metadata: {\n            ...config.metadata,\n            currency: supportedCurrency,\n        },\n    };\n}\n","import type { AttributionDefaults, AttributionParams } from \"../types/tracking\";\n\n/**\n * Inputs for {@link mergeAttribution}.\n */\nexport type MergeAttributionInput = {\n    /**\n     * Per-call attribution override passed to actions like `displaySharingPage`.\n     *\n     * - `null` explicitly disables attribution (no UTM/ref/via params are added).\n     * - `undefined` means \"no per-call override\" — defaults apply if present.\n     * - An object (including `{}`) merges field-by-field with defaults.\n     */\n    perCall: AttributionParams | null | undefined;\n    /**\n     * Pre-merged merchant-level defaults (backend config > SDK static config).\n     * `utm_content` is intentionally absent from this shape.\n     */\n    defaults?: AttributionDefaults;\n    /**\n     * Per-product `utm_content` override (from the currently selected\n     * `SharingPageProduct`). Takes precedence over `perCall.utmContent`.\n     */\n    productUtmContent?: string;\n};\n\n/**\n * Merge the three attribution layers into a single {@link AttributionParams}\n * value suitable for `FrakContextManager.update`.\n *\n * Priority per field:\n *   1. `perCall` (wins)\n *   2. `defaults` (merchant-level, backend > SDK static, already pre-merged)\n *   3. Hardcoded fallbacks resolved later by `FrakContextManager`\n *\n * Special rules:\n * - `perCall === null` returns `undefined` (explicit disable: no UTM/ref/via).\n * - `perCall === undefined` (no opinion) yields at least `{}` so `FrakContextManager`\n *   applies its hardcoded defaults (utm_source=frak, utm_medium=referral,\n *   utm_campaign=<merchantId>, ref=<clientId>, via=frak).\n * - `utm_content` never comes from `defaults`; only `productUtmContent` or\n *   `perCall.utmContent` can populate it.\n */\nexport function mergeAttribution({\n    perCall,\n    defaults,\n    productUtmContent,\n}: MergeAttributionInput): AttributionParams | undefined {\n    // Explicit disable\n    if (perCall === null) return undefined;\n\n    const hasPerCall = perCall !== undefined;\n    const hasDefaults =\n        defaults !== undefined && Object.keys(defaults).length > 0;\n    const hasProductUtm =\n        productUtmContent !== undefined && productUtmContent !== \"\";\n\n    if (!hasPerCall && !hasDefaults && !hasProductUtm) return undefined;\n\n    // Per-field merge: per-call wins over defaults.\n    const merged: AttributionParams = {\n        ...defaults,\n        ...(perCall ?? {}),\n    };\n\n    // utm_content priority: productUtmContent > perCall.utmContent; never from defaults.\n    const utmContent = productUtmContent ?? perCall?.utmContent;\n    if (utmContent !== undefined && utmContent !== \"\") {\n        merged.utmContent = utmContent;\n    } else {\n        delete merged.utmContent;\n    }\n\n    return merged;\n}\n"],"mappings":"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"}