@walletwright/core 0.5.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
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @walletwright/core
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5b687ad: Declare `"sideEffects": false` so bundlers can drop unused entry points. None of the exports runs code at import time.
|
|
8
|
+
- 22d280c: Retry opening Slush's passphrase import screen until its word inputs appear, and fail with a clear error after three attempts. On a slow runner the first "Import existing from passphrase" click could land before the menu was interactive, which left the import waiting on inputs that never rendered.
|
|
9
|
+
|
|
3
10
|
## 0.5.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
|
@@ -583,6 +583,8 @@ const rabby = {
|
|
|
583
583
|
const HOME_ROUTE = "#/tokens";
|
|
584
584
|
const IMPORT_HOME_TIMEOUT_MS = 3e4;
|
|
585
585
|
const PERSIST_TIMEOUT_MS = 6e4;
|
|
586
|
+
const PASSPHRASE_SCREEN_TIMEOUT_MS = 2e4;
|
|
587
|
+
const PASSPHRASE_SCREEN_ATTEMPTS = 3;
|
|
586
588
|
const ACCOUNTS_DATABASE = "signaldb-accounts";
|
|
587
589
|
const PREFERENCES_DATABASE = "signaldb-preferences";
|
|
588
590
|
const ITEMS_STORE = "items";
|
|
@@ -642,6 +644,22 @@ const fclick = async (page, text, timeoutMs = 15e3) => {
|
|
|
642
644
|
});
|
|
643
645
|
return true;
|
|
644
646
|
};
|
|
647
|
+
/** The first click can land before the menu is interactive on a slow runner, so retry until the words show. */
|
|
648
|
+
const openPassphraseImport = async (page) => {
|
|
649
|
+
const firstWord = page.getByLabel("Word 1", { exact: true });
|
|
650
|
+
for (let attempt = 0; attempt < PASSPHRASE_SCREEN_ATTEMPTS; attempt++) {
|
|
651
|
+
await fclick(page, "More options");
|
|
652
|
+
await sleep(1e3);
|
|
653
|
+
await fclick(page, "Import existing from passphrase");
|
|
654
|
+
if (await firstWord.waitFor({
|
|
655
|
+
state: "visible",
|
|
656
|
+
timeout: PASSPHRASE_SCREEN_TIMEOUT_MS
|
|
657
|
+
}).then(() => true).catch(() => false)) return;
|
|
658
|
+
await page.goto(page.url().split("#")[0] ?? page.url());
|
|
659
|
+
await sleep(2e3);
|
|
660
|
+
}
|
|
661
|
+
throw new Error(`[walletwright] Slush never opened the passphrase import screen after ${PASSPHRASE_SCREEN_ATTEMPTS} attempts`);
|
|
662
|
+
};
|
|
645
663
|
const slush = {
|
|
646
664
|
approve: async (popup, password) => {
|
|
647
665
|
await sleep(2e3);
|
|
@@ -656,10 +674,7 @@ const slush = {
|
|
|
656
674
|
ecosystems: ["sui"],
|
|
657
675
|
extensionName: "Slush",
|
|
658
676
|
importWallet: async (page, seedPhrase, password) => {
|
|
659
|
-
await
|
|
660
|
-
await sleep(1e3);
|
|
661
|
-
await fclick(page, "Import existing from passphrase");
|
|
662
|
-
await sleep(1500);
|
|
677
|
+
await openPassphraseImport(page);
|
|
663
678
|
const words = seedPhrase.trim().split(new RegExp("\\s+", "v"));
|
|
664
679
|
for (let i = 0; i < words.length; i++) await page.getByLabel(`Word ${i + 1}`, { exact: true }).fill(words[i] ?? "");
|
|
665
680
|
await page.getByRole("button", { name: "Next" }).click();
|
|
@@ -955,4 +970,4 @@ const buildCache = (setup, options) => buildCacheWithDependencies(setup, options
|
|
|
955
970
|
|
|
956
971
|
//#endregion
|
|
957
972
|
export { walletKinds as a, locateProfile as c, gotoWithRetry as d, waitUntil as f, isWalletKind as i, pathExists as l, openWalletContext as n, walletKindsByEcosystem as o, prepareExtension as r, wallets as s, buildCache as t, formatTimeout as u };
|
|
958
|
-
//# sourceMappingURL=cache-
|
|
973
|
+
//# sourceMappingURL=cache-gBvk86TW.mjs.map
|
|
@@ -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/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as walletKinds, i as isWalletKind, t as buildCache } from "./cache-
|
|
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";
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as walletKinds, c as locateProfile, d as gotoWithRetry, f as waitUntil, i as isWalletKind, l as pathExists, n as openWalletContext, o as walletKindsByEcosystem, r as prepareExtension, s as wallets, t as buildCache, u as formatTimeout } from "./cache-
|
|
1
|
+
import { a as walletKinds, c as locateProfile, d as gotoWithRetry, f as waitUntil, i as isWalletKind, l as pathExists, n as openWalletContext, o as walletKindsByEcosystem, r as prepareExtension, s as wallets, t as buildCache, u as formatTimeout } from "./cache-gBvk86TW.mjs";
|
|
2
2
|
import { cp, mkdtemp, rm } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { test } from "@playwright/test";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walletwright/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Playwright wallet automation for MetaMask, Phantom, Rabby, Solflare, and Slush across EVM, Solana, and Sui. Connect and sign in real browser extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"e2e",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"CHANGELOG.md"
|
|
33
33
|
],
|
|
34
34
|
"type": "module",
|
|
35
|
+
"sideEffects": false,
|
|
35
36
|
"types": "dist/index.d.mts",
|
|
36
37
|
"exports": {
|
|
37
38
|
".": {
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@playwright/test": "1.63.0",
|
|
64
|
-
"@types/adm-zip": "^0.5.
|
|
65
|
+
"@types/adm-zip": "^0.5.8",
|
|
65
66
|
"@types/node": "^26.6.2",
|
|
66
67
|
"prool": "^0.2.16",
|
|
67
68
|
"typescript": "^7.0.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cache-ztGj4R8m.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;\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\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 fclick(page, \"More options\");\n await sleep(1000);\n await fclick(page, \"Import existing from passphrase\");\n await sleep(1500);\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":";;;;;;;;;AAIA,MAAM,kBAAkB,OAAO,SAA8B;CAC3D,MAAM,KAAK,YAAY,mBAAmB,CAAC,CAAC,MAAM;CAClD,MAAM,KAAK,YAAY,+BAA+B,CAAC,CAAC,QAAQ,EAAE,OAAO,UAAU,CAAC;AACtF;;AAGA,MAAM,mBAAmB,OAAO,SAA8B;CAC5D,MAAM,KAAK,KAAK,GAAG,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG;CAC/C,MAAM,KAAK,YAAY,mBAAmB,CAAC,CAAC,QAAQ,EAAE,OAAO,UAAU,CAAC;AAC1E;AAEA,MAAMA,QAAM,OAAO,EAAE,WAA+C;CAClE,MAAM,gBAAgB,IAAI;CAC1B,MAAM,QAAQ,KAAK,YAAY,qBAAqB;CACpD,MAAM,SAAS,MAAM,MAAM,MAAM;CACjC,MAAM,KAAK,YAAY,+BAA+B,CAAC,CAAC,MAAM;CAC9D,MAAM,MAAM,IAAI,MAAM,CAAC,CAAC,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAO,CAAC;CACrE,MAAM,iBAAiB,IAAI;AAC7B;AAEA,MAAM,mBAAmB,OACvB,EAAE,QACF,eACkB;CAClB,MAAM,gBAAgB,IAAI;CAC1B,MAAM,KAAK,YAAY,gCAAgC,CAAC,CAAC,MAAM;CAC/D,MAAM,eAAe,KAAK,YAAY,mCAAmC;CACzE,MAAM,aAAa,QAAQ,EAAE,OAAO,UAAU,CAAC;CAC/C,MAAM,aAAa,MAAM;CACzB,MAAM,UAAU,KAAK,YAAY,+BAA+B;CAChE,MAAM,QAAQ,QAAQ,EAAE,OAAO,UAAU,CAAC;CAC1C,MAAM,KAAK,QAAQ,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,UAAU;CACnD,MAAM,QAAQ,MAAM;CACpB,MAAM,QAAQ,QAAQ;EAAE,OAAO;EAAU,SAAS;CAAO,CAAC;CAC1D,MAAM,iBAAiB,IAAI;AAC7B;AAEA,MAAMC,WAAS,OACb,EAAE,QACF,YACkB;CAClB,MAAM,gBAAgB,IAAI;CAC1B,MAAM,KAAK,YAAY,uCAAuC,CAAC,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,MAAM;CACzF,MAAM,KAAK,YAAY,qCAAqC,CAAC,CAAC,MAAM;CACpE,MAAM,KAAK,YAAY,oBAAoB,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,KAAK,QAAQ,IAAI;CAC/E,MAAM,KAAK,UAAU,UAAU,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,MAAM;CAC1D,MAAM,iBAAiB,IAAI;AAC7B;AAEA,MAAM,WAAW,OAAO,EAAE,QAA6B,UAAiC;CACtF,MAAM,gBAAgB,IAAI;CAC1B,MAAM,KAAK,YAAY,qBAAqB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM;CAM/D,IAAI,CAAC,MALoB,KACtB,YAAY,+BAA+B,CAAC,CAC5C,QAAQ;EAAE,OAAO;EAAU,SAAS;CAAO,CAAC,CAAC,CAC7C,WAAW,IAAI,CAAC,CAChB,YAAY,KAAK,GAElB,MAAM,IAAI,MAAM,kCAAkC,MAAM,oCAAoC;AAEhG;AAEA,MAAa,WAA2B;CACtC;CACA;CACA;CACA,QAAQ;AACV;;;;ACnEA,MAAM,eAAe,SACnB,KAAK,YAAY,2BAA2B,CAAC,CAAC,OAAO,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,MAAM;AAEhF,MAAM,qBAAqB,OAAO,SAA8B;CAC9D,MAAM,KAAK,YAAY,kBAAkB,CAAC,CAAC,MAAM;CACjD,MAAM,YAAY,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,UAAU,CAAC;AACtD;AAEA,MAAM,MAAM,OAAO,EAAE,QAA6B,WAAyC;CACzF,MAAM,mBAAmB,IAAI;CAE7B,MAAM,KAAK,UAAU,UAAU,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;CAC9D,MAAM,KAAK,UAAU,UAAU,EAAE,MAAM,qBAAqB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;CAE7E,MAAM,KAAK,YAAY,2BAA2B,CAAC,CAAC,KAAK,OAAO,IAAI;CACpE,MAAM,KAAK,YAAY,uBAAuB,CAAC,CAAC,KAAK,OAAO,OAAO,OAAO,CAAC;CAC3E,MAAM,KAAK,YAAY,2BAA2B,CAAC,CAAC,KAAK,OAAO,MAAM;CAEtE,MAAM,KAAK,YAAY,wBAAwB,CAAC,CAAC,MAAM;CACvD,MAAM,KAAK,UAAU,eAAe,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;CACnE,MAAM,KAAK,YAAY,oBAAoB,CAAC,CAAC,KAAK,OAAO,MAAM;CAC/D,MAAM,KAAK,YAAY,4BAA4B,CAAC,CAAC,MAAM;CAE3D,MAAM,KAAK,YAAY,2BAA2B,CAAC,CAAC,QAAQ;CAC5D,MAAM,KAAK,YAAY,4BAA4B,CAAC,CAAC,MAAM;CAE3D,MAAM,YAAY,IAAI,CAAC,CAAC,MAAM;CAC9B,MAAM,YAAY,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,SAAS,CAAC;AACrD;AAEA,MAAa,UAA0B,EACrC,IACF;;;;AClCA,MAAa,WAA4B;CACvC,MAAM,OAAO,EAAE,WAAW;EACxB,MAAM,KAAK,YAAY,6BAA6B,CAAC,CAAC,MAAM;EAC5D,MAAM,KAAK,YAAY,kBAAkB,CAAC,CAAC,OAAO,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;EACnF,MAAM,KAAK,QAAQ,0BAAwB,CAAC,CAAC,QAAQ;GAAE,OAAO;GAAW,SAAS;EAAO,CAAC;CAC5F;CAEA,QAAQ,OAAO,EAAE,aAAa;EAC5B,MAAM,OAAO;CACf;AACF;;;;ACVA,MAAM,sBAAsB;AAC5B,MAAM,iCAAiC;AASvC,MAAa,SAAS,OACpB,IAAI,SAAS,YAAY;CACvB,WAAW,SAAS,EAAE;AACxB,CAAC;;AAGH,MAAa,iBAAiB,OAAuB,GAAG,KAAK,MAAM,KAAK,GAAG,IAAI,GAAG;;AAGlF,MAAa,YAAY,OACvB,OACA,EAAE,aAAa,qBAAqB,gBACT;CAC3B,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,SAAS;EACP,MAAM,SAAS,MAAM,MAAM;EAC3B,IAAI,WAAW,UAAa,WAAW,OACrC,OAAO;EAET,IAAI,KAAK,IAAI,KAAK,UAChB;EAEF,MAAM,MAAM,UAAU;CACxB;AACF;AAEA,MAAa,mBAAmB,OAC9B,OACA,YACe;CACf,MAAM,SAAS,MAAM,UAAU,OAAO,OAAO;CAC7C,IAAI,WAAW,QACb,MAAM,IAAI,MACR,kBAAkB,QAAQ,QAAQ,kBAAkB,cAAc,QAAQ,SAAS,EAAE,EACvF;CAEF,OAAO;AACT;;;;;AAMA,MAAa,gBAAgB,OAC3B,MACA,KACA,EACE,aAAa,gCACb,OACA,gBAEgB;CAClB,MAAM,uBAEF,KACG,KAAK,KAAK,EAAE,WAAW,mBAAmB,CAAC,CAAC,CAC5C,WAAW,IAAI,CAAC,CAChB,YAAY,MAAS,GAC1B;EAAE;EAAY,SAAS,GAAG,MAAM,iBAAiB,IAAI;EAAI;CAAU,CACrE;AACF;;;;;;;;AChEA,MAAM,oBAAoB;;AAE1B,MAAM,mBAAmB;;AAGzB,MAAM,yBAAyB,OAAO,UAA+B;CACnE,MAAM,QAAQ,MAAM,QAAQ,uBAAuB;CACnD,IAAK,MAAM,MAAM,MAAM,CAAC,CAAC,YAAY,CAAC,MAAO,GAC3C;CAEF,MAAM,MACH,QAAQ,GAAG,CAAC,CACZ,KAAK,CAAC,CACN,uBAAuB,CAAC,CACxB,YAAY,CAAC,CAAC;CACjB,MAAM,MACH,UAAU,UAAU,EAAE,MAAM,SAAS,CAAC,CAAC,CACvC,MAAM,EAAE,SAAS,IAAK,CAAC,CAAC,CACxB,YAAY,CAAC,CAAC;AACnB;;AAGA,MAAM,iBAAiB,UACrB,MACG,YAAY,aAAa,CAAC,CAC1B,GAAG,MAAM,YAAY,uBAAuB,CAAC,CAAC,CAC9C,GAAG,MAAM,QAAQ,8CAA4C,CAAC,CAAC,CAC/D,GAAG,MAAM,QAAQ,gDAA8C,CAAC,CAAC,CACjE,MAAM;AAEX,MAAM,gBAAgB,UACpB,MACG,YAAY,YAAY,CAAC,CACzB,GAAG,MAAM,YAAY,8BAA8B,CAAC,CAAC,CACrD,GAAG,MAAM,QAAQ,gDAA8C,CAAC,CAAC,CACjE,GAAG,MAAM,QAAQ,+CAA6C,CAAC,CAAC,CAChE,MAAM;;AAGX,MAAa,oBAAoB,UAC/B,cAAc,KAAK,CAAC,CAAC,GAAG,aAAa,KAAK,CAAC;;;;;;AAO7C,MAAM,oBAAoB,OAAO,OAAa,WAAmC;CAC/E,MAAM,uBAAuB,KAAK;CAClC,IAAI;EACF,MAAM,OAAO,MAAM,EAAE,SAAS,kBAAkB,CAAC;CACnD,SAAS,OAAO;EACd,MAAM,uBAAuB,KAAK;EAClC,MAAM,OAAO,MAAM,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,YAAY;GAC5D,MAAM;EACR,CAAC;CACH;AACF;;AAGA,MAAM,mBAAmB;;;;;;AAOzB,MAAa,yBAAyB,OAAO,UAA+B;CAC1E,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,YAAY,GACpC;CAEF,MAAM,UAAU,MAAM,YAAY,aAAa;CAI/C,IAAI,MAHkB,gBAAgB,QAAQ,UAAU,EAAE,SAAS,IAAI,CAAC,CAAC,CAAC,YAAY,KAAK,GAAG,EAC5F,WAAW,iBACb,CAAC,MACe,QACd,MAAM,MAAM,OAAO;AAEvB;AAEA,MAAa,UAAU,OAAO,UAA+B;CAC3D,MAAM,uBAAuB,KAAK;CAClC,MAAM,kBAAkB,OAAO,cAAc,KAAK,CAAC;AACrD;;AAGA,MAAa,UAAU,UAA+B,kBAAkB,OAAO,aAAa,KAAK,CAAC;;;;AC1FlG,MAAM,wBAAwB;AAC9B,MAAM,yBAAyB;AAE/B,MAAaC,iBAAe,OAC1B,MACA,YACA,aACkB;CAClB,MAAM,KAAK,YAAY,0BAA0B,CAAC,CAAC,MAAM;CACzD,MAAM,KAAK,YAAY,mCAAmC,CAAC,CAAC,MAAM;CAElE,MAAM,WAAW,KAAK,YAAY,4BAA4B;CAC9D,MAAM,iBAAiB,KAAK,QAAQ,sDAAoD;CAaxF,IAAI,MAZmB,UACrB,YAAY;EACV,MAAM,SAAS,MAAM;EACrB,MAAM,SAAS,KAAK,EAAE;EACtB,MAAM,SAAS,kBAAkB,YAAY,EAAE,OAAO,GAAG,CAAC;EAC1D,OAAO,eACJ,QAAQ;GAAE,OAAO;GAAW,SAAS;EAAuB,CAAC,CAAC,CAC9D,WAAW,IAAI,CAAC,CAChB,YAAY,MAAS;CAC1B,GACA;EAAE,YAAY;EAAG,WAAW;CAAsB,CACpD,MACiB,QACf,MAAM,IAAI,MAAM,4EAA4E;CAE9F,MAAM,eAAe,MAAM;CAE3B,MAAM,KAAK,YAAY,2BAA2B,CAAC,CAAC,KAAK,QAAQ;CACjE,MAAM,KAAK,YAAY,+BAA+B,CAAC,CAAC,KAAK,QAAQ;CACrE,MAAM,KAAK,YAAY,uBAAuB,CAAC,CAAC,MAAM;CACtD,MAAM,KAAK,QAAQ,0DAAwD,CAAC,CAAC,MAAM;CAEnF,MAAM,KACH,YAAY,4BAA4B,CAAC,CACzC,MAAM,EAAE,SAAS,IAAO,CAAC,CAAC,CAC1B,YAAY,CAAC,CAAC;CACjB,MAAM,KACH,YAAY,qBAAqB,CAAC,CAClC,MAAM,EAAE,SAAS,IAAO,CAAC,CAAC,CAC1B,YAAY,CAAC,CAAC;AACnB;;;;;AC5CA,MAAM,gBAAgB,OAAU,WAA6B,aAA4B;CACvF,IAAI;EACF,OAAO,MAAM,UAAU;CACzB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,OAAO;EAET,MAAM;CACR;AACF;AAEA,MAAa,cAAc,WACzB,cAAc,YAAY;CACxB,MAAM,KAAK,MAAM;CACjB,OAAO;AACT,GAAG,KAAK;;AAGV,MAAa,kBAAkB,MAAc,OAC3C,cAAc,YAAY;CACxB,MAAM,OAAO,MAAM,EAAE;CACrB,OAAO;AACT,GAAG,KAAK;;;;ACjBV,MAAM,oBAAoB;AAE1B,MAAM,kBAAkB;;AAGxB,MAAa,cAAc,UACzB,WAAW,QAAQ,CAAC,CACjB,OAAO,GAAG,MAAM,OAAO,GAAG,MAAM,WAAW,UAAU,GAAG,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,CAC7F,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;;AAGhB,MAAa,qBAAqB,YAAoB,gBACpD,KAAK,KAAK,YAAY,WAAW,4BAA4B,WAAW;AAE1E,MAAM,sBAAsB,eAA+B,GAAG,aAAa;;AAG3E,MAAa,yBAAyB,OAAO,eAAsC;CACjF,MAAM,WAAW,mBAAmB,UAAU;CAC9C,IAAI,MAAM,WAAW,UAAU,GAAG;EAChC,MAAM,GAAG,UAAU;GAAE,OAAO;GAAM,WAAW;EAAK,CAAC;EACnD;CACF;CACA,MAAM,eAAe,UAAU,UAAU;AAC3C;;;;;AAMA,MAAa,gBAAgB,OAC3B,UACsD;CACtD,MAAM,WAAW,KAAK,QAAQ,MAAM,YAAY,iBAAiB;CACjE,MAAM,aAAa,KAAK,KAAK,UAAU,WAAW,KAAK,CAAC;CACxD,MAAM,uBAAuB,UAAU;CACvC,OAAO;EAAE;EAAU;CAAW;AAChC;;AAGA,MAAa,iBAAiB,OAAO,SAAiB,eAAsC;CAC1F,MAAM,uBAAuB,UAAU;CACvC,MAAM,WAAW,mBAAmB,UAAU;CAC9C,MAAM,gBAAgB,MAAM,eAAe,YAAY,QAAQ;CAE/D,IAAI;EACF,MAAM,OAAO,SAAS,UAAU;CAClC,SAAS,OAAO;EACd,IAAI,eACF,IAAI;GACF,MAAM,OAAO,UAAU,UAAU;EACnC,SAAS,eAAe;GACtB,MAAM,iBAAiB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC5E,MAAM,kBACJ,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa;GAC/E,MAAM,IAAI,MACR,6CAA6C,WAAW,IAAI,eAAe,qBAAqB,mBAChG,EAAE,OAAO,cAAc,CACzB;EACF;EAEF,MAAM;CACR;CAEA,IAAI,eACF,MAAM,GAAG,UAAU;EAAE,OAAO;EAAM,WAAW;CAAK,CAAC;AAEvD;;;;ACrEA,MAAM,wBAAwB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,KAAK,CAAC;AAI3D,MAAM,cAAc,MAAc,UAAmC;CACnE,MAAM,SAAS,sBAAsB,UAAU,KAAK,MAAM,IAAI,CAAC;CAC/D,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MAAM,6BAA6B,MAAM,0BAA0B,EAC3E,OAAO,OAAO,MAChB,CAAC;CAEH,OAAO,OAAO;AAChB;AAEA,MAAM,iBAAiB,gBAAiC;CACtD,GAAG;CACH,qBAAqB;CACrB,mBACE,OAAO,WAAW,sBAAsB,WAAW,WAAW,oBAAoB;CACpF,gBAAgB,CAAC;AACnB;;AAGA,MAAa,wBAAwB,OACnC,YACA,gBACkB;CAClB,MAAM,QAAQ,kBAAkB,YAAY,WAAW;CACvD,IAAI;EACF,MAAM,OAAO,KAAK;CACpB,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,uDAAuD,SAAS,EAC9E,OAAO,MACT,CAAC;CACH;CAEA,MAAM,KAAK,IAAI,aAAa,OAAO;EACjC,iBAAiB;EACjB,aAAa;EACb,eAAe;CACjB,CAAC;CACD,IAAI;EACF,MAAM,GAAG,KAAK;EAEd,MAAM,gBAAgB,MAAM,GAAG,IAAI,sBAAsB;EACzD,IAAI,kBAAkB,UAAa,kBAAkB,IAAI;GACvD,MAAM,aAAa,WAAW,eAAe,sBAAsB;GACnE,IAAI,WAAW,wBAAwB,MACrC,MAAM,GAAG,IAAI,wBAAwB,KAAK,UAAU,cAAc,UAAU,CAAC,CAAC;GAEhF;EACF;EAEA,MAAM,MAAM,MAAM,GAAG,IAAI,MAAM;EAC/B,IAAI,QAAQ,UAAa,QAAQ,IAC/B,MAAM,IAAI,MACR,4DAA4D,MAAM,sDACpE;EAEF,MAAM,QAAQ,WAAW,KAAK,UAAQ;EACtC,MAAM,eAAe,sBAAsB,UAAU,MAAM,IAAI;EAC/D,MAAM,SAAS,aAAa,UAAU,aAAa,OAAO;EAC1D,MAAM,mBAAmB,sBAAsB,WAC5C,UAAU,MAAK,CAAE,oBACpB;EACA,IAAI,CAAC,iBAAiB,SACpB,MAAM,IAAI,MAAM,sEAAsE;EAExF,MAAM,UAAU,cAAc,iBAAiB,IAAI;EACnD,MAAM,GAAG,IACP,QACA,KAAK,UACH,WAAW,SACP;GAAE,GAAG;GAAO,sBAAsB;EAAQ,IAC1C;GAAE,GAAG;GAAO,MAAM;IAAE,GAAG;IAAQ,sBAAsB;GAAQ;EAAE,CACrE,CACF;CACF,UAAU;EACR,MAAM,GAAG,MAAM;CACjB;AACF;;;;;AC7EA,MAAM,iBAAiB,EACrB,WAAW,mEACb;;;;;;AAOA,MAAa,kBAA+C;AAE5D,MAAM,iBAAiB,YACrB,OAAO,QAAQ,cAAc,CAAC,CAAC,MAAM,CAAC,YAAY,WAAW,OAAO,CAAC,GAAG;;AAG1E,MAAa,mBAAmB,aAAuC;CACrE,QAAQ;CACR,MAAM,mBAAmB;CACzB,QAAQ,cAAc,OAAO;CAC7B,KAAK,qEAAqE,QAAQ,mBAAmB,QAAQ;AAC/G;;AAGA,MAAM,oBAAoB;AAE1B,MAAa,WAA6B;CACxC,SAAS;EAAE;EAAU;EAAS;CAAS;CACvC;CACA;CACA,YAAY,CAAC,OAAO,KAAK;CACzB,eAAe;CACf,eAAe;CACf;CACA,gBAAgB;CAChB,gBAAgB,OAAO,YAAY;EACjC,MAAM,QAAQ,MAAM,QAAQ,kBAAkB,OAAO,UAAU,MAAM,MAAM,CAAC;CAC9E;CACA;CACA,QAAQ;EAAE,gBAAgB;EAAiB,MAAM;EAAW,SAAS;CAAgB;CACrF,cAAc,EAAE,OAAO,YAAY;AACrC;;;;AC/CA,MAAa,UAA4B;CACvC,SAAS,OAAO,UAAU;EACxB,MAAM,MAAM,YAAY,gBAAgB,CAAC,CAAC,MAAM,EAAE,SAAS,KAAO,CAAC;CACrE;CACA,YAAY,CAAC,OAAO,KAAK;CAEzB,eAAe;CAEf,mBAAmB;CAEnB,cAAc,OAAO,MAAM,YAAY,aAAa;EAClD,MAAM,UAAU,OAAe,KAAK,QAAQ,iBAAiB,GAAG,GAAG;EAEnE,MAAM,KAAK,QAAQ,8BAA8B,CAAC,CAAC,MAAM;EACzD,MAAM,MAAM,GAAI;EAChB,MAAM,KAAK,QAAQ,6BAA6B,CAAC,CAAC,MAAM;EACxD,MAAM,MAAM,IAAI;EAEhB,MAAM,QAAQ,WAAW,KAAK,CAAC,CAAC,MAAM,sBAAK,CAAC;EAC5C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAChC,MAAM,OAAO,qCAAqC,GAAG,CAAC,CAAC,KAAK,MAAM,MAAM,EAAE;EAE5E,MAAM,OAAO,+BAA+B,CAAC,CAAC,MAAM;EAEpD,MAAM,OAAO,kCAAkC,CAAC,CAAC,QAAQ;GAAE,OAAO;GAAW,SAAS;EAAO,CAAC;EAC9F,MAAM,OAAO,+BAA+B,CAAC,CAAC,MAAM;EAEpD,MAAM,OAAO,gCAAgC,CAAC,CAAC,KAAK,QAAQ;EAC5D,MAAM,OAAO,wCAAwC,CAAC,CAAC,KAAK,QAAQ;EACpE,MAAM,OAAO,2CAA2C,CAAC,CAAC,MAAM;EAChE,MAAM,OAAO,+BAA+B,CAAC,CAAC,MAAM;EACpD,MAAM,MAAM,IAAI;EAEhB,MAAM,aAAa,KAAK,QAAQ,kBAAkB;EAClD,IAAI,MAAM,WAAW,UAAU,CAAC,CAAC,YAAY,KAAK,GAChD,MAAM,WAAW,MAAM;EAEzB,MAAM,MAAM,IAAI;CAClB;CAEA,gBAAgB;CAEhB,QAAQ,OAAO,UAAU;EACvB,MAAM,MAAM,YAAY,kBAAkB,CAAC,CAAC,MAAM,EAAE,SAAS,KAAO,CAAC;CACvE;CAEA,QAAQ;EAAE,IAAI;EAAoC,MAAM;CAAW;CAEnE,cAAc;EACZ,OAAO;EACP,YAAY,OAAO,SAAS;GAM1B,OAAO,MALgB,KACpB,QAAQ,QAAQ,CAAC,CACjB,MAAM,CAAC,CACP,UAAU,CAAC,CACX,YAAY,KAAK;EAEtB;EACA,QAAQ,OAAO,MAAM,UAAU;GAC7B,MAAM,eAAe,KAAK,UAAU,UAAU,EAAE,MAAM,yBAAS,EAAE,CAAC;GAClE,OAAQ,MAAM,aAAa,UAAU,CAAC,CAAC,YAAY,KAAK,IACpD,aAAa,MAAM,IACnB,MAAM,MAAM,OAAO;EACzB;CACF;AACF;;;;AC/DA,MAAM,mBAAmB;AACzB,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAE5B,MAAM,mBAAmB;AAEzB,MAAM,YAAY,OAAO,MAAY,MAAc,YAAY,QAA0B;CACvF,MAAM,SAAS,KAAK,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM;CAC3D,MAAM,OAAO,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAU,CAAC;CAC7D,MAAM,OAAO,MAAM;AACrB;;;;;;AAOA,MAAM,eAAe,OACnB,MACA,UACA,YAAY,qBACM;CAClB,MAAM,uBAAuB,KAAK,IAAI,CAAC,CAAC,SAAS,QAAQ,GAAG;EAC1D,SAAS,uBAAuB;EAChC;CACF,CAAC;AACH;AAEA,MAAMC,iBAAe,OAAO,MAAY,YAAoB,aAAoC;CAC9F,MAAM,UAAU,MAAM,2BAA2B;CACjD,MAAM,UAAU,MAAM,4BAA4B;CAClD,MAAM,aAAa,MAAM,aAAa;CAEtC,MAAM,QAAQ,KAAK,QAAQ,0BAAwB;CACnD,MAAM,MAAM,MAAM,CAAC,CAAC,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAO,CAAC;CACjE,MAAM,KACH,QAAQ,CAAC,CACT,iBAAiB,CAAC,kBAAkB,iBAAiB,CAAC,CAAC,CACvD,YAAY,CAAC,CAAC;CACjB,MAAM,OAAO,WAAW,KAAK,CAAC,CAAC,MAAM,sBAAK,CAAC;CAC3C,MAAM,aAAa,YAA8B;EAK/C,QAAO,MAJa,MACjB,IAAI,KAAK,SAAS,CAAC,CAAC,CACpB,WAAW,CAAC,CACZ,YAAY,EAAE,EACL,CAAC,KAAK,CAAC,CAAC,SAAS;CAC/B;CAEA,MAAM,MAAM,MAAM,CAAC,CAAC,MAAM;CAC1B,MAAM,KAAK,UAAU,WAAW,UAAU,UAAU,UAAU,MAAM,GAAG,WAAW,KAAK,CAAC;CACxF,MAAM,KAAK,SAAS,MAAM,iBAAiB;CAE3C,IAAI,MADiB,UAAU,YAAY,EAAE,WAAW,sBAAsB,CAAC,MAChE,QACb,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,MAAM,MAAM,IAAI,KAAK;EAC3B,MAAM,IAAI,MAAM;EAChB,MAAM,IAAI,KAAK,KAAK,UAAU,EAAE;CAClC;CAEF,MAAM,KAAK,UAAU,UAAU;EAAE,OAAO;EAAM,MAAM;CAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;CAE5E,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,YAAY,KAAK,QAAQ,0BAAwB;CACvD,MAAM,UAAU,IAAI,CAAC,CAAC,CAAC,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAO,CAAC;CACpE,MAAM,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;CACpC,MAAM,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;CACpC,MAAM,KAAK,UAAU,UAAU;EAAE,OAAO;EAAM,MAAM;CAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;CAE/E,MAAM,KACH,UAAU,oBAAoB,EAAE,OAAO,MAAM,CAAC,CAAC,CAC/C,MAAM,CAAC,CACP,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAO,CAAC;CAChD,MAAM,MAAM,GAAI;AAClB;AAEA,MAAM,iBAAiB;CAAC;CAAW;CAAQ;AAAS;AACpD,MAAM,gBAAgB,CAAC,UAAU,QAAQ;;AAGzC,MAAM,sBAAsB,OAC1B,OACA,WACqB;CAGrB,MAAM,gCAAgB,IAAI,IAAY;CA0BtC,OAAO,MAzBgB,UACrB,YAAY;EACV,IAAI,MAAM,SAAS,GACjB,OAAO;EAET,MAAM,UAAU,MAAM,MACnB,UACE,QAAQ;GACP,MAAM,SAAS,CAAC,GAAG,SAAS,iBAAiB,QAAQ,CAAC,CAAC,CAAC,MAAM,WAAW;IACvE,MAAM,OAAO,OAAO,YAAY,KAAK;IACrC,OAAO,IAAI,MAAM,SAAS,IAAI,KAAK,CAAC,OAAO,YAAY,CAAC,IAAI,KAAK,SAAS,IAAI;GAChF,CAAC;GACD,QAAQ,MAAM;GACd,OAAO,SAAS,OAAO,YAAY,KAAK,IAAI;EAC9C,GACA;GAAE,OAAO,CAAC,GAAG,MAAM;GAAG,MAAM,CAAC,GAAG,aAAa;EAAE,CACjD,CAAC,CACA,YAAY,EAAE;EACjB,IAAI,SACF,cAAc,IAAI,OAAO;CAG7B,GACA;EAAE,YAAY;EAAK,WAAW;CAAoB,CACpD,MACoB,QAAQ,MAAM,SAAS;AAC7C;AAEA,MAAa,QAA0B;CACrC,SAAS,OAAO,UAAU;EACxB,IAAI,CAAE,MAAM,oBAAoB,OAAO,cAAc,GACnD,MAAM,IAAI,MAAM,oEAAoE;CAExF;CAEA,YAAY,CAAC,KAAK;CAElB,eAAe;CAEf,mBAAmB;CAEnB;CAEA,gBAAgB;CAEhB,QAAQ,OAAO,UAAU;EACvB,IAAI,CAAE,MAAM,oBAAoB,OAAO,aAAa,GAClD,MAAM,IAAI,MAAM,iEAAiE;CAErF;CAEA,QAAQ;EAAE,IAAI;EAAoC,MAAM;CAAW;CAEnE,cAAc,EAAE,OAAO,aAAa;AACtC;;;;AC5IA,MAAM,aAAa;AAEnB,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;AAE3B,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAC7B,MAAM,cAAc;AAEpB,MAAM,SAAS,OAAO,SACnB,MAAM,KAAK,eAAe,WAAW,SAAS,IAAI,CAAC,CAAC,YAAY,EAAE,MAAO;;;;;;AAO5E,MAAM,mBAAmB,SACvB,KAAK,SACH,OAAO,EAAE,kBAAkB,qBAAqB,gBAAgB;CAC9D,MAAM,YAAY,OAAO,iBAAiD;EAExE,IAAI,EAAC,MADmB,UAAU,UAAU,EAC9B,CAAC,MAAM,aAAa,SAAS,SAAS,YAAY,GAC9D,OAAO,CAAC;EAGV,MAAM,WAAW,MAAM,IAAI,SAAsB,SAAS,WAAW;GACnE,MAAM,UAAU,UAAU,KAAK,YAAY;GAC3C,QAAQ,iBAAiB,iBAAiB;IACxC,QAAQ,QAAQ,MAAM;GACxB,CAAC;GACD,QAAQ,iBAAiB,eAAe;IACtC,OAAO,QAAQ,yBAAS,IAAI,MAAM,kBAAkB,cAAc,CAAC;GACrE,CAAC;EACH,CAAC;EAED,IAAI;GACF,IAAI,CAAC,SAAS,iBAAiB,SAAS,SAAS,GAC/C,OAAO,CAAC;GAGV,MAAM,UADQ,SAAS,YAAY,WAAW,UAAU,CAAC,CAAC,YAAY,SAClD,CAAC,CAAC,OAAO;GAC7B,OAAO,MAAM,IAAI,SAAwB,SAAS,WAAW;IAC3D,QAAQ,iBAAiB,iBAAiB;KACxC,QAAQ,QAAQ,OAAO,QAAQ,SAAS,OAAO,SAAS,YAAY,SAAS,IAAI,CAAC;IACpF,CAAC;IACD,QAAQ,iBAAiB,eAAe;KACtC,OAAO,QAAQ,yBAAS,IAAI,MAAM,kBAAkB,WAAW,CAAC;IAClE,CAAC;GACH,CAAC;EACH,UAAU;GACR,SAAS,MAAM;EACjB;CACF;CAEA,MAAM,WAAW,MAAM,UAAU,gBAAgB;CACjD,MAAM,6BAAa,IAAI,IAAY;CACnC,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,WAAW,OAAO,QAAQ,OAAO,UAC3C,WAAW,IAAI,QAAQ,EAAE;CAG7B,IAAI,WAAW,SAAS,GACtB,OAAO;CAIT,QAAO,MADmB,UAAU,mBAAmB,EACrC,CAAC,MAChB,eACC,sBAAsB,cACtB,OAAO,WAAW,qBAAqB,YACvC,WAAW,IAAI,WAAW,gBAAgB,CAC9C;AACF,GACA;CACE,kBAAkB;CAClB,qBAAqB;CACrB,WAAW;AACb,CACF;AAEF,MAAM,SAAS,OAAO,MAAY,MAAc,YAAY,SAA6B;CACvF,MAAM,SAAS,KAAK,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM;CAK3D,IAAI,CAAC,MAJiB,OACnB,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAU,CAAC,CAAC,CACjD,WAAW,IAAI,CAAC,CAChB,YAAY,KAAK,GAElB,OAAO;CAET,MAAM,OAAO,uBAAuB,CAAC,CAAC,YAAY,CAAC,CAAC;CACpD,MAAM,OAAO,MAAM;EAAE,OAAO;EAAM,SAAS;CAAU,CAAC;CACtD,OAAO;AACT;AAEA,MAAa,QAA0B;CACrC,SAAS,OAAO,OAAO,aAAa;EAClC,MAAM,MAAM,GAAI;EAEhB,IAAI,EADe,MAAM,OAAO,OAAO,WAAW,GAAI,KAAO,MAAM,OAAO,OAAO,MAAM,IAErF,MAAM,IAAI,MAAM,wEAAwE;EAE1F,MAAM,MAAM,IAAI;EAChB,MAAM,QAAQ,MAAM,QAAQ,0BAAwB;EACpD,IAAI,MAAM,MAAM,UAAU,CAAC,CAAC,YAAY,KAAK,GAAG;GAC9C,MAAM,MAAM,KAAK,QAAQ;GACzB,MAAM,OAAO,OAAO,QAAQ;EAC9B;CACF;CAEA,YAAY,CAAC,KAAK;CAElB,eAAe;CAEf,cAAc,OAAO,MAAM,YAAY,aAAa;EAClD,MAAM,OAAO,MAAM,cAAc;EACjC,MAAM,MAAM,GAAI;EAChB,MAAM,OAAO,MAAM,iCAAiC;EACpD,MAAM,MAAM,IAAI;EAEhB,MAAM,QAAQ,WAAW,KAAK,CAAC,CAAC,MAAM,sBAAK,CAAC;EAC5C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAChC,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,MAAM,EAAE;EAE7E,MAAM,KAAK,UAAU,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM;EACvD,MAAM,MAAM,GAAI;EAEhB,MAAM,KAAK,QAAQ,iCAA+B,CAAC,CAAC,KAAK,QAAQ;EACjE,MAAM,KAAK,QAAQ,yCAAuC,CAAC,CAAC,KAAK,QAAQ;EACzE,MAAM,KAAK,UAAU,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM;EACvD,MAAM,MAAM,GAAI;EAEhB,MAAM,KACH,UAAU,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CACrC,MAAM,CAAC,CACP,YAAY,CAAC,CAAC;EACjB,MAAM,uBAAuB,OAAO,IAAI,GAAG;GACzC,YAAY;GACZ,SAAS,+CAA+C,WAAW;GACnE,WAAW;EACb,CAAC;EACD,MAAM,uBAAuB,gBAAgB,IAAI,GAAG;GAClD,YAAY;GACZ,SAAS;GACT,WAAW;EACb,CAAC;CACH;CAEA,mBAAmB;CAEnB,gBAAgB;CAEhB,gBAAgB,OAAO,YAAY;EACjC,MAAM,QAAQ,MAAM,wBAAwB,UAC1C,MAAM,QAAQ;GACZ,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;GACjC,aAAa;GACb,QAAQ;EACV,CAAC,CACH;CACF;CAEA,QAAQ,OAAO,UAAU;EACvB,MAAM,MAAM,GAAI;EAChB,IAAI,CAAE,MAAM,OAAO,OAAO,QAAQ,GAChC,MAAM,IAAI,MAAM,+DAA+D;CAEnF;CAEA,QAAQ;EAAE,IAAI;EAAoC,MAAM;CAAW;CAEnE,cAAc;EACZ,OAAO;EACP,YAAY;EACZ,QAAQ,OAAO,SAAS;GACtB,MAAM,OAAO,MAAM,QAAQ;EAC7B;CACF;AACF;;;;ACnLA,MAAM,eAAe,OAAO,MAAY,YAAoB,aAAoC;CAC9F,MAAM,KAAK,YAAY,4BAA4B,CAAC,CAAC,MAAM,EAAE,SAAS,IAAO,CAAC;CAC9E,MAAM,KAAK,UAAU,mBAAmB,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,SAAS,IAAO,CAAC;CAE1F,MAAM,QAAQ,WAAW,KAAK,CAAC,CAAC,MAAM,sBAAK,CAAC;CAC5C,MAAM,KAAK,YAAY,yBAAyB,CAAC,CAAC,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAO,CAAC;CAC/F,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SACxC,MAAM,KAAK,YAAY,yBAAyB,QAAQ,GAAG,CAAC,CAAC,KAAK,MAAM,UAAU,EAAE;CAEtF,MAAM,KAAK,YAAY,cAAc,CAAC,CAAC,MAAM,EAAE,SAAS,IAAO,CAAC;CAEhE,MAAM,KAAK,YAAY,oBAAoB,CAAC,CAAC,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAO,CAAC;CAC1F,MAAM,KAAK,YAAY,oBAAoB,CAAC,CAAC,KAAK,QAAQ;CAC1D,MAAM,KAAK,YAAY,uBAAuB,CAAC,CAAC,KAAK,QAAQ;CAC7D,MAAM,KAAK,YAAY,cAAc,CAAC,CAAC,MAAM,EAAE,SAAS,IAAO,CAAC;CAEhE,MAAM,KAAK,YAAY,iBAAiB,CAAC,CAAC,MAAM,EAAE,SAAS,IAAO,CAAC;CAEnE,MAAM,KAAK,YAAY,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,IAAO,CAAC;AACjE;AAEA,MAAa,WAA6B;CACxC,SAAS,OAAO,UAAU;EACxB,MAAM,MACH,YAAY,aAAa,CAAC,CAC1B,GAAG,MAAM,YAAY,aAAa,CAAC,CAAC,CACpC,MAAM,CAAC,CACP,MAAM,EAAE,SAAS,IAAO,CAAC;CAC9B;CAEA,YAAY,CAAC,KAAK;CAElB,eAAe;CAEf;CAEA,mBAAmB;CAEnB,gBAAgB;CAEhB,QAAQ,OAAO,UAAU;EACvB,MAAM,MACH,YAAY,YAAY,CAAC,CACzB,GAAG,MAAM,YAAY,YAAY,CAAC,CAAC,CACnC,MAAM,CAAC,CACP,MAAM,EAAE,SAAS,IAAO,CAAC;CAC9B;CAEA,QAAQ;EAAE,IAAI;EAAoC,MAAM;CAAW;CAEnE,cAAc,EAAE,OAAO,cAAc;AACvC;;;;AC/CA,MAAa,UAAU;CACrB;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAa,gBAAgB,UAAuC,OAAO,OAAO,SAAS,KAAK;;AAGhG,MAAa,cAAyC,OAAO,KAAK,OAAO,CAAC,CAAC,OAAO,YAAY;;AAG9F,MAAa,0BAA0B,cACrC,YAAY,QAAQ,SAAS,QAAQ,KAAK,CAAC,WAAW,SAAS,SAAS,CAAC;;;;ACd3E,MAAM,gBAAgB,OAAO,KAAK;CAAC;CAAI;CAAI;CAAG;AAAC,CAAC;;;;;;AAOhD,MAAM,iBAAiB;;AAGvB,MAAa,8BAA8B,OACzC,YACoB;CACpB,MAAM,EAAE,UAAU,QAAQ,MAAM,QAAQ,QAAQ;CAChD,MAAM,YAAY,KAAK,QAAQ,QAAQ;CACvC,MAAM,SAAS,KAAK,QAAQ,UAAU,IAAI;CAC1C,IAAI,WAAW,aAAa,CAAC,OAAO,WAAW,YAAY,KAAK,GAAG,GACjE,MAAM,IAAI,MAAM,0CAA0C,MAAM;CAElE,IAAI,MAAM,WAAW,KAAK,KAAK,QAAQ,eAAe,CAAC,GACrD,OAAO;CAGT,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;CAEzC,MAAM,WAAW,MAAM,MAAM,KAAK,EAAE,UAAU,SAAS,CAAC;CACxD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,qCAAqC,IAAI,IAAI,SAAS,OAAO,GAAG,SAAS,YAC3E;CAEF,MAAM,QAAQ,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;CAEtD,IAAI,WAAW,QAAW;EACxB,MAAM,SAAS,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;EAC9D,IAAI,WAAW,OAAO,YAAY,GAChC,MAAM,IAAI,MACR,kBAAkB,KAAK,oCAAoC,OAAO,QAAQ,QAC5E;CAEJ;CAEA,IAAI,WAAW;CACf,IAAI,WAAW,OAAO;EACpB,MAAM,QAAQ,MAAM,QAAQ,aAAa;EACzC,IAAI,UAAU,IACZ,MAAM,IAAI,MAAM,kBAAkB,IAAI,0CAA0C;EAElF,WAAW,MAAM,SAAS,KAAK;CACjC;CAEA,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK,WAAW,cAAc,CAAC;CAClE,IAAI;EACF,MAAM,MAAM,IAAI,OAAO,QAAQ;EAC/B,KAAK,MAAM,SAAS,IAAI,WAAW,GAAG;GACpC,MAAM,SAAS,KAAK,QAAQ,SAAS,MAAM,SAAS;GACpD,IAAI,WAAW,WAAW,CAAC,OAAO,WAAW,UAAU,KAAK,GAAG,GAC7D,MAAM,IAAI,MAAM,sCAAsC,MAAM,UAAU,YAAY,QAAQ;EAE9F;EACA,IAAI,aAAa,SAAyB,IAAI;EAE9C,IAAI,CAAE,MAAM,WAAW,KAAK,KAAK,SAAS,eAAe,CAAC,GACxD,MAAM,IAAI,MAAM,4BAA4B,KAAK,iCAAiC,QAAQ;EAG5F,IAAI,MAAM,WAAW,KAAK,KAAK,QAAQ,eAAe,CAAC,GACrD,OAAO;EAET,MAAM,GAAG,QAAQ;GAAE,OAAO;GAAM,WAAW;EAAK,CAAC;EACjD,MAAM,OAAO,SAAS,MAAM;EAC5B,OAAO;CACT,UAAU;EACR,MAAM,GAAG,SAAS;GAAE,OAAO;GAAM,WAAW;EAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;CACpE;AACF;;AAGA,MAAa,wBAAwB,gBACnC,qHAAqH,YAAY;;;;;;;;AC1EnI,MAAa,oBAAoB,UAAyC;CACxE,MAAM,EAAE,eAAe,WAAW,QAAQ,MAAM;CAChD,IAAI,OAAO,SAAS,WAClB,OAAO,OAAO,QAAQ,MAAM,WAAW,OAAO,cAAc;CAE9D,IAAI,MAAM,YAAY,QACpB,MAAM,IAAI,MACR,kBAAkB,cAAc,mGAClC;CAEF,OAAO;EACL,QAAQ;EACR,MAAM,GAAG,MAAM,OAAO;EACtB,QAAQ;EACR,KAAK,qBAAqB,OAAO,EAAE;CACrC;AACF;;AAGA,MAAa,mBAAmB,OAAO,OAAoB,aAAsC;CAE/F,OAAO,MADqB,4BAA4B;EAAE;EAAU,GAAG,iBAAiB,KAAK;CAAE,CAAC;AAElG;;AAGA,MAAa,sBAAsB,OAAO,kBAA2C;CACnF,MAAM,WAAW,KAAK,QAAQ,aAAa;CAC3C,MAAM,MAAM,MAAM,SAAS,QAAQ,CAAC,CAAC,YAAY,QAAQ;CACzD,IAAI;CACJ,IAAI;EACF,MAAM,OAAO,MAAM,SAAS,KAAK,KAAK,KAAK,eAAe,GAAG,MAAM;EAEnE,MADiB,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAClE,CAAC,CAAC;CACjB,QAAQ;EACN,MAAM;CACR;CACA,MAAM,SACJ,QAAQ,UAAa,QAAQ,KAAK,OAAO,KAAK,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ;CACxF,MAAM,MAAM,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;CACzE,OAAO,MAAM,KAAK,MAAM,WAAW,OAAO,cAAc,KAAK,OAAO,SAAS,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,KACzF,EACF;AACF;;;;ACjDA,MAAM,iBAA0C,SAAS,wBAAwB,KAAK,QAAQ;;;;;;AAO9F,MAAa,oBAAoB,OAAO,EACtC,YACA,eACA,UACA,0BAA0B,gBAC1B,kBAQ+D;CAC/D,MAAM,UAAU,MAAM,wBAAwB,aAAa;EACzD,MAAM,CAAC,+BAA+B,iBAAiB,oBAAoB,eAAe;EAE1F,SAAS;EACT;CACF,CAAC;CACD,IAAI;EACF,MAAM,WAAW,iBAAiB,OAAO;EACzC,OAAO;GAAE;GAAS,aAAa,MAAM,oBAAoB,aAAa;EAAE;CAC1E,SAAS,OAAO;EACd,MAAM,QAAQ,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EACpC,MAAM;CACR;AACF;;;;AC7BA,MAAM,wBAAwB;AAC9B,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;;;;;AAM/B,MAAM,eAAe;;AAGrB,MAAM,oBAAoB,OAAO,YAAoB,gBAA0C;CAC7F,MAAM,SAAS,CACb,kBAAkB,YAAY,WAAW,GACzC,KAAK,KACH,YACA,WACA,aACA,oBAAoB,YAAY,qBAClC,CACF;CAEA,QAAO,MADgB,QAAQ,WAAW,OAAO,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,EAC5D,CAAC,MAAM,YAAY,QAAQ,WAAW,eAAe,QAAQ,MAAM,SAAS,CAAC;AAC9F;AAQA,MAAM,sBAA8C,EAAE,iBAAiB;;AAGvE,MAAM,6BAA6B,OACjC,OACA,EAAE,WAAW,UAAkC,CAAC,GAChD,eAAuC,wBACnB;CACpB,MAAM,aAAa,QAAQ,MAAM;CACjC,MAAM,EAAE,UAAU,eAAe,MAAM,cAAc,KAAK;CAC1D,MAAM,gBAAgB,MAAM,aAAa,iBAAiB,OAAO,QAAQ;CACzE,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;CACzC,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK,UAAU,YAAY,CAAC;CAE/D,IAAI;CACJ,IAAI;EACF,MAAM,SAAS,MAAM,kBAAkB;GACrC;GACA;GACA;GACA,yBAAyB,aAAa;GACtC,aAAa;EACf,CAAC;EACD,UAAU,OAAO;EACjB,MAAM,EAAE,gBAAgB;EAExB,MAAM,OACJ,QAAQ,MAAM,CAAC,CAAC,MAAM,cAAc,UAAU,IAAI,MAAM,aAAa,KACpE,MAAM,QAAQ,QAAQ;EACzB,MAAM,cAAc,MAAM,sBAAsB,YAAY,GAAG,WAAW,kBAAkB;GAC1F,OAAO,GAAG,WAAW,cAAc;GACnC,WAAW;EACb,CAAC;EACD,MAAM,MAAM,GAAI;EAEhB,MAAM,WAAW,aAAa,MAAM,MAAM,YAAY,MAAM,QAAQ;EACpE,MAAM,MAAM,eAAe;EAC3B,MAAM,uBAAuB,kBAAkB,SAAS,WAAW,GAAG;GACpE,YAAY;GACZ,SAAS,GAAG,WAAW,cAAc,yCAAyC;GAC9E,WAAW;EACb,CAAC;EACD,MAAM,QAAQ,MAAM;EACpB,MAAM,WAAW,gBAAgB,SAAS,WAAW;EAErD,MAAM,eAAe,SAAS,UAAU;EACxC,OAAO;CACT,SAAS,OAAO;EACd,MAAM,SAAS,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EACrC,MAAM;CACR,UAAU;EACR,MAAM,GAAG,SAAS;GAAE,OAAO;GAAM,WAAW;EAAK,CAAC;CACpD;AACF;AAEA,MAAM,cAAc,OAAoB,YACtC,2BAA2B,OAAO,OAAO"}
|