diff --git a/packages/playwright-utils/environment.d.ts b/packages/playwright-utils/environment.d.ts new file mode 100644 index 000000000..e214b5b09 --- /dev/null +++ b/packages/playwright-utils/environment.d.ts @@ -0,0 +1,10 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + CI: boolean + HEADLESS: boolean + } + } +} + +export {} diff --git a/packages/playwright-utils/package.json b/packages/playwright-utils/package.json new file mode 100644 index 000000000..e4820c9ac --- /dev/null +++ b/packages/playwright-utils/package.json @@ -0,0 +1,55 @@ +{ + "name": "playwright-utils", + "version": "1.0.0", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./types/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "main": "./dist/index.js", + "files": [ + "dist", + "src", + "types" + ], + "scripts": { + "build": "pnpm run clean && pnpm run build:dist && pnpm run build:types", + "build:cache": "tsx src/buildCache.ts", + "build:cache:force": "tsx src/buildCache.ts --force", + "build:cache:force:headless": "HEADLESS=true tsx src/buildCache.ts --force", + "build:cache:headless": "HEADLESS=true tsx src/buildCache.ts", + "build:dist": "tsup --tsconfig tsconfig.build.json", + "build:types": "tsc --emitDeclarationOnly --project tsconfig.build.json", + "clean": "rimraf dist types", + "lint": "biome check . --apply", + "lint:check": "biome check . --verbose", + "lint:unsafe": "biome check . --apply-unsafe", + "local:test:e2e": "playwright test", + "local:test:e2e:headless": "HEADLESS=true playwright test", + "serve:test-dapp": "serve node_modules/@metamask/test-dapp/dist -p 9011", + "types:check": "tsc --noEmit" + }, + "dependencies": { + "core": "workspace:*", + "fs-extra": "^11.1.1", + "metamask": "workspace:*" + }, + "devDependencies": { + "@metamask/test-dapp": "^7.2.0", + "@types/fs-extra": "^11.0.2", + "@types/node": "^20.8.0", + "rimraf": "^5.0.1", + "serve": "^14.2.1", + "tsconfig": "workspace:*", + "tsup": "^7.2.0", + "tsx": "^3.12.10", + "typescript": "^5.2.2" + }, + "peerDependencies": { + "@playwright/test": "1.39.0" + } +} diff --git a/packages/playwright-utils/playwright.config.ts b/packages/playwright-utils/playwright.config.ts new file mode 100644 index 000000000..3a1106347 --- /dev/null +++ b/packages/playwright-utils/playwright.config.ts @@ -0,0 +1,47 @@ +import { defineConfig, devices } from '@playwright/test' + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + // Look for test files in the "test" directory, relative to this configuration file. + testDir: './test', + + // Run all tests in parallel. + // TODO: Enable later once we have more tests. + fullyParallel: false, + + // Fail the build on CI if you accidentally left test.only in the source code. + forbidOnly: !!process.env.CI, + + // Workers to run parallel tests with. + workers: 2, + + // Concise 'dot' for CI, default 'html' when running locally. + // See https://playwright.dev/docs/test-reporters. + reporter: process.env.CI ? 'list' : 'html', + + // Shared settings for all the projects below. + // See https://playwright.dev/docs/api/class-testoptions. + use: { + // Collect traces when running locally. + // See https://playwright.dev/docs/trace-viewer. + trace: process.env.CI ? 'off' : 'on', + baseURL: 'http://localhost:9011/' + }, + + // Configure projects for major browsers. + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + } + ], + + // Web server configuration. + webServer: { + command: 'pnpm run serve:test-dapp', + url: 'http://127.0.0.1:9011', + reuseExistingServer: !process.env.CI + } +}) diff --git a/packages/playwright-utils/src/buildCache.ts b/packages/playwright-utils/src/buildCache.ts new file mode 100644 index 000000000..7f623c5d9 --- /dev/null +++ b/packages/playwright-utils/src/buildCache.ts @@ -0,0 +1,16 @@ +import path from 'node:path' +import { createCache } from 'core' +import { prepareExtension } from 'metamask' +import { WALLET_SETUP_DIR_NAME } from './constants' + +const isForce = process.argv.includes('--force') +const isHeadless = !!process.env.HEADLESS + +const walletSetupDirPath = path.join(process.cwd(), 'test', WALLET_SETUP_DIR_NAME) +console.log({ walletSetupDirPath, isForce, isHeadless }) + +console.log('\t---- ⏳ Triggering the `createCache` function ----\n') + +await createCache(walletSetupDirPath, prepareExtension, isForce) + +console.log('\n\t---- ✅ `createCache` function finished ----\n') diff --git a/packages/playwright-utils/src/constants.ts b/packages/playwright-utils/src/constants.ts new file mode 100644 index 000000000..24e6f0d8d --- /dev/null +++ b/packages/playwright-utils/src/constants.ts @@ -0,0 +1 @@ +export const WALLET_SETUP_DIR_NAME = 'wallet-setup' diff --git a/packages/playwright-utils/src/index.ts b/packages/playwright-utils/src/index.ts new file mode 100644 index 000000000..3d28bd128 --- /dev/null +++ b/packages/playwright-utils/src/index.ts @@ -0,0 +1 @@ +export * from './testWithSynpress' diff --git a/packages/playwright-utils/src/testWithSynpress.ts b/packages/playwright-utils/src/testWithSynpress.ts new file mode 100644 index 000000000..1eb0e4ebe --- /dev/null +++ b/packages/playwright-utils/src/testWithSynpress.ts @@ -0,0 +1,104 @@ +import path from 'node:path' +import type { + Fixtures, + Page, + PlaywrightTestArgs, + PlaywrightTestOptions, + PlaywrightWorkerArgs, + PlaywrightWorkerOptions +} from '@playwright/test' +import { chromium, test as base } from '@playwright/test' +import { defineWalletSetup, waitForExtensionOnLoadPage } from 'core' +import { createTempContextDir, removeTempContextDir } from 'core' +import { CACHE_DIR_NAME } from 'core' +import fs from 'fs-extra' +import { getExtensionId, prepareExtension } from 'metamask' +import { unlockMetaMask } from './utils/unlockMetaMask' + +// Base types of the `test` fixture from Playwright. +type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions +type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions + +type PrivateSynpressFixtures = { + _contextPath: string +} + +type PublicSynpressFixtures = { + extensionId: string + metamaskPage: Page +} + +type SynpressFixtures = TestFixtures & PrivateSynpressFixtures & PublicSynpressFixtures + +// TODO: Bad practice. Use a store! +let _extensionId: string +let _metamaskPage: Page + +const synpressFixtures = ( + walletSetup: ReturnType, + slowMo = 0 +): Fixtures => ({ + _contextPath: async ({ browserName }, use, testInfo) => { + const contextPath = await createTempContextDir(browserName, testInfo.testId) + + await use(contextPath) + + const error = await removeTempContextDir(contextPath) + if (error) { + console.error(error) + } + }, + context: async ({ context: _, _contextPath }, use) => { + const cacheDirPath = path.join(process.cwd(), CACHE_DIR_NAME, walletSetup.hash) + if (!(await fs.exists(cacheDirPath))) { + throw new Error(`Cache for ${walletSetup.hash} does not exist. Create it first!`) + } + + // Copying the cache to the temporary context directory. + await fs.copy(cacheDirPath, _contextPath) + + const metamaskPath = await prepareExtension() + + // We don't need the `--load-extension` args since it is already loaded in the cache. + const browserArgs = [`--disable-extensions-except=${metamaskPath}`] + + if (process.env.HEADLESS) { + browserArgs.push('--headless=new') + + if (slowMo > 0) { + console.warn('[WARNING] Slow motion makes no sense in headless mode. It will be ignored!') + } + } + + const context = await chromium.launchPersistentContext(_contextPath, { + headless: false, + args: browserArgs, + slowMo: process.env.HEADLESS ? 0 : slowMo + }) + + _metamaskPage = await waitForExtensionOnLoadPage(context) + + await unlockMetaMask(_metamaskPage, walletSetup.walletPassword) + + await use(context) + + await context.close() + }, + extensionId: async ({ context }, use) => { + if (_extensionId) { + await use(_extensionId) + } + + _extensionId = await getExtensionId(context, 'MetaMask') + + await use(_extensionId) + }, + metamaskPage: async ({ context: _ }, use) => { + await use(_metamaskPage) + } +}) + +export const testWithSynpress = (walletSetup: ReturnType, slowMo?: number) => { + // biome-ignore lint/suspicious/noExplicitAny: satisfying TypeScript here - this type doesn't matter since we are overriding it + return base.extend(synpressFixtures(walletSetup, slowMo) as any) +} diff --git a/packages/playwright-utils/src/utils/unlockMetaMask.ts b/packages/playwright-utils/src/utils/unlockMetaMask.ts new file mode 100644 index 000000000..5f42f3455 --- /dev/null +++ b/packages/playwright-utils/src/utils/unlockMetaMask.ts @@ -0,0 +1,47 @@ +import type { Page } from '@playwright/test' +import { errors as playwrightErrors } from '@playwright/test' +import { CrashPageSelectors, HomePageSelectors, LoadingSelectors, unlock } from 'metamask' + +export async function unlockMetaMask(page: Page, password: string) { + await unlock(page, password) + + await page.locator(LoadingSelectors.spinner).waitFor({ + state: 'hidden', + timeout: 3000 // TODO: Extract & Make this timeout configurable. + }) + + await retryIfMetaMaskCrashAfterUnlock(page) +} + +async function retryIfMetaMaskCrashAfterUnlock(page: Page) { + const isHomePageVisible = await page.locator(HomePageSelectors.logo).isVisible() + + if (!isHomePageVisible) { + if (await page.locator(CrashPageSelectors.header).isVisible()) { + const errors = await page.locator(CrashPageSelectors.errors).allTextContents() + + console.warn(['[RetryIfMetaMaskCrashAfterUnlock] MetaMask crashed due to:', ...errors].join('\n')) + + console.log('[RetryIfMetaMaskCrashAfterUnlock] Reloading page...') + await page.reload() + + try { + await page.locator(HomePageSelectors.logo).waitFor({ + state: 'visible', + timeout: 10_000 // TODO: Extract & Make this timeout configurable. + }) + console.log('[RetryIfMetaMaskCrashAfterUnlock] Successfully restored MetaMask!') + } catch (e) { + if (e instanceof playwrightErrors.TimeoutError) { + throw new Error( + ['[RetryIfMetaMaskCrashAfterUnlock] Reload did not help. Throwing with the crash cause:', ...errors].join( + '\n' + ) + ) + } + + throw e + } + } + } +} diff --git a/packages/playwright-utils/test/testWithSynpress.spec.ts b/packages/playwright-utils/test/testWithSynpress.spec.ts new file mode 100644 index 000000000..b24ac6477 --- /dev/null +++ b/packages/playwright-utils/test/testWithSynpress.spec.ts @@ -0,0 +1,33 @@ +import { HomePageSelectors, connectToDapp, lock, unlock } from 'metamask' +import { testWithSynpress } from '../src' +import basicSetup from './wallet-setup/basic.setup' + +const test = testWithSynpress(basicSetup, 1_000) + +const { describe, expect } = test + +describe.configure({ + mode: 'parallel' +}) + +describe('testWithSynpress', () => { + test('should connect wallet to MetaMask E2E Test Dapp', async ({ context, page, extensionId }) => { + await page.goto('/') + + await page.locator('#connectButton').click() + + await connectToDapp(context, extensionId) + + await expect(page.locator('#accounts')).toHaveText('0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266') + }) + + test('should lock & unlock MetaMask', async ({ metamaskPage }) => { + await metamaskPage.bringToFront() + + await lock(metamaskPage) + + await unlock(metamaskPage, basicSetup.walletPassword) + + await expect(metamaskPage.locator(HomePageSelectors.logo)).toBeVisible() + }) +}) diff --git a/packages/playwright-utils/test/wallet-setup/basic.setup.ts b/packages/playwright-utils/test/wallet-setup/basic.setup.ts new file mode 100644 index 000000000..9bd5acbed --- /dev/null +++ b/packages/playwright-utils/test/wallet-setup/basic.setup.ts @@ -0,0 +1,13 @@ +import { defineWalletSetup } from 'core' +import { OnboardingPage } from 'metamask' + +const SEED_PHRASE = 'test test test test test test test test test test test junk' +const PASSWORD = 'Tester@1234' + +export default defineWalletSetup(PASSWORD, async (_, walletPage) => { + const onboardingPage = new OnboardingPage(walletPage) + + await onboardingPage.importWallet(SEED_PHRASE, PASSWORD) + + await walletPage.getByTestId('selected-account-click').click() +}) diff --git a/packages/playwright-utils/tsconfig.build.json b/packages/playwright-utils/tsconfig.build.json new file mode 100644 index 000000000..97afa3ef9 --- /dev/null +++ b/packages/playwright-utils/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "types", + "declaration": true, + "sourceMap": true, + "declarationMap": true + }, + "include": ["src"], + "files": ["environment.d.ts"] +} diff --git a/packages/playwright-utils/tsconfig.json b/packages/playwright-utils/tsconfig.json new file mode 100644 index 000000000..ab36e816e --- /dev/null +++ b/packages/playwright-utils/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "rootDir": "." + }, + "include": [ + "src", + "test" + ], + "files": ["environment.d.ts", "playwright.config.ts"] +} diff --git a/packages/playwright-utils/tsup.config.ts b/packages/playwright-utils/tsup.config.ts new file mode 100644 index 000000000..908b0d090 --- /dev/null +++ b/packages/playwright-utils/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + name: 'playwright-utils', + entry: ['src/index.ts'], + outDir: 'dist', + format: 'esm', + splitting: false, + sourcemap: true +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee6fbd99e..628e3c959 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,49 @@ importers: specifier: ^5.2.2 version: 5.2.2 + packages/playwright-utils: + dependencies: + '@playwright/test': + specifier: 1.39.0 + version: 1.39.0 + core: + specifier: workspace:* + version: link:../core + fs-extra: + specifier: ^11.1.1 + version: 11.1.1 + metamask: + specifier: workspace:* + version: link:../../wallets/metamask + devDependencies: + '@metamask/test-dapp': + specifier: ^7.2.0 + version: 7.2.0 + '@types/fs-extra': + specifier: ^11.0.2 + version: 11.0.2 + '@types/node': + specifier: ^20.8.0 + version: 20.8.0 + rimraf: + specifier: ^5.0.1 + version: 5.0.1 + serve: + specifier: ^14.2.1 + version: 14.2.1 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + tsup: + specifier: ^7.2.0 + version: 7.2.0(typescript@5.2.2) + tsx: + specifier: ^3.12.10 + version: 3.12.10 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + packages/tsconfig: {} wallets/metamask: @@ -794,6 +837,11 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@metamask/test-dapp@7.2.0: + resolution: {integrity: sha512-WxdaLIUlxJ9Ucdasa2FVhW1D3B8g9kgSsBfXb5RQ6QWjzx2WeEb8YKwfe4u80xtbBdefrgS24UU6+9wfwxK17w==} + engines: {node: '>= 16.0.0'} + dev: true + /@mswjs/cookies@0.2.2: resolution: {integrity: sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==} engines: {node: '>=14'} @@ -883,7 +931,6 @@ packages: hasBin: true dependencies: playwright: 1.39.0 - dev: true /@scure/base@1.1.3: resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} @@ -1071,6 +1118,10 @@ packages: engines: {node: '>=10.0.0'} dev: true + /@zeit/schemas@2.29.0: + resolution: {integrity: sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==} + dev: true + /@zxing/text-encoding@0.9.0: resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} requiresBuild: true @@ -1091,6 +1142,14 @@ packages: typescript: 5.2.2 dev: false + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: true + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} @@ -1106,6 +1165,21 @@ packages: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} dev: false + /ajv@8.11.0: + resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + dependencies: + string-width: 4.2.3 + dev: true + /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -1164,6 +1238,10 @@ packages: engines: {node: '>= 6.0.0'} dev: false + /arch@2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + dev: true + /archive-type@4.0.0: resolution: {integrity: sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==} engines: {node: '>=4'} @@ -1278,6 +1356,20 @@ packages: resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} dev: false + /boxen@7.0.0: + resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==} + engines: {node: '>=14.16'} + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + dev: true + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1345,6 +1437,11 @@ packages: load-tsconfig: 0.2.5 dev: true + /bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + dev: true + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1369,6 +1466,11 @@ packages: get-intrinsic: 1.2.1 dev: true + /camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + dev: true + /chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} @@ -1388,6 +1490,13 @@ packages: traverse: 0.3.9 dev: false + /chalk-template@0.4.0: + resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} + engines: {node: '>=12'} + dependencies: + chalk: 4.1.2 + dev: true + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1396,6 +1505,11 @@ packages: supports-color: 7.2.0 dev: true + /chalk@5.0.1: + resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /chalk@5.3.0: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -1425,6 +1539,11 @@ packages: fsevents: 2.3.3 dev: true + /cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + dev: true + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -1457,6 +1576,15 @@ packages: engines: {node: '>= 10'} dev: true + /clipboardy@3.0.0: + resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + arch: 2.2.0 + execa: 5.1.1 + is-wsl: 2.2.0 + dev: true + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1540,9 +1668,36 @@ packages: readable-stream: 3.6.2 dev: true + /compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + + /compression@1.7.4: + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /content-disposition@0.5.2: + resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} + engines: {node: '>= 0.6'} + dev: true + /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -1585,6 +1740,17 @@ packages: which: 2.0.2 dev: true + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1668,6 +1834,11 @@ packages: type-detect: 4.0.8 dev: true + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true + /defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: @@ -1898,6 +2069,10 @@ packages: tmp: 0.0.33 dev: true + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + /fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true @@ -1917,6 +2092,12 @@ packages: micromatch: 4.0.5 dev: true + /fast-url-parser@1.1.3: + resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + dependencies: + punycode: 1.4.1 + dev: true + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -2329,6 +2510,10 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + /inquirer@8.2.6: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} @@ -2378,6 +2563,12 @@ packages: engines: {node: '>= 0.4'} dev: true + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2439,6 +2630,11 @@ packages: engines: {node: '>=12'} dev: true + /is-port-reachable@4.0.0: + resolution: {integrity: sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-retry-allowed@1.2.0: resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==} engines: {node: '>=0.10.0'} @@ -2471,6 +2667,13 @@ packages: engines: {node: '>=10'} dev: true + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -2566,6 +2769,10 @@ packages: tslib: 2.4.0 dev: true + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true @@ -2774,17 +2981,27 @@ packages: picomatch: 2.3.1 dev: true + /mime-db@1.33.0: + resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} + engines: {node: '>= 0.6'} + dev: true + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: false + + /mime-types@2.1.18: + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.33.0 + dev: true /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 - dev: false /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -2822,7 +3039,6 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: false /minipass@7.0.3: resolution: {integrity: sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==} @@ -2845,6 +3061,10 @@ packages: ufo: 1.3.1 dev: true + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -2902,6 +3122,11 @@ packages: hasBin: true dev: true + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: true + /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -2945,6 +3170,11 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + /on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + dev: true + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -3028,6 +3258,10 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + /path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + dev: true + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3046,6 +3280,10 @@ packages: minipass: 7.0.3 dev: true + /path-to-regexp@2.2.1: + resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + dev: true + /path-to-regexp@6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} dev: true @@ -3151,7 +3389,6 @@ packages: playwright-core: 1.39.0 optionalDependencies: fsevents: 2.3.2 - dev: true /postcss-load-config@4.0.1: resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} @@ -3206,6 +3443,10 @@ packages: once: 1.4.0 dev: false + /punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + dev: true + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -3237,6 +3478,21 @@ packages: lodash.isequal: 4.5.0 dev: true + /range-parser@1.2.0: + resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} + engines: {node: '>= 0.6'} + dev: true + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: true + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true @@ -3274,11 +3530,30 @@ packages: picomatch: 2.3.1 dev: true + /registry-auth-token@3.3.2: + resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==} + dependencies: + rc: 1.2.8 + safe-buffer: 5.2.1 + dev: true + + /registry-url@3.1.0: + resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} + engines: {node: '>=0.10.0'} + dependencies: + rc: 1.2.8 + dev: true + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} dev: true + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -3364,7 +3639,6 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -3390,6 +3664,39 @@ packages: lru-cache: 6.0.0 dev: true + /serve-handler@6.1.5: + resolution: {integrity: sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==} + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + fast-url-parser: 1.1.3 + mime-types: 2.1.18 + minimatch: 3.1.2 + path-is-inside: 1.0.2 + path-to-regexp: 2.2.1 + range-parser: 1.2.0 + dev: true + + /serve@14.2.1: + resolution: {integrity: sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==} + engines: {node: '>= 14'} + hasBin: true + dependencies: + '@zeit/schemas': 2.29.0 + ajv: 8.11.0 + arg: 5.0.2 + boxen: 7.0.0 + chalk: 5.0.1 + chalk-template: 0.4.0 + clipboardy: 3.0.0 + compression: 1.7.4 + is-port-reachable: 4.0.0 + serve-handler: 6.1.5 + update-check: 1.5.4 + transitivePeerDependencies: + - supports-color + dev: true + /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} dev: true @@ -3591,6 +3898,11 @@ packages: engines: {node: '>=12'} dev: true + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + /strip-literal@1.3.0: resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: @@ -3916,6 +4228,19 @@ packages: setimmediate: 1.0.5 dev: false + /update-check@1.5.4: + resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + /url-parse-lax@3.0.0: resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} engines: {node: '>=4'} @@ -3950,6 +4275,11 @@ packages: convert-source-map: 1.9.0 dev: true + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: true + /viem@1.11.0(typescript@5.2.2): resolution: {integrity: sha512-k53ewCObCHMc5zex/lXB4+HeRW1NIlezqkR/S4OXKV5MZvltMYqsgKABuWVgiMdfIL/NvDjtIgwbaA24AAn31g==} peerDependencies: @@ -4160,6 +4490,13 @@ packages: stackback: 0.0.2 dev: true + /widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + dev: true + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'}