@walletwright/core 0.4.0 → 0.5.1
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/CHANGELOG.md +31 -0
- package/README.md +19 -15
- package/dist/{cache-BaWu5vyM.mjs → cache-gBvk86TW.mjs} +334 -401
- package/dist/cache-gBvk86TW.mjs.map +1 -0
- package/dist/chain.d.mts +3 -3
- package/dist/cli.d.mts +4 -4
- package/dist/cli.mjs +52 -39
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +17 -9
- package/dist/index.mjs +226 -75
- package/dist/index.mjs.map +1 -1
- package/dist/mock-standard.d.mts +5 -0
- package/dist/mock-standard.mjs +9 -7
- package/dist/mock-standard.mjs.map +1 -1
- package/dist/mock.d.mts +2 -2
- package/dist/mock.mjs +5 -3
- package/dist/mock.mjs.map +1 -1
- package/dist/{types-BXDYOEaq.d.mts → types-CjNm7Tik.d.mts} +63 -19
- package/package.json +10 -9
- package/dist/cache-BaWu5vyM.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-gBvk86TW.mjs","names":["add","rename","importWallet","importWallet"],"sources":["../src/wallets/metamask/actions/accounts.ts","../src/wallets/metamask/actions/network.ts","../src/wallets/metamask/actions/settings.ts","../src/internal/wait.ts","../src/wallets/metamask/approve.ts","../src/wallets/metamask/onboarding.ts","../src/internal/fs.ts","../src/internal/profile.ts","../src/wallets/metamask/onboarding-patch.ts","../src/wallets/metamask.ts","../src/wallets/phantom.ts","../src/wallets/rabby.ts","../src/wallets/slush.ts","../src/wallets/solflare.ts","../src/wallets/index.ts","../src/internal/download.ts","../src/internal/extension.ts","../src/internal/browser.ts","../src/internal/cache.ts"],"sourcesContent":["import type { Page } from \"@playwright/test\";\n\nimport type { AccountActions, WalletActionContext } from \"../../../types\";\n\nconst openAccountMenu = async (home: Page): Promise<void> => {\n await home.getByTestId(\"account-menu-icon\").click();\n await home.getByTestId(\"add-multichain-account-button\").waitFor({ state: \"visible\" });\n};\n\n/** The menu is a popover with no close affordance; going home is the reliable way out. */\nconst closeAccountMenu = async (home: Page): Promise<void> => {\n await home.goto(`${home.url().split(\"#\")[0]}#/`);\n await home.getByTestId(\"account-menu-icon\").waitFor({ state: \"visible\" });\n};\n\nconst add = async ({ home }: WalletActionContext): Promise<void> => {\n await openAccountMenu(home);\n const cells = home.getByTestId(\"account-cell-avatar\");\n const before = await cells.count();\n await home.getByTestId(\"add-multichain-account-button\").click();\n await cells.nth(before).waitFor({ state: \"visible\", timeout: 15_000 });\n await closeAccountMenu(home);\n};\n\nconst importPrivateKey = async (\n { home }: WalletActionContext,\n privateKey: string,\n): Promise<void> => {\n await openAccountMenu(home);\n await home.getByTestId(\"account-list-add-wallet-button\").click();\n const chooseImport = home.getByTestId(\"choose-wallet-type-import-account\");\n await chooseImport.waitFor({ state: \"visible\" });\n await chooseImport.click();\n const confirm = home.getByTestId(\"import-account-confirm-button\");\n await confirm.waitFor({ state: \"visible\" });\n await home.locator(\"input\").first().fill(privateKey);\n await confirm.click();\n await confirm.waitFor({ state: \"hidden\", timeout: 15_000 });\n await closeAccountMenu(home);\n};\n\nconst rename = async (\n { home }: WalletActionContext,\n options: { index: number; name: string },\n): Promise<void> => {\n await openAccountMenu(home);\n await home.getByTestId(\"multichain-account-cell-end-accessory\").nth(options.index).click();\n await home.getByTestId(\"multichain-account-menu-item-rename\").click();\n await home.getByTestId(\"account-name-input\").locator(\"input\").fill(options.name);\n await home.getByRole(\"button\", { name: \"Confirm\" }).click();\n await closeAccountMenu(home);\n};\n\nconst switchTo = async ({ home }: WalletActionContext, index: number): Promise<void> => {\n await openAccountMenu(home);\n await home.getByTestId(\"account-cell-avatar\").nth(index).click();\n const menuClosed = await home\n .getByTestId(\"add-multichain-account-button\")\n .waitFor({ state: \"hidden\", timeout: 10_000 })\n .then(() => true)\n .catch(() => false);\n if (!menuClosed) {\n throw new Error(`[walletwright] accounts.switch(${index}) did not switch (menu stayed open)`);\n }\n};\n\nexport const accounts: AccountActions = {\n add,\n importPrivateKey,\n rename,\n switch: switchTo,\n};\n","import type { Page } from \"@playwright/test\";\n\nimport type { NetworkActions, NetworkConfig, WalletActionContext } from \"../../../types\";\n\nconst closeButton = (home: Page) =>\n home.getByTestId(\"modal-header-close-button\").filter({ visible: true }).first();\n\nconst openNetworkManager = async (home: Page): Promise<void> => {\n await home.getByTestId(\"sort-by-networks\").click();\n await closeButton(home).waitFor({ state: \"visible\" });\n};\n\nconst add = async ({ home }: WalletActionContext, config: NetworkConfig): Promise<void> => {\n await openNetworkManager(home);\n\n await home.getByText(\"Custom\", { exact: true }).first().click();\n await home.getByRole(\"button\", { name: \"Add custom network\" }).first().click();\n\n await home.getByTestId(\"network-form-network-name\").fill(config.name);\n await home.getByTestId(\"network-form-chain-id\").fill(String(config.chainId));\n await home.getByTestId(\"network-form-ticker-input\").fill(config.symbol);\n\n await home.getByTestId(\"test-add-rpc-drop-down\").click();\n await home.getByText(\"Add RPC URL\", { exact: true }).first().click();\n await home.getByTestId(\"rpc-url-input-test\").fill(config.rpcUrl);\n await home.getByTestId(\"page-container-footer-next\").click();\n\n await home.getByTestId(\"network-form-network-name\").waitFor();\n await home.getByTestId(\"page-container-footer-next\").click();\n\n await closeButton(home).click();\n await closeButton(home).waitFor({ state: \"hidden\" });\n};\n\nexport const network: NetworkActions = {\n add,\n};\n","import type { SettingsActions } from \"../../../types\";\n\nexport const settings: SettingsActions = {\n lock: async ({ home }) => {\n await home.getByTestId(\"account-options-menu-button\").click();\n await home.getByTestId(\"global-menu-lock\").filter({ visible: true }).first().click();\n await home.locator('input[type=\"password\"]').waitFor({ state: \"visible\", timeout: 15_000 });\n },\n\n unlock: async ({ unlock }) => {\n await unlock();\n },\n};\n","import type { Page } from \"@playwright/test\";\n\nconst DEFAULT_INTERVAL_MS = 200;\nconst DEFAULT_NAVIGATION_INTERVAL_MS = 1000;\n\nexport type WaitOptions = {\n /** Gap between checks. */\n intervalMs?: number;\n /** Total budget. `check` runs at least once, and never again once the budget has elapsed. */\n timeoutMs: number;\n};\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/** Render a timeout for an error message, so what a failure claims matches what it waited. */\nexport const formatTimeout = (ms: number): string => `${Math.round(ms / 100) / 10}s`;\n\n/** Poll until `check` returns a value, or `undefined` when the timeout expires. */\nexport const waitUntil = async <T>(\n check: () => Promise<T | false | undefined> | T | false | undefined,\n { intervalMs = DEFAULT_INTERVAL_MS, timeoutMs }: WaitOptions,\n): Promise<T | undefined> => {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const result = await check();\n if (result !== undefined && result !== false) {\n return result;\n }\n if (Date.now() >= deadline) {\n return undefined;\n }\n await sleep(intervalMs);\n }\n};\n\nexport const waitUntilOrThrow = async <T>(\n check: () => Promise<T | false | undefined> | T | false | undefined,\n options: WaitOptions & { message: string },\n): Promise<T> => {\n const result = await waitUntil(check, options);\n if (result === undefined) {\n throw new Error(\n `[walletwright] ${options.message} (gave up after ${formatTimeout(options.timeoutMs)})`,\n );\n }\n return result;\n};\n\n/**\n * A `chrome-extension://` URL fails with `ERR_BLOCKED_BY_CLIENT` until the extension registers,\n * which takes seconds after launch, so reaching an extension page is a poll rather than a one-shot.\n */\nexport const gotoWithRetry = async (\n page: Page,\n url: string,\n {\n intervalMs = DEFAULT_NAVIGATION_INTERVAL_MS,\n label,\n timeoutMs,\n }: WaitOptions & { label: string },\n): Promise<void> => {\n await waitUntilOrThrow(\n () =>\n page\n .goto(url, { waitUntil: \"domcontentloaded\" })\n .then(() => true)\n .catch(() => undefined),\n { intervalMs, message: `${label} never loaded (${url})`, timeoutMs },\n );\n};\n","import type { Locator, Page } from \"@playwright/test\";\n\nimport { waitUntil } from \"../../internal/wait\";\n\n/**\n * How long a confirm or cancel button gets to become clickable, once the page is already showing\n * one. Generous because a loaded CI runner renders MetaMask's footer slowly.\n */\nconst BUTTON_TIMEOUT_MS = 45_000;\n/** The retry only covers a notice that rendered late, so it does not need the full budget again. */\nconst RETRY_TIMEOUT_MS = 10_000;\n\n/** Snap notices intercept footer clicks and disable Accept until their content is scrolled. */\nconst acceptThirdPartyNotice = async (popup: Page): Promise<void> => {\n const modal = popup.locator(\".snap-install-warning\");\n if ((await modal.count().catch(() => 0)) === 0) {\n return;\n }\n await modal\n .locator(\"p\")\n .last()\n .scrollIntoViewIfNeeded()\n .catch(() => {});\n await modal\n .getByRole(\"button\", { name: \"Accept\" })\n .click({ timeout: 3000 })\n .catch(() => {});\n};\n\n/** Resolve the confirm test id used by standard, legacy, and Snap request footers. */\nconst confirmButton = (popup: Page): Locator =>\n popup\n .getByTestId(\"confirm-btn\")\n .or(popup.getByTestId(\"confirm-footer-button\"))\n .or(popup.locator('[data-testid=\"page-container-footer-next\"]'))\n .or(popup.locator('[data-testid$=\"-confirm-snap-footer-button\"]'))\n .first();\n\nconst cancelButton = (popup: Page): Locator =>\n popup\n .getByTestId(\"cancel-btn\")\n .or(popup.getByTestId(\"confirm-footer-cancel-button\"))\n .or(popup.locator('[data-testid=\"page-container-footer-cancel\"]'))\n .or(popup.locator('[data-testid$=\"-cancel-snap-footer-button\"]'))\n .first();\n\n/** Distinguishes a pending request from MetaMask's requestless notification home screen. */\nexport const approvalControls = (popup: Page): Locator =>\n confirmButton(popup).or(cancelButton(popup));\n\n/**\n * Click one of the popup's footer buttons. The third-party notice covers that whole footer, so both\n * confirm and cancel are intercepted while it is up, and both need the dismiss-then-retry: without it\n * a click times out against a button that is rendered and enabled, which reads as a stale selector.\n */\nconst clickFooterButton = async (popup: Page, button: Locator): Promise<void> => {\n await acceptThirdPartyNotice(popup);\n try {\n await button.click({ timeout: BUTTON_TIMEOUT_MS });\n } catch (error) {\n await acceptThirdPartyNotice(popup);\n await button.click({ timeout: RETRY_TIMEOUT_MS }).catch(() => {\n throw error;\n });\n }\n};\n\n/** How long a connect page may keep Connect disabled before it counts as rendered too early. */\nconst STALE_CONNECT_MS = 3000;\n\n/**\n * MetaMask can render a connect page before enabling the requested network (seen with Solana): no\n * networks under Permissions, Connect disabled, never recomputed. The request is still pending, so\n * a reload renders it from current state. Connect route only: elsewhere a disabled confirm is busy.\n */\nexport const reloadStaleConnectPage = async (popup: Page): Promise<void> => {\n if (!popup.url().includes(\"#/connect/\")) {\n return;\n }\n const connect = popup.getByTestId(\"confirm-btn\");\n const enabled = await waitUntil(() => connect.isEnabled({ timeout: 500 }).catch(() => false), {\n timeoutMs: STALE_CONNECT_MS,\n });\n if (enabled === undefined) {\n await popup.reload();\n }\n};\n\nexport const approve = async (popup: Page): Promise<void> => {\n await reloadStaleConnectPage(popup);\n await clickFooterButton(popup, confirmButton(popup));\n};\n\n/** The cancel counterpart of `approve`; same union-by-DOM-order resolution, not written order. */\nexport const reject = (popup: Page): Promise<void> => clickFooterButton(popup, cancelButton(popup));\n","import type { Page } from \"@playwright/test\";\n\nimport { waitUntil } from \"../../internal/wait\";\n\nconst SRP_RETYPE_TIMEOUT_MS = 40_000;\nconst SRP_CONFIRM_TIMEOUT_MS = 5000;\n\nexport const importWallet = async (\n page: Page,\n seedPhrase: string,\n password: string,\n): Promise<void> => {\n await page.getByTestId(\"onboarding-import-wallet\").click();\n await page.getByTestId(\"onboarding-import-with-srp-button\").click();\n\n const srpField = page.getByTestId(\"srp-input-import__srp-note\");\n const confirmEnabled = page.locator('[data-testid=\"import-srp-confirm\"]:not([disabled])');\n const accepted = await waitUntil(\n async () => {\n await srpField.click();\n await srpField.fill(\"\");\n await srpField.pressSequentially(seedPhrase, { delay: 50 });\n return confirmEnabled\n .waitFor({ state: \"visible\", timeout: SRP_CONFIRM_TIMEOUT_MS })\n .then(() => true)\n .catch(() => undefined);\n },\n { intervalMs: 0, timeoutMs: SRP_RETYPE_TIMEOUT_MS },\n );\n if (accepted === undefined) {\n throw new Error(\"[walletwright] MetaMask rejected the seed phrase (confirm stayed disabled)\");\n }\n await confirmEnabled.click();\n\n await page.getByTestId(\"create-password-new-input\").fill(password);\n await page.getByTestId(\"create-password-confirm-input\").fill(password);\n await page.getByTestId(\"create-password-terms\").click();\n await page.locator('[data-testid=\"create-password-submit\"]:not([disabled])').click();\n\n await page\n .getByTestId(\"passkey-maybe-later-button\")\n .click({ timeout: 10_000 })\n .catch(() => {});\n await page\n .getByTestId(\"metametrics-i-agree\")\n .click({ timeout: 10_000 })\n .catch(() => {});\n};\n","import { rename, stat } from \"node:fs/promises\";\n\n/** Run `operation`, answering `fallback` instead of throwing when its target does not exist. */\nconst unlessMissing = async <T>(operation: () => Promise<T>, fallback: T): Promise<T> => {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return fallback;\n }\n throw error;\n }\n};\n\nexport const pathExists = (target: string): Promise<boolean> =>\n unlessMissing(async () => {\n await stat(target);\n return true;\n }, false);\n\n/** `rename`, reporting whether `from` existed to be moved rather than throwing when it did not. */\nexport const renameIfExists = (from: string, to: string): Promise<boolean> =>\n unlessMissing(async () => {\n await rename(from, to);\n return true;\n }, false);\n","import { createHash } from \"node:crypto\";\nimport { rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { WalletSetup } from \"../types\";\n\nimport { pathExists, renameIfExists } from \"./fs\";\n\nconst DEFAULT_CACHE_DIR = \".walletwright\";\n\nconst PREVIOUS_SUFFIX = \".previous\";\n\n/** Stable per-setup profile directory name, so the same wallet+seed+password reuses one cache. */\nexport const profileKey = (setup: WalletSetup): string =>\n createHash(\"sha256\")\n .update(`${setup.wallet}:${setup.version ?? \"default\"}:${setup.seedPhrase}:${setup.password}`)\n .digest(\"hex\")\n .slice(0, 20);\n\n/** Where Chrome persists an extension's `chrome.storage.local` inside a browser profile. */\nexport const extensionStateDir = (profileDir: string, extensionId: string): string =>\n path.join(profileDir, \"Default\", \"Local Extension Settings\", extensionId);\n\nconst previousProfileDir = (profileDir: string): string => `${profileDir}${PREVIOUS_SUFFIX}`;\n\n/** Put back a profile that an interrupted publication left at its recovery path. */\nexport const restorePreviousProfile = async (profileDir: string): Promise<void> => {\n const previous = previousProfileDir(profileDir);\n if (await pathExists(profileDir)) {\n await rm(previous, { force: true, recursive: true });\n return;\n }\n await renameIfExists(previous, profileDir);\n};\n\n/**\n * The cache directory and this setup's profile inside it, with any interrupted publication already\n * recovered, so building and launching both start from the same answer.\n */\nexport const locateProfile = async (\n setup: WalletSetup,\n): Promise<{ cacheDir: string; profileDir: string }> => {\n const cacheDir = path.resolve(setup.cacheDir ?? DEFAULT_CACHE_DIR);\n const profileDir = path.join(cacheDir, profileKey(setup));\n await restorePreviousProfile(profileDir);\n return { cacheDir, profileDir };\n};\n\n/** Swap `staging` in as the profile, keeping the prior one at its recovery path until it lands. */\nexport const publishProfile = async (staging: string, profileDir: string): Promise<void> => {\n await restorePreviousProfile(profileDir);\n const previous = previousProfileDir(profileDir);\n const movedPrevious = await renameIfExists(profileDir, previous);\n\n try {\n await rename(staging, profileDir);\n } catch (error) {\n if (movedPrevious) {\n try {\n await rename(previous, profileDir);\n } catch (rollbackError) {\n const publishMessage = error instanceof Error ? error.message : String(error);\n const rollbackMessage =\n rollbackError instanceof Error ? rollbackError.message : String(rollbackError);\n throw new Error(\n `[walletwright] failed to publish cache at ${profileDir}: ${publishMessage}; rollback failed: ${rollbackMessage}`,\n { cause: rollbackError },\n );\n }\n }\n throw error;\n }\n\n if (movedPrevious) {\n await rm(previous, { force: true, recursive: true });\n }\n};\n","import { access } from \"node:fs/promises\";\n\nimport { ClassicLevel } from \"classic-level\";\nimport { z } from \"zod\";\n\nimport { extensionStateDir } from \"../../internal/profile\";\n\nconst onboardingStateSchema = z.record(z.string(), z.json());\n\ntype OnboardingState = z.infer<typeof onboardingStateSchema>;\n\nconst parseState = (text: string, label: string): OnboardingState => {\n const result = onboardingStateSchema.safeParse(JSON.parse(text));\n if (!result.success) {\n throw new Error(`[walletwright] MetaMask's ${label} state is not an object`, {\n cause: result.error,\n });\n }\n return result.data;\n};\n\nconst markOnboarded = (onboarding: OnboardingState) => ({\n ...onboarding,\n completedOnboarding: true,\n firstTimeFlowType:\n typeof onboarding.firstTimeFlowType === \"string\" ? onboarding.firstTimeFlowType : \"import\",\n onboardingTabs: {},\n});\n\n/** Patch both MetaMask 13.x onboarding layouts while the browser has released LevelDB. */\nexport const markMetaMaskOnboarded = async (\n profileDir: string,\n extensionId: string,\n): Promise<void> => {\n const dbDir = extensionStateDir(profileDir, extensionId);\n try {\n await access(dbDir);\n } catch (error) {\n throw new Error(`[walletwright] MetaMask wrote no extension state to ${dbDir}`, {\n cause: error,\n });\n }\n\n const db = new ClassicLevel(dbDir, {\n createIfMissing: false,\n keyEncoding: \"utf8\",\n valueEncoding: \"utf8\",\n });\n try {\n await db.open();\n\n const perController = await db.get(\"OnboardingController\");\n if (perController !== undefined && perController !== \"\") {\n const onboarding = parseState(perController, \"OnboardingController\");\n if (onboarding.completedOnboarding !== true) {\n await db.put(\"OnboardingController\", JSON.stringify(markOnboarded(onboarding)));\n }\n return;\n }\n\n const raw = await db.get(\"data\");\n if (raw === undefined || raw === \"\") {\n throw new Error(\n `[walletwright] MetaMask persisted no onboarding state in ${dbDir} (neither an OnboardingController key nor a data key)`,\n );\n }\n const state = parseState(raw, '\"data\"');\n const nestedResult = onboardingStateSchema.safeParse(state.data);\n const nested = nestedResult.success ? nestedResult.data : undefined;\n const controllerResult = onboardingStateSchema.safeParse(\n (nested ?? state).OnboardingController,\n );\n if (!controllerResult.success) {\n throw new Error(`[walletwright] MetaMask's \"data\" state holds no OnboardingController`);\n }\n const patched = markOnboarded(controllerResult.data);\n await db.put(\n \"data\",\n JSON.stringify(\n nested === undefined\n ? { ...state, OnboardingController: patched }\n : { ...state, data: { ...nested, OnboardingController: patched } },\n ),\n );\n } finally {\n await db.close();\n }\n};\n","import type { ExtensionArchive, WalletDefinition } from \"../types\";\n\nimport { accounts } from \"./metamask/actions/accounts\";\nimport { network } from \"./metamask/actions/network\";\nimport { settings } from \"./metamask/actions/settings\";\nimport { approvalControls, approve, reject } from \"./metamask/approve\";\nimport { importWallet } from \"./metamask/onboarding\";\nimport { markMetaMaskOnboarded } from \"./metamask/onboarding-patch\";\n\n/** Release hashes for the pinned default. Add an entry before changing `DEFAULT_VERSION`. */\nconst RELEASE_SHA256 = {\n \"13.35.1\": \"4e0f8626df0ae9fb15f5f3ad6784a0b518f3ede067b2b0d4f539f9f457c5049c\",\n} as const satisfies Readonly<Record<string, string>>;\n\n/**\n * Typed as a key of `RELEASE_SHA256`, so bumping the default without adding its hash above is a\n * compile error. `download.ts` skips verification for an unpinned `sha256`, so the same bump would\n * otherwise silently downgrade every default MetaMask download to unverified.\n */\nexport const DEFAULT_VERSION: keyof typeof RELEASE_SHA256 = \"13.35.1\";\n\nconst releaseSha256 = (version: string): string | undefined =>\n Object.entries(RELEASE_SHA256).find(([pinned]) => pinned === version)?.[1];\n\n/** The archive for a MetaMask release, split out so the integrity wiring is unit-testable. */\nexport const metamaskRelease = (version: string): ExtensionArchive => ({\n format: \"zip\",\n name: `metamask-chrome-${version}`,\n sha256: releaseSha256(version),\n url: `https://github.com/MetaMask/metamask-extension/releases/download/v${version}/metamask-chrome-${version}.zip`,\n});\n\n/** MetaMask's account-name sync mutates shared test-seed profiles, so its own e2e suite blocks this host. */\nconst ACCOUNT_SYNC_HOST = \"user-storage.api.cx.metamask.io\";\n\nexport const metamask: WalletDefinition = {\n actions: { accounts, network, settings },\n approvalControls,\n approve,\n ecosystems: [\"evm\", \"svm\"],\n extensionName: \"MetaMask\",\n finalizeCache: markMetaMaskOnboarded,\n importWallet,\n onboardingPage: \"home.html\",\n prepareContext: async (context) => {\n await context.route(`**://${ACCOUNT_SYNC_HOST}/**`, (route) => route.abort());\n },\n reject,\n source: { defaultVersion: DEFAULT_VERSION, kind: \"release\", release: metamaskRelease },\n unlockScreen: { entry: \"home.html\" },\n};\n","import { sleep } from \"../internal/wait\";\nimport type { WalletDefinition } from \"../types\";\n\nexport const phantom: WalletDefinition = {\n approve: async (popup) => {\n await popup.getByTestId(\"primary-button\").click({ timeout: 15_000 });\n },\n ecosystems: [\"evm\", \"svm\"],\n\n extensionName: \"Phantom\",\n\n headlessApprovals: true,\n\n importWallet: async (page, seedPhrase, password) => {\n const testId = (id: string) => page.locator(`[data-testid=\"${id}\"]`);\n\n await page.locator(\"text=I already have a wallet\").click();\n await sleep(1000);\n await page.locator(\"text=Import Recovery Phrase\").click();\n await sleep(1500);\n\n const words = seedPhrase.trim().split(/\\s+/v);\n for (let i = 0; i < words.length; i++) {\n await testId(`secret-recovery-phrase-word-input-${i}`).fill(words[i] ?? \"\");\n }\n await testId(\"onboarding-form-submit-button\").click();\n\n await testId(\"onboarding-form-secondary-button\").waitFor({ state: \"visible\", timeout: 60_000 });\n await testId(\"onboarding-form-submit-button\").click();\n\n await testId(\"onboarding-form-password-input\").fill(password);\n await testId(\"onboarding-form-confirm-password-input\").fill(password);\n await testId(\"onboarding-form-terms-of-service-checkbox\").click();\n await testId(\"onboarding-form-submit-button\").click();\n await sleep(2500);\n\n const getStarted = page.locator(\"text=Get Started\");\n if (await getStarted.isVisible().catch(() => false)) {\n await getStarted.click();\n }\n await sleep(1500);\n },\n\n onboardingPage: \"onboarding.html\",\n\n reject: async (popup) => {\n await popup.getByTestId(\"secondary-button\").click({ timeout: 15_000 });\n },\n\n source: { id: \"bfnaelmomeimhlpmgjnjophhpkkoljpa\", kind: \"webStore\" },\n\n unlockScreen: {\n entry: \"popup.html\",\n isUnlocked: async (page) => {\n const rendered = await page\n .locator(\"button\")\n .first()\n .isVisible()\n .catch(() => false);\n return rendered;\n },\n submit: async (page, field) => {\n const unlockButton = page.getByRole(\"button\", { name: /unlock/iv });\n await ((await unlockButton.isVisible().catch(() => false))\n ? unlockButton.click()\n : field.press(\"Enter\"));\n },\n },\n};\n","import type { Page } from \"@playwright/test\";\n\nimport { sleep, waitUntil, waitUntilOrThrow } from \"../internal/wait\";\nimport type { WalletDefinition } from \"../types\";\n\nconst ROUTE_TIMEOUT_MS = 30_000;\nconst SEED_PASTE_TIMEOUT_MS = 3000;\nconst APPROVAL_TIMEOUT_MS = 30_000;\n\nconst ONBOARDING_ROUTE = \"index.html#/new-user/guide\";\n\nconst clickText = async (page: Page, text: string, timeoutMs = 30_000): Promise<void> => {\n const target = page.getByText(text, { exact: true }).first();\n await target.waitFor({ state: \"visible\", timeout: timeoutMs });\n await target.click();\n};\n\n/**\n * Rabby routes with a hash router, so `waitForURL` never settles (a hash change fires no navigation\n * event). Poll the URL instead. Anchoring each onboarding step on its route matters because several\n * screens share the same `input[type=\"password\"]` selector.\n */\nconst waitForRoute = async (\n page: Page,\n fragment: string,\n timeoutMs = ROUTE_TIMEOUT_MS,\n): Promise<void> => {\n await waitUntilOrThrow(() => page.url().includes(fragment), {\n message: `Rabby never reached ${fragment}`,\n timeoutMs,\n });\n};\n\nconst importWallet = async (page: Page, seedPhrase: string, password: string): Promise<void> => {\n await clickText(page, \"I already have an address\");\n await clickText(page, \"Seed Phrase or Private Key\");\n await waitForRoute(page, \"seed-or-key\");\n\n const words = page.locator('input[type=\"password\"]');\n await words.first().waitFor({ state: \"visible\", timeout: 30_000 });\n await page\n .context()\n .grantPermissions([\"clipboard-read\", \"clipboard-write\"])\n .catch(() => {});\n const list = seedPhrase.trim().split(/\\s+/v);\n const lastFilled = async (): Promise<boolean> => {\n const value = await words\n .nth(list.length - 1)\n .inputValue()\n .catch(() => \"\");\n return value.trim().length > 0;\n };\n\n await words.first().click();\n await page.evaluate((phrase) => navigator.clipboard.writeText(phrase), seedPhrase.trim());\n await page.keyboard.press(\"ControlOrMeta+v\");\n const pasted = await waitUntil(lastFilled, { timeoutMs: SEED_PASTE_TIMEOUT_MS });\n if (pasted === undefined) {\n for (let index = 0; index < list.length; index++) {\n const box = words.nth(index);\n await box.click();\n await box.fill(list[index] ?? \"\");\n }\n }\n await page.getByRole(\"button\", { exact: true, name: \"Next\" }).first().click();\n\n await waitForRoute(page, \"set-password\");\n const passwords = page.locator('input[type=\"password\"]');\n await passwords.nth(1).waitFor({ state: \"visible\", timeout: 30_000 });\n await passwords.nth(0).fill(password);\n await passwords.nth(1).fill(password);\n await page.getByRole(\"button\", { exact: true, name: \"Confirm\" }).first().click();\n\n await page\n .getByText(\"Address Imported\", { exact: false })\n .first()\n .waitFor({ state: \"visible\", timeout: 30_000 });\n await sleep(1000);\n};\n\nconst CONFIRM_LABELS = [\"Connect\", \"Sign\", \"Confirm\"];\nconst CANCEL_LABELS = [\"Cancel\", \"Reject\"];\n\n/** Rabby's focus-fragile popup requires in-page clicks; signing then requires a second Confirm click. */\nconst clickApprovalButton = async (\n popup: Page,\n labels: ReadonlyArray<string>,\n): Promise<boolean> => {\n // Every label already clicked, not just the most recent one: after Sign then Confirm, a one-slot\n // memory makes \"Sign\" eligible again, and re-clicking it re-issues the request.\n const clickedLabels = new Set<string>();\n const answered = await waitUntil(\n async () => {\n if (popup.isClosed()) {\n return true;\n }\n const clicked = await popup\n .evaluate(\n (arg) => {\n const target = [...document.querySelectorAll(\"button\")].find((button) => {\n const text = button.textContent.trim();\n return arg.names.includes(text) && !button.disabled && !arg.skip.includes(text);\n });\n target?.click();\n return target ? target.textContent.trim() : \"\";\n },\n { names: [...labels], skip: [...clickedLabels] },\n )\n .catch(() => \"\");\n if (clicked) {\n clickedLabels.add(clicked);\n }\n return undefined;\n },\n { intervalMs: 300, timeoutMs: APPROVAL_TIMEOUT_MS },\n );\n return answered === true || popup.isClosed();\n};\n\nexport const rabby: WalletDefinition = {\n approve: async (popup) => {\n if (!(await clickApprovalButton(popup, CONFIRM_LABELS))) {\n throw new Error(\"[walletwright] Rabby approval: no confirm button became actionable\");\n }\n },\n\n ecosystems: [\"evm\"],\n\n extensionName: \"Rabby\",\n\n headlessApprovals: true,\n\n importWallet,\n\n onboardingPage: ONBOARDING_ROUTE,\n\n reject: async (popup) => {\n if (!(await clickApprovalButton(popup, CANCEL_LABELS))) {\n throw new Error(\"[walletwright] Rabby reject: no cancel button became actionable\");\n }\n },\n\n source: { id: \"acmacodkjbdgmoleebolmdjonilkdbch\", kind: \"webStore\" },\n\n unlockScreen: { entry: \"index.html\" },\n};\n","import type { Page } from \"@playwright/test\";\n\nimport { sleep, waitUntilOrThrow } from \"../internal/wait\";\nimport type { WalletDefinition } from \"../types\";\n\nconst HOME_ROUTE = \"#/tokens\";\n\nconst IMPORT_HOME_TIMEOUT_MS = 30_000;\nconst PERSIST_TIMEOUT_MS = 60_000;\nconst PASSPHRASE_SCREEN_TIMEOUT_MS = 20_000;\nconst PASSPHRASE_SCREEN_ATTEMPTS = 3;\n\nconst ACCOUNTS_DATABASE = \"signaldb-accounts\";\nconst PREFERENCES_DATABASE = \"signaldb-preferences\";\nconst ITEMS_STORE = \"items\";\n\nconst atHome = async (page: Page): Promise<boolean> =>\n (await page.evaluate(() => globalThis.location.hash).catch(() => \"\")) === HOME_ROUTE;\n\n/**\n * Slush writes the imported account, its provider and the preferences row that selects it in\n * separate transactions, so reaching the home route does not mean the profile is durable yet.\n * Caching between those writes yields a profile that unlocks to an account-less wallet.\n */\nconst walletPersisted = (page: Page): Promise<boolean> =>\n page.evaluate(\n async ({ accountsDatabase, preferencesDatabase, storeName }) => {\n const readItems = async (databaseName: string): Promise<Array<object>> => {\n const databases = await indexedDB.databases();\n if (!databases.some((database) => database.name === databaseName)) {\n return [];\n }\n\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(databaseName);\n request.addEventListener(\"success\", () => {\n resolve(request.result);\n });\n request.addEventListener(\"error\", () => {\n reject(request.error ?? new Error(`Unable to open ${databaseName}`));\n });\n });\n\n try {\n if (!database.objectStoreNames.contains(storeName)) {\n return [];\n }\n const store = database.transaction(storeName, \"readonly\").objectStore(storeName);\n const request = store.getAll();\n return await new Promise<Array<object>>((resolve, reject) => {\n request.addEventListener(\"success\", () => {\n resolve(request.result.filter((item) => typeof item === \"object\" && item !== null));\n });\n request.addEventListener(\"error\", () => {\n reject(request.error ?? new Error(`Unable to read ${storeName}`));\n });\n });\n } finally {\n database.close();\n }\n };\n\n const accounts = await readItems(accountsDatabase);\n const accountIds = new Set<string>();\n for (const account of accounts) {\n if (\"id\" in account && typeof account.id === \"string\") {\n accountIds.add(account.id);\n }\n }\n if (accountIds.size === 0) {\n return false;\n }\n\n const preferences = await readItems(preferencesDatabase);\n return preferences.some(\n (preference) =>\n \"currentAccountId\" in preference &&\n typeof preference.currentAccountId === \"string\" &&\n accountIds.has(preference.currentAccountId),\n );\n },\n {\n accountsDatabase: ACCOUNTS_DATABASE,\n preferencesDatabase: PREFERENCES_DATABASE,\n storeName: ITEMS_STORE,\n },\n );\n\nconst fclick = async (page: Page, text: string, timeoutMs = 15_000): Promise<boolean> => {\n const target = page.getByText(text, { exact: true }).first();\n const visible = await target\n .waitFor({ state: \"visible\", timeout: timeoutMs })\n .then(() => true)\n .catch(() => false);\n if (!visible) {\n return false;\n }\n await target.scrollIntoViewIfNeeded().catch(() => {});\n await target.click({ force: true, timeout: timeoutMs });\n return true;\n};\n\n/** The first click can land before the menu is interactive on a slow runner, so retry until the words show. */\nconst openPassphraseImport = async (page: Page): Promise<void> => {\n const firstWord = page.getByLabel(\"Word 1\", { exact: true });\n for (let attempt = 0; attempt < PASSPHRASE_SCREEN_ATTEMPTS; attempt++) {\n await fclick(page, \"More options\");\n await sleep(1000);\n await fclick(page, \"Import existing from passphrase\");\n const opened = await firstWord\n .waitFor({ state: \"visible\", timeout: PASSPHRASE_SCREEN_TIMEOUT_MS })\n .then(() => true)\n .catch(() => false);\n if (opened) {\n return;\n }\n await page.goto(page.url().split(\"#\")[0] ?? page.url());\n await sleep(2000);\n }\n throw new Error(\n `[walletwright] Slush never opened the passphrase import screen after ${PASSPHRASE_SCREEN_ATTEMPTS} attempts`,\n );\n};\n\nexport const slush: WalletDefinition = {\n approve: async (popup, password) => {\n await sleep(2000);\n const confirmed = (await fclick(popup, \"Approve\", 4000)) || (await fclick(popup, \"Sign\"));\n if (!confirmed) {\n throw new Error(\"[walletwright] Slush approval: neither Approve nor Sign was actionable\");\n }\n await sleep(1500);\n const input = popup.locator('input[type=\"password\"]');\n if (await input.isVisible().catch(() => false)) {\n await input.fill(password);\n await fclick(popup, \"Unlock\");\n }\n },\n\n ecosystems: [\"sui\"],\n\n extensionName: \"Slush\",\n\n importWallet: async (page, seedPhrase, password) => {\n await openPassphraseImport(page);\n\n const words = seedPhrase.trim().split(/\\s+/v);\n for (let i = 0; i < words.length; i++) {\n await page.getByLabel(`Word ${i + 1}`, { exact: true }).fill(words[i] ?? \"\");\n }\n await page.getByRole(\"button\", { name: \"Next\" }).click();\n await sleep(2000);\n\n await page.locator('input[placeholder=\"Password\"]').fill(password);\n await page.locator('input[placeholder=\"Confirm Password\"]').fill(password);\n await page.getByRole(\"button\", { name: \"Next\" }).click();\n await sleep(2000);\n\n await page\n .getByRole(\"button\", { name: \"Next\" })\n .click()\n .catch(() => {});\n await waitUntilOrThrow(() => atHome(page), {\n intervalMs: 1000,\n message: `Slush import never reached the wallet home (${HOME_ROUTE})`,\n timeoutMs: IMPORT_HOME_TIMEOUT_MS,\n });\n await waitUntilOrThrow(() => walletPersisted(page), {\n intervalMs: 1000,\n message: \"Slush import never persisted an account and its selected preference\",\n timeoutMs: PERSIST_TIMEOUT_MS,\n });\n },\n\n notificationMatch: \"isPopup=1\",\n\n onboardingPage: \"index.html\",\n\n prepareContext: async (context) => {\n await context.route(\"**://*.slush.app/**\", (route) =>\n route.fulfill({\n body: JSON.stringify({ data: {} }),\n contentType: \"application/json\",\n status: 200,\n }),\n );\n },\n\n reject: async (popup) => {\n await sleep(2000);\n if (!(await fclick(popup, \"Reject\"))) {\n throw new Error(\"[walletwright] Slush reject: no Reject control was actionable\");\n }\n },\n\n source: { id: \"opcgpfmipidbgpenhmajoajpbobppdil\", kind: \"webStore\" },\n\n unlockScreen: {\n entry: \"index.html\",\n isUnlocked: atHome,\n submit: async (page) => {\n await fclick(page, \"Unlock\");\n },\n },\n};\n","import type { Page } from \"@playwright/test\";\n\nimport type { WalletDefinition } from \"../types\";\n\nconst importWallet = async (page: Page, seedPhrase: string, password: string): Promise<void> => {\n await page.getByTestId(\"btn-import-existing-wallet\").click({ timeout: 30_000 });\n await page.getByText(\"Recovery phrase\", { exact: true }).first().click({ timeout: 30_000 });\n\n const words = seedPhrase.trim().split(/\\s+/v);\n await page.getByTestId(\"input-recovery-phrase-1\").waitFor({ state: \"visible\", timeout: 30_000 });\n for (let index = 0; index < words.length; index++) {\n await page.getByTestId(`input-recovery-phrase-${index + 1}`).fill(words[index] ?? \"\");\n }\n await page.getByTestId(\"btn-continue\").click({ timeout: 30_000 });\n\n await page.getByTestId(\"input-new-password\").waitFor({ state: \"visible\", timeout: 30_000 });\n await page.getByTestId(\"input-new-password\").fill(password);\n await page.getByTestId(\"input-repeat-password\").fill(password);\n await page.getByTestId(\"btn-continue\").click({ timeout: 30_000 });\n\n await page.getByTestId(\"btn-quick-setup\").click({ timeout: 60_000 });\n\n await page.getByTestId(\"btn-explore\").click({ timeout: 60_000 });\n};\n\nexport const solflare: WalletDefinition = {\n approve: async (popup) => {\n await popup\n .getByTestId(\"btn-connect\")\n .or(popup.getByTestId(\"btn-approve\"))\n .first()\n .click({ timeout: 30_000 });\n },\n\n ecosystems: [\"svm\"],\n\n extensionName: \"Solflare\",\n\n importWallet,\n\n notificationMatch: \"confirm_popup.html\",\n\n onboardingPage: \"wallet.html\",\n\n reject: async (popup) => {\n await popup\n .getByTestId(\"btn-cancel\")\n .or(popup.getByTestId(\"btn-reject\"))\n .first()\n .click({ timeout: 30_000 });\n },\n\n source: { id: \"bhhhlbepdkbapadjdnnojkbgioiodbic\", kind: \"webStore\" },\n\n unlockScreen: { entry: \"wallet.html\" },\n};\n","import type { Ecosystem, WalletDefinition, WalletKind } from \"../types\";\n\nimport { metamask } from \"./metamask\";\nimport { phantom } from \"./phantom\";\nimport { rabby } from \"./rabby\";\nimport { slush } from \"./slush\";\nimport { solflare } from \"./solflare\";\n\nexport const wallets = {\n metamask,\n phantom,\n rabby,\n slush,\n solflare,\n} satisfies Record<WalletKind, WalletDefinition>;\n\n/** Narrow an arbitrary string (CLI flag, setup file field) to a supported wallet. */\nexport const isWalletKind = (value: string): value is WalletKind => Object.hasOwn(wallets, value);\n\n/** Every supported wallet, in registry order. */\nexport const walletKinds: ReadonlyArray<WalletKind> = Object.keys(wallets).filter(isWalletKind);\n\n/** Wallet kinds that can drive the given ecosystem (e.g. `\"evm\"` → MetaMask, Phantom). */\nexport const walletKindsByEcosystem = (ecosystem: Ecosystem): Array<WalletKind> =>\n walletKinds.filter((kind) => wallets[kind].ecosystems.includes(ecosystem));\n","import { createHash } from \"node:crypto\";\nimport { mkdir, mkdtemp, rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport AdmZip from \"adm-zip\";\n\nimport type { ExtensionArchive } from \"../types\";\n\nimport { pathExists } from \"./fs\";\n\nconst ZIP_SIGNATURE = Buffer.from([80, 75, 3, 4]);\n\n/**\n * Prefix for in-progress extractions. It has to live inside `cacheDir` so publishing is a `rename`\n * on one filesystem (a cross-device rename fails with EXDEV), and it starts with a dot so it can\n * never collide with a wallet's `name`.\n */\nconst STAGING_PREFIX = \".staging-\";\n\n/** Download and validate an extension archive, then publish its extraction atomically from staging. */\nexport const downloadAndExtractExtension = async (\n options: ExtensionArchive & { cacheDir: string },\n): Promise<string> => {\n const { cacheDir, format, name, sha256, url } = options;\n const cacheRoot = path.resolve(cacheDir);\n const outDir = path.resolve(cacheDir, name);\n if (outDir === cacheRoot || !outDir.startsWith(cacheRoot + path.sep)) {\n throw new Error(`[walletwright] invalid extension name: ${name}`);\n }\n if (await pathExists(path.join(outDir, \"manifest.json\"))) {\n return outDir;\n }\n\n await mkdir(cacheDir, { recursive: true });\n\n const response = await fetch(url, { redirect: \"follow\" });\n if (!response.ok) {\n throw new Error(\n `[walletwright] failed to download ${url}: ${response.status} ${response.statusText}`,\n );\n }\n const bytes = Buffer.from(await response.arrayBuffer());\n\n if (sha256 !== undefined) {\n const actual = createHash(\"sha256\").update(bytes).digest(\"hex\");\n if (actual !== sha256.toLowerCase()) {\n throw new Error(\n `[walletwright] ${name} failed integrity check: expected ${sha256}, got ${actual}`,\n );\n }\n }\n\n let zipBytes = bytes;\n if (format === \"crx\") {\n const start = bytes.indexOf(ZIP_SIGNATURE);\n if (start === -1) {\n throw new Error(`[walletwright] ${url} is not a valid CRX (no ZIP header found)`);\n }\n zipBytes = bytes.subarray(start);\n }\n\n const staging = await mkdtemp(path.join(cacheRoot, STAGING_PREFIX));\n try {\n const zip = new AdmZip(zipBytes);\n for (const entry of zip.getEntries()) {\n const target = path.resolve(staging, entry.entryName);\n if (target !== staging && !target.startsWith(staging + path.sep)) {\n throw new Error(`[walletwright] refusing to extract ${entry.entryName}: escapes ${outDir}`);\n }\n }\n zip.extractAllTo(staging, /* overwrite */ true);\n\n if (!(await pathExists(path.join(staging, \"manifest.json\")))) {\n throw new Error(`[walletwright] extracted ${name} but no manifest.json found in ${outDir}`);\n }\n\n if (await pathExists(path.join(outDir, \"manifest.json\"))) {\n return outDir; // another worker published while we were downloading\n }\n await rm(outDir, { force: true, recursive: true });\n await rename(staging, outDir);\n return outDir;\n } finally {\n await rm(staging, { force: true, recursive: true }).catch(() => {});\n }\n};\n\n/** Build the Chrome Web Store CRX download URL for an extension id. */\nexport const chromeWebStoreCrxUrl = (extensionId: string): string =>\n `https://clients2.google.com/service/update2/crx?response=redirect&prodversion=130.0&acceptformat=crx2,crx3&x=id%3D${extensionId}%26uc`;\n","import { createHash } from \"node:crypto\";\nimport { readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { z } from \"zod\";\n\nimport type { ExtensionArchive, WalletSetup } from \"../types\";\nimport { wallets } from \"../wallets/index\";\n\nimport { chromeWebStoreCrxUrl, downloadAndExtractExtension } from \"./download\";\n\n/**\n * The archive a setup runs. A release source honours `version`; a Web Store source cannot pin one,\n * so asking for it is an error rather than a setting that forks the profile cache for the same build.\n */\nexport const extensionArchive = (setup: WalletSetup): ExtensionArchive => {\n const { extensionName, source } = wallets[setup.wallet];\n if (source.kind === \"release\") {\n return source.release(setup.version ?? source.defaultVersion);\n }\n if (setup.version !== undefined) {\n throw new Error(\n `[walletwright] ${extensionName} runs its Chrome Web Store build, which cannot be pinned; remove \\`version\\` from its WalletSetup.`,\n );\n }\n return {\n format: \"crx\",\n name: `${setup.wallet}-chrome-latest`,\n sha256: undefined,\n url: chromeWebStoreCrxUrl(source.id),\n };\n};\n\n/** Download and unpack the setup's extension into `cacheDir`, returning its path. */\nexport const prepareExtension = async (setup: WalletSetup, cacheDir: string): Promise<string> => {\n const extensionPath = await downloadAndExtractExtension({ cacheDir, ...extensionArchive(setup) });\n return extensionPath;\n};\n\n/** Mirror Chrome's extension-id derivation; resolve symlinks because Chrome hashes the real path. */\nexport const extensionIdFromPath = async (extensionPath: string): Promise<string> => {\n const resolved = path.resolve(extensionPath);\n const abs = await realpath(resolved).catch(() => resolved);\n let key: string | undefined;\n try {\n const text = await readFile(path.join(abs, \"manifest.json\"), \"utf8\");\n const manifest = z.object({ key: z.string().optional() }).parse(JSON.parse(text));\n key = manifest.key;\n } catch {\n key = undefined;\n }\n const source =\n key === undefined || key === \"\" ? Buffer.from(abs, \"utf8\") : Buffer.from(key, \"base64\");\n const hex = createHash(\"sha256\").update(source).digest(\"hex\").slice(0, 32);\n return Array.from(hex, (nibble) => String.fromCodePoint(97 + Number.parseInt(nibble, 16))).join(\n \"\",\n );\n};\n","import { type BrowserContext, chromium } from \"@playwright/test\";\n\nimport type { WalletDefinition } from \"../types\";\n\nimport { extensionIdFromPath } from \"./extension\";\n\ntype LaunchPersistentContext = typeof chromium.launchPersistentContext;\n\nconst launchChromium: LaunchPersistentContext = chromium.launchPersistentContext.bind(chromium);\n\n/**\n * Launch Chromium with the wallet's extension loaded and its `prepareContext` applied. The cache\n * build and the test run both open the wallet here, so a route stubbed or an extension loaded for\n * one but not the other cannot fail far from its cause. Closes the context if preparing it fails.\n */\nexport const openWalletContext = async ({\n definition,\n extensionPath,\n headless,\n launchPersistentContext = launchChromium,\n userDataDir,\n}: {\n definition: WalletDefinition;\n extensionPath: string;\n headless: boolean;\n /** Injected by tests; defaults to Playwright's own. */\n launchPersistentContext?: LaunchPersistentContext;\n userDataDir: string;\n}): Promise<{ context: BrowserContext; extensionId: string }> => {\n const context = await launchPersistentContext(userDataDir, {\n args: [`--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`],\n // The default headless build is the headless shell, which cannot load an extension at all.\n channel: \"chromium\",\n headless,\n });\n try {\n await definition.prepareContext?.(context);\n return { context, extensionId: await extensionIdFromPath(extensionPath) };\n } catch (error) {\n await context.close().catch(() => {});\n throw error;\n }\n};\n","import { mkdir, mkdtemp, readdir, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { BrowserContext } from \"@playwright/test\";\n\nimport type { WalletSetup } from \"../types\";\nimport { wallets } from \"../wallets/index\";\n\nimport { openWalletContext } from \"./browser\";\nimport { prepareExtension } from \"./extension\";\nimport { extensionStateDir, locateProfile, publishProfile } from \"./profile\";\nimport { gotoWithRetry, sleep, waitUntilOrThrow } from \"./wait\";\n\nconst NAVIGATION_TIMEOUT_MS = 15_000;\nconst FLUSH_SETTLE_MS = 3000;\nconst STATE_WRITE_TIMEOUT_MS = 10_000;\n\n/**\n * Prefix for an in-progress build. Inside `cacheDir` so publishing is a `rename` on one filesystem,\n * dot-prefixed so it can never be mistaken for a 20-hex-character `profileKey`.\n */\nconst BUILD_PREFIX = \".building-\";\n\n/** Wallet state may live in `chrome.storage.local` or extension IndexedDB. */\nconst hasPersistedState = async (profileDir: string, extensionId: string): Promise<boolean> => {\n const stores = [\n extensionStateDir(profileDir, extensionId),\n path.join(\n profileDir,\n \"Default\",\n \"IndexedDB\",\n `chrome-extension_${extensionId}_0.indexeddb.leveldb`,\n ),\n ];\n const listings = await Promise.allSettled(stores.map((dir) => readdir(dir)));\n return listings.some((listing) => listing.status === \"fulfilled\" && listing.value.length > 0);\n};\n\n/** The seams a unit test replaces, so the build can be exercised without a browser or network. */\ntype BuildCacheDependencies = {\n launchPersistentContext?: Parameters<typeof openWalletContext>[0][\"launchPersistentContext\"];\n prepareExtension: typeof prepareExtension;\n};\n\nconst defaultDependencies: BuildCacheDependencies = { prepareExtension };\n\n/** Build in staging and retain the prior cache until the new profile publishes successfully. */\nconst buildCacheWithDependencies = async (\n setup: WalletSetup,\n { headless = false }: { headless?: boolean } = {},\n dependencies: BuildCacheDependencies = defaultDependencies,\n): Promise<string> => {\n const definition = wallets[setup.wallet];\n const { cacheDir, profileDir } = await locateProfile(setup);\n const extensionPath = await dependencies.prepareExtension(setup, cacheDir);\n await mkdir(cacheDir, { recursive: true });\n const staging = await mkdtemp(path.join(cacheDir, BUILD_PREFIX));\n\n let context: BrowserContext | undefined;\n try {\n const opened = await openWalletContext({\n definition,\n extensionPath,\n headless,\n launchPersistentContext: dependencies.launchPersistentContext,\n userDataDir: staging,\n });\n context = opened.context;\n const { extensionId } = opened;\n\n const page =\n context.pages().find((candidate) => candidate.url() === \"about:blank\") ??\n (await context.newPage());\n await gotoWithRetry(page, `chrome-extension://${extensionId}/${definition.onboardingPage}`, {\n label: `${definition.extensionName} onboarding page`,\n timeoutMs: NAVIGATION_TIMEOUT_MS,\n });\n await sleep(2000);\n\n await definition.importWallet(page, setup.seedPhrase, setup.password);\n await sleep(FLUSH_SETTLE_MS); // let the wallet flush state to disk before we close\n await waitUntilOrThrow(() => hasPersistedState(staging, extensionId), {\n intervalMs: 500,\n message: `${definition.extensionName} onboarding wrote no wallet state into ${staging}`,\n timeoutMs: STATE_WRITE_TIMEOUT_MS,\n });\n await context.close();\n await definition.finalizeCache?.(staging, extensionId);\n\n await publishProfile(staging, profileDir);\n return profileDir;\n } catch (error) {\n await context?.close().catch(() => {});\n throw error;\n } finally {\n await rm(staging, { force: true, recursive: true });\n }\n};\n\nconst buildCache = (setup: WalletSetup, options?: { headless?: boolean }): Promise<string> =>\n buildCacheWithDependencies(setup, options);\n\nexport { buildCache, buildCacheWithDependencies };\nexport type { BuildCacheDependencies };\n"],"mappings":"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"}
|
package/dist/chain.d.mts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* separate entry point: it needs `prool` and Foundry's `anvil` binary, which the core connect/sign
|
|
5
5
|
* flow does not, so it stays out of the default install.
|
|
6
6
|
*/
|
|
7
|
-
type LocalChain = {
|
|
7
|
+
export type LocalChain = {
|
|
8
8
|
chainId: number;
|
|
9
9
|
/** JSON-RPC endpoint to hand to the wallet (via `addNetwork` or `wallet_addEthereumChain`). */
|
|
10
10
|
rpcUrl: string;
|
|
11
11
|
stop: () => Promise<void>;
|
|
12
12
|
};
|
|
13
|
-
type LocalChainOptions = {
|
|
13
|
+
export type LocalChainOptions = {
|
|
14
14
|
/** `anvil` binary to run. Defaults to `anvil` on `PATH` (Foundry's standard install location). */
|
|
15
15
|
binary?: string;
|
|
16
16
|
chainId?: number;
|
|
@@ -20,5 +20,5 @@ type LocalChainOptions = {
|
|
|
20
20
|
};
|
|
21
21
|
declare const createLocalChain: (options?: LocalChainOptions) => Promise<LocalChain>;
|
|
22
22
|
//#endregion
|
|
23
|
-
export {
|
|
23
|
+
export { createLocalChain };
|
|
24
24
|
//# sourceMappingURL=chain.d.mts.map
|
package/dist/cli.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as WalletSetup } from "./types-CjNm7Tik.mjs";
|
|
2
2
|
//#region src/cli.d.ts
|
|
3
3
|
type Flags = {
|
|
4
4
|
"cache-dir"?: string;
|
|
@@ -15,12 +15,12 @@ type Command = {
|
|
|
15
15
|
} | {
|
|
16
16
|
headless: boolean;
|
|
17
17
|
kind: "cache";
|
|
18
|
-
|
|
18
|
+
setups: ReadonlyArray<WalletSetup>;
|
|
19
19
|
};
|
|
20
|
-
declare const
|
|
20
|
+
declare const resolveSetups: (flags: Flags) => Promise<Array<WalletSetup>>;
|
|
21
21
|
declare const parseArgv: (argv: Array<string>) => Promise<Command>;
|
|
22
22
|
/** Resolve the CLI symlink before comparing `process.argv[1]` with `import.meta.url`. */
|
|
23
23
|
declare const isEntryPoint: (moduleUrl: string, entryPath: string | undefined) => Promise<boolean>;
|
|
24
24
|
//#endregion
|
|
25
|
-
export { isEntryPoint, parseArgv,
|
|
25
|
+
export { isEntryPoint, parseArgv, resolveSetups };
|
|
26
26
|
//# sourceMappingURL=cli.d.mts.map
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { a as walletKinds, i as isWalletKind, t as buildCache } from "./cache-gBvk86TW.mjs";
|
|
3
3
|
import { realpath } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -7,25 +7,25 @@ import { pathToFileURL } from "node:url";
|
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
|
|
9
9
|
//#region src/cli.ts
|
|
10
|
-
const KINDS = Object.keys(wallets);
|
|
11
10
|
const HELP = `walletwright: build the onboarded wallet cache for Playwright tests
|
|
12
11
|
|
|
13
12
|
Usage:
|
|
14
|
-
walletwright cache --setup <file>
|
|
15
|
-
walletwright cache --wallet <${
|
|
13
|
+
walletwright cache --setup <file> [--wallet <kind>]
|
|
14
|
+
walletwright cache --wallet <${walletKinds.join("|")}> --seed "<phrase>" --password "<pw>" [--version <v>]
|
|
16
15
|
|
|
17
16
|
Options:
|
|
18
|
-
--setup <file>
|
|
19
|
-
--wallet <kind> ${
|
|
17
|
+
--setup <file> Build every WalletSetup the module exports, named or default (.ts works on modern Node)
|
|
18
|
+
--wallet <kind> ${walletKinds.join(" | ")}; with --setup, builds only that wallet's setups
|
|
20
19
|
--seed <phrase> seed phrase to import
|
|
21
20
|
--password <pw> wallet password
|
|
22
|
-
--version <v> pin an extension version
|
|
21
|
+
--version <v> pin an extension version (MetaMask)
|
|
23
22
|
--cache-dir <dir> cache directory (default: .walletwright)
|
|
24
23
|
--headless build the cache headless
|
|
25
24
|
-h, --help show this help
|
|
26
25
|
|
|
27
|
-
--setup carries the
|
|
28
|
-
--cache-dir
|
|
26
|
+
--setup carries the credentials, so it cannot be combined with --seed/--password/--version;
|
|
27
|
+
--wallet and --cache-dir work with either form. A value that begins with "-" needs the --flag=value
|
|
28
|
+
spelling. Setups from one file build one after another.
|
|
29
29
|
|
|
30
30
|
--seed and --password are visible in shell history and process lists when passed as flags; use
|
|
31
31
|
test-only values, or prefer --setup <file> to keep them out of argv.
|
|
@@ -44,12 +44,11 @@ const OPTIONS = {
|
|
|
44
44
|
version: { type: "string" },
|
|
45
45
|
wallet: { type: "string" }
|
|
46
46
|
};
|
|
47
|
-
/** The flags `--setup` makes redundant: accepting both silently discarded one of the two. */
|
|
47
|
+
/** The flags a `--setup` file makes redundant: accepting both silently discarded one of the two. */
|
|
48
48
|
const SETUP_CONFLICTS = [
|
|
49
49
|
"password",
|
|
50
50
|
"seed",
|
|
51
|
-
"version"
|
|
52
|
-
"wallet"
|
|
51
|
+
"version"
|
|
53
52
|
];
|
|
54
53
|
const nonEmptyStringSchema = z.string().min(1);
|
|
55
54
|
const walletSetupSchema = z.object({
|
|
@@ -57,44 +56,55 @@ const walletSetupSchema = z.object({
|
|
|
57
56
|
password: nonEmptyStringSchema,
|
|
58
57
|
seedPhrase: nonEmptyStringSchema,
|
|
59
58
|
version: nonEmptyStringSchema.optional(),
|
|
60
|
-
wallet: z.enum(
|
|
61
|
-
"metamask",
|
|
62
|
-
"phantom",
|
|
63
|
-
"rabby",
|
|
64
|
-
"slush",
|
|
65
|
-
"solflare"
|
|
66
|
-
])
|
|
59
|
+
wallet: z.enum(walletKinds)
|
|
67
60
|
});
|
|
68
|
-
const
|
|
61
|
+
const moduleSchema = z.record(z.string(), z.unknown());
|
|
69
62
|
const isNonEmptyString = (value) => value !== void 0 && nonEmptyStringSchema.safeParse(value).success;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
/** Every `WalletSetup` a module exports, named or default, once each and in export order. */
|
|
64
|
+
const loadSetups = async (file) => {
|
|
65
|
+
const exported = moduleSchema.parse(await import(pathToFileURL(path.resolve(file)).href));
|
|
66
|
+
const seen = /* @__PURE__ */ new Set();
|
|
67
|
+
const setups = [];
|
|
68
|
+
for (const [name, value] of Object.entries(exported)) {
|
|
69
|
+
if (seen.has(value) || typeof value !== "object" || value === null || !("wallet" in value)) continue;
|
|
70
|
+
seen.add(value);
|
|
71
|
+
const result = walletSetupSchema.safeParse(value);
|
|
72
|
+
if (!result.success) throw new Error(`[walletwright] ${file}: export "${name}" is not a valid WalletSetup\n${z.prettifyError(result.error)}`);
|
|
73
|
+
setups.push(result.data);
|
|
74
|
+
}
|
|
75
|
+
if (setups.length === 0) throw new Error(`[walletwright] ${file} exports no WalletSetup`);
|
|
76
|
+
return setups;
|
|
77
|
+
};
|
|
78
|
+
/** `--wallet`, narrowed to a supported kind; absent or empty means unset. */
|
|
79
|
+
const walletFlag = (value) => {
|
|
80
|
+
if (!isNonEmptyString(value)) return;
|
|
81
|
+
if (!isWalletKind(value)) throw new Error(`[walletwright] unknown --wallet "${value}". Expected one of: ${walletKinds.join(", ")}.`);
|
|
82
|
+
return value;
|
|
75
83
|
};
|
|
76
|
-
const
|
|
84
|
+
const resolveSetups = async (flags) => {
|
|
77
85
|
const withCacheDir = (setup) => isNonEmptyString(flags["cache-dir"]) ? {
|
|
78
86
|
...setup,
|
|
79
87
|
cacheDir: flags["cache-dir"]
|
|
80
88
|
} : setup;
|
|
89
|
+
const wallet = walletFlag(flags.wallet);
|
|
81
90
|
if (isNonEmptyString(flags.setup)) {
|
|
82
91
|
const redundant = SETUP_CONFLICTS.filter((name) => flags[name] !== void 0);
|
|
83
|
-
if (redundant.length > 0) throw new Error(`[walletwright] --setup carries the
|
|
84
|
-
|
|
92
|
+
if (redundant.length > 0) throw new Error(`[walletwright] --setup carries the credentials, so --${redundant.join(", --")} would be ignored. Pass one or the other.`);
|
|
93
|
+
const selected = (await loadSetups(flags.setup)).filter((setup) => wallet === void 0 || setup.wallet === wallet);
|
|
94
|
+
if (selected.length === 0) throw new Error(`[walletwright] ${flags.setup} exports no ${wallet} WalletSetup`);
|
|
95
|
+
return selected.map(withCacheDir);
|
|
85
96
|
}
|
|
86
|
-
const { password, seed
|
|
87
|
-
if (
|
|
88
|
-
if (!isWalletKind(wallet)) throw new Error(`[walletwright] unknown --wallet "${wallet}". Expected one of: ${KINDS.join(", ")}.`);
|
|
97
|
+
const { password, seed } = flags;
|
|
98
|
+
if (wallet === void 0 || !isNonEmptyString(seed) || !isNonEmptyString(password)) throw new Error("[walletwright] provide --setup <file> or --wallet/--seed/--password. See --help.");
|
|
89
99
|
const base = {
|
|
90
100
|
password,
|
|
91
101
|
seedPhrase: seed,
|
|
92
102
|
wallet
|
|
93
103
|
};
|
|
94
|
-
return withCacheDir(isNonEmptyString(flags.version) ? {
|
|
104
|
+
return [withCacheDir(isNonEmptyString(flags.version) ? {
|
|
95
105
|
...base,
|
|
96
106
|
version: flags.version
|
|
97
|
-
} : base);
|
|
107
|
+
} : base)];
|
|
98
108
|
};
|
|
99
109
|
const parseArgv = async (argv) => {
|
|
100
110
|
let parsed;
|
|
@@ -109,14 +119,15 @@ const parseArgv = async (argv) => {
|
|
|
109
119
|
throw new Error(`[walletwright] ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
110
120
|
}
|
|
111
121
|
const flags = parsed.values;
|
|
112
|
-
const
|
|
122
|
+
const command = parsed.positionals.at(0);
|
|
123
|
+
const extra = parsed.positionals.slice(1);
|
|
113
124
|
if (flags.help === true || command === void 0 || command === "help") return { kind: "help" };
|
|
114
125
|
if (command !== "cache") throw new Error(`[walletwright] unknown command "${command}". Run \`walletwright --help\`.`);
|
|
115
126
|
if (extra.length > 0) throw new Error(`[walletwright] unexpected argument "${extra[0]}". Run \`walletwright --help\`.`);
|
|
116
127
|
return {
|
|
117
128
|
headless: flags.headless === true,
|
|
118
129
|
kind: "cache",
|
|
119
|
-
|
|
130
|
+
setups: await resolveSetups(flags)
|
|
120
131
|
};
|
|
121
132
|
};
|
|
122
133
|
const main = async () => {
|
|
@@ -125,9 +136,11 @@ const main = async () => {
|
|
|
125
136
|
process.stdout.write(HELP);
|
|
126
137
|
return;
|
|
127
138
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
for (const setup of command.setups) {
|
|
140
|
+
process.stdout.write(`[walletwright] building ${setup.wallet} cache…\n`);
|
|
141
|
+
const profileDir = await buildCache(setup, { headless: command.headless });
|
|
142
|
+
process.stdout.write(`[walletwright] cache ready: ${profileDir}\n`);
|
|
143
|
+
}
|
|
131
144
|
};
|
|
132
145
|
/** Resolve the CLI symlink before comparing `process.argv[1]` with `import.meta.url`. */
|
|
133
146
|
const isEntryPoint = async (moduleUrl, entryPath) => entryPath !== void 0 && moduleUrl === pathToFileURL(await realpath(entryPath)).href;
|
|
@@ -139,5 +152,5 @@ if (await isEntryPoint(import.meta.url, process.argv[1])) try {
|
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
//#endregion
|
|
142
|
-
export { isEntryPoint, parseArgv,
|
|
155
|
+
export { isEntryPoint, parseArgv, resolveSetups };
|
|
143
156
|
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { parseArgs } from \"node:util\";\n\nimport { z } from \"zod\";\n\nimport { buildCache } from \"./internal/cache\";\nimport type { WalletSetup } from \"./types\";\nimport { isWalletKind, wallets } from \"./wallets/index\";\n\nconst KINDS = Object.keys(wallets);\n\nconst HELP = `walletwright: build the onboarded wallet cache for Playwright tests\n\nUsage:\n walletwright cache --setup <file> Build cache from a module's default-exported WalletSetup\n walletwright cache --wallet <${KINDS.join(\"|\")}> --seed \"<phrase>\" --password \"<pw>\" [--version <v>]\n\nOptions:\n --setup <file> A module whose default export is a WalletSetup (.ts works on modern Node)\n --wallet <kind> ${KINDS.join(\" | \")}\n --seed <phrase> seed phrase to import\n --password <pw> wallet password\n --version <v> pin an extension version\n --cache-dir <dir> cache directory (default: .walletwright)\n --headless build the cache headless\n -h, --help show this help\n\n--setup carries the whole setup, so it cannot be combined with --wallet/--seed/--password/--version;\n--cache-dir works with either form. A value that begins with \"-\" needs the --flag=value spelling.\n\n--seed and --password are visible in shell history and process lists when passed as flags; use\ntest-only values, or prefer --setup <file> to keep them out of argv.\n`;\n\n/** `parseArgs` schema; strict mode rejects unknown, missing, or misplaced flag values. */\nconst OPTIONS = {\n \"cache-dir\": { type: \"string\" },\n headless: { type: \"boolean\" },\n help: { short: \"h\", type: \"boolean\" },\n password: { type: \"string\" },\n seed: { type: \"string\" },\n setup: { type: \"string\" },\n version: { type: \"string\" },\n wallet: { type: \"string\" },\n} as const;\n\ntype Flags = {\n \"cache-dir\"?: string;\n headless?: boolean;\n help?: boolean;\n password?: string;\n seed?: string;\n setup?: string;\n version?: string;\n wallet?: string;\n};\n\n/** The flags `--setup` makes redundant: accepting both silently discarded one of the two. */\nconst SETUP_CONFLICTS = [\"password\", \"seed\", \"version\", \"wallet\"] as const;\n\ntype Command = { kind: \"help\" } | { headless: boolean; kind: \"cache\"; setup: WalletSetup };\n\nconst nonEmptyStringSchema = z.string().min(1);\nconst walletSetupSchema = z.object({\n cacheDir: nonEmptyStringSchema.optional(),\n password: nonEmptyStringSchema,\n seedPhrase: nonEmptyStringSchema,\n version: nonEmptyStringSchema.optional(),\n wallet: z.enum([\"metamask\", \"phantom\", \"rabby\", \"slush\", \"solflare\"]),\n});\nconst setupModuleSchema = z.object({ default: walletSetupSchema });\n\nconst isNonEmptyString = (value: string | undefined): value is string =>\n value !== undefined && nonEmptyStringSchema.safeParse(value).success;\n\nconst loadSetup = async (file: string): Promise<WalletSetup> => {\n const resolved = pathToFileURL(path.resolve(file)).href;\n const result = setupModuleSchema.safeParse(await import(resolved));\n if (!result.success) {\n throw new Error(`[walletwright] ${file} must default-export a WalletSetup`);\n }\n return result.data.default;\n};\n\nconst resolveSetup = async (flags: Flags): Promise<WalletSetup> => {\n const withCacheDir = (setup: WalletSetup): WalletSetup =>\n isNonEmptyString(flags[\"cache-dir\"]) ? { ...setup, cacheDir: flags[\"cache-dir\"] } : setup;\n\n if (isNonEmptyString(flags.setup)) {\n const redundant = SETUP_CONFLICTS.filter((name) => flags[name] !== undefined);\n if (redundant.length > 0) {\n throw new Error(\n `[walletwright] --setup carries the whole setup, so --${redundant.join(\", --\")} would be ignored. Pass one or the other.`,\n );\n }\n return withCacheDir(await loadSetup(flags.setup));\n }\n\n const { password, seed, wallet } = flags;\n if (!isNonEmptyString(wallet) || !isNonEmptyString(seed) || !isNonEmptyString(password)) {\n throw new Error(\n \"[walletwright] provide --setup <file> or --wallet/--seed/--password. See --help.\",\n );\n }\n if (!isWalletKind(wallet)) {\n throw new Error(\n `[walletwright] unknown --wallet \"${wallet}\". Expected one of: ${KINDS.join(\", \")}.`,\n );\n }\n const base: WalletSetup = { password, seedPhrase: seed, wallet };\n return withCacheDir(isNonEmptyString(flags.version) ? { ...base, version: flags.version } : base);\n};\n\nconst parseArgv = async (argv: Array<string>): Promise<Command> => {\n let parsed;\n try {\n parsed = parseArgs({ allowPositionals: true, args: argv, options: OPTIONS, strict: true });\n } catch (error) {\n // node's ERR_PARSE_ARGS_* messages are good; they just need the prefix everything else here has.\n throw new Error(`[walletwright] ${error instanceof Error ? error.message : String(error)}`, {\n cause: error,\n });\n }\n\n const flags: Flags = parsed.values;\n const [command, ...extra] = parsed.positionals;\n\n if (flags.help === true || command === undefined || command === \"help\") {\n return { kind: \"help\" };\n }\n if (command !== \"cache\") {\n throw new Error(`[walletwright] unknown command \"${command}\". Run \\`walletwright --help\\`.`);\n }\n if (extra.length > 0) {\n throw new Error(\n `[walletwright] unexpected argument \"${extra[0]}\". Run \\`walletwright --help\\`.`,\n );\n }\n\n return { headless: flags.headless === true, kind: \"cache\", setup: await resolveSetup(flags) };\n};\n\nconst main = async (): Promise<void> => {\n const command = await parseArgv(process.argv.slice(2));\n if (command.kind === \"help\") {\n process.stdout.write(HELP);\n return;\n }\n\n process.stdout.write(`[walletwright] building ${command.setup.wallet} cache…\\n`);\n const profileDir = await buildCache(command.setup, { headless: command.headless });\n process.stdout.write(`[walletwright] cache ready: ${profileDir}\\n`);\n};\n\n/** Resolve the CLI symlink before comparing `process.argv[1]` with `import.meta.url`. */\nconst isEntryPoint = async (moduleUrl: string, entryPath: string | undefined): Promise<boolean> =>\n entryPath !== undefined && moduleUrl === pathToFileURL(await realpath(entryPath)).href;\n\nif (await isEntryPoint(import.meta.url, process.argv[1])) {\n try {\n await main();\n } catch (error) {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n // Not process.exit(1): writes to a piped stderr are async and exit() does not flush them, so\n // `walletwright cache … 2>&1 | tee build.log` could exit 1 with an empty diagnostic. Nothing runs\n // after this block, so the process ends on its own once the write drains.\n process.exitCode = 1;\n }\n}\n\nexport { isEntryPoint, parseArgv, resolveSetup };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { parseArgs } from \"node:util\";\n\nimport { z } from \"zod\";\n\nimport { buildCache } from \"./internal/cache\";\nimport type { WalletKind, WalletSetup } from \"./types\";\nimport { isWalletKind, walletKinds } from \"./wallets/index\";\n\nconst HELP = `walletwright: build the onboarded wallet cache for Playwright tests\n\nUsage:\n walletwright cache --setup <file> [--wallet <kind>]\n walletwright cache --wallet <${walletKinds.join(\"|\")}> --seed \"<phrase>\" --password \"<pw>\" [--version <v>]\n\nOptions:\n --setup <file> Build every WalletSetup the module exports, named or default (.ts works on modern Node)\n --wallet <kind> ${walletKinds.join(\" | \")}; with --setup, builds only that wallet's setups\n --seed <phrase> seed phrase to import\n --password <pw> wallet password\n --version <v> pin an extension version (MetaMask)\n --cache-dir <dir> cache directory (default: .walletwright)\n --headless build the cache headless\n -h, --help show this help\n\n--setup carries the credentials, so it cannot be combined with --seed/--password/--version;\n--wallet and --cache-dir work with either form. A value that begins with \"-\" needs the --flag=value\nspelling. Setups from one file build one after another.\n\n--seed and --password are visible in shell history and process lists when passed as flags; use\ntest-only values, or prefer --setup <file> to keep them out of argv.\n`;\n\n/** `parseArgs` schema; strict mode rejects unknown, missing, or misplaced flag values. */\nconst OPTIONS = {\n \"cache-dir\": { type: \"string\" },\n headless: { type: \"boolean\" },\n help: { short: \"h\", type: \"boolean\" },\n password: { type: \"string\" },\n seed: { type: \"string\" },\n setup: { type: \"string\" },\n version: { type: \"string\" },\n wallet: { type: \"string\" },\n} as const;\n\ntype Flags = {\n \"cache-dir\"?: string;\n headless?: boolean;\n help?: boolean;\n password?: string;\n seed?: string;\n setup?: string;\n version?: string;\n wallet?: string;\n};\n\n/** The flags a `--setup` file makes redundant: accepting both silently discarded one of the two. */\nconst SETUP_CONFLICTS = [\"password\", \"seed\", \"version\"] as const;\n\ntype Command =\n | { kind: \"help\" }\n | { headless: boolean; kind: \"cache\"; setups: ReadonlyArray<WalletSetup> };\n\nconst nonEmptyStringSchema = z.string().min(1);\nconst walletSetupSchema = z.object({\n cacheDir: nonEmptyStringSchema.optional(),\n password: nonEmptyStringSchema,\n seedPhrase: nonEmptyStringSchema,\n version: nonEmptyStringSchema.optional(),\n wallet: z.enum(walletKinds),\n});\nconst moduleSchema = z.record(z.string(), z.unknown());\n\nconst isNonEmptyString = (value: string | undefined): value is string =>\n value !== undefined && nonEmptyStringSchema.safeParse(value).success;\n\n/** Every `WalletSetup` a module exports, named or default, once each and in export order. */\nconst loadSetups = async (file: string): Promise<Array<WalletSetup>> => {\n const exported = moduleSchema.parse(await import(pathToFileURL(path.resolve(file)).href));\n const seen = new Set<unknown>();\n const setups: Array<WalletSetup> = [];\n for (const [name, value] of Object.entries(exported)) {\n // The field's presence marks a candidate; the full schema validates its value below.\n if (seen.has(value) || typeof value !== \"object\" || value === null || !(\"wallet\" in value)) {\n continue;\n }\n seen.add(value);\n const result = walletSetupSchema.safeParse(value);\n if (!result.success) {\n throw new Error(\n `[walletwright] ${file}: export \"${name}\" is not a valid WalletSetup\\n${z.prettifyError(result.error)}`,\n );\n }\n setups.push(result.data);\n }\n if (setups.length === 0) {\n throw new Error(`[walletwright] ${file} exports no WalletSetup`);\n }\n return setups;\n};\n\n/** `--wallet`, narrowed to a supported kind; absent or empty means unset. */\nconst walletFlag = (value: string | undefined): WalletKind | undefined => {\n if (!isNonEmptyString(value)) {\n return undefined;\n }\n if (!isWalletKind(value)) {\n throw new Error(\n `[walletwright] unknown --wallet \"${value}\". Expected one of: ${walletKinds.join(\", \")}.`,\n );\n }\n return value;\n};\n\nconst resolveSetups = async (flags: Flags): Promise<Array<WalletSetup>> => {\n const withCacheDir = (setup: WalletSetup): WalletSetup =>\n isNonEmptyString(flags[\"cache-dir\"]) ? { ...setup, cacheDir: flags[\"cache-dir\"] } : setup;\n const wallet = walletFlag(flags.wallet);\n\n if (isNonEmptyString(flags.setup)) {\n const redundant = SETUP_CONFLICTS.filter((name) => flags[name] !== undefined);\n if (redundant.length > 0) {\n throw new Error(\n `[walletwright] --setup carries the credentials, so --${redundant.join(\", --\")} would be ignored. Pass one or the other.`,\n );\n }\n const setups = await loadSetups(flags.setup);\n const selected = setups.filter((setup) => wallet === undefined || setup.wallet === wallet);\n if (selected.length === 0) {\n throw new Error(`[walletwright] ${flags.setup} exports no ${wallet} WalletSetup`);\n }\n return selected.map(withCacheDir);\n }\n\n const { password, seed } = flags;\n if (wallet === undefined || !isNonEmptyString(seed) || !isNonEmptyString(password)) {\n throw new Error(\n \"[walletwright] provide --setup <file> or --wallet/--seed/--password. See --help.\",\n );\n }\n const base: WalletSetup = { password, seedPhrase: seed, wallet };\n return [\n withCacheDir(isNonEmptyString(flags.version) ? { ...base, version: flags.version } : base),\n ];\n};\n\nconst parseArgv = async (argv: Array<string>): Promise<Command> => {\n let parsed;\n try {\n parsed = parseArgs({ allowPositionals: true, args: argv, options: OPTIONS, strict: true });\n } catch (error) {\n // node's ERR_PARSE_ARGS_* messages are good; they just need the prefix everything else here has.\n throw new Error(`[walletwright] ${error instanceof Error ? error.message : String(error)}`, {\n cause: error,\n });\n }\n\n const flags: Flags = parsed.values;\n const command = parsed.positionals.at(0);\n const extra = parsed.positionals.slice(1);\n\n if (flags.help === true || command === undefined || command === \"help\") {\n return { kind: \"help\" };\n }\n if (command !== \"cache\") {\n throw new Error(`[walletwright] unknown command \"${command}\". Run \\`walletwright --help\\`.`);\n }\n if (extra.length > 0) {\n throw new Error(\n `[walletwright] unexpected argument \"${extra[0]}\". Run \\`walletwright --help\\`.`,\n );\n }\n\n return { headless: flags.headless === true, kind: \"cache\", setups: await resolveSetups(flags) };\n};\n\nconst main = async (): Promise<void> => {\n const command = await parseArgv(process.argv.slice(2));\n if (command.kind === \"help\") {\n process.stdout.write(HELP);\n return;\n }\n\n for (const setup of command.setups) {\n process.stdout.write(`[walletwright] building ${setup.wallet} cache…\\n`);\n const profileDir = await buildCache(setup, { headless: command.headless });\n process.stdout.write(`[walletwright] cache ready: ${profileDir}\\n`);\n }\n};\n\n/** Resolve the CLI symlink before comparing `process.argv[1]` with `import.meta.url`. */\nconst isEntryPoint = async (moduleUrl: string, entryPath: string | undefined): Promise<boolean> =>\n entryPath !== undefined && moduleUrl === pathToFileURL(await realpath(entryPath)).href;\n\nif (await isEntryPoint(import.meta.url, process.argv[1])) {\n try {\n await main();\n } catch (error) {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n // Not process.exit(1): writes to a piped stderr are async and exit() does not flush them, so\n // `walletwright cache … 2>&1 | tee build.log` could exit 1 with an empty diagnostic. Nothing runs\n // after this block, so the process ends on its own once the write drains.\n process.exitCode = 1;\n }\n}\n\nexport { isEntryPoint, parseArgv, resolveSetups };\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { BrowserContext, chromium } from "@playwright/test";
|
|
1
|
+
import { a as WalletKind, i as WalletDefinition, n as NetworkConfig, o as WalletSetup, r as Wallet, t as Ecosystem } from "./types-CjNm7Tik.mjs";
|
|
2
|
+
import { BrowserContext, Page, chromium } from "@playwright/test";
|
|
3
3
|
//#region src/internal/cache.d.ts
|
|
4
4
|
declare const buildCache: (setup: WalletSetup, options?: {
|
|
5
5
|
headless?: boolean;
|
|
@@ -7,6 +7,8 @@ declare const buildCache: (setup: WalletSetup, options?: {
|
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/internal/launch.d.ts
|
|
9
9
|
type LaunchedWallet = {
|
|
10
|
+
/** `close()`, so `await using launched = await launchWallet(setup)` cleans up on any exit. */
|
|
11
|
+
[Symbol.asyncDispose]: () => Promise<void>;
|
|
10
12
|
/**
|
|
11
13
|
* Close the browser and remove the throwaway profile copy, in that order. Prefer this over
|
|
12
14
|
* `context.close()`: the copy is a full onboarded profile (tens of MB), and awaiting its removal is
|
|
@@ -17,7 +19,7 @@ type LaunchedWallet = {
|
|
|
17
19
|
wallet: Wallet;
|
|
18
20
|
};
|
|
19
21
|
/** Launch and unlock a disposable profile copy. Headless mode requires verified wallet support. */
|
|
20
|
-
declare const launchWallet: (setup: WalletSetup, { headless }?: {
|
|
22
|
+
export declare const launchWallet: (setup: WalletSetup, { headless }?: {
|
|
21
23
|
headless?: boolean;
|
|
22
24
|
}) => Promise<LaunchedWallet>;
|
|
23
25
|
//#endregion
|
|
@@ -29,11 +31,15 @@ type WalletFixtures = {
|
|
|
29
31
|
type LaunchFixture = {
|
|
30
32
|
walletLaunch: LaunchedWallet;
|
|
31
33
|
};
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Create Playwright fixtures backed by one unlocked wallet launch per test. `headless` applies only
|
|
36
|
+
* to wallets that can be driven that way (Phantom, Rabby); the rest launch headed, so a suite runs
|
|
37
|
+
* under Playwright's defaults. `launchWallet` is the strict form that refuses them headless instead.
|
|
38
|
+
*/
|
|
39
|
+
export declare const createWalletFixtures: (setup: WalletSetup) => import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & LaunchFixture & WalletFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
34
40
|
//#endregion
|
|
35
41
|
//#region src/wallets/index.d.ts
|
|
36
|
-
declare const wallets: {
|
|
42
|
+
export declare const wallets: {
|
|
37
43
|
metamask: WalletDefinition;
|
|
38
44
|
phantom: WalletDefinition;
|
|
39
45
|
rabby: WalletDefinition;
|
|
@@ -41,9 +47,11 @@ declare const wallets: {
|
|
|
41
47
|
solflare: WalletDefinition;
|
|
42
48
|
};
|
|
43
49
|
/** Narrow an arbitrary string (CLI flag, setup file field) to a supported wallet. */
|
|
44
|
-
declare const isWalletKind: (value: string) => value is WalletKind;
|
|
50
|
+
export declare const isWalletKind: (value: string) => value is WalletKind;
|
|
51
|
+
/** Every supported wallet, in registry order. */
|
|
52
|
+
export declare const walletKinds: ReadonlyArray<WalletKind>;
|
|
45
53
|
/** Wallet kinds that can drive the given ecosystem (e.g. `"evm"` → MetaMask, Phantom). */
|
|
46
|
-
declare const walletKindsByEcosystem: (ecosystem: Ecosystem) => Array<WalletKind>;
|
|
54
|
+
export declare const walletKindsByEcosystem: (ecosystem: Ecosystem) => Array<WalletKind>;
|
|
47
55
|
//#endregion
|
|
48
|
-
export { type Ecosystem, type LaunchedWallet, type Wallet, type WalletDefinition, type WalletFixtures, type WalletKind, type WalletSetup, buildCache
|
|
56
|
+
export { type Ecosystem, type LaunchedWallet, type NetworkConfig, type Wallet, type WalletDefinition, type WalletFixtures, type WalletKind, type WalletSetup, buildCache };
|
|
49
57
|
//# sourceMappingURL=index.d.mts.map
|