@dynamic-labs-sdk/legacy-embedded-wallet-upgrade 1.25.0 → 1.26.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/index.cjs +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.native.esm.js +1 -1
- package/dist/{openUpgradePopup-BQD4grKx.cjs → openUpgradePopup-B6ZD1yVO.cjs} +2 -2
- package/dist/{openUpgradePopup-BQD4grKx.cjs.map → openUpgradePopup-B6ZD1yVO.cjs.map} +1 -1
- package/dist/{openUpgradePopup-BoVMtDUQ.esm.js → openUpgradePopup-D6EwC7q3.esm.js} +2 -2
- package/dist/{openUpgradePopup-BoVMtDUQ.esm.js.map → openUpgradePopup-D6EwC7q3.esm.js.map} +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.esm.js +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_openUpgradePopup = require('./openUpgradePopup-
|
|
1
|
+
const require_openUpgradePopup = require('./openUpgradePopup-B6ZD1yVO.cjs');
|
|
2
2
|
let _dynamic_labs_sdk_assert_package_version = require("@dynamic-labs-sdk/assert-package-version");
|
|
3
3
|
let _dynamic_labs_sdk_client = require("@dynamic-labs-sdk/client");
|
|
4
4
|
let _dynamic_labs_sdk_client_core = require("@dynamic-labs-sdk/client/core");
|
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as getWalletsRequiringUpgrade, c as UPGRADE_STATUS_PARAM, d as version, i as completeUpgradeRedirect, l as UPGRADE_STATUS_SUCCESS, n as subscribeToUpgradeCompletion, o as buildUpgradeUrl, r as clearUpgradeRedirectParams, s as UPGRADE_STATUS_ERROR, t as openUpgradePopup, u as name } from "./openUpgradePopup-
|
|
1
|
+
import { a as getWalletsRequiringUpgrade, c as UPGRADE_STATUS_PARAM, d as version, i as completeUpgradeRedirect, l as UPGRADE_STATUS_SUCCESS, n as subscribeToUpgradeCompletion, o as buildUpgradeUrl, r as clearUpgradeRedirectParams, s as UPGRADE_STATUS_ERROR, t as openUpgradePopup, u as name } from "./openUpgradePopup-D6EwC7q3.esm.js";
|
|
2
2
|
import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
|
|
3
3
|
import { BaseError, getDefaultClient } from "@dynamic-labs-sdk/client";
|
|
4
4
|
import { getCore } from "@dynamic-labs-sdk/client/core";
|
package/dist/index.native.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import { MethodNotImplementedError, assertDefined, assertSafeDeeplinkUrl, getApp
|
|
|
4
4
|
|
|
5
5
|
//#region package.json
|
|
6
6
|
var name = "@dynamic-labs-sdk/legacy-embedded-wallet-upgrade";
|
|
7
|
-
var version = "1.
|
|
7
|
+
var version = "1.26.0";
|
|
8
8
|
|
|
9
9
|
//#endregion
|
|
10
10
|
//#region src/errors/InvalidUpgradeBaseUrlError.ts
|
|
@@ -3,7 +3,7 @@ let _dynamic_labs_sdk_client_core = require("@dynamic-labs-sdk/client/core");
|
|
|
3
3
|
|
|
4
4
|
//#region package.json
|
|
5
5
|
var name = "@dynamic-labs-sdk/legacy-embedded-wallet-upgrade";
|
|
6
|
-
var version = "1.
|
|
6
|
+
var version = "1.26.0";
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region src/errors/InvalidUpgradeBaseUrlError.ts
|
|
@@ -516,4 +516,4 @@ Object.defineProperty(exports, 'version', {
|
|
|
516
516
|
return version;
|
|
517
517
|
}
|
|
518
518
|
});
|
|
519
|
-
//# sourceMappingURL=openUpgradePopup-
|
|
519
|
+
//# sourceMappingURL=openUpgradePopup-B6ZD1yVO.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openUpgradePopup-BQD4grKx.cjs","names":["BaseError","BaseError","UnavailableInServerSideError","parsedRedirectUrl: URL","url: URL","UnavailableInServerSideError","UnavailableInServerSideError","outcome: WalletUpgradeOutcome","BaseError","BaseError","UnavailableInServerSideError","poll: { id?: ReturnType<typeof setInterval> }"],"sources":["../package.json","../src/errors/InvalidUpgradeBaseUrlError.ts","../src/errors/InvalidUpgradeRedirectUrlError.ts","../src/upgradeAppContract.ts","../src/buildUpgradeUrl/buildUpgradeUrl.ts","../src/walletAccountRequiresUpgrade/walletAccountRequiresUpgrade.ts","../src/getWalletsRequiringUpgrade/getWalletsRequiringUpgrade.ts","../src/completeUpgradeRedirect/completeUpgradeRedirect.ts","../src/clearUpgradeRedirectParams/clearUpgradeRedirectParams.ts","../src/subscribeToUpgradeCompletion/subscribeToUpgradeCompletion.ts","../src/errors/UpgradePopupBlockedError.ts","../src/errors/UpgradePopupClosedError.ts","../src/openUpgradePopup/openUpgradePopup.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeBaseUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(baseUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_base_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeBaseUrlError',\n shortMessage: `Upgrade baseUrl must be an absolute https or http URL, received: ${baseUrl}`,\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeRedirectUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(redirectUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_redirect_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeRedirectUrlError',\n shortMessage: `Upgrade redirectUrl must be an absolute https or http URL, received: ${redirectUrl}`,\n });\n }\n}\n","// Contract shared with the standalone WaaS upgrade app\n// (dynamic-labs-oss/waas-migration). Every identifier below MUST stay\n// byte-for-byte identical to the ones the upgrade app reads/emits in its\n// `migrationConfig.ts`, otherwise the redirect round-trip and the iframe\n// postMessage handshake break silently.\n\n/**\n * Default base URL of the hosted WaaS upgrade app. Both upgrade paths point at\n * this origin and append `environmentId` + `redirectUrl` query params. Callers\n * can override it (e.g. to target a local or preview build during QA) via the\n * `baseUrl` option on the public functions.\n */\nexport const UPGRADE_APP_BASE_URL = 'https://upgrade-wallet.dynamic.dev';\n\n/**\n * Query param the upgrade app requires: which Dynamic environment to\n * authenticate the user against. Derived from the Dynamic client so callers\n * never pass it explicitly.\n */\nexport const UPGRADE_ENVIRONMENT_ID_PARAM = 'environmentId';\n\n/**\n * Query param the upgrade app requires: where to send the user (redirect flow)\n * or which origin it postMessages to (iframe flow) once the upgrade finishes.\n * The upgrade app rejects anything that is not an absolute https or http URL.\n */\nexport const UPGRADE_REDIRECT_URL_PARAM = 'redirectUrl';\n\n/**\n * Query param the upgrade app reads to learn which Dynamic API base URL to\n * authenticate against, forwarded as-is from the client's core config. Lets a\n * non-production client (e.g. preprod) drive the hosted upgrade app against the\n * same API it already uses; when it matches the app's own default (production)\n * it is a harmless no-op.\n */\nexport const UPGRADE_API_BASE_URL_PARAM = 'apiBaseUrl';\n\n/**\n * postMessage `type` the upgrade app emits to its iframe parent on completion.\n */\nexport const UPGRADE_COMPLETE_MESSAGE_TYPE = 'dynamic-waas-migration:complete';\n\n/**\n * Query param the upgrade app appends to `redirectUrl` when it runs top-level\n * (redirect flow), carrying the outcome back to the app after navigation.\n */\nexport const UPGRADE_STATUS_PARAM = 'dynamicWaasMigrationStatus';\n\n/** Outcome value the upgrade app reports when the upgrade succeeded. */\nexport const UPGRADE_STATUS_SUCCESS = 'success';\n\n/** Outcome value the upgrade app reports when the upgrade failed. */\nexport const UPGRADE_STATUS_ERROR = 'error';\n\n/**\n * The outcome of a legacy embedded wallet upgrade attempt: `success` when the\n * wallet was upgraded to Dynamic WaaS, `error` when it failed. These are the\n * only two values the upgrade app ever reports, on both the redirect and the\n * iframe paths.\n *\n * @example\n * ```ts\n * const isSuccess = (status: WalletUpgradeStatus) => status === 'success';\n * ```\n * @see WalletUpgradeOutcome\n * @see completeUpgradeRedirect\n */\nexport type WalletUpgradeStatus =\n | typeof UPGRADE_STATUS_SUCCESS\n | typeof UPGRADE_STATUS_ERROR;\n\n/**\n * The resolved result of an upgrade flow, carrying the reported\n * {@link WalletUpgradeStatus}. Returned by the redirect-result consumer and\n * handed to the iframe completion callback so callers can react (toast,\n * refetch, close a sheet, etc.).\n *\n * @example\n * ```ts\n * const onComplete = (outcome: WalletUpgradeOutcome) => {\n * if (outcome.status === 'success') showSuccessToast();\n * };\n * ```\n * @see WalletUpgradeStatus\n * @see subscribeToUpgradeCompletion\n */\nexport type WalletUpgradeOutcome = {\n status: WalletUpgradeStatus;\n};\n\n/**\n * Shape of the completion message the upgrade app posts to its parent window\n * in the iframe flow. Internal to the message handshake — consumers receive the\n * parsed {@link WalletUpgradeOutcome} instead.\n */\nexport type UpgradeCompleteMessage = {\n status: WalletUpgradeStatus;\n type: typeof UPGRADE_COMPLETE_MESSAGE_TYPE;\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { InvalidUpgradeBaseUrlError } from '../errors/InvalidUpgradeBaseUrlError';\nimport { InvalidUpgradeRedirectUrlError } from '../errors/InvalidUpgradeRedirectUrlError';\nimport {\n UPGRADE_API_BASE_URL_PARAM,\n UPGRADE_APP_BASE_URL,\n UPGRADE_ENVIRONMENT_ID_PARAM,\n UPGRADE_REDIRECT_URL_PARAM,\n} from '../upgradeAppContract';\nimport type { BuildUpgradeUrlParams } from './buildUpgradeUrl.types';\n\nconst HTTPS_PROTOCOL = 'https:';\n\nconst HTTP_PROTOCOL = 'http:';\n\n// The upgrade app accepts both https and plain http, so it works for local\n// dev, self-hosting behind a non-TLS proxy, and hosts without TLS. https stays\n// the recommended default. Every other scheme — including script-executing ones\n// like `javascript:` and `data:` — is rejected so we never navigate to or mount\n// a dangerous origin.\nconst ALLOWED_PROTOCOLS = new Set<string>([HTTPS_PROTOCOL, HTTP_PROTOCOL]);\n\n/**\n * Builds the URL that drives the hosted upgrade app for the client's Dynamic\n * environment and a return target.\n *\n * This is the low-level primitive behind every upgrade path: the redirect flow\n * navigates the top-level window to it, the iframe flow loads it as the iframe\n * `src`, and a React Native WebView can load it directly. The environment id is\n * read from the client, and `redirectUrl` defaults to the current page, so the\n * common call is zero-arg. Prefer {@link startUpgradeRedirect} or\n * {@link openUpgradeIframe} unless you are mounting the upgrade app yourself.\n *\n * @example\n * ```ts\n * const src = buildUpgradeUrl();\n * // or target a local upgrade app during QA:\n * const localSrc = buildUpgradeUrl({ baseUrl: 'http://localhost:3000' });\n * ```\n * @param [params] - Options for the generated URL.\n * @param [params.redirectUrl] - Absolute https or http return URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The fully-qualified upgrade app URL to load.\n * @throws {InvalidUpgradeRedirectUrlError} When `redirectUrl` is not an absolute https or http URL.\n * @throws {InvalidUpgradeBaseUrlError} When `baseUrl` is not an absolute https or http URL.\n * @throws {UnavailableInServerSideError} When called during SSR without an explicit `redirectUrl`.\n * @see startUpgradeRedirect\n * @see openUpgradeIframe\n */\nexport const buildUpgradeUrl = (\n { redirectUrl, baseUrl = UPGRADE_APP_BASE_URL }: BuildUpgradeUrlParams = {},\n client = getDefaultClient()\n): string => {\n // The default return target is the current page, which only exists in the\n // browser; fail loudly during SSR rather than dereferencing an absent window.\n if (redirectUrl === undefined && typeof window === 'undefined') {\n throw new UnavailableInServerSideError('buildUpgradeUrl');\n }\n\n const resolvedRedirectUrl = redirectUrl ?? window.location.href;\n\n // Validate the return target up front: reject anything that is not an\n // absolute https/http URL (relative, other schemes) here rather than letting\n // it be silently refused after navigation. A non-absolute value throws,\n // surfaced as the specific InvalidUpgradeRedirectUrlError.\n let parsedRedirectUrl: URL;\n try {\n parsedRedirectUrl = new URL(resolvedRedirectUrl);\n } catch {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(parsedRedirectUrl.protocol)) {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n // Validate the upgrade app origin before we ever load it in an iframe or\n // navigate to it. The default is a trusted https app, but `baseUrl` is an\n // override (custom deployments, forks, local QA), so enforce the allowed\n // schemes to reject dangerous ones (`javascript:`, `data:`) rather than\n // mounting an untrusted origin. Any https/http host is allowed on purpose —\n // customers self-host the upgrade app on their own domains.\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n const { apiBaseUrl, environmentId } = getCore(client);\n\n url.searchParams.set(UPGRADE_ENVIRONMENT_ID_PARAM, environmentId);\n url.searchParams.set(UPGRADE_REDIRECT_URL_PARAM, resolvedRedirectUrl);\n\n // Forward the client's configured API base URL as-is so a non-production\n // client (e.g. preprod) drives the upgrade app against the same API. Core\n // always resolves this to a concrete URL (production by default), so prod is\n // a harmless no-op.\n url.searchParams.set(UPGRADE_API_BASE_URL_PARAM, apiBaseUrl);\n\n return url.toString();\n};\n","import type { WalletAccount } from '@dynamic-labs-sdk/client';\n\n/**\n * Provider-key substring shared by every Dynamic embedded wallet that predates\n * Dynamic WaaS and can therefore be upgraded to it. A wallet account's\n * `walletProviderKey` is a composite backend value (e.g. `<provider>evm` /\n * `<provider>hdsolana` suffixed with `:embeddedWallet`); both the standard and\n * HD variants contain this marker, so matching it as a substring identifies all\n * of them. This is an opaque backend value, matched verbatim — not a user-facing\n * concept.\n */\nconst LEGACY_EMBEDDED_WALLET_PROVIDER_KEY = 'turnkey';\n\n/**\n * Returns whether a wallet account is a legacy Dynamic embedded wallet that\n * requires an upgrade to Dynamic WaaS.\n *\n * Shared predicate behind both the imperative {@link getWalletsRequiringUpgrade}\n * getter and the reactive `useWalletsRequiringUpgrade` hook, so the definition\n * of \"requires upgrade\" lives in exactly one place.\n *\n * @example\n * ```ts\n * const upgradeable = walletAccounts.filter(walletAccountRequiresUpgrade);\n * ```\n * @param walletAccount - The wallet account to test.\n * @returns True when the account is a legacy embedded wallet requiring an upgrade.\n * @see getWalletsRequiringUpgrade\n * @see hasWalletsRequiringUpgrade\n */\nexport const walletAccountRequiresUpgrade = (\n walletAccount: WalletAccount\n): boolean =>\n walletAccount.walletProviderKey.includes(\n LEGACY_EMBEDDED_WALLET_PROVIDER_KEY\n );\n","import {\n type WalletAccount,\n getDefaultClient,\n getWalletAccounts,\n} from '@dynamic-labs-sdk/client';\n\nimport { walletAccountRequiresUpgrade } from '../walletAccountRequiresUpgrade';\n\n/**\n * Returns the current session's wallet accounts that are legacy Dynamic\n * embedded wallets requiring an upgrade to Dynamic WaaS.\n *\n * These are earlier-generation Dynamic embedded wallets that predate Dynamic\n * WaaS (the current TSS-MPC embedded wallet) and can be upgraded to it. Use this\n * when you need the accounts themselves — for example to render only the\n * upgrade entry points on those wallets. Already-upgraded Dynamic WaaS accounts\n * are never returned. For a simple boolean check, use\n * {@link hasWalletsRequiringUpgrade}.\n *\n * @example\n * ```ts\n * const walletsRequiringUpgrade = getWalletsRequiringUpgrade();\n * if (walletsRequiringUpgrade.length > 0) {\n * promptUpgrade(walletsRequiringUpgrade);\n * }\n * ```\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The wallet accounts requiring an upgrade to Dynamic WaaS (empty when none).\n * @see hasWalletsRequiringUpgrade\n * @see startUpgradeRedirect\n */\nexport const getWalletsRequiringUpgrade = (\n client = getDefaultClient()\n): WalletAccount[] =>\n getWalletAccounts(client).filter(walletAccountRequiresUpgrade);\n","import { getDefaultClient, refreshAuth } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport {\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_PARAM,\n UPGRADE_STATUS_SUCCESS,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\n/**\n * Options for {@link completeUpgradeRedirect}.\n *\n * @example\n * ```ts\n * // Browser: read the outcome from the current page.\n * await completeUpgradeRedirect({ url: window.location.href });\n * ```\n * @see completeUpgradeRedirect\n * @see detectUpgradeRedirect\n */\nexport type CompleteUpgradeRedirectParams = {\n /**\n * The return URL to read the upgrade outcome from. Pass the URL yourself so\n * the flow works everywhere: in the browser pass `window.location.href`; in\n * React Native pass the deep-link URL you received. This mirrors how social\n * sign-in and device registration consume their redirects, and keeps the\n * function free of any implicit `window` dependency.\n */\n url: string;\n};\n\n/**\n * Completes the redirect upgrade path after {@link detectUpgradeRedirect}.\n *\n * The upgrade app sends the user back with the outcome in the\n * `dynamicWaasMigrationStatus` query param. Once `detectUpgradeRedirect` tells\n * you the return URL is an upgrade redirect, call this to finish it: it reads\n * the param and, on success, refreshes the session (so the upgraded wallet\n * appears) before returning the outcome. The refresh is best-effort — the\n * upgrade already succeeded on the backend, so a failed refresh still reports\n * success and reconciles on the next auth refresh. This detect-then-complete\n * split mirrors the OAuth (`detectSocialRedirectUrl` + `completeSocialRedirect`)\n * and\n * device-registration (`detectDeviceRegistrationRedirect` +\n * `completeDeviceRegistration`) flows. Returns `null` when a well-formed URL\n * simply carries no upgrade result, so that common case is safe to call\n * directly without `detectUpgradeRedirect` first. A malformed/unparseable URL\n * rejects instead — either gate the call with `detectUpgradeRedirect` (which\n * parses defensively) or surface the rejection (e.g. via\n * `useUpgradeRedirectResult`'s `onError`).\n *\n * You always pass the URL (`window.location.href` in the browser, the deep-link\n * URL in React Native), matching how social sign-in and device registration\n * consume their redirects. The function never touches browser history, so you\n * own clearing the param from your navigation state once you have the outcome.\n *\n * @example\n * ```ts\n * // Browser\n * if (detectUpgradeRedirect({ url: window.location.href })) {\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome?.status === 'error') showRetryToast();\n * }\n * ```\n * @example\n * ```ts\n * // React Native: pass the deep-link URL you received.\n * const outcome = await completeUpgradeRedirect({ url: deepLinkUrl });\n * ```\n * @param params - Options.\n * @param params.url - Return URL to read the outcome from (e.g. `window.location.href`).\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A promise resolving to the upgrade outcome, or `null` when there was no upgrade result to consume.\n * @see detectUpgradeRedirect\n * @see startUpgradeRedirect\n * @see subscribeToUpgradeCompletion\n */\nexport const completeUpgradeRedirect = async (\n { url }: CompleteUpgradeRedirectParams,\n client = getDefaultClient()\n): Promise<WalletUpgradeOutcome | null> => {\n // A malformed URL throws here on purpose: it is an unreadable return URL, not\n // a \"no result\" case, so it surfaces to the caller (e.g. the hook's onError)\n // rather than being silently swallowed. detectUpgradeRedirect parses\n // defensively, so gating the call with it avoids this path entirely.\n const status = new URL(url).searchParams.get(UPGRADE_STATUS_PARAM);\n\n // The param is user-controlled, so only ever act on the two values the\n // upgrade app emits and ignore anything else (including tampered values).\n const isKnownStatus =\n status === UPGRADE_STATUS_SUCCESS || status === UPGRADE_STATUS_ERROR;\n\n if (!isKnownStatus) {\n return null;\n }\n\n if (status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so success is always reported — matching\n // subscribeToUpgradeCompletion, so the redirect and embed paths agree.\n try {\n await refreshAuth(client);\n } catch (error) {\n // Report success anyway (the next auth refresh reconciles), but log so\n // the swallowed failure is still traceable in Datadog.\n getCore(client).logger.warn(\n '[completeUpgradeRedirect] refreshAuth failed after a successful upgrade; reporting success and deferring to the next auth refresh',\n error\n );\n }\n\n return { status: UPGRADE_STATUS_SUCCESS };\n }\n\n return { status: UPGRADE_STATUS_ERROR };\n};\n","/* eslint-disable no-restricted-globals */\n\nimport { UnavailableInServerSideError } from '@dynamic-labs-sdk/client';\n\nimport { UPGRADE_STATUS_PARAM } from '../upgradeAppContract';\n\n/**\n * Removes the upgrade outcome parameter (`dynamicWaasMigrationStatus`) that the\n * upgrade app appends to the return URL from the browser's current URL, leaving\n * every other query parameter, the path, and the hash untouched.\n *\n * Call this once you have handled the outcome from `completeUpgradeRedirect` so a\n * page refresh does not replay it. `completeUpgradeRedirect` never touches\n * browser history, so clearing the param is your responsibility — this helper\n * does it in one call, mirroring `clearSocialRedirectParams` and\n * `clearDeviceRegistrationRedirectParams`. Unlike clearing the whole query\n * string, it preserves any of your own parameters on the callback URL.\n *\n * This reads and rewrites `window.location`, so it can only run in the browser.\n * Calling it during server-side rendering (no `window`) throws\n * `UnavailableInServerSideError`. On React Native there is no browser address\n * bar to clean — you own your navigation state — so a `.native` build throws\n * instead.\n *\n * @example\n * ```ts\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome) {\n * handleOutcome(outcome);\n * clearUpgradeRedirectParams();\n * }\n * ```\n * @returns Nothing. The current URL is rewritten in place via\n * `window.history.replaceState`.\n * @throws {UnavailableInServerSideError} When called during SSR (no `window`).\n * @see completeUpgradeRedirect\n * @see startUpgradeRedirect\n */\nexport const clearUpgradeRedirectParams = (): void => {\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('clearUpgradeRedirectParams');\n }\n\n const url = new URL(window.location.href);\n\n url.searchParams.delete(UPGRADE_STATUS_PARAM);\n\n window.history.replaceState({}, '', url.toString());\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n refreshAuth,\n} from '@dynamic-labs-sdk/client';\n\nimport {\n UPGRADE_APP_BASE_URL,\n UPGRADE_COMPLETE_MESSAGE_TYPE,\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_SUCCESS,\n type UpgradeCompleteMessage,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\ntype SubscribeToUpgradeCompletionParams = {\n /**\n * Base URL of the upgrade app the message is expected from. Its origin is\n * used to reject completion messages from any other window. Defaults to the\n * production upgrade app; must match the URL the app is loaded from.\n */\n baseUrl?: string;\n /**\n * Called once with the outcome when the upgrade app reports completion. On\n * success the session has already been refreshed by the time this runs.\n */\n onComplete: (outcome: WalletUpgradeOutcome) => void;\n /**\n * Called synchronously the moment a valid completion message is received,\n * before the (async) success session refresh runs — so `onComplete` still\n * fires after the refresh. Use it to treat the upgrade as already finished\n * (e.g. so dismissing the surface during the refresh is not mistaken for a\n * cancellation, or to show a \"finalizing\" state).\n */\n onReceived?: (outcome: WalletUpgradeOutcome) => void;\n};\n\n// Fully validate the incoming postMessage before acting on it: even though the\n// origin check already gates who can send, the payload shape is still untrusted\n// input, so we verify both the message type AND that `status` is one of the\n// known outcomes rather than trusting whatever `status` string arrives.\nconst isUpgradeCompleteMessage = (\n data: unknown\n): data is UpgradeCompleteMessage =>\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n (data as { type: unknown }).type === UPGRADE_COMPLETE_MESSAGE_TYPE &&\n 'status' in data &&\n ((data as { status: unknown }).status === UPGRADE_STATUS_SUCCESS ||\n (data as { status: unknown }).status === UPGRADE_STATUS_ERROR);\n\n/**\n * Subscribes to the upgrade app's completion signal for an embedded upgrade\n * (iframe or WebView).\n *\n * This is the DOM-agnostic primitive behind {@link openUpgradeIframe}: use it\n * directly when you mount the upgrade app yourself (e.g. a React Native\n * `WebView` or a custom iframe). It listens for the app's origin-checked\n * completion postMessage, refreshes the session on success, then invokes\n * `onComplete` with the outcome. Returns an unsubscribe function that detaches\n * the listener — always call it when your surface unmounts.\n *\n * @example\n * ```ts\n * const unsubscribe = subscribeToUpgradeCompletion({\n * onComplete: ({ status }) => closeSheet(status),\n * });\n * // later, on unmount:\n * unsubscribe();\n * ```\n * @param params - Subscription options.\n * @param params.onComplete - Invoked once with the resolved outcome on completion.\n * @param [params.baseUrl] - Upgrade app base URL whose origin gates messages; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A function that removes the completion listener.\n * @throws {UnavailableInServerSideError} When called during SSR (no window to listen on).\n * @see openUpgradeIframe\n * @see buildUpgradeUrl\n */\nexport const subscribeToUpgradeCompletion = (\n {\n onComplete,\n onReceived,\n baseUrl = UPGRADE_APP_BASE_URL,\n }: SubscribeToUpgradeCompletionParams,\n client = getDefaultClient()\n): (() => void) => {\n // The completion handshake relies on the browser's postMessage; there is no\n // window to listen on during SSR.\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('subscribeToUpgradeCompletion');\n }\n\n const expectedOrigin = new URL(baseUrl).origin;\n\n const handleMessage = async (event: MessageEvent) => {\n // Ignore messages from any other window so a random page cannot fake an\n // upgrade result.\n if (event.origin !== expectedOrigin) {\n return;\n }\n\n if (!isUpgradeCompleteMessage(event.data)) {\n return;\n }\n\n // Only the first valid completion is honored. Detach synchronously, before\n // the async session refresh, so a duplicate message cannot trigger a second\n // refresh or a second `onComplete` call.\n window.removeEventListener('message', handleMessage);\n\n let outcome: WalletUpgradeOutcome;\n if (event.data.status === UPGRADE_STATUS_SUCCESS) {\n outcome = { status: UPGRADE_STATUS_SUCCESS };\n } else {\n outcome = { status: UPGRADE_STATUS_ERROR };\n }\n\n // Signal receipt synchronously, before the async refresh below, so a\n // consumer can lock in that the upgrade already finished — otherwise a\n // surface dismissed during the refresh window would look like a cancel.\n onReceived?.(outcome);\n\n if (outcome.status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so completion is always reported — otherwise, since\n // this runs in an event handler with no caller to catch it, a rejected\n // refresh would hang openUpgradeIframe's `completed` promise forever.\n try {\n await refreshAuth(client);\n } catch {\n // ignore: report success anyway, the refresh will reconcile later\n }\n }\n\n onComplete(outcome);\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => window.removeEventListener('message', handleMessage);\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupBlockedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_blocked_error',\n docsUrl: null,\n name: 'UpgradePopupBlockedError',\n shortMessage:\n 'Upgrade popup was blocked by the browser. Call openUpgradePopup directly from a user gesture (e.g. a click handler) so the browser allows the window to open.',\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupClosedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_closed_error',\n docsUrl: null,\n name: 'UpgradePopupClosedError',\n shortMessage: 'Upgrade popup was closed before the upgrade completed',\n });\n }\n}\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport {\n createDeferredPromise,\n isServerSideRendering,\n} from '@dynamic-labs-sdk/client/core';\n\nimport { buildUpgradeUrl } from '../buildUpgradeUrl';\nimport { UpgradePopupBlockedError } from '../errors/UpgradePopupBlockedError';\nimport { UpgradePopupClosedError } from '../errors/UpgradePopupClosedError';\nimport { subscribeToUpgradeCompletion } from '../subscribeToUpgradeCompletion';\nimport type { WalletUpgradeOutcome } from '../upgradeAppContract';\nimport type {\n OpenUpgradePopupParams,\n UpgradePopupSession,\n} from './openUpgradePopup.types';\n\nexport type { UpgradePopupSession } from './openUpgradePopup.types';\n\nconst UPGRADE_POPUP_TARGET = 'dynamic-embedded-wallet-upgrade';\n\n// Small window (browser picks the position); large enough for the upgrade\n// app's passkey prompts.\nconst DEFAULT_WINDOW_FEATURES = 'popup=yes,width=420,height=640';\n\n// A closed popup has no reliable DOM event, so `popup.closed` is polled.\nconst POPUP_CLOSED_POLL_INTERVAL_MS = 500;\n\n/**\n * Runs the upgrade path in a browser popup window.\n *\n * Opens the hosted upgrade app with `window.open`, wires the same\n * origin-checked completion handshake as {@link openUpgradeIframe}, and\n * refreshes the session on success. `completed` resolves with the outcome once\n * the app reports back; it rejects with {@link UpgradePopupClosedError} if the\n * user closes the popup first. Because popup blockers reject windows opened\n * outside a user gesture, **call this synchronously from a click handler** — do\n * no `await` before it. If the browser blocks the popup (window.open returns\n * `null`), it throws {@link UpgradePopupBlockedError} synchronously.\n *\n * Prefer {@link openUpgradeIframe} when you want the flow embedded in your own\n * modal/sheet; use the popup when a separate window is preferable.\n *\n * @example\n * ```ts\n * // in a click handler — no awaits before openUpgradePopup\n * const session = openUpgradePopup();\n * try {\n * const { status } = await session.completed;\n * } catch (error) {\n * // UpgradePopupClosedError when the user dismissed the popup\n * }\n * ```\n * @param [params] - Popup options.\n * @param [params.redirectUrl] - Absolute https or http completion URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [params.webOptions.popupFeatures] - `window.open` feature string; defaults to a small window.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A session handle exposing a `close` function and the `completed` promise.\n * @throws {UpgradePopupBlockedError} When the browser blocks the popup (`window.open` returns null).\n * @throws {UnavailableInServerSideError} When called during SSR (no window to open from).\n * @see openUpgradeIframe\n * @see subscribeToUpgradeCompletion\n */\nexport const openUpgradePopup = (\n { baseUrl, redirectUrl, webOptions }: OpenUpgradePopupParams = {},\n client = getDefaultClient()\n): UpgradePopupSession => {\n // window.open only exists in the browser; fail fast during SSR.\n if (isServerSideRendering()) {\n throw new UnavailableInServerSideError('openUpgradePopup');\n }\n\n const url = buildUpgradeUrl({ baseUrl, redirectUrl }, client);\n\n const popup = window.open(\n url,\n UPGRADE_POPUP_TARGET,\n webOptions?.popupFeatures ?? DEFAULT_WINDOW_FEATURES\n );\n\n // A blocked popup returns null; surface it as an explicit, actionable error\n // rather than a session whose `completed` never settles.\n if (popup === null) {\n throw new UpgradePopupBlockedError();\n }\n\n const completed = createDeferredPromise<WalletUpgradeOutcome>();\n\n // Callers may use `close()` purely for teardown and never await the promise.\n // Attach a no-op catch so an early-close rejection is treated as handled\n // globally (no unhandled-rejection noise); a caller that does await\n // `completed.promise` still receives the rejection in its own context.\n completed.promise.catch(() => undefined);\n\n // Set the instant a valid completion message arrives — before the async\n // session refresh resolves `completed`. The close poll checks this so a popup\n // that closes right after signalling success is not treated as a cancel.\n let hasReceivedCompletion = false;\n\n // Holds the close-poll interval id. A holder object lets `onComplete` (defined\n // before the interval is created) clear the poll once completion arrives,\n // without a forward `let` reference.\n // `ReturnType<typeof setInterval>` (not `window.setInterval`) because the\n // lib includes both \"dom\" and \"@types/node\": DOM returns `number`, Node\n // returns `Timeout`, and the unqualified form is the one both resolve to.\n const poll: { id?: ReturnType<typeof setInterval> } = {};\n\n const unsubscribe = subscribeToUpgradeCompletion(\n {\n baseUrl,\n onComplete: (outcome) => {\n completed.resolve(outcome);\n // The subscription already removed its own message listener on the\n // first completion; just stop the close poll and leave the popup open\n // for the app's own completion screen (the caller closes it).\n clearInterval(poll.id);\n },\n onReceived: () => {\n hasReceivedCompletion = true;\n },\n },\n client\n );\n\n // Detach the listener, stop polling, and close the window. Reject `completed`\n // as a cancellation only when no completion has been received yet, so a popup\n // closed after a successful handshake still resolves with the real outcome.\n const close = () => {\n unsubscribe();\n clearInterval(poll.id);\n if (!popup.closed) {\n popup.close();\n }\n if (!hasReceivedCompletion) {\n completed.reject(new UpgradePopupClosedError());\n }\n };\n\n poll.id = setInterval(() => {\n if (popup.closed) {\n close();\n }\n }, POPUP_CLOSED_POLL_INTERVAL_MS);\n\n return { close, completed: completed.promise };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openUpgradePopup-B6ZD1yVO.cjs","names":["BaseError","BaseError","UnavailableInServerSideError","parsedRedirectUrl: URL","url: URL","UnavailableInServerSideError","UnavailableInServerSideError","outcome: WalletUpgradeOutcome","BaseError","BaseError","UnavailableInServerSideError","poll: { id?: ReturnType<typeof setInterval> }"],"sources":["../package.json","../src/errors/InvalidUpgradeBaseUrlError.ts","../src/errors/InvalidUpgradeRedirectUrlError.ts","../src/upgradeAppContract.ts","../src/buildUpgradeUrl/buildUpgradeUrl.ts","../src/walletAccountRequiresUpgrade/walletAccountRequiresUpgrade.ts","../src/getWalletsRequiringUpgrade/getWalletsRequiringUpgrade.ts","../src/completeUpgradeRedirect/completeUpgradeRedirect.ts","../src/clearUpgradeRedirectParams/clearUpgradeRedirectParams.ts","../src/subscribeToUpgradeCompletion/subscribeToUpgradeCompletion.ts","../src/errors/UpgradePopupBlockedError.ts","../src/errors/UpgradePopupClosedError.ts","../src/openUpgradePopup/openUpgradePopup.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeBaseUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(baseUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_base_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeBaseUrlError',\n shortMessage: `Upgrade baseUrl must be an absolute https or http URL, received: ${baseUrl}`,\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeRedirectUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(redirectUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_redirect_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeRedirectUrlError',\n shortMessage: `Upgrade redirectUrl must be an absolute https or http URL, received: ${redirectUrl}`,\n });\n }\n}\n","// Contract shared with the standalone WaaS upgrade app\n// (dynamic-labs-oss/waas-migration). Every identifier below MUST stay\n// byte-for-byte identical to the ones the upgrade app reads/emits in its\n// `migrationConfig.ts`, otherwise the redirect round-trip and the iframe\n// postMessage handshake break silently.\n\n/**\n * Default base URL of the hosted WaaS upgrade app. Both upgrade paths point at\n * this origin and append `environmentId` + `redirectUrl` query params. Callers\n * can override it (e.g. to target a local or preview build during QA) via the\n * `baseUrl` option on the public functions.\n */\nexport const UPGRADE_APP_BASE_URL = 'https://upgrade-wallet.dynamic.dev';\n\n/**\n * Query param the upgrade app requires: which Dynamic environment to\n * authenticate the user against. Derived from the Dynamic client so callers\n * never pass it explicitly.\n */\nexport const UPGRADE_ENVIRONMENT_ID_PARAM = 'environmentId';\n\n/**\n * Query param the upgrade app requires: where to send the user (redirect flow)\n * or which origin it postMessages to (iframe flow) once the upgrade finishes.\n * The upgrade app rejects anything that is not an absolute https or http URL.\n */\nexport const UPGRADE_REDIRECT_URL_PARAM = 'redirectUrl';\n\n/**\n * Query param the upgrade app reads to learn which Dynamic API base URL to\n * authenticate against, forwarded as-is from the client's core config. Lets a\n * non-production client (e.g. preprod) drive the hosted upgrade app against the\n * same API it already uses; when it matches the app's own default (production)\n * it is a harmless no-op.\n */\nexport const UPGRADE_API_BASE_URL_PARAM = 'apiBaseUrl';\n\n/**\n * postMessage `type` the upgrade app emits to its iframe parent on completion.\n */\nexport const UPGRADE_COMPLETE_MESSAGE_TYPE = 'dynamic-waas-migration:complete';\n\n/**\n * Query param the upgrade app appends to `redirectUrl` when it runs top-level\n * (redirect flow), carrying the outcome back to the app after navigation.\n */\nexport const UPGRADE_STATUS_PARAM = 'dynamicWaasMigrationStatus';\n\n/** Outcome value the upgrade app reports when the upgrade succeeded. */\nexport const UPGRADE_STATUS_SUCCESS = 'success';\n\n/** Outcome value the upgrade app reports when the upgrade failed. */\nexport const UPGRADE_STATUS_ERROR = 'error';\n\n/**\n * The outcome of a legacy embedded wallet upgrade attempt: `success` when the\n * wallet was upgraded to Dynamic WaaS, `error` when it failed. These are the\n * only two values the upgrade app ever reports, on both the redirect and the\n * iframe paths.\n *\n * @example\n * ```ts\n * const isSuccess = (status: WalletUpgradeStatus) => status === 'success';\n * ```\n * @see WalletUpgradeOutcome\n * @see completeUpgradeRedirect\n */\nexport type WalletUpgradeStatus =\n | typeof UPGRADE_STATUS_SUCCESS\n | typeof UPGRADE_STATUS_ERROR;\n\n/**\n * The resolved result of an upgrade flow, carrying the reported\n * {@link WalletUpgradeStatus}. Returned by the redirect-result consumer and\n * handed to the iframe completion callback so callers can react (toast,\n * refetch, close a sheet, etc.).\n *\n * @example\n * ```ts\n * const onComplete = (outcome: WalletUpgradeOutcome) => {\n * if (outcome.status === 'success') showSuccessToast();\n * };\n * ```\n * @see WalletUpgradeStatus\n * @see subscribeToUpgradeCompletion\n */\nexport type WalletUpgradeOutcome = {\n status: WalletUpgradeStatus;\n};\n\n/**\n * Shape of the completion message the upgrade app posts to its parent window\n * in the iframe flow. Internal to the message handshake — consumers receive the\n * parsed {@link WalletUpgradeOutcome} instead.\n */\nexport type UpgradeCompleteMessage = {\n status: WalletUpgradeStatus;\n type: typeof UPGRADE_COMPLETE_MESSAGE_TYPE;\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { InvalidUpgradeBaseUrlError } from '../errors/InvalidUpgradeBaseUrlError';\nimport { InvalidUpgradeRedirectUrlError } from '../errors/InvalidUpgradeRedirectUrlError';\nimport {\n UPGRADE_API_BASE_URL_PARAM,\n UPGRADE_APP_BASE_URL,\n UPGRADE_ENVIRONMENT_ID_PARAM,\n UPGRADE_REDIRECT_URL_PARAM,\n} from '../upgradeAppContract';\nimport type { BuildUpgradeUrlParams } from './buildUpgradeUrl.types';\n\nconst HTTPS_PROTOCOL = 'https:';\n\nconst HTTP_PROTOCOL = 'http:';\n\n// The upgrade app accepts both https and plain http, so it works for local\n// dev, self-hosting behind a non-TLS proxy, and hosts without TLS. https stays\n// the recommended default. Every other scheme — including script-executing ones\n// like `javascript:` and `data:` — is rejected so we never navigate to or mount\n// a dangerous origin.\nconst ALLOWED_PROTOCOLS = new Set<string>([HTTPS_PROTOCOL, HTTP_PROTOCOL]);\n\n/**\n * Builds the URL that drives the hosted upgrade app for the client's Dynamic\n * environment and a return target.\n *\n * This is the low-level primitive behind every upgrade path: the redirect flow\n * navigates the top-level window to it, the iframe flow loads it as the iframe\n * `src`, and a React Native WebView can load it directly. The environment id is\n * read from the client, and `redirectUrl` defaults to the current page, so the\n * common call is zero-arg. Prefer {@link startUpgradeRedirect} or\n * {@link openUpgradeIframe} unless you are mounting the upgrade app yourself.\n *\n * @example\n * ```ts\n * const src = buildUpgradeUrl();\n * // or target a local upgrade app during QA:\n * const localSrc = buildUpgradeUrl({ baseUrl: 'http://localhost:3000' });\n * ```\n * @param [params] - Options for the generated URL.\n * @param [params.redirectUrl] - Absolute https or http return URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The fully-qualified upgrade app URL to load.\n * @throws {InvalidUpgradeRedirectUrlError} When `redirectUrl` is not an absolute https or http URL.\n * @throws {InvalidUpgradeBaseUrlError} When `baseUrl` is not an absolute https or http URL.\n * @throws {UnavailableInServerSideError} When called during SSR without an explicit `redirectUrl`.\n * @see startUpgradeRedirect\n * @see openUpgradeIframe\n */\nexport const buildUpgradeUrl = (\n { redirectUrl, baseUrl = UPGRADE_APP_BASE_URL }: BuildUpgradeUrlParams = {},\n client = getDefaultClient()\n): string => {\n // The default return target is the current page, which only exists in the\n // browser; fail loudly during SSR rather than dereferencing an absent window.\n if (redirectUrl === undefined && typeof window === 'undefined') {\n throw new UnavailableInServerSideError('buildUpgradeUrl');\n }\n\n const resolvedRedirectUrl = redirectUrl ?? window.location.href;\n\n // Validate the return target up front: reject anything that is not an\n // absolute https/http URL (relative, other schemes) here rather than letting\n // it be silently refused after navigation. A non-absolute value throws,\n // surfaced as the specific InvalidUpgradeRedirectUrlError.\n let parsedRedirectUrl: URL;\n try {\n parsedRedirectUrl = new URL(resolvedRedirectUrl);\n } catch {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(parsedRedirectUrl.protocol)) {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n // Validate the upgrade app origin before we ever load it in an iframe or\n // navigate to it. The default is a trusted https app, but `baseUrl` is an\n // override (custom deployments, forks, local QA), so enforce the allowed\n // schemes to reject dangerous ones (`javascript:`, `data:`) rather than\n // mounting an untrusted origin. Any https/http host is allowed on purpose —\n // customers self-host the upgrade app on their own domains.\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n const { apiBaseUrl, environmentId } = getCore(client);\n\n url.searchParams.set(UPGRADE_ENVIRONMENT_ID_PARAM, environmentId);\n url.searchParams.set(UPGRADE_REDIRECT_URL_PARAM, resolvedRedirectUrl);\n\n // Forward the client's configured API base URL as-is so a non-production\n // client (e.g. preprod) drives the upgrade app against the same API. Core\n // always resolves this to a concrete URL (production by default), so prod is\n // a harmless no-op.\n url.searchParams.set(UPGRADE_API_BASE_URL_PARAM, apiBaseUrl);\n\n return url.toString();\n};\n","import type { WalletAccount } from '@dynamic-labs-sdk/client';\n\n/**\n * Provider-key substring shared by every Dynamic embedded wallet that predates\n * Dynamic WaaS and can therefore be upgraded to it. A wallet account's\n * `walletProviderKey` is a composite backend value (e.g. `<provider>evm` /\n * `<provider>hdsolana` suffixed with `:embeddedWallet`); both the standard and\n * HD variants contain this marker, so matching it as a substring identifies all\n * of them. This is an opaque backend value, matched verbatim — not a user-facing\n * concept.\n */\nconst LEGACY_EMBEDDED_WALLET_PROVIDER_KEY = 'turnkey';\n\n/**\n * Returns whether a wallet account is a legacy Dynamic embedded wallet that\n * requires an upgrade to Dynamic WaaS.\n *\n * Shared predicate behind both the imperative {@link getWalletsRequiringUpgrade}\n * getter and the reactive `useWalletsRequiringUpgrade` hook, so the definition\n * of \"requires upgrade\" lives in exactly one place.\n *\n * @example\n * ```ts\n * const upgradeable = walletAccounts.filter(walletAccountRequiresUpgrade);\n * ```\n * @param walletAccount - The wallet account to test.\n * @returns True when the account is a legacy embedded wallet requiring an upgrade.\n * @see getWalletsRequiringUpgrade\n * @see hasWalletsRequiringUpgrade\n */\nexport const walletAccountRequiresUpgrade = (\n walletAccount: WalletAccount\n): boolean =>\n walletAccount.walletProviderKey.includes(\n LEGACY_EMBEDDED_WALLET_PROVIDER_KEY\n );\n","import {\n type WalletAccount,\n getDefaultClient,\n getWalletAccounts,\n} from '@dynamic-labs-sdk/client';\n\nimport { walletAccountRequiresUpgrade } from '../walletAccountRequiresUpgrade';\n\n/**\n * Returns the current session's wallet accounts that are legacy Dynamic\n * embedded wallets requiring an upgrade to Dynamic WaaS.\n *\n * These are earlier-generation Dynamic embedded wallets that predate Dynamic\n * WaaS (the current TSS-MPC embedded wallet) and can be upgraded to it. Use this\n * when you need the accounts themselves — for example to render only the\n * upgrade entry points on those wallets. Already-upgraded Dynamic WaaS accounts\n * are never returned. For a simple boolean check, use\n * {@link hasWalletsRequiringUpgrade}.\n *\n * @example\n * ```ts\n * const walletsRequiringUpgrade = getWalletsRequiringUpgrade();\n * if (walletsRequiringUpgrade.length > 0) {\n * promptUpgrade(walletsRequiringUpgrade);\n * }\n * ```\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The wallet accounts requiring an upgrade to Dynamic WaaS (empty when none).\n * @see hasWalletsRequiringUpgrade\n * @see startUpgradeRedirect\n */\nexport const getWalletsRequiringUpgrade = (\n client = getDefaultClient()\n): WalletAccount[] =>\n getWalletAccounts(client).filter(walletAccountRequiresUpgrade);\n","import { getDefaultClient, refreshAuth } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport {\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_PARAM,\n UPGRADE_STATUS_SUCCESS,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\n/**\n * Options for {@link completeUpgradeRedirect}.\n *\n * @example\n * ```ts\n * // Browser: read the outcome from the current page.\n * await completeUpgradeRedirect({ url: window.location.href });\n * ```\n * @see completeUpgradeRedirect\n * @see detectUpgradeRedirect\n */\nexport type CompleteUpgradeRedirectParams = {\n /**\n * The return URL to read the upgrade outcome from. Pass the URL yourself so\n * the flow works everywhere: in the browser pass `window.location.href`; in\n * React Native pass the deep-link URL you received. This mirrors how social\n * sign-in and device registration consume their redirects, and keeps the\n * function free of any implicit `window` dependency.\n */\n url: string;\n};\n\n/**\n * Completes the redirect upgrade path after {@link detectUpgradeRedirect}.\n *\n * The upgrade app sends the user back with the outcome in the\n * `dynamicWaasMigrationStatus` query param. Once `detectUpgradeRedirect` tells\n * you the return URL is an upgrade redirect, call this to finish it: it reads\n * the param and, on success, refreshes the session (so the upgraded wallet\n * appears) before returning the outcome. The refresh is best-effort — the\n * upgrade already succeeded on the backend, so a failed refresh still reports\n * success and reconciles on the next auth refresh. This detect-then-complete\n * split mirrors the OAuth (`detectSocialRedirectUrl` + `completeSocialRedirect`)\n * and\n * device-registration (`detectDeviceRegistrationRedirect` +\n * `completeDeviceRegistration`) flows. Returns `null` when a well-formed URL\n * simply carries no upgrade result, so that common case is safe to call\n * directly without `detectUpgradeRedirect` first. A malformed/unparseable URL\n * rejects instead — either gate the call with `detectUpgradeRedirect` (which\n * parses defensively) or surface the rejection (e.g. via\n * `useUpgradeRedirectResult`'s `onError`).\n *\n * You always pass the URL (`window.location.href` in the browser, the deep-link\n * URL in React Native), matching how social sign-in and device registration\n * consume their redirects. The function never touches browser history, so you\n * own clearing the param from your navigation state once you have the outcome.\n *\n * @example\n * ```ts\n * // Browser\n * if (detectUpgradeRedirect({ url: window.location.href })) {\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome?.status === 'error') showRetryToast();\n * }\n * ```\n * @example\n * ```ts\n * // React Native: pass the deep-link URL you received.\n * const outcome = await completeUpgradeRedirect({ url: deepLinkUrl });\n * ```\n * @param params - Options.\n * @param params.url - Return URL to read the outcome from (e.g. `window.location.href`).\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A promise resolving to the upgrade outcome, or `null` when there was no upgrade result to consume.\n * @see detectUpgradeRedirect\n * @see startUpgradeRedirect\n * @see subscribeToUpgradeCompletion\n */\nexport const completeUpgradeRedirect = async (\n { url }: CompleteUpgradeRedirectParams,\n client = getDefaultClient()\n): Promise<WalletUpgradeOutcome | null> => {\n // A malformed URL throws here on purpose: it is an unreadable return URL, not\n // a \"no result\" case, so it surfaces to the caller (e.g. the hook's onError)\n // rather than being silently swallowed. detectUpgradeRedirect parses\n // defensively, so gating the call with it avoids this path entirely.\n const status = new URL(url).searchParams.get(UPGRADE_STATUS_PARAM);\n\n // The param is user-controlled, so only ever act on the two values the\n // upgrade app emits and ignore anything else (including tampered values).\n const isKnownStatus =\n status === UPGRADE_STATUS_SUCCESS || status === UPGRADE_STATUS_ERROR;\n\n if (!isKnownStatus) {\n return null;\n }\n\n if (status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so success is always reported — matching\n // subscribeToUpgradeCompletion, so the redirect and embed paths agree.\n try {\n await refreshAuth(client);\n } catch (error) {\n // Report success anyway (the next auth refresh reconciles), but log so\n // the swallowed failure is still traceable in Datadog.\n getCore(client).logger.warn(\n '[completeUpgradeRedirect] refreshAuth failed after a successful upgrade; reporting success and deferring to the next auth refresh',\n error\n );\n }\n\n return { status: UPGRADE_STATUS_SUCCESS };\n }\n\n return { status: UPGRADE_STATUS_ERROR };\n};\n","/* eslint-disable no-restricted-globals */\n\nimport { UnavailableInServerSideError } from '@dynamic-labs-sdk/client';\n\nimport { UPGRADE_STATUS_PARAM } from '../upgradeAppContract';\n\n/**\n * Removes the upgrade outcome parameter (`dynamicWaasMigrationStatus`) that the\n * upgrade app appends to the return URL from the browser's current URL, leaving\n * every other query parameter, the path, and the hash untouched.\n *\n * Call this once you have handled the outcome from `completeUpgradeRedirect` so a\n * page refresh does not replay it. `completeUpgradeRedirect` never touches\n * browser history, so clearing the param is your responsibility — this helper\n * does it in one call, mirroring `clearSocialRedirectParams` and\n * `clearDeviceRegistrationRedirectParams`. Unlike clearing the whole query\n * string, it preserves any of your own parameters on the callback URL.\n *\n * This reads and rewrites `window.location`, so it can only run in the browser.\n * Calling it during server-side rendering (no `window`) throws\n * `UnavailableInServerSideError`. On React Native there is no browser address\n * bar to clean — you own your navigation state — so a `.native` build throws\n * instead.\n *\n * @example\n * ```ts\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome) {\n * handleOutcome(outcome);\n * clearUpgradeRedirectParams();\n * }\n * ```\n * @returns Nothing. The current URL is rewritten in place via\n * `window.history.replaceState`.\n * @throws {UnavailableInServerSideError} When called during SSR (no `window`).\n * @see completeUpgradeRedirect\n * @see startUpgradeRedirect\n */\nexport const clearUpgradeRedirectParams = (): void => {\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('clearUpgradeRedirectParams');\n }\n\n const url = new URL(window.location.href);\n\n url.searchParams.delete(UPGRADE_STATUS_PARAM);\n\n window.history.replaceState({}, '', url.toString());\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n refreshAuth,\n} from '@dynamic-labs-sdk/client';\n\nimport {\n UPGRADE_APP_BASE_URL,\n UPGRADE_COMPLETE_MESSAGE_TYPE,\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_SUCCESS,\n type UpgradeCompleteMessage,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\ntype SubscribeToUpgradeCompletionParams = {\n /**\n * Base URL of the upgrade app the message is expected from. Its origin is\n * used to reject completion messages from any other window. Defaults to the\n * production upgrade app; must match the URL the app is loaded from.\n */\n baseUrl?: string;\n /**\n * Called once with the outcome when the upgrade app reports completion. On\n * success the session has already been refreshed by the time this runs.\n */\n onComplete: (outcome: WalletUpgradeOutcome) => void;\n /**\n * Called synchronously the moment a valid completion message is received,\n * before the (async) success session refresh runs — so `onComplete` still\n * fires after the refresh. Use it to treat the upgrade as already finished\n * (e.g. so dismissing the surface during the refresh is not mistaken for a\n * cancellation, or to show a \"finalizing\" state).\n */\n onReceived?: (outcome: WalletUpgradeOutcome) => void;\n};\n\n// Fully validate the incoming postMessage before acting on it: even though the\n// origin check already gates who can send, the payload shape is still untrusted\n// input, so we verify both the message type AND that `status` is one of the\n// known outcomes rather than trusting whatever `status` string arrives.\nconst isUpgradeCompleteMessage = (\n data: unknown\n): data is UpgradeCompleteMessage =>\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n (data as { type: unknown }).type === UPGRADE_COMPLETE_MESSAGE_TYPE &&\n 'status' in data &&\n ((data as { status: unknown }).status === UPGRADE_STATUS_SUCCESS ||\n (data as { status: unknown }).status === UPGRADE_STATUS_ERROR);\n\n/**\n * Subscribes to the upgrade app's completion signal for an embedded upgrade\n * (iframe or WebView).\n *\n * This is the DOM-agnostic primitive behind {@link openUpgradeIframe}: use it\n * directly when you mount the upgrade app yourself (e.g. a React Native\n * `WebView` or a custom iframe). It listens for the app's origin-checked\n * completion postMessage, refreshes the session on success, then invokes\n * `onComplete` with the outcome. Returns an unsubscribe function that detaches\n * the listener — always call it when your surface unmounts.\n *\n * @example\n * ```ts\n * const unsubscribe = subscribeToUpgradeCompletion({\n * onComplete: ({ status }) => closeSheet(status),\n * });\n * // later, on unmount:\n * unsubscribe();\n * ```\n * @param params - Subscription options.\n * @param params.onComplete - Invoked once with the resolved outcome on completion.\n * @param [params.baseUrl] - Upgrade app base URL whose origin gates messages; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A function that removes the completion listener.\n * @throws {UnavailableInServerSideError} When called during SSR (no window to listen on).\n * @see openUpgradeIframe\n * @see buildUpgradeUrl\n */\nexport const subscribeToUpgradeCompletion = (\n {\n onComplete,\n onReceived,\n baseUrl = UPGRADE_APP_BASE_URL,\n }: SubscribeToUpgradeCompletionParams,\n client = getDefaultClient()\n): (() => void) => {\n // The completion handshake relies on the browser's postMessage; there is no\n // window to listen on during SSR.\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('subscribeToUpgradeCompletion');\n }\n\n const expectedOrigin = new URL(baseUrl).origin;\n\n const handleMessage = async (event: MessageEvent) => {\n // Ignore messages from any other window so a random page cannot fake an\n // upgrade result.\n if (event.origin !== expectedOrigin) {\n return;\n }\n\n if (!isUpgradeCompleteMessage(event.data)) {\n return;\n }\n\n // Only the first valid completion is honored. Detach synchronously, before\n // the async session refresh, so a duplicate message cannot trigger a second\n // refresh or a second `onComplete` call.\n window.removeEventListener('message', handleMessage);\n\n let outcome: WalletUpgradeOutcome;\n if (event.data.status === UPGRADE_STATUS_SUCCESS) {\n outcome = { status: UPGRADE_STATUS_SUCCESS };\n } else {\n outcome = { status: UPGRADE_STATUS_ERROR };\n }\n\n // Signal receipt synchronously, before the async refresh below, so a\n // consumer can lock in that the upgrade already finished — otherwise a\n // surface dismissed during the refresh window would look like a cancel.\n onReceived?.(outcome);\n\n if (outcome.status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so completion is always reported — otherwise, since\n // this runs in an event handler with no caller to catch it, a rejected\n // refresh would hang openUpgradeIframe's `completed` promise forever.\n try {\n await refreshAuth(client);\n } catch {\n // ignore: report success anyway, the refresh will reconcile later\n }\n }\n\n onComplete(outcome);\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => window.removeEventListener('message', handleMessage);\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupBlockedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_blocked_error',\n docsUrl: null,\n name: 'UpgradePopupBlockedError',\n shortMessage:\n 'Upgrade popup was blocked by the browser. Call openUpgradePopup directly from a user gesture (e.g. a click handler) so the browser allows the window to open.',\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupClosedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_closed_error',\n docsUrl: null,\n name: 'UpgradePopupClosedError',\n shortMessage: 'Upgrade popup was closed before the upgrade completed',\n });\n }\n}\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport {\n createDeferredPromise,\n isServerSideRendering,\n} from '@dynamic-labs-sdk/client/core';\n\nimport { buildUpgradeUrl } from '../buildUpgradeUrl';\nimport { UpgradePopupBlockedError } from '../errors/UpgradePopupBlockedError';\nimport { UpgradePopupClosedError } from '../errors/UpgradePopupClosedError';\nimport { subscribeToUpgradeCompletion } from '../subscribeToUpgradeCompletion';\nimport type { WalletUpgradeOutcome } from '../upgradeAppContract';\nimport type {\n OpenUpgradePopupParams,\n UpgradePopupSession,\n} from './openUpgradePopup.types';\n\nexport type { UpgradePopupSession } from './openUpgradePopup.types';\n\nconst UPGRADE_POPUP_TARGET = 'dynamic-embedded-wallet-upgrade';\n\n// Small window (browser picks the position); large enough for the upgrade\n// app's passkey prompts.\nconst DEFAULT_WINDOW_FEATURES = 'popup=yes,width=420,height=640';\n\n// A closed popup has no reliable DOM event, so `popup.closed` is polled.\nconst POPUP_CLOSED_POLL_INTERVAL_MS = 500;\n\n/**\n * Runs the upgrade path in a browser popup window.\n *\n * Opens the hosted upgrade app with `window.open`, wires the same\n * origin-checked completion handshake as {@link openUpgradeIframe}, and\n * refreshes the session on success. `completed` resolves with the outcome once\n * the app reports back; it rejects with {@link UpgradePopupClosedError} if the\n * user closes the popup first. Because popup blockers reject windows opened\n * outside a user gesture, **call this synchronously from a click handler** — do\n * no `await` before it. If the browser blocks the popup (window.open returns\n * `null`), it throws {@link UpgradePopupBlockedError} synchronously.\n *\n * Prefer {@link openUpgradeIframe} when you want the flow embedded in your own\n * modal/sheet; use the popup when a separate window is preferable.\n *\n * @example\n * ```ts\n * // in a click handler — no awaits before openUpgradePopup\n * const session = openUpgradePopup();\n * try {\n * const { status } = await session.completed;\n * } catch (error) {\n * // UpgradePopupClosedError when the user dismissed the popup\n * }\n * ```\n * @param [params] - Popup options.\n * @param [params.redirectUrl] - Absolute https or http completion URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [params.webOptions.popupFeatures] - `window.open` feature string; defaults to a small window.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A session handle exposing a `close` function and the `completed` promise.\n * @throws {UpgradePopupBlockedError} When the browser blocks the popup (`window.open` returns null).\n * @throws {UnavailableInServerSideError} When called during SSR (no window to open from).\n * @see openUpgradeIframe\n * @see subscribeToUpgradeCompletion\n */\nexport const openUpgradePopup = (\n { baseUrl, redirectUrl, webOptions }: OpenUpgradePopupParams = {},\n client = getDefaultClient()\n): UpgradePopupSession => {\n // window.open only exists in the browser; fail fast during SSR.\n if (isServerSideRendering()) {\n throw new UnavailableInServerSideError('openUpgradePopup');\n }\n\n const url = buildUpgradeUrl({ baseUrl, redirectUrl }, client);\n\n const popup = window.open(\n url,\n UPGRADE_POPUP_TARGET,\n webOptions?.popupFeatures ?? DEFAULT_WINDOW_FEATURES\n );\n\n // A blocked popup returns null; surface it as an explicit, actionable error\n // rather than a session whose `completed` never settles.\n if (popup === null) {\n throw new UpgradePopupBlockedError();\n }\n\n const completed = createDeferredPromise<WalletUpgradeOutcome>();\n\n // Callers may use `close()` purely for teardown and never await the promise.\n // Attach a no-op catch so an early-close rejection is treated as handled\n // globally (no unhandled-rejection noise); a caller that does await\n // `completed.promise` still receives the rejection in its own context.\n completed.promise.catch(() => undefined);\n\n // Set the instant a valid completion message arrives — before the async\n // session refresh resolves `completed`. The close poll checks this so a popup\n // that closes right after signalling success is not treated as a cancel.\n let hasReceivedCompletion = false;\n\n // Holds the close-poll interval id. A holder object lets `onComplete` (defined\n // before the interval is created) clear the poll once completion arrives,\n // without a forward `let` reference.\n // `ReturnType<typeof setInterval>` (not `window.setInterval`) because the\n // lib includes both \"dom\" and \"@types/node\": DOM returns `number`, Node\n // returns `Timeout`, and the unqualified form is the one both resolve to.\n const poll: { id?: ReturnType<typeof setInterval> } = {};\n\n const unsubscribe = subscribeToUpgradeCompletion(\n {\n baseUrl,\n onComplete: (outcome) => {\n completed.resolve(outcome);\n // The subscription already removed its own message listener on the\n // first completion; just stop the close poll and leave the popup open\n // for the app's own completion screen (the caller closes it).\n clearInterval(poll.id);\n },\n onReceived: () => {\n hasReceivedCompletion = true;\n },\n },\n client\n );\n\n // Detach the listener, stop polling, and close the window. Reject `completed`\n // as a cancellation only when no completion has been received yet, so a popup\n // closed after a successful handshake still resolves with the real outcome.\n const close = () => {\n unsubscribe();\n clearInterval(poll.id);\n if (!popup.closed) {\n popup.close();\n }\n if (!hasReceivedCompletion) {\n completed.reject(new UpgradePopupClosedError());\n }\n };\n\n poll.id = setInterval(() => {\n if (popup.closed) {\n close();\n }\n }, POPUP_CLOSED_POLL_INTERVAL_MS);\n\n return { close, completed: completed.promise };\n};\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { createDeferredPromise, getCore, isServerSideRendering } from "@dynamic-
|
|
|
3
3
|
|
|
4
4
|
//#region package.json
|
|
5
5
|
var name = "@dynamic-labs-sdk/legacy-embedded-wallet-upgrade";
|
|
6
|
-
var version = "1.
|
|
6
|
+
var version = "1.26.0";
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region src/errors/InvalidUpgradeBaseUrlError.ts
|
|
@@ -451,4 +451,4 @@ const openUpgradePopup = ({ baseUrl, redirectUrl, webOptions } = {}, client = ge
|
|
|
451
451
|
|
|
452
452
|
//#endregion
|
|
453
453
|
export { getWalletsRequiringUpgrade as a, UPGRADE_STATUS_PARAM as c, version as d, completeUpgradeRedirect as i, UPGRADE_STATUS_SUCCESS as l, subscribeToUpgradeCompletion as n, buildUpgradeUrl as o, clearUpgradeRedirectParams as r, UPGRADE_STATUS_ERROR as s, openUpgradePopup as t, name as u };
|
|
454
|
-
//# sourceMappingURL=openUpgradePopup-
|
|
454
|
+
//# sourceMappingURL=openUpgradePopup-D6EwC7q3.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openUpgradePopup-BoVMtDUQ.esm.js","names":["parsedRedirectUrl: URL","url: URL","outcome: WalletUpgradeOutcome","poll: { id?: ReturnType<typeof setInterval> }"],"sources":["../package.json","../src/errors/InvalidUpgradeBaseUrlError.ts","../src/errors/InvalidUpgradeRedirectUrlError.ts","../src/upgradeAppContract.ts","../src/buildUpgradeUrl/buildUpgradeUrl.ts","../src/walletAccountRequiresUpgrade/walletAccountRequiresUpgrade.ts","../src/getWalletsRequiringUpgrade/getWalletsRequiringUpgrade.ts","../src/completeUpgradeRedirect/completeUpgradeRedirect.ts","../src/clearUpgradeRedirectParams/clearUpgradeRedirectParams.ts","../src/subscribeToUpgradeCompletion/subscribeToUpgradeCompletion.ts","../src/errors/UpgradePopupBlockedError.ts","../src/errors/UpgradePopupClosedError.ts","../src/openUpgradePopup/openUpgradePopup.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeBaseUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(baseUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_base_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeBaseUrlError',\n shortMessage: `Upgrade baseUrl must be an absolute https or http URL, received: ${baseUrl}`,\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeRedirectUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(redirectUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_redirect_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeRedirectUrlError',\n shortMessage: `Upgrade redirectUrl must be an absolute https or http URL, received: ${redirectUrl}`,\n });\n }\n}\n","// Contract shared with the standalone WaaS upgrade app\n// (dynamic-labs-oss/waas-migration). Every identifier below MUST stay\n// byte-for-byte identical to the ones the upgrade app reads/emits in its\n// `migrationConfig.ts`, otherwise the redirect round-trip and the iframe\n// postMessage handshake break silently.\n\n/**\n * Default base URL of the hosted WaaS upgrade app. Both upgrade paths point at\n * this origin and append `environmentId` + `redirectUrl` query params. Callers\n * can override it (e.g. to target a local or preview build during QA) via the\n * `baseUrl` option on the public functions.\n */\nexport const UPGRADE_APP_BASE_URL = 'https://upgrade-wallet.dynamic.dev';\n\n/**\n * Query param the upgrade app requires: which Dynamic environment to\n * authenticate the user against. Derived from the Dynamic client so callers\n * never pass it explicitly.\n */\nexport const UPGRADE_ENVIRONMENT_ID_PARAM = 'environmentId';\n\n/**\n * Query param the upgrade app requires: where to send the user (redirect flow)\n * or which origin it postMessages to (iframe flow) once the upgrade finishes.\n * The upgrade app rejects anything that is not an absolute https or http URL.\n */\nexport const UPGRADE_REDIRECT_URL_PARAM = 'redirectUrl';\n\n/**\n * Query param the upgrade app reads to learn which Dynamic API base URL to\n * authenticate against, forwarded as-is from the client's core config. Lets a\n * non-production client (e.g. preprod) drive the hosted upgrade app against the\n * same API it already uses; when it matches the app's own default (production)\n * it is a harmless no-op.\n */\nexport const UPGRADE_API_BASE_URL_PARAM = 'apiBaseUrl';\n\n/**\n * postMessage `type` the upgrade app emits to its iframe parent on completion.\n */\nexport const UPGRADE_COMPLETE_MESSAGE_TYPE = 'dynamic-waas-migration:complete';\n\n/**\n * Query param the upgrade app appends to `redirectUrl` when it runs top-level\n * (redirect flow), carrying the outcome back to the app after navigation.\n */\nexport const UPGRADE_STATUS_PARAM = 'dynamicWaasMigrationStatus';\n\n/** Outcome value the upgrade app reports when the upgrade succeeded. */\nexport const UPGRADE_STATUS_SUCCESS = 'success';\n\n/** Outcome value the upgrade app reports when the upgrade failed. */\nexport const UPGRADE_STATUS_ERROR = 'error';\n\n/**\n * The outcome of a legacy embedded wallet upgrade attempt: `success` when the\n * wallet was upgraded to Dynamic WaaS, `error` when it failed. These are the\n * only two values the upgrade app ever reports, on both the redirect and the\n * iframe paths.\n *\n * @example\n * ```ts\n * const isSuccess = (status: WalletUpgradeStatus) => status === 'success';\n * ```\n * @see WalletUpgradeOutcome\n * @see completeUpgradeRedirect\n */\nexport type WalletUpgradeStatus =\n | typeof UPGRADE_STATUS_SUCCESS\n | typeof UPGRADE_STATUS_ERROR;\n\n/**\n * The resolved result of an upgrade flow, carrying the reported\n * {@link WalletUpgradeStatus}. Returned by the redirect-result consumer and\n * handed to the iframe completion callback so callers can react (toast,\n * refetch, close a sheet, etc.).\n *\n * @example\n * ```ts\n * const onComplete = (outcome: WalletUpgradeOutcome) => {\n * if (outcome.status === 'success') showSuccessToast();\n * };\n * ```\n * @see WalletUpgradeStatus\n * @see subscribeToUpgradeCompletion\n */\nexport type WalletUpgradeOutcome = {\n status: WalletUpgradeStatus;\n};\n\n/**\n * Shape of the completion message the upgrade app posts to its parent window\n * in the iframe flow. Internal to the message handshake — consumers receive the\n * parsed {@link WalletUpgradeOutcome} instead.\n */\nexport type UpgradeCompleteMessage = {\n status: WalletUpgradeStatus;\n type: typeof UPGRADE_COMPLETE_MESSAGE_TYPE;\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { InvalidUpgradeBaseUrlError } from '../errors/InvalidUpgradeBaseUrlError';\nimport { InvalidUpgradeRedirectUrlError } from '../errors/InvalidUpgradeRedirectUrlError';\nimport {\n UPGRADE_API_BASE_URL_PARAM,\n UPGRADE_APP_BASE_URL,\n UPGRADE_ENVIRONMENT_ID_PARAM,\n UPGRADE_REDIRECT_URL_PARAM,\n} from '../upgradeAppContract';\nimport type { BuildUpgradeUrlParams } from './buildUpgradeUrl.types';\n\nconst HTTPS_PROTOCOL = 'https:';\n\nconst HTTP_PROTOCOL = 'http:';\n\n// The upgrade app accepts both https and plain http, so it works for local\n// dev, self-hosting behind a non-TLS proxy, and hosts without TLS. https stays\n// the recommended default. Every other scheme — including script-executing ones\n// like `javascript:` and `data:` — is rejected so we never navigate to or mount\n// a dangerous origin.\nconst ALLOWED_PROTOCOLS = new Set<string>([HTTPS_PROTOCOL, HTTP_PROTOCOL]);\n\n/**\n * Builds the URL that drives the hosted upgrade app for the client's Dynamic\n * environment and a return target.\n *\n * This is the low-level primitive behind every upgrade path: the redirect flow\n * navigates the top-level window to it, the iframe flow loads it as the iframe\n * `src`, and a React Native WebView can load it directly. The environment id is\n * read from the client, and `redirectUrl` defaults to the current page, so the\n * common call is zero-arg. Prefer {@link startUpgradeRedirect} or\n * {@link openUpgradeIframe} unless you are mounting the upgrade app yourself.\n *\n * @example\n * ```ts\n * const src = buildUpgradeUrl();\n * // or target a local upgrade app during QA:\n * const localSrc = buildUpgradeUrl({ baseUrl: 'http://localhost:3000' });\n * ```\n * @param [params] - Options for the generated URL.\n * @param [params.redirectUrl] - Absolute https or http return URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The fully-qualified upgrade app URL to load.\n * @throws {InvalidUpgradeRedirectUrlError} When `redirectUrl` is not an absolute https or http URL.\n * @throws {InvalidUpgradeBaseUrlError} When `baseUrl` is not an absolute https or http URL.\n * @throws {UnavailableInServerSideError} When called during SSR without an explicit `redirectUrl`.\n * @see startUpgradeRedirect\n * @see openUpgradeIframe\n */\nexport const buildUpgradeUrl = (\n { redirectUrl, baseUrl = UPGRADE_APP_BASE_URL }: BuildUpgradeUrlParams = {},\n client = getDefaultClient()\n): string => {\n // The default return target is the current page, which only exists in the\n // browser; fail loudly during SSR rather than dereferencing an absent window.\n if (redirectUrl === undefined && typeof window === 'undefined') {\n throw new UnavailableInServerSideError('buildUpgradeUrl');\n }\n\n const resolvedRedirectUrl = redirectUrl ?? window.location.href;\n\n // Validate the return target up front: reject anything that is not an\n // absolute https/http URL (relative, other schemes) here rather than letting\n // it be silently refused after navigation. A non-absolute value throws,\n // surfaced as the specific InvalidUpgradeRedirectUrlError.\n let parsedRedirectUrl: URL;\n try {\n parsedRedirectUrl = new URL(resolvedRedirectUrl);\n } catch {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(parsedRedirectUrl.protocol)) {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n // Validate the upgrade app origin before we ever load it in an iframe or\n // navigate to it. The default is a trusted https app, but `baseUrl` is an\n // override (custom deployments, forks, local QA), so enforce the allowed\n // schemes to reject dangerous ones (`javascript:`, `data:`) rather than\n // mounting an untrusted origin. Any https/http host is allowed on purpose —\n // customers self-host the upgrade app on their own domains.\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n const { apiBaseUrl, environmentId } = getCore(client);\n\n url.searchParams.set(UPGRADE_ENVIRONMENT_ID_PARAM, environmentId);\n url.searchParams.set(UPGRADE_REDIRECT_URL_PARAM, resolvedRedirectUrl);\n\n // Forward the client's configured API base URL as-is so a non-production\n // client (e.g. preprod) drives the upgrade app against the same API. Core\n // always resolves this to a concrete URL (production by default), so prod is\n // a harmless no-op.\n url.searchParams.set(UPGRADE_API_BASE_URL_PARAM, apiBaseUrl);\n\n return url.toString();\n};\n","import type { WalletAccount } from '@dynamic-labs-sdk/client';\n\n/**\n * Provider-key substring shared by every Dynamic embedded wallet that predates\n * Dynamic WaaS and can therefore be upgraded to it. A wallet account's\n * `walletProviderKey` is a composite backend value (e.g. `<provider>evm` /\n * `<provider>hdsolana` suffixed with `:embeddedWallet`); both the standard and\n * HD variants contain this marker, so matching it as a substring identifies all\n * of them. This is an opaque backend value, matched verbatim — not a user-facing\n * concept.\n */\nconst LEGACY_EMBEDDED_WALLET_PROVIDER_KEY = 'turnkey';\n\n/**\n * Returns whether a wallet account is a legacy Dynamic embedded wallet that\n * requires an upgrade to Dynamic WaaS.\n *\n * Shared predicate behind both the imperative {@link getWalletsRequiringUpgrade}\n * getter and the reactive `useWalletsRequiringUpgrade` hook, so the definition\n * of \"requires upgrade\" lives in exactly one place.\n *\n * @example\n * ```ts\n * const upgradeable = walletAccounts.filter(walletAccountRequiresUpgrade);\n * ```\n * @param walletAccount - The wallet account to test.\n * @returns True when the account is a legacy embedded wallet requiring an upgrade.\n * @see getWalletsRequiringUpgrade\n * @see hasWalletsRequiringUpgrade\n */\nexport const walletAccountRequiresUpgrade = (\n walletAccount: WalletAccount\n): boolean =>\n walletAccount.walletProviderKey.includes(\n LEGACY_EMBEDDED_WALLET_PROVIDER_KEY\n );\n","import {\n type WalletAccount,\n getDefaultClient,\n getWalletAccounts,\n} from '@dynamic-labs-sdk/client';\n\nimport { walletAccountRequiresUpgrade } from '../walletAccountRequiresUpgrade';\n\n/**\n * Returns the current session's wallet accounts that are legacy Dynamic\n * embedded wallets requiring an upgrade to Dynamic WaaS.\n *\n * These are earlier-generation Dynamic embedded wallets that predate Dynamic\n * WaaS (the current TSS-MPC embedded wallet) and can be upgraded to it. Use this\n * when you need the accounts themselves — for example to render only the\n * upgrade entry points on those wallets. Already-upgraded Dynamic WaaS accounts\n * are never returned. For a simple boolean check, use\n * {@link hasWalletsRequiringUpgrade}.\n *\n * @example\n * ```ts\n * const walletsRequiringUpgrade = getWalletsRequiringUpgrade();\n * if (walletsRequiringUpgrade.length > 0) {\n * promptUpgrade(walletsRequiringUpgrade);\n * }\n * ```\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The wallet accounts requiring an upgrade to Dynamic WaaS (empty when none).\n * @see hasWalletsRequiringUpgrade\n * @see startUpgradeRedirect\n */\nexport const getWalletsRequiringUpgrade = (\n client = getDefaultClient()\n): WalletAccount[] =>\n getWalletAccounts(client).filter(walletAccountRequiresUpgrade);\n","import { getDefaultClient, refreshAuth } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport {\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_PARAM,\n UPGRADE_STATUS_SUCCESS,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\n/**\n * Options for {@link completeUpgradeRedirect}.\n *\n * @example\n * ```ts\n * // Browser: read the outcome from the current page.\n * await completeUpgradeRedirect({ url: window.location.href });\n * ```\n * @see completeUpgradeRedirect\n * @see detectUpgradeRedirect\n */\nexport type CompleteUpgradeRedirectParams = {\n /**\n * The return URL to read the upgrade outcome from. Pass the URL yourself so\n * the flow works everywhere: in the browser pass `window.location.href`; in\n * React Native pass the deep-link URL you received. This mirrors how social\n * sign-in and device registration consume their redirects, and keeps the\n * function free of any implicit `window` dependency.\n */\n url: string;\n};\n\n/**\n * Completes the redirect upgrade path after {@link detectUpgradeRedirect}.\n *\n * The upgrade app sends the user back with the outcome in the\n * `dynamicWaasMigrationStatus` query param. Once `detectUpgradeRedirect` tells\n * you the return URL is an upgrade redirect, call this to finish it: it reads\n * the param and, on success, refreshes the session (so the upgraded wallet\n * appears) before returning the outcome. The refresh is best-effort — the\n * upgrade already succeeded on the backend, so a failed refresh still reports\n * success and reconciles on the next auth refresh. This detect-then-complete\n * split mirrors the OAuth (`detectSocialRedirectUrl` + `completeSocialRedirect`)\n * and\n * device-registration (`detectDeviceRegistrationRedirect` +\n * `completeDeviceRegistration`) flows. Returns `null` when a well-formed URL\n * simply carries no upgrade result, so that common case is safe to call\n * directly without `detectUpgradeRedirect` first. A malformed/unparseable URL\n * rejects instead — either gate the call with `detectUpgradeRedirect` (which\n * parses defensively) or surface the rejection (e.g. via\n * `useUpgradeRedirectResult`'s `onError`).\n *\n * You always pass the URL (`window.location.href` in the browser, the deep-link\n * URL in React Native), matching how social sign-in and device registration\n * consume their redirects. The function never touches browser history, so you\n * own clearing the param from your navigation state once you have the outcome.\n *\n * @example\n * ```ts\n * // Browser\n * if (detectUpgradeRedirect({ url: window.location.href })) {\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome?.status === 'error') showRetryToast();\n * }\n * ```\n * @example\n * ```ts\n * // React Native: pass the deep-link URL you received.\n * const outcome = await completeUpgradeRedirect({ url: deepLinkUrl });\n * ```\n * @param params - Options.\n * @param params.url - Return URL to read the outcome from (e.g. `window.location.href`).\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A promise resolving to the upgrade outcome, or `null` when there was no upgrade result to consume.\n * @see detectUpgradeRedirect\n * @see startUpgradeRedirect\n * @see subscribeToUpgradeCompletion\n */\nexport const completeUpgradeRedirect = async (\n { url }: CompleteUpgradeRedirectParams,\n client = getDefaultClient()\n): Promise<WalletUpgradeOutcome | null> => {\n // A malformed URL throws here on purpose: it is an unreadable return URL, not\n // a \"no result\" case, so it surfaces to the caller (e.g. the hook's onError)\n // rather than being silently swallowed. detectUpgradeRedirect parses\n // defensively, so gating the call with it avoids this path entirely.\n const status = new URL(url).searchParams.get(UPGRADE_STATUS_PARAM);\n\n // The param is user-controlled, so only ever act on the two values the\n // upgrade app emits and ignore anything else (including tampered values).\n const isKnownStatus =\n status === UPGRADE_STATUS_SUCCESS || status === UPGRADE_STATUS_ERROR;\n\n if (!isKnownStatus) {\n return null;\n }\n\n if (status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so success is always reported — matching\n // subscribeToUpgradeCompletion, so the redirect and embed paths agree.\n try {\n await refreshAuth(client);\n } catch (error) {\n // Report success anyway (the next auth refresh reconciles), but log so\n // the swallowed failure is still traceable in Datadog.\n getCore(client).logger.warn(\n '[completeUpgradeRedirect] refreshAuth failed after a successful upgrade; reporting success and deferring to the next auth refresh',\n error\n );\n }\n\n return { status: UPGRADE_STATUS_SUCCESS };\n }\n\n return { status: UPGRADE_STATUS_ERROR };\n};\n","/* eslint-disable no-restricted-globals */\n\nimport { UnavailableInServerSideError } from '@dynamic-labs-sdk/client';\n\nimport { UPGRADE_STATUS_PARAM } from '../upgradeAppContract';\n\n/**\n * Removes the upgrade outcome parameter (`dynamicWaasMigrationStatus`) that the\n * upgrade app appends to the return URL from the browser's current URL, leaving\n * every other query parameter, the path, and the hash untouched.\n *\n * Call this once you have handled the outcome from `completeUpgradeRedirect` so a\n * page refresh does not replay it. `completeUpgradeRedirect` never touches\n * browser history, so clearing the param is your responsibility — this helper\n * does it in one call, mirroring `clearSocialRedirectParams` and\n * `clearDeviceRegistrationRedirectParams`. Unlike clearing the whole query\n * string, it preserves any of your own parameters on the callback URL.\n *\n * This reads and rewrites `window.location`, so it can only run in the browser.\n * Calling it during server-side rendering (no `window`) throws\n * `UnavailableInServerSideError`. On React Native there is no browser address\n * bar to clean — you own your navigation state — so a `.native` build throws\n * instead.\n *\n * @example\n * ```ts\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome) {\n * handleOutcome(outcome);\n * clearUpgradeRedirectParams();\n * }\n * ```\n * @returns Nothing. The current URL is rewritten in place via\n * `window.history.replaceState`.\n * @throws {UnavailableInServerSideError} When called during SSR (no `window`).\n * @see completeUpgradeRedirect\n * @see startUpgradeRedirect\n */\nexport const clearUpgradeRedirectParams = (): void => {\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('clearUpgradeRedirectParams');\n }\n\n const url = new URL(window.location.href);\n\n url.searchParams.delete(UPGRADE_STATUS_PARAM);\n\n window.history.replaceState({}, '', url.toString());\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n refreshAuth,\n} from '@dynamic-labs-sdk/client';\n\nimport {\n UPGRADE_APP_BASE_URL,\n UPGRADE_COMPLETE_MESSAGE_TYPE,\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_SUCCESS,\n type UpgradeCompleteMessage,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\ntype SubscribeToUpgradeCompletionParams = {\n /**\n * Base URL of the upgrade app the message is expected from. Its origin is\n * used to reject completion messages from any other window. Defaults to the\n * production upgrade app; must match the URL the app is loaded from.\n */\n baseUrl?: string;\n /**\n * Called once with the outcome when the upgrade app reports completion. On\n * success the session has already been refreshed by the time this runs.\n */\n onComplete: (outcome: WalletUpgradeOutcome) => void;\n /**\n * Called synchronously the moment a valid completion message is received,\n * before the (async) success session refresh runs — so `onComplete` still\n * fires after the refresh. Use it to treat the upgrade as already finished\n * (e.g. so dismissing the surface during the refresh is not mistaken for a\n * cancellation, or to show a \"finalizing\" state).\n */\n onReceived?: (outcome: WalletUpgradeOutcome) => void;\n};\n\n// Fully validate the incoming postMessage before acting on it: even though the\n// origin check already gates who can send, the payload shape is still untrusted\n// input, so we verify both the message type AND that `status` is one of the\n// known outcomes rather than trusting whatever `status` string arrives.\nconst isUpgradeCompleteMessage = (\n data: unknown\n): data is UpgradeCompleteMessage =>\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n (data as { type: unknown }).type === UPGRADE_COMPLETE_MESSAGE_TYPE &&\n 'status' in data &&\n ((data as { status: unknown }).status === UPGRADE_STATUS_SUCCESS ||\n (data as { status: unknown }).status === UPGRADE_STATUS_ERROR);\n\n/**\n * Subscribes to the upgrade app's completion signal for an embedded upgrade\n * (iframe or WebView).\n *\n * This is the DOM-agnostic primitive behind {@link openUpgradeIframe}: use it\n * directly when you mount the upgrade app yourself (e.g. a React Native\n * `WebView` or a custom iframe). It listens for the app's origin-checked\n * completion postMessage, refreshes the session on success, then invokes\n * `onComplete` with the outcome. Returns an unsubscribe function that detaches\n * the listener — always call it when your surface unmounts.\n *\n * @example\n * ```ts\n * const unsubscribe = subscribeToUpgradeCompletion({\n * onComplete: ({ status }) => closeSheet(status),\n * });\n * // later, on unmount:\n * unsubscribe();\n * ```\n * @param params - Subscription options.\n * @param params.onComplete - Invoked once with the resolved outcome on completion.\n * @param [params.baseUrl] - Upgrade app base URL whose origin gates messages; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A function that removes the completion listener.\n * @throws {UnavailableInServerSideError} When called during SSR (no window to listen on).\n * @see openUpgradeIframe\n * @see buildUpgradeUrl\n */\nexport const subscribeToUpgradeCompletion = (\n {\n onComplete,\n onReceived,\n baseUrl = UPGRADE_APP_BASE_URL,\n }: SubscribeToUpgradeCompletionParams,\n client = getDefaultClient()\n): (() => void) => {\n // The completion handshake relies on the browser's postMessage; there is no\n // window to listen on during SSR.\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('subscribeToUpgradeCompletion');\n }\n\n const expectedOrigin = new URL(baseUrl).origin;\n\n const handleMessage = async (event: MessageEvent) => {\n // Ignore messages from any other window so a random page cannot fake an\n // upgrade result.\n if (event.origin !== expectedOrigin) {\n return;\n }\n\n if (!isUpgradeCompleteMessage(event.data)) {\n return;\n }\n\n // Only the first valid completion is honored. Detach synchronously, before\n // the async session refresh, so a duplicate message cannot trigger a second\n // refresh or a second `onComplete` call.\n window.removeEventListener('message', handleMessage);\n\n let outcome: WalletUpgradeOutcome;\n if (event.data.status === UPGRADE_STATUS_SUCCESS) {\n outcome = { status: UPGRADE_STATUS_SUCCESS };\n } else {\n outcome = { status: UPGRADE_STATUS_ERROR };\n }\n\n // Signal receipt synchronously, before the async refresh below, so a\n // consumer can lock in that the upgrade already finished — otherwise a\n // surface dismissed during the refresh window would look like a cancel.\n onReceived?.(outcome);\n\n if (outcome.status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so completion is always reported — otherwise, since\n // this runs in an event handler with no caller to catch it, a rejected\n // refresh would hang openUpgradeIframe's `completed` promise forever.\n try {\n await refreshAuth(client);\n } catch {\n // ignore: report success anyway, the refresh will reconcile later\n }\n }\n\n onComplete(outcome);\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => window.removeEventListener('message', handleMessage);\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupBlockedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_blocked_error',\n docsUrl: null,\n name: 'UpgradePopupBlockedError',\n shortMessage:\n 'Upgrade popup was blocked by the browser. Call openUpgradePopup directly from a user gesture (e.g. a click handler) so the browser allows the window to open.',\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupClosedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_closed_error',\n docsUrl: null,\n name: 'UpgradePopupClosedError',\n shortMessage: 'Upgrade popup was closed before the upgrade completed',\n });\n }\n}\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport {\n createDeferredPromise,\n isServerSideRendering,\n} from '@dynamic-labs-sdk/client/core';\n\nimport { buildUpgradeUrl } from '../buildUpgradeUrl';\nimport { UpgradePopupBlockedError } from '../errors/UpgradePopupBlockedError';\nimport { UpgradePopupClosedError } from '../errors/UpgradePopupClosedError';\nimport { subscribeToUpgradeCompletion } from '../subscribeToUpgradeCompletion';\nimport type { WalletUpgradeOutcome } from '../upgradeAppContract';\nimport type {\n OpenUpgradePopupParams,\n UpgradePopupSession,\n} from './openUpgradePopup.types';\n\nexport type { UpgradePopupSession } from './openUpgradePopup.types';\n\nconst UPGRADE_POPUP_TARGET = 'dynamic-embedded-wallet-upgrade';\n\n// Small window (browser picks the position); large enough for the upgrade\n// app's passkey prompts.\nconst DEFAULT_WINDOW_FEATURES = 'popup=yes,width=420,height=640';\n\n// A closed popup has no reliable DOM event, so `popup.closed` is polled.\nconst POPUP_CLOSED_POLL_INTERVAL_MS = 500;\n\n/**\n * Runs the upgrade path in a browser popup window.\n *\n * Opens the hosted upgrade app with `window.open`, wires the same\n * origin-checked completion handshake as {@link openUpgradeIframe}, and\n * refreshes the session on success. `completed` resolves with the outcome once\n * the app reports back; it rejects with {@link UpgradePopupClosedError} if the\n * user closes the popup first. Because popup blockers reject windows opened\n * outside a user gesture, **call this synchronously from a click handler** — do\n * no `await` before it. If the browser blocks the popup (window.open returns\n * `null`), it throws {@link UpgradePopupBlockedError} synchronously.\n *\n * Prefer {@link openUpgradeIframe} when you want the flow embedded in your own\n * modal/sheet; use the popup when a separate window is preferable.\n *\n * @example\n * ```ts\n * // in a click handler — no awaits before openUpgradePopup\n * const session = openUpgradePopup();\n * try {\n * const { status } = await session.completed;\n * } catch (error) {\n * // UpgradePopupClosedError when the user dismissed the popup\n * }\n * ```\n * @param [params] - Popup options.\n * @param [params.redirectUrl] - Absolute https or http completion URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [params.webOptions.popupFeatures] - `window.open` feature string; defaults to a small window.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A session handle exposing a `close` function and the `completed` promise.\n * @throws {UpgradePopupBlockedError} When the browser blocks the popup (`window.open` returns null).\n * @throws {UnavailableInServerSideError} When called during SSR (no window to open from).\n * @see openUpgradeIframe\n * @see subscribeToUpgradeCompletion\n */\nexport const openUpgradePopup = (\n { baseUrl, redirectUrl, webOptions }: OpenUpgradePopupParams = {},\n client = getDefaultClient()\n): UpgradePopupSession => {\n // window.open only exists in the browser; fail fast during SSR.\n if (isServerSideRendering()) {\n throw new UnavailableInServerSideError('openUpgradePopup');\n }\n\n const url = buildUpgradeUrl({ baseUrl, redirectUrl }, client);\n\n const popup = window.open(\n url,\n UPGRADE_POPUP_TARGET,\n webOptions?.popupFeatures ?? DEFAULT_WINDOW_FEATURES\n );\n\n // A blocked popup returns null; surface it as an explicit, actionable error\n // rather than a session whose `completed` never settles.\n if (popup === null) {\n throw new UpgradePopupBlockedError();\n }\n\n const completed = createDeferredPromise<WalletUpgradeOutcome>();\n\n // Callers may use `close()` purely for teardown and never await the promise.\n // Attach a no-op catch so an early-close rejection is treated as handled\n // globally (no unhandled-rejection noise); a caller that does await\n // `completed.promise` still receives the rejection in its own context.\n completed.promise.catch(() => undefined);\n\n // Set the instant a valid completion message arrives — before the async\n // session refresh resolves `completed`. The close poll checks this so a popup\n // that closes right after signalling success is not treated as a cancel.\n let hasReceivedCompletion = false;\n\n // Holds the close-poll interval id. A holder object lets `onComplete` (defined\n // before the interval is created) clear the poll once completion arrives,\n // without a forward `let` reference.\n // `ReturnType<typeof setInterval>` (not `window.setInterval`) because the\n // lib includes both \"dom\" and \"@types/node\": DOM returns `number`, Node\n // returns `Timeout`, and the unqualified form is the one both resolve to.\n const poll: { id?: ReturnType<typeof setInterval> } = {};\n\n const unsubscribe = subscribeToUpgradeCompletion(\n {\n baseUrl,\n onComplete: (outcome) => {\n completed.resolve(outcome);\n // The subscription already removed its own message listener on the\n // first completion; just stop the close poll and leave the popup open\n // for the app's own completion screen (the caller closes it).\n clearInterval(poll.id);\n },\n onReceived: () => {\n hasReceivedCompletion = true;\n },\n },\n client\n );\n\n // Detach the listener, stop polling, and close the window. Reject `completed`\n // as a cancellation only when no completion has been received yet, so a popup\n // closed after a successful handshake still resolves with the real outcome.\n const close = () => {\n unsubscribe();\n clearInterval(poll.id);\n if (!popup.closed) {\n popup.close();\n }\n if (!hasReceivedCompletion) {\n completed.reject(new UpgradePopupClosedError());\n }\n };\n\n poll.id = setInterval(() => {\n if (popup.closed) {\n close();\n }\n }, POPUP_CLOSED_POLL_INTERVAL_MS);\n\n return { close, completed: completed.promise };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openUpgradePopup-D6EwC7q3.esm.js","names":["parsedRedirectUrl: URL","url: URL","outcome: WalletUpgradeOutcome","poll: { id?: ReturnType<typeof setInterval> }"],"sources":["../package.json","../src/errors/InvalidUpgradeBaseUrlError.ts","../src/errors/InvalidUpgradeRedirectUrlError.ts","../src/upgradeAppContract.ts","../src/buildUpgradeUrl/buildUpgradeUrl.ts","../src/walletAccountRequiresUpgrade/walletAccountRequiresUpgrade.ts","../src/getWalletsRequiringUpgrade/getWalletsRequiringUpgrade.ts","../src/completeUpgradeRedirect/completeUpgradeRedirect.ts","../src/clearUpgradeRedirectParams/clearUpgradeRedirectParams.ts","../src/subscribeToUpgradeCompletion/subscribeToUpgradeCompletion.ts","../src/errors/UpgradePopupBlockedError.ts","../src/errors/UpgradePopupClosedError.ts","../src/openUpgradePopup/openUpgradePopup.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeBaseUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(baseUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_base_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeBaseUrlError',\n shortMessage: `Upgrade baseUrl must be an absolute https or http URL, received: ${baseUrl}`,\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class InvalidUpgradeRedirectUrlError extends BaseError {\n // eslint-disable-next-line custom-rules/require-single-object-param\n constructor(redirectUrl: string) {\n super({\n cause: null,\n code: 'invalid_upgrade_redirect_url_error',\n docsUrl: null,\n name: 'InvalidUpgradeRedirectUrlError',\n shortMessage: `Upgrade redirectUrl must be an absolute https or http URL, received: ${redirectUrl}`,\n });\n }\n}\n","// Contract shared with the standalone WaaS upgrade app\n// (dynamic-labs-oss/waas-migration). Every identifier below MUST stay\n// byte-for-byte identical to the ones the upgrade app reads/emits in its\n// `migrationConfig.ts`, otherwise the redirect round-trip and the iframe\n// postMessage handshake break silently.\n\n/**\n * Default base URL of the hosted WaaS upgrade app. Both upgrade paths point at\n * this origin and append `environmentId` + `redirectUrl` query params. Callers\n * can override it (e.g. to target a local or preview build during QA) via the\n * `baseUrl` option on the public functions.\n */\nexport const UPGRADE_APP_BASE_URL = 'https://upgrade-wallet.dynamic.dev';\n\n/**\n * Query param the upgrade app requires: which Dynamic environment to\n * authenticate the user against. Derived from the Dynamic client so callers\n * never pass it explicitly.\n */\nexport const UPGRADE_ENVIRONMENT_ID_PARAM = 'environmentId';\n\n/**\n * Query param the upgrade app requires: where to send the user (redirect flow)\n * or which origin it postMessages to (iframe flow) once the upgrade finishes.\n * The upgrade app rejects anything that is not an absolute https or http URL.\n */\nexport const UPGRADE_REDIRECT_URL_PARAM = 'redirectUrl';\n\n/**\n * Query param the upgrade app reads to learn which Dynamic API base URL to\n * authenticate against, forwarded as-is from the client's core config. Lets a\n * non-production client (e.g. preprod) drive the hosted upgrade app against the\n * same API it already uses; when it matches the app's own default (production)\n * it is a harmless no-op.\n */\nexport const UPGRADE_API_BASE_URL_PARAM = 'apiBaseUrl';\n\n/**\n * postMessage `type` the upgrade app emits to its iframe parent on completion.\n */\nexport const UPGRADE_COMPLETE_MESSAGE_TYPE = 'dynamic-waas-migration:complete';\n\n/**\n * Query param the upgrade app appends to `redirectUrl` when it runs top-level\n * (redirect flow), carrying the outcome back to the app after navigation.\n */\nexport const UPGRADE_STATUS_PARAM = 'dynamicWaasMigrationStatus';\n\n/** Outcome value the upgrade app reports when the upgrade succeeded. */\nexport const UPGRADE_STATUS_SUCCESS = 'success';\n\n/** Outcome value the upgrade app reports when the upgrade failed. */\nexport const UPGRADE_STATUS_ERROR = 'error';\n\n/**\n * The outcome of a legacy embedded wallet upgrade attempt: `success` when the\n * wallet was upgraded to Dynamic WaaS, `error` when it failed. These are the\n * only two values the upgrade app ever reports, on both the redirect and the\n * iframe paths.\n *\n * @example\n * ```ts\n * const isSuccess = (status: WalletUpgradeStatus) => status === 'success';\n * ```\n * @see WalletUpgradeOutcome\n * @see completeUpgradeRedirect\n */\nexport type WalletUpgradeStatus =\n | typeof UPGRADE_STATUS_SUCCESS\n | typeof UPGRADE_STATUS_ERROR;\n\n/**\n * The resolved result of an upgrade flow, carrying the reported\n * {@link WalletUpgradeStatus}. Returned by the redirect-result consumer and\n * handed to the iframe completion callback so callers can react (toast,\n * refetch, close a sheet, etc.).\n *\n * @example\n * ```ts\n * const onComplete = (outcome: WalletUpgradeOutcome) => {\n * if (outcome.status === 'success') showSuccessToast();\n * };\n * ```\n * @see WalletUpgradeStatus\n * @see subscribeToUpgradeCompletion\n */\nexport type WalletUpgradeOutcome = {\n status: WalletUpgradeStatus;\n};\n\n/**\n * Shape of the completion message the upgrade app posts to its parent window\n * in the iframe flow. Internal to the message handshake — consumers receive the\n * parsed {@link WalletUpgradeOutcome} instead.\n */\nexport type UpgradeCompleteMessage = {\n status: WalletUpgradeStatus;\n type: typeof UPGRADE_COMPLETE_MESSAGE_TYPE;\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { InvalidUpgradeBaseUrlError } from '../errors/InvalidUpgradeBaseUrlError';\nimport { InvalidUpgradeRedirectUrlError } from '../errors/InvalidUpgradeRedirectUrlError';\nimport {\n UPGRADE_API_BASE_URL_PARAM,\n UPGRADE_APP_BASE_URL,\n UPGRADE_ENVIRONMENT_ID_PARAM,\n UPGRADE_REDIRECT_URL_PARAM,\n} from '../upgradeAppContract';\nimport type { BuildUpgradeUrlParams } from './buildUpgradeUrl.types';\n\nconst HTTPS_PROTOCOL = 'https:';\n\nconst HTTP_PROTOCOL = 'http:';\n\n// The upgrade app accepts both https and plain http, so it works for local\n// dev, self-hosting behind a non-TLS proxy, and hosts without TLS. https stays\n// the recommended default. Every other scheme — including script-executing ones\n// like `javascript:` and `data:` — is rejected so we never navigate to or mount\n// a dangerous origin.\nconst ALLOWED_PROTOCOLS = new Set<string>([HTTPS_PROTOCOL, HTTP_PROTOCOL]);\n\n/**\n * Builds the URL that drives the hosted upgrade app for the client's Dynamic\n * environment and a return target.\n *\n * This is the low-level primitive behind every upgrade path: the redirect flow\n * navigates the top-level window to it, the iframe flow loads it as the iframe\n * `src`, and a React Native WebView can load it directly. The environment id is\n * read from the client, and `redirectUrl` defaults to the current page, so the\n * common call is zero-arg. Prefer {@link startUpgradeRedirect} or\n * {@link openUpgradeIframe} unless you are mounting the upgrade app yourself.\n *\n * @example\n * ```ts\n * const src = buildUpgradeUrl();\n * // or target a local upgrade app during QA:\n * const localSrc = buildUpgradeUrl({ baseUrl: 'http://localhost:3000' });\n * ```\n * @param [params] - Options for the generated URL.\n * @param [params.redirectUrl] - Absolute https or http return URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The fully-qualified upgrade app URL to load.\n * @throws {InvalidUpgradeRedirectUrlError} When `redirectUrl` is not an absolute https or http URL.\n * @throws {InvalidUpgradeBaseUrlError} When `baseUrl` is not an absolute https or http URL.\n * @throws {UnavailableInServerSideError} When called during SSR without an explicit `redirectUrl`.\n * @see startUpgradeRedirect\n * @see openUpgradeIframe\n */\nexport const buildUpgradeUrl = (\n { redirectUrl, baseUrl = UPGRADE_APP_BASE_URL }: BuildUpgradeUrlParams = {},\n client = getDefaultClient()\n): string => {\n // The default return target is the current page, which only exists in the\n // browser; fail loudly during SSR rather than dereferencing an absent window.\n if (redirectUrl === undefined && typeof window === 'undefined') {\n throw new UnavailableInServerSideError('buildUpgradeUrl');\n }\n\n const resolvedRedirectUrl = redirectUrl ?? window.location.href;\n\n // Validate the return target up front: reject anything that is not an\n // absolute https/http URL (relative, other schemes) here rather than letting\n // it be silently refused after navigation. A non-absolute value throws,\n // surfaced as the specific InvalidUpgradeRedirectUrlError.\n let parsedRedirectUrl: URL;\n try {\n parsedRedirectUrl = new URL(resolvedRedirectUrl);\n } catch {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(parsedRedirectUrl.protocol)) {\n throw new InvalidUpgradeRedirectUrlError(resolvedRedirectUrl);\n }\n\n // Validate the upgrade app origin before we ever load it in an iframe or\n // navigate to it. The default is a trusted https app, but `baseUrl` is an\n // override (custom deployments, forks, local QA), so enforce the allowed\n // schemes to reject dangerous ones (`javascript:`, `data:`) rather than\n // mounting an untrusted origin. Any https/http host is allowed on purpose —\n // customers self-host the upgrade app on their own domains.\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new InvalidUpgradeBaseUrlError(baseUrl);\n }\n\n const { apiBaseUrl, environmentId } = getCore(client);\n\n url.searchParams.set(UPGRADE_ENVIRONMENT_ID_PARAM, environmentId);\n url.searchParams.set(UPGRADE_REDIRECT_URL_PARAM, resolvedRedirectUrl);\n\n // Forward the client's configured API base URL as-is so a non-production\n // client (e.g. preprod) drives the upgrade app against the same API. Core\n // always resolves this to a concrete URL (production by default), so prod is\n // a harmless no-op.\n url.searchParams.set(UPGRADE_API_BASE_URL_PARAM, apiBaseUrl);\n\n return url.toString();\n};\n","import type { WalletAccount } from '@dynamic-labs-sdk/client';\n\n/**\n * Provider-key substring shared by every Dynamic embedded wallet that predates\n * Dynamic WaaS and can therefore be upgraded to it. A wallet account's\n * `walletProviderKey` is a composite backend value (e.g. `<provider>evm` /\n * `<provider>hdsolana` suffixed with `:embeddedWallet`); both the standard and\n * HD variants contain this marker, so matching it as a substring identifies all\n * of them. This is an opaque backend value, matched verbatim — not a user-facing\n * concept.\n */\nconst LEGACY_EMBEDDED_WALLET_PROVIDER_KEY = 'turnkey';\n\n/**\n * Returns whether a wallet account is a legacy Dynamic embedded wallet that\n * requires an upgrade to Dynamic WaaS.\n *\n * Shared predicate behind both the imperative {@link getWalletsRequiringUpgrade}\n * getter and the reactive `useWalletsRequiringUpgrade` hook, so the definition\n * of \"requires upgrade\" lives in exactly one place.\n *\n * @example\n * ```ts\n * const upgradeable = walletAccounts.filter(walletAccountRequiresUpgrade);\n * ```\n * @param walletAccount - The wallet account to test.\n * @returns True when the account is a legacy embedded wallet requiring an upgrade.\n * @see getWalletsRequiringUpgrade\n * @see hasWalletsRequiringUpgrade\n */\nexport const walletAccountRequiresUpgrade = (\n walletAccount: WalletAccount\n): boolean =>\n walletAccount.walletProviderKey.includes(\n LEGACY_EMBEDDED_WALLET_PROVIDER_KEY\n );\n","import {\n type WalletAccount,\n getDefaultClient,\n getWalletAccounts,\n} from '@dynamic-labs-sdk/client';\n\nimport { walletAccountRequiresUpgrade } from '../walletAccountRequiresUpgrade';\n\n/**\n * Returns the current session's wallet accounts that are legacy Dynamic\n * embedded wallets requiring an upgrade to Dynamic WaaS.\n *\n * These are earlier-generation Dynamic embedded wallets that predate Dynamic\n * WaaS (the current TSS-MPC embedded wallet) and can be upgraded to it. Use this\n * when you need the accounts themselves — for example to render only the\n * upgrade entry points on those wallets. Already-upgraded Dynamic WaaS accounts\n * are never returned. For a simple boolean check, use\n * {@link hasWalletsRequiringUpgrade}.\n *\n * @example\n * ```ts\n * const walletsRequiringUpgrade = getWalletsRequiringUpgrade();\n * if (walletsRequiringUpgrade.length > 0) {\n * promptUpgrade(walletsRequiringUpgrade);\n * }\n * ```\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns The wallet accounts requiring an upgrade to Dynamic WaaS (empty when none).\n * @see hasWalletsRequiringUpgrade\n * @see startUpgradeRedirect\n */\nexport const getWalletsRequiringUpgrade = (\n client = getDefaultClient()\n): WalletAccount[] =>\n getWalletAccounts(client).filter(walletAccountRequiresUpgrade);\n","import { getDefaultClient, refreshAuth } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport {\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_PARAM,\n UPGRADE_STATUS_SUCCESS,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\n/**\n * Options for {@link completeUpgradeRedirect}.\n *\n * @example\n * ```ts\n * // Browser: read the outcome from the current page.\n * await completeUpgradeRedirect({ url: window.location.href });\n * ```\n * @see completeUpgradeRedirect\n * @see detectUpgradeRedirect\n */\nexport type CompleteUpgradeRedirectParams = {\n /**\n * The return URL to read the upgrade outcome from. Pass the URL yourself so\n * the flow works everywhere: in the browser pass `window.location.href`; in\n * React Native pass the deep-link URL you received. This mirrors how social\n * sign-in and device registration consume their redirects, and keeps the\n * function free of any implicit `window` dependency.\n */\n url: string;\n};\n\n/**\n * Completes the redirect upgrade path after {@link detectUpgradeRedirect}.\n *\n * The upgrade app sends the user back with the outcome in the\n * `dynamicWaasMigrationStatus` query param. Once `detectUpgradeRedirect` tells\n * you the return URL is an upgrade redirect, call this to finish it: it reads\n * the param and, on success, refreshes the session (so the upgraded wallet\n * appears) before returning the outcome. The refresh is best-effort — the\n * upgrade already succeeded on the backend, so a failed refresh still reports\n * success and reconciles on the next auth refresh. This detect-then-complete\n * split mirrors the OAuth (`detectSocialRedirectUrl` + `completeSocialRedirect`)\n * and\n * device-registration (`detectDeviceRegistrationRedirect` +\n * `completeDeviceRegistration`) flows. Returns `null` when a well-formed URL\n * simply carries no upgrade result, so that common case is safe to call\n * directly without `detectUpgradeRedirect` first. A malformed/unparseable URL\n * rejects instead — either gate the call with `detectUpgradeRedirect` (which\n * parses defensively) or surface the rejection (e.g. via\n * `useUpgradeRedirectResult`'s `onError`).\n *\n * You always pass the URL (`window.location.href` in the browser, the deep-link\n * URL in React Native), matching how social sign-in and device registration\n * consume their redirects. The function never touches browser history, so you\n * own clearing the param from your navigation state once you have the outcome.\n *\n * @example\n * ```ts\n * // Browser\n * if (detectUpgradeRedirect({ url: window.location.href })) {\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome?.status === 'error') showRetryToast();\n * }\n * ```\n * @example\n * ```ts\n * // React Native: pass the deep-link URL you received.\n * const outcome = await completeUpgradeRedirect({ url: deepLinkUrl });\n * ```\n * @param params - Options.\n * @param params.url - Return URL to read the outcome from (e.g. `window.location.href`).\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A promise resolving to the upgrade outcome, or `null` when there was no upgrade result to consume.\n * @see detectUpgradeRedirect\n * @see startUpgradeRedirect\n * @see subscribeToUpgradeCompletion\n */\nexport const completeUpgradeRedirect = async (\n { url }: CompleteUpgradeRedirectParams,\n client = getDefaultClient()\n): Promise<WalletUpgradeOutcome | null> => {\n // A malformed URL throws here on purpose: it is an unreadable return URL, not\n // a \"no result\" case, so it surfaces to the caller (e.g. the hook's onError)\n // rather than being silently swallowed. detectUpgradeRedirect parses\n // defensively, so gating the call with it avoids this path entirely.\n const status = new URL(url).searchParams.get(UPGRADE_STATUS_PARAM);\n\n // The param is user-controlled, so only ever act on the two values the\n // upgrade app emits and ignore anything else (including tampered values).\n const isKnownStatus =\n status === UPGRADE_STATUS_SUCCESS || status === UPGRADE_STATUS_ERROR;\n\n if (!isKnownStatus) {\n return null;\n }\n\n if (status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so success is always reported — matching\n // subscribeToUpgradeCompletion, so the redirect and embed paths agree.\n try {\n await refreshAuth(client);\n } catch (error) {\n // Report success anyway (the next auth refresh reconciles), but log so\n // the swallowed failure is still traceable in Datadog.\n getCore(client).logger.warn(\n '[completeUpgradeRedirect] refreshAuth failed after a successful upgrade; reporting success and deferring to the next auth refresh',\n error\n );\n }\n\n return { status: UPGRADE_STATUS_SUCCESS };\n }\n\n return { status: UPGRADE_STATUS_ERROR };\n};\n","/* eslint-disable no-restricted-globals */\n\nimport { UnavailableInServerSideError } from '@dynamic-labs-sdk/client';\n\nimport { UPGRADE_STATUS_PARAM } from '../upgradeAppContract';\n\n/**\n * Removes the upgrade outcome parameter (`dynamicWaasMigrationStatus`) that the\n * upgrade app appends to the return URL from the browser's current URL, leaving\n * every other query parameter, the path, and the hash untouched.\n *\n * Call this once you have handled the outcome from `completeUpgradeRedirect` so a\n * page refresh does not replay it. `completeUpgradeRedirect` never touches\n * browser history, so clearing the param is your responsibility — this helper\n * does it in one call, mirroring `clearSocialRedirectParams` and\n * `clearDeviceRegistrationRedirectParams`. Unlike clearing the whole query\n * string, it preserves any of your own parameters on the callback URL.\n *\n * This reads and rewrites `window.location`, so it can only run in the browser.\n * Calling it during server-side rendering (no `window`) throws\n * `UnavailableInServerSideError`. On React Native there is no browser address\n * bar to clean — you own your navigation state — so a `.native` build throws\n * instead.\n *\n * @example\n * ```ts\n * const outcome = await completeUpgradeRedirect({ url: window.location.href });\n * if (outcome) {\n * handleOutcome(outcome);\n * clearUpgradeRedirectParams();\n * }\n * ```\n * @returns Nothing. The current URL is rewritten in place via\n * `window.history.replaceState`.\n * @throws {UnavailableInServerSideError} When called during SSR (no `window`).\n * @see completeUpgradeRedirect\n * @see startUpgradeRedirect\n */\nexport const clearUpgradeRedirectParams = (): void => {\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('clearUpgradeRedirectParams');\n }\n\n const url = new URL(window.location.href);\n\n url.searchParams.delete(UPGRADE_STATUS_PARAM);\n\n window.history.replaceState({}, '', url.toString());\n};\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n refreshAuth,\n} from '@dynamic-labs-sdk/client';\n\nimport {\n UPGRADE_APP_BASE_URL,\n UPGRADE_COMPLETE_MESSAGE_TYPE,\n UPGRADE_STATUS_ERROR,\n UPGRADE_STATUS_SUCCESS,\n type UpgradeCompleteMessage,\n type WalletUpgradeOutcome,\n} from '../upgradeAppContract';\n\ntype SubscribeToUpgradeCompletionParams = {\n /**\n * Base URL of the upgrade app the message is expected from. Its origin is\n * used to reject completion messages from any other window. Defaults to the\n * production upgrade app; must match the URL the app is loaded from.\n */\n baseUrl?: string;\n /**\n * Called once with the outcome when the upgrade app reports completion. On\n * success the session has already been refreshed by the time this runs.\n */\n onComplete: (outcome: WalletUpgradeOutcome) => void;\n /**\n * Called synchronously the moment a valid completion message is received,\n * before the (async) success session refresh runs — so `onComplete` still\n * fires after the refresh. Use it to treat the upgrade as already finished\n * (e.g. so dismissing the surface during the refresh is not mistaken for a\n * cancellation, or to show a \"finalizing\" state).\n */\n onReceived?: (outcome: WalletUpgradeOutcome) => void;\n};\n\n// Fully validate the incoming postMessage before acting on it: even though the\n// origin check already gates who can send, the payload shape is still untrusted\n// input, so we verify both the message type AND that `status` is one of the\n// known outcomes rather than trusting whatever `status` string arrives.\nconst isUpgradeCompleteMessage = (\n data: unknown\n): data is UpgradeCompleteMessage =>\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n (data as { type: unknown }).type === UPGRADE_COMPLETE_MESSAGE_TYPE &&\n 'status' in data &&\n ((data as { status: unknown }).status === UPGRADE_STATUS_SUCCESS ||\n (data as { status: unknown }).status === UPGRADE_STATUS_ERROR);\n\n/**\n * Subscribes to the upgrade app's completion signal for an embedded upgrade\n * (iframe or WebView).\n *\n * This is the DOM-agnostic primitive behind {@link openUpgradeIframe}: use it\n * directly when you mount the upgrade app yourself (e.g. a React Native\n * `WebView` or a custom iframe). It listens for the app's origin-checked\n * completion postMessage, refreshes the session on success, then invokes\n * `onComplete` with the outcome. Returns an unsubscribe function that detaches\n * the listener — always call it when your surface unmounts.\n *\n * @example\n * ```ts\n * const unsubscribe = subscribeToUpgradeCompletion({\n * onComplete: ({ status }) => closeSheet(status),\n * });\n * // later, on unmount:\n * unsubscribe();\n * ```\n * @param params - Subscription options.\n * @param params.onComplete - Invoked once with the resolved outcome on completion.\n * @param [params.baseUrl] - Upgrade app base URL whose origin gates messages; defaults to the production app.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A function that removes the completion listener.\n * @throws {UnavailableInServerSideError} When called during SSR (no window to listen on).\n * @see openUpgradeIframe\n * @see buildUpgradeUrl\n */\nexport const subscribeToUpgradeCompletion = (\n {\n onComplete,\n onReceived,\n baseUrl = UPGRADE_APP_BASE_URL,\n }: SubscribeToUpgradeCompletionParams,\n client = getDefaultClient()\n): (() => void) => {\n // The completion handshake relies on the browser's postMessage; there is no\n // window to listen on during SSR.\n if (typeof window === 'undefined') {\n throw new UnavailableInServerSideError('subscribeToUpgradeCompletion');\n }\n\n const expectedOrigin = new URL(baseUrl).origin;\n\n const handleMessage = async (event: MessageEvent) => {\n // Ignore messages from any other window so a random page cannot fake an\n // upgrade result.\n if (event.origin !== expectedOrigin) {\n return;\n }\n\n if (!isUpgradeCompleteMessage(event.data)) {\n return;\n }\n\n // Only the first valid completion is honored. Detach synchronously, before\n // the async session refresh, so a duplicate message cannot trigger a second\n // refresh or a second `onComplete` call.\n window.removeEventListener('message', handleMessage);\n\n let outcome: WalletUpgradeOutcome;\n if (event.data.status === UPGRADE_STATUS_SUCCESS) {\n outcome = { status: UPGRADE_STATUS_SUCCESS };\n } else {\n outcome = { status: UPGRADE_STATUS_ERROR };\n }\n\n // Signal receipt synchronously, before the async refresh below, so a\n // consumer can lock in that the upgrade already finished — otherwise a\n // surface dismissed during the refresh window would look like a cancel.\n onReceived?.(outcome);\n\n if (outcome.status === UPGRADE_STATUS_SUCCESS) {\n // The upgrade already succeeded on the backend; refreshing the local\n // session is best-effort. Swallow a refresh failure (it reconciles on the\n // next auth refresh) so completion is always reported — otherwise, since\n // this runs in an event handler with no caller to catch it, a rejected\n // refresh would hang openUpgradeIframe's `completed` promise forever.\n try {\n await refreshAuth(client);\n } catch {\n // ignore: report success anyway, the refresh will reconcile later\n }\n }\n\n onComplete(outcome);\n };\n\n window.addEventListener('message', handleMessage);\n\n return () => window.removeEventListener('message', handleMessage);\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupBlockedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_blocked_error',\n docsUrl: null,\n name: 'UpgradePopupBlockedError',\n shortMessage:\n 'Upgrade popup was blocked by the browser. Call openUpgradePopup directly from a user gesture (e.g. a click handler) so the browser allows the window to open.',\n });\n }\n}\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class UpgradePopupClosedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'upgrade_popup_closed_error',\n docsUrl: null,\n name: 'UpgradePopupClosedError',\n shortMessage: 'Upgrade popup was closed before the upgrade completed',\n });\n }\n}\n","/* eslint-disable no-restricted-globals */\n\nimport {\n UnavailableInServerSideError,\n getDefaultClient,\n} from '@dynamic-labs-sdk/client';\nimport {\n createDeferredPromise,\n isServerSideRendering,\n} from '@dynamic-labs-sdk/client/core';\n\nimport { buildUpgradeUrl } from '../buildUpgradeUrl';\nimport { UpgradePopupBlockedError } from '../errors/UpgradePopupBlockedError';\nimport { UpgradePopupClosedError } from '../errors/UpgradePopupClosedError';\nimport { subscribeToUpgradeCompletion } from '../subscribeToUpgradeCompletion';\nimport type { WalletUpgradeOutcome } from '../upgradeAppContract';\nimport type {\n OpenUpgradePopupParams,\n UpgradePopupSession,\n} from './openUpgradePopup.types';\n\nexport type { UpgradePopupSession } from './openUpgradePopup.types';\n\nconst UPGRADE_POPUP_TARGET = 'dynamic-embedded-wallet-upgrade';\n\n// Small window (browser picks the position); large enough for the upgrade\n// app's passkey prompts.\nconst DEFAULT_WINDOW_FEATURES = 'popup=yes,width=420,height=640';\n\n// A closed popup has no reliable DOM event, so `popup.closed` is polled.\nconst POPUP_CLOSED_POLL_INTERVAL_MS = 500;\n\n/**\n * Runs the upgrade path in a browser popup window.\n *\n * Opens the hosted upgrade app with `window.open`, wires the same\n * origin-checked completion handshake as {@link openUpgradeIframe}, and\n * refreshes the session on success. `completed` resolves with the outcome once\n * the app reports back; it rejects with {@link UpgradePopupClosedError} if the\n * user closes the popup first. Because popup blockers reject windows opened\n * outside a user gesture, **call this synchronously from a click handler** — do\n * no `await` before it. If the browser blocks the popup (window.open returns\n * `null`), it throws {@link UpgradePopupBlockedError} synchronously.\n *\n * Prefer {@link openUpgradeIframe} when you want the flow embedded in your own\n * modal/sheet; use the popup when a separate window is preferable.\n *\n * @example\n * ```ts\n * // in a click handler — no awaits before openUpgradePopup\n * const session = openUpgradePopup();\n * try {\n * const { status } = await session.completed;\n * } catch (error) {\n * // UpgradePopupClosedError when the user dismissed the popup\n * }\n * ```\n * @param [params] - Popup options.\n * @param [params.redirectUrl] - Absolute https or http completion URL; defaults to `window.location.href`.\n * @param [params.baseUrl] - Upgrade app base URL; defaults to the production app.\n * @param [params.webOptions.popupFeatures] - `window.open` feature string; defaults to a small window.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns A session handle exposing a `close` function and the `completed` promise.\n * @throws {UpgradePopupBlockedError} When the browser blocks the popup (`window.open` returns null).\n * @throws {UnavailableInServerSideError} When called during SSR (no window to open from).\n * @see openUpgradeIframe\n * @see subscribeToUpgradeCompletion\n */\nexport const openUpgradePopup = (\n { baseUrl, redirectUrl, webOptions }: OpenUpgradePopupParams = {},\n client = getDefaultClient()\n): UpgradePopupSession => {\n // window.open only exists in the browser; fail fast during SSR.\n if (isServerSideRendering()) {\n throw new UnavailableInServerSideError('openUpgradePopup');\n }\n\n const url = buildUpgradeUrl({ baseUrl, redirectUrl }, client);\n\n const popup = window.open(\n url,\n UPGRADE_POPUP_TARGET,\n webOptions?.popupFeatures ?? DEFAULT_WINDOW_FEATURES\n );\n\n // A blocked popup returns null; surface it as an explicit, actionable error\n // rather than a session whose `completed` never settles.\n if (popup === null) {\n throw new UpgradePopupBlockedError();\n }\n\n const completed = createDeferredPromise<WalletUpgradeOutcome>();\n\n // Callers may use `close()` purely for teardown and never await the promise.\n // Attach a no-op catch so an early-close rejection is treated as handled\n // globally (no unhandled-rejection noise); a caller that does await\n // `completed.promise` still receives the rejection in its own context.\n completed.promise.catch(() => undefined);\n\n // Set the instant a valid completion message arrives — before the async\n // session refresh resolves `completed`. The close poll checks this so a popup\n // that closes right after signalling success is not treated as a cancel.\n let hasReceivedCompletion = false;\n\n // Holds the close-poll interval id. A holder object lets `onComplete` (defined\n // before the interval is created) clear the poll once completion arrives,\n // without a forward `let` reference.\n // `ReturnType<typeof setInterval>` (not `window.setInterval`) because the\n // lib includes both \"dom\" and \"@types/node\": DOM returns `number`, Node\n // returns `Timeout`, and the unqualified form is the one both resolve to.\n const poll: { id?: ReturnType<typeof setInterval> } = {};\n\n const unsubscribe = subscribeToUpgradeCompletion(\n {\n baseUrl,\n onComplete: (outcome) => {\n completed.resolve(outcome);\n // The subscription already removed its own message listener on the\n // first completion; just stop the close poll and leave the popup open\n // for the app's own completion screen (the caller closes it).\n clearInterval(poll.id);\n },\n onReceived: () => {\n hasReceivedCompletion = true;\n },\n },\n client\n );\n\n // Detach the listener, stop polling, and close the window. Reject `completed`\n // as a cancellation only when no completion has been received yet, so a popup\n // closed after a successful handshake still resolves with the real outcome.\n const close = () => {\n unsubscribe();\n clearInterval(poll.id);\n if (!popup.closed) {\n popup.close();\n }\n if (!hasReceivedCompletion) {\n completed.reject(new UpgradePopupClosedError());\n }\n };\n\n poll.id = setInterval(() => {\n if (popup.closed) {\n close();\n }\n }, POPUP_CLOSED_POLL_INTERVAL_MS);\n\n return { close, completed: completed.promise };\n};\n"],"mappings":"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"}
|
package/dist/react.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_openUpgradePopup = require('./openUpgradePopup-
|
|
1
|
+
const require_openUpgradePopup = require('./openUpgradePopup-B6ZD1yVO.cjs');
|
|
2
2
|
let _dynamic_labs_sdk_assert_package_version = require("@dynamic-labs-sdk/assert-package-version");
|
|
3
3
|
let _dynamic_labs_sdk_react_hooks_core = require("@dynamic-labs-sdk/react-hooks/core");
|
|
4
4
|
let react = require("react");
|
package/dist/react.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as getWalletsRequiringUpgrade, d as version, i as completeUpgradeRedirect, n as subscribeToUpgradeCompletion, o as buildUpgradeUrl, r as clearUpgradeRedirectParams, t as openUpgradePopup, u as name } from "./openUpgradePopup-
|
|
1
|
+
import { a as getWalletsRequiringUpgrade, d as version, i as completeUpgradeRedirect, n as subscribeToUpgradeCompletion, o as buildUpgradeUrl, r as clearUpgradeRedirectParams, t as openUpgradePopup, u as name } from "./openUpgradePopup-D6EwC7q3.esm.js";
|
|
2
2
|
import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
|
|
3
3
|
import { useBaseMutation, useBaseState, useDynamicClient } from "@dynamic-labs-sdk/react-hooks/core";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|