From 98006dc4668b73ac06394ad268aad87b30215ef3 Mon Sep 17 00:00:00 2001 From: Zehao Zhang Date: Mon, 24 Mar 2025 11:01:59 +0800 Subject: [PATCH 01/14] feat: dynamic import retry plugin [KM-865] --- .../setup-pnpm-with-dependencies/action.yaml | 23 + README.md | 6 +- build.config.ts | 1 + package.json | 28 +- playground/README.md | 12 + playground/dynamic-import-retry/index.spec.ts | 78 ++ .../vue-router/index.html | 12 + .../vue-router/src/AboutView.vue | 32 + .../vue-router/src/App.vue | 20 + .../vue-router/src/HomeView.vue | 18 + .../vue-router/src/main.ts | 16 + .../vue-router/tsconfig.json | 11 + .../vue-router/vite.config.ts | 21 + playground/vitestGlobalSetup.ts | 25 + playground/vitestSetup.ts | 46 + pnpm-lock.yaml | 989 +++++++++++++++++- src/plugin-dynamic-import-retry/README.md | 28 + .../css-preload-retry.ts | 57 + .../import-wrapper.ts | 71 ++ src/plugin-dynamic-import-retry/index.ts | 91 ++ vitest.config.e2e.ts | 22 + 21 files changed, 1584 insertions(+), 23 deletions(-) create mode 100644 playground/README.md create mode 100644 playground/dynamic-import-retry/index.spec.ts create mode 100644 playground/dynamic-import-retry/vue-router/index.html create mode 100644 playground/dynamic-import-retry/vue-router/src/AboutView.vue create mode 100644 playground/dynamic-import-retry/vue-router/src/App.vue create mode 100644 playground/dynamic-import-retry/vue-router/src/HomeView.vue create mode 100644 playground/dynamic-import-retry/vue-router/src/main.ts create mode 100644 playground/dynamic-import-retry/vue-router/tsconfig.json create mode 100644 playground/dynamic-import-retry/vue-router/vite.config.ts create mode 100644 playground/vitestGlobalSetup.ts create mode 100644 playground/vitestSetup.ts create mode 100644 src/plugin-dynamic-import-retry/README.md create mode 100644 src/plugin-dynamic-import-retry/css-preload-retry.ts create mode 100644 src/plugin-dynamic-import-retry/import-wrapper.ts create mode 100644 src/plugin-dynamic-import-retry/index.ts create mode 100644 vitest.config.e2e.ts diff --git a/.github/actions/setup-pnpm-with-dependencies/action.yaml b/.github/actions/setup-pnpm-with-dependencies/action.yaml index e600d16..6569dd6 100644 --- a/.github/actions/setup-pnpm-with-dependencies/action.yaml +++ b/.github/actions/setup-pnpm-with-dependencies/action.yaml @@ -71,3 +71,26 @@ runs: with: path: './node_modules' key: ${{ steps.node-version.outputs.cache-key }} + + - name: Get Playwright Chromium version + id: get-playwright-version + run: echo "PLAYWRIGHT_VERSION=$(jq -r '.devDependencies["playwright-chromium"]' < package.json | sed 's/[^0-9.]//g' | sed 's/\./-/g')" >> $GITHUB_OUTPUT + shell: bash + + - name: Set Playwright path + id: playwright-path + shell: bash + run: echo "PLAYWRIGHT_BROWSERS_PATH=/home/runner/.cache/ms-playwright" >> $GITHUB_OUTPUT + + - name: Cache Playwright's binary + id: playwright-cache + uses: actions/cache@v4 + with: + key: playwright-bin-v${{ steps.get-playwright-version.outputs.PLAYWRIGHT_VERSION }} + path: ${{ steps.playwright-path.outputs.PLAYWRIGHT_BROWSERS_PATH }} + + - name: Install Playwright + id: playwright-install + shell: bash + # does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved + run: pnpm playwright install chromium diff --git a/README.md b/README.md index e352d02..5535c33 100644 --- a/README.md +++ b/README.md @@ -94,13 +94,11 @@ pnpm lint:fix Unit tests are run with [Vitest](https://vitest.dev/). ```shell -# Run tests pnpm test - -# Run tests in the Vitest UI -pnpm test:ui ``` +See the [Steps to Test Your Plugin](./playground/README.md) for more information on how to write tests. + ### Build Build for production and inspect the files in the `/dist` directory. diff --git a/build.config.ts b/build.config.ts index b9f0f0b..5ac5440 100644 --- a/build.config.ts +++ b/build.config.ts @@ -6,6 +6,7 @@ export default defineBuildConfig({ // Each separate plugin's entry file should be listed here entries: [ './src/plugin-example-one/index.ts', + './src/plugin-dynamic-import-retry/index.ts', ], // Generates .d.ts declaration file(s) declaration: true, diff --git a/package.json b/package.json index db0fb89..cceb662 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,7 @@ "scripts": { "lint": "eslint", "lint:fix": "eslint --fix", - "test": "vitest run --passWithNoTests", - "test:ui": "vitest --ui --passWithNoTests", + "test": "vitest run -c vitest.config.e2e.ts", "typecheck": "vue-tsc --noEmit", "build": "unbuild", "commit": "cz" @@ -16,9 +15,15 @@ "./plugin-example-one": { "import": "./dist/plugin-example-one/index.mjs", "types": "./dist/plugin-example-one/index.d.ts" + }, + "./plugin-dynamic-import-retry": { + "import": "./dist/plugin-dynamic-import-retry/index.mjs", + "types": "./dist/plugin-dynamic-import-retry/index.d.ts" } }, - "files": ["dist"], + "files": [ + "dist" + ], "author": "Kong, Inc.", "license": "Apache-2.0", "devDependencies": { @@ -27,11 +32,20 @@ "@digitalroute/cz-conventional-changelog-for-jira": "^8.0.1", "@evilmartians/lefthook": "^1.10.1", "@kong/eslint-config-kong-ui": "^1.2.6", + "@types/fs-extra": "^11.0.4", + "@vitejs/plugin-vue": "^5.2.1", "@vitest/ui": "^2.1.8", + "@vue/tsconfig": "^0.7.0", "eslint": "^9.17.0", + "fs-extra": "^11.2.0", + "npm-run-all2": "^7.0.2", + "playwright-chromium": "^1.49.1", "typescript": "^5.7.2", "unbuild": "^3.2.0", + "vite": "^6.0.7", "vitest": "^2.1.8", + "vue": "^3.5.13", + "vue-router": "^4.5.0", "vue-tsc": "^2.2.0" }, "engines": { @@ -52,5 +66,13 @@ "jiraPrepend": "[", "jiraAppend": "]" } + }, + "dependencies": { + "@rollup/pluginutils": "^5.1.4", + "acorn-walk": "^8.3.4", + "magic-string": "^0.30.17" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0" } } diff --git a/playground/README.md b/playground/README.md new file mode 100644 index 0000000..46959e5 --- /dev/null +++ b/playground/README.md @@ -0,0 +1,12 @@ +## Directory Overview + +This directory serves as a testing environment for plugins. Each subdirectory corresponds to a specific plugin, identified by its name. + +* `vitestGlobalSetup.ts`: This is the global setup file for Vitest, executed once before all tests. It initializes a headless browser environment. +* `vitestSetup.ts`: This is the per-test setup file for Vitest, executed before each individual test. It provides references to the browser instance and the page context for use during testing. + +## Steps to Test Your Plugin +1. Create a new subdirectory within the playground directory, using your plugin’s name as the folder name. +2. Within this folder, set up one or more Vite projects for testing. +3. Write test files with the `.spec.ts` extension to validate your plugin’s functionality. +4. If browser-based testing is required, you can import `page` or `browser` from the `vitestSetup.ts` file to interact with the headless browser environment. diff --git a/playground/dynamic-import-retry/index.spec.ts b/playground/dynamic-import-retry/index.spec.ts new file mode 100644 index 0000000..2abfc3c --- /dev/null +++ b/playground/dynamic-import-retry/index.spec.ts @@ -0,0 +1,78 @@ +import { beforeAll, expect, test } from 'vitest' +import { resolve } from 'node:path' +import type { InlineConfig } from 'vite' +import { build, preview } from 'vite' + +import { page, browserErrors, browserLogs } from '../vitestSetup' + +const root = resolve(__dirname, 'vue-router') + +let viteTestUrl: string + +beforeAll(async () => { + const testConfig: InlineConfig = { + configFile: resolve(root, 'vite.config.ts'), + } + + await build(testConfig) + const previewServer = await preview(testConfig) + + viteTestUrl = previewServer.resolvedUrls!.local[0] + await page.goto(viteTestUrl) + + return async () => { + previewServer.close() + } +}) + +test('should dynamic import module successful', async () => { + await page.click('#nav-about') + await page.waitForTimeout(100) + expect(await page.textContent('h2')).toMatch('AboutView') +}) + +const resources = ['css', 'js'] + +for (const resourceType of resources) { + const filter = (url: URL) => url.pathname.includes('AboutView') && url.pathname.includes(`.${resourceType}`) + + test('should recover by retrying import ' + resourceType, async () => { + await page.goto(viteTestUrl) + + let retries = 0 + await page.route(filter, route => { + retries++ + if (retries < 3) { + return route.fulfill({ status: 404, body: 'Not Found' }) + } + return route.continue() + }) + await page.click('#nav-about') + await page.waitForTimeout(1000) + expect(await page.textContent('h2')).toMatch('AboutView') + expect(retries).toBe(3) + }) + + test('should has an error when reach max attempts on loading ' + resourceType, async () => { + await page.goto(viteTestUrl) + + await page.route(filter, route => { + return route.fulfill({ status: 404, body: 'Not Found' }) + }) + await page.click('#nav-about') + await page.waitForTimeout(1500) + + if (resourceType === 'js') { + expect(await page.textContent('h2')).toMatch('HomeView') + } + + if (resourceType === 'css') { + const e = browserErrors.find(e => e.message.includes('[preload-css-retried]')) + expect(e).toBeDefined() + } else { + const e = browserLogs.find(l => l.includes('[dynamic-import-retry]')) + expect(e).toBeDefined() + } + }) +} + diff --git a/playground/dynamic-import-retry/vue-router/index.html b/playground/dynamic-import-retry/vue-router/index.html new file mode 100644 index 0000000..e763020 --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/index.html @@ -0,0 +1,12 @@ + + + + + + Vite + Vue + TS + + +
+ + + diff --git a/playground/dynamic-import-retry/vue-router/src/AboutView.vue b/playground/dynamic-import-retry/vue-router/src/AboutView.vue new file mode 100644 index 0000000..d5967fb --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/src/AboutView.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/playground/dynamic-import-retry/vue-router/src/App.vue b/playground/dynamic-import-retry/vue-router/src/App.vue new file mode 100644 index 0000000..27d69a5 --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/src/App.vue @@ -0,0 +1,20 @@ + diff --git a/playground/dynamic-import-retry/vue-router/src/HomeView.vue b/playground/dynamic-import-retry/vue-router/src/HomeView.vue new file mode 100644 index 0000000..7c54224 --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/src/HomeView.vue @@ -0,0 +1,18 @@ + + + + + diff --git a/playground/dynamic-import-retry/vue-router/src/main.ts b/playground/dynamic-import-retry/vue-router/src/main.ts new file mode 100644 index 0000000..febc5c0 --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/src/main.ts @@ -0,0 +1,16 @@ +import { createApp } from 'vue' +import App from './App.vue' + +import { createRouter, createWebHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', component: () => import('./HomeView.vue') }, + { path: '/about', component: () => import('./AboutView.vue') }, + ], +}) + +createApp(App) + .use(router) + .mount('#app') diff --git a/playground/dynamic-import-retry/vue-router/tsconfig.json b/playground/dynamic-import-retry/vue-router/tsconfig.json new file mode 100644 index 0000000..1e22a42 --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist", + "declaration": false, + "declarationDir": null, + "types": ["vite/client"] + }, + "include": ["vite.config.ts"] +} diff --git a/playground/dynamic-import-retry/vue-router/vite.config.ts b/playground/dynamic-import-retry/vue-router/vite.config.ts new file mode 100644 index 0000000..f9fde03 --- /dev/null +++ b/playground/dynamic-import-retry/vue-router/vite.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'node:path' + +import { DynamicImportRetryPlugin } from '../../../src/plugin-dynamic-import-retry' + +// https://vite.dev/config/ +export default defineConfig({ + logLevel: 'silent', + root: resolve(__dirname), + plugins: [ + vue(), + DynamicImportRetryPlugin(), + ], + build: { + minify: false, + outDir: 'dist', + target: 'esnext', + emptyOutDir: false, + }, +}) diff --git a/playground/vitestGlobalSetup.ts b/playground/vitestGlobalSetup.ts new file mode 100644 index 0000000..6a62e57 --- /dev/null +++ b/playground/vitestGlobalSetup.ts @@ -0,0 +1,25 @@ +import os from 'node:os' +import path from 'node:path' +import fs from 'fs-extra' +import type { BrowserServer } from 'playwright-chromium' +import { chromium } from 'playwright-chromium' + +const DIR = path.join(os.tmpdir(), 'vitest_playwright_global_setup') + +let browserServer: BrowserServer | undefined + +export async function setup(): Promise { + browserServer = await chromium.launchServer({ + headless: !process.env.VITE_DEBUG_SERVE, + args: process.env.CI + ? ['--no-sandbox', '--disable-setuid-sandbox'] + : undefined, + }) + + await fs.mkdirp(DIR) + await fs.writeFile(path.join(DIR, 'wsEndpoint'), browserServer.wsEndpoint()) +} + +export async function teardown(): Promise { + await browserServer?.close() +} diff --git a/playground/vitestSetup.ts b/playground/vitestSetup.ts new file mode 100644 index 0000000..5f07728 --- /dev/null +++ b/playground/vitestSetup.ts @@ -0,0 +1,46 @@ +import { join } from 'node:path' +import os from 'node:os' +import fs from 'fs-extra' +import { chromium } from 'playwright-chromium' +import type { Browser, Page } from 'playwright-chromium' +import { beforeAll } from 'vitest' + +export const browserErrors: Error[] = [] +export const browserLogs: string[] = [] + +export let page: Page = undefined! +export let browser: Browser = undefined! + +const DIR = join(os.tmpdir(), 'vitest_playwright_global_setup') + +beforeAll(async () => { + const wsEndpoint = fs.readFileSync(join(DIR, 'wsEndpoint'), 'utf-8') + if (!wsEndpoint) { + throw new Error('wsEndpoint not found') + } + + browser = await chromium.connect(wsEndpoint) + page = await browser.newPage() + + page.on('console', (msg) => { + // ignore favicon request in headed browser + if ( + process.env.VITE_DEBUG_SERVE && + msg.location().url.includes('favicon.ico') + ) { + return + } + browserLogs.push(msg.text()) + }) + + page.on('pageerror', (error) => { + browserErrors.push(error) + }) + + return async () => { + await page?.close() + if (browser) { + await browser.close() + } + } +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b33e6d..d1c562e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,16 @@ settings: importers: .: + dependencies: + '@rollup/pluginutils': + specifier: ^5.1.4 + version: 5.1.4(rollup@4.29.1) + acorn-walk: + specifier: ^8.3.4 + version: 8.3.4 + magic-string: + specifier: ^0.30.17 + version: 0.30.17 devDependencies: '@commitlint/cli': specifier: ^19.6.1 @@ -23,21 +33,67 @@ importers: '@kong/eslint-config-kong-ui': specifier: ^1.2.6 version: 1.2.6(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) + '@types/fs-extra': + specifier: ^11.0.4 + version: 11.0.4 + '@vitejs/plugin-vue': + specifier: ^5.2.1 + version: 5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2))(vue@3.5.13(typescript@5.7.2)) '@vitest/ui': specifier: ^2.1.8 version: 2.1.8(vitest@2.1.9) + '@vue/tsconfig': + specifier: ^0.7.0 + version: 0.7.0(typescript@5.7.2)(vue@3.5.13(typescript@5.7.2)) eslint: specifier: ^9.17.0 version: 9.17.0(jiti@2.4.2) + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 + npm-run-all2: + specifier: ^7.0.2 + version: 7.0.2 + playwright-chromium: + specifier: ^1.49.1 + version: 1.49.1 typescript: specifier: ^5.7.2 version: 5.7.2 unbuild: specifier: ^3.2.0 - version: 3.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2)) + version: 3.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2))(vue@3.5.13(typescript@5.7.2)) + vite: + specifier: ^6.0.7 + version: 6.0.7(@types/node@22.10.5)(jiti@2.4.2) vitest: specifier: ^2.1.8 - version: 2.1.9(@types/node@22.10.5)(@vitest/ui@2.1.8) + version: 2.1.9(@types/node@22.10.5)(@vitest/ui@2.1.8)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)) + vue: + specifier: ^3.5.13 + version: 3.5.13(typescript@5.7.2) + vue-router: + specifier: ^4.5.0 + version: 4.5.0(vue@3.5.13(typescript@5.7.2)) + vue-tsc: + specifier: ^2.2.0 + version: 2.2.0(typescript@5.7.2) + + playground/vue: + dependencies: + vue: + specifier: ^3.5.13 + version: 3.5.13(typescript@5.7.2) + vue-router: + specifier: ^4.5.0 + version: 4.5.0(vue@3.5.13(typescript@5.7.2)) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.1 + version: 5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2))(vue@3.5.13(typescript@5.7.2)) + '@vue/tsconfig': + specifier: ^0.7.0 + version: 0.7.0(typescript@5.7.2)(vue@3.5.13(typescript@5.7.2)) vue-tsc: specifier: ^2.2.0 version: 2.2.0(typescript@5.7.2) @@ -115,6 +171,15 @@ packages: resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} engines: {node: '>=6.9.0'} + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@commitlint/cli@19.6.1': resolution: {integrity: sha512-8hcyA6ZoHwWXC76BoC8qVOSr8xHy00LZhZpauiD0iO0VYbVhMnED0da85lTfIULxl7Lj4c6vZgF0Wu/ed1+jlQ==} engines: {node: '>=v18'} @@ -525,7 +590,6 @@ packages: '@evilmartians/lefthook@1.10.1': resolution: {integrity: sha512-G1NPLB4yLYYEyz8oH7yWgHsxUiF546aS1ChSRPNFPSosLxZFM8wqDxek/J9sT447v83gJbKdrnstxeQW/9CIRA==} - cpu: [x64, arm64, ia32] os: [darwin, linux, win32] hasBin: true @@ -549,6 +613,26 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} + '@inquirer/confirm@5.1.1': + resolution: {integrity: sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/core@10.1.2': + resolution: {integrity: sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==} + engines: {node: '>=18'} + + '@inquirer/figures@1.0.9': + resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} + engines: {node: '>=18'} + + '@inquirer/type@3.0.2': + resolution: {integrity: sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -573,6 +657,10 @@ packages: peerDependencies: eslint: '>= 9.18.0' + '@mswjs/interceptors@0.37.5': + resolution: {integrity: sha512-AAwRb5vXFcY4L+FvZ7LZusDuZ0vEe0Zm8ohn1FM6/X7A3bj4mqmkAcGRWuvC2JwSygNwHAAmMnAI73vPHeqsHA==} + engines: {node: '>=18'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -585,6 +673,15 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} @@ -845,18 +942,33 @@ packages: '@types/conventional-commits-parser@5.0.1': resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/fs-extra@11.0.4': + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/jsonfile@6.1.4': + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/node@22.10.5': resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@typescript-eslint/eslint-plugin@8.24.0': resolution: {integrity: sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -904,6 +1016,13 @@ packages: resolution: {integrity: sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitejs/plugin-vue@5.2.1': + resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + '@vitest/expect@2.1.9': resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} @@ -959,9 +1078,18 @@ packages: '@vue/compiler-dom@3.5.13': resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + '@vue/compiler-sfc@3.5.13': + resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + + '@vue/compiler-ssr@3.5.13': + resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + '@vue/language-core@2.2.0': resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} peerDependencies: @@ -970,9 +1098,34 @@ packages: typescript: optional: true + '@vue/reactivity@3.5.13': + resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + + '@vue/runtime-core@3.5.13': + resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + + '@vue/runtime-dom@3.5.13': + resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + + '@vue/server-renderer@3.5.13': + resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + peerDependencies: + vue: 3.5.13 + '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@vue/tsconfig@0.7.0': + resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==} + peerDependencies: + typescript: 5.x + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -982,11 +1135,19 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1015,6 +1176,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1025,6 +1190,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} @@ -1134,6 +1302,10 @@ packages: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1158,6 +1330,10 @@ packages: colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -1202,6 +1378,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cosmiconfig-typescript-loader@6.1.0: resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==} engines: {node: '>=v18'} @@ -1271,6 +1451,13 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + cssstyle@4.1.0: + resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==} + engines: {node: '>=18'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + cz-conventional-changelog@3.3.0: resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} engines: {node: '>= 10'} @@ -1279,6 +1466,10 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -1291,6 +1482,9 @@ packages: supports-color: optional: true + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} @@ -1311,6 +1505,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + detect-file@1.0.0: resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} engines: {node: '>=0.10.0'} @@ -1575,9 +1773,17 @@ packages: flatted@3.3.2: resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -1655,6 +1861,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@16.10.0: + resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1671,6 +1881,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -1678,10 +1891,26 @@ packages: hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1748,6 +1977,9 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1756,6 +1988,9 @@ packages: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -1777,6 +2012,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -1792,6 +2031,15 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsdom@25.0.1: + resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1803,6 +2051,10 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@4.0.0: + resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} + engines: {node: ^18.17.0 || >=20.5.0} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1897,9 +2149,6 @@ packages: resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==} engines: {node: '>=0.10.0'} - loupe@3.1.2: - resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} - loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} @@ -1915,6 +2164,10 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} @@ -1930,6 +2183,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -1975,12 +2236,26 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.7.0: + resolution: {integrity: sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1996,9 +2271,21 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + npm-normalize-package-bin@4.0.0: + resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-run-all2@7.0.2: + resolution: {integrity: sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA==} + engines: {node: ^18.17.0 || >=20.5.0, npm: '>= 9'} + hasBin: true + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nwsapi@2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2018,6 +2305,9 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2046,6 +2336,9 @@ packages: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} engines: {node: '>=0.10.0'} + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -2068,6 +2361,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -2086,9 +2382,24 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + pkg-types@1.3.0: resolution: {integrity: sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==} + playwright-chromium@1.49.1: + resolution: {integrity: sha512-XAQDkZ1Eem1OONhfS8B2LM2mgHG/i5jIxooxjvqjbF/9GnLnRTJHdQamNjo1e4FZvt7J0BFD/15+qAcT0eKlfA==} + engines: {node: '>=18'} + hasBin: true + + playwright-core@1.49.1: + resolution: {integrity: sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==} + engines: {node: '>=18'} + hasBin: true + postcss-calc@10.0.2: resolution: {integrity: sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==} engines: {node: ^18.12 || ^20.9 || >=22.0} @@ -2284,13 +2595,23 @@ packages: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + read-package-json-fast@4.0.0: + resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==} + engines: {node: ^18.17.0 || >=20.5.0} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2303,6 +2624,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-dir@1.0.1: resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} engines: {node: '>=0.10.0'} @@ -2353,6 +2677,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -2369,6 +2696,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} @@ -2389,12 +2720,20 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + sirv@3.0.0: resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} engines: {node: '>=18'} @@ -2410,9 +2749,16 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.8.0: resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2455,6 +2801,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.6.2: resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} engines: {node: '>=12.20'} @@ -2492,6 +2841,13 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tldts-core@6.1.70: + resolution: {integrity: sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==} + + tldts@6.1.70: + resolution: {integrity: sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==} + hasBin: true + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -2504,6 +2860,18 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tough-cookie@5.0.0: + resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} + engines: {node: '>=16'} + + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + ts-api-utils@2.0.1: resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} engines: {node: '>=18.12'} @@ -2525,6 +2893,10 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + type-fest@4.31.0: + resolution: {integrity: sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==} + engines: {node: '>=16'} + typescript-eslint@8.24.0: resolution: {integrity: sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2556,6 +2928,10 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -2573,6 +2949,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2612,6 +2991,46 @@ packages: terser: optional: true + vite@6.0.7: + resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@2.1.9: resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2646,15 +3065,48 @@ packages: peerDependencies: eslint: '>=6.0.0' + vue-router@4.5.0: + resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} + peerDependencies: + vue: ^3.2.0 + vue-tsc@2.2.0: resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==} hasBin: true peerDependencies: typescript: '>=5.0.0' + vue@3.5.13: + resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} + engines: {node: '>=18'} + which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -2664,6 +3116,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -2688,10 +3145,29 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2715,6 +3191,10 @@ packages: resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + snapshots: '@ampproject/remapping@2.3.0': @@ -2822,6 +3302,22 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + optional: true + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + optional: true + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + optional: true + '@commitlint/cli@19.6.1(@types/node@22.10.5)(typescript@5.7.2)': dependencies: '@commitlint/format': 19.5.0 @@ -3155,6 +3651,36 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} + '@inquirer/confirm@5.1.1(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + optional: true + + '@inquirer/core@10.1.2(@types/node@22.10.5)': + dependencies: + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.5) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + transitivePeerDependencies: + - '@types/node' + optional: true + + '@inquirer/figures@1.0.9': + optional: true + + '@inquirer/type@3.0.2(@types/node@22.10.5)': + dependencies: + '@types/node': 22.10.5 + optional: true + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -3193,6 +3719,16 @@ snapshots: - supports-color - typescript + '@mswjs/interceptors@0.37.5': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3205,6 +3741,18 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.18.0 + '@open-draft/deferred-promise@2.2.0': + optional: true + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + optional: true + + '@open-draft/until@2.1.0': + optional: true + '@polka/url@1.0.0-next.28': {} '@rollup/plugin-alias@5.1.1(rollup@4.29.1)': @@ -3386,16 +3934,34 @@ snapshots: dependencies: '@types/node': 22.10.5 + '@types/cookie@0.6.0': + optional: true + '@types/estree@1.0.6': {} + '@types/fs-extra@11.0.4': + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 22.10.5 + '@types/json-schema@7.0.15': {} + '@types/jsonfile@6.1.4': + dependencies: + '@types/node': 22.10.5 + '@types/node@22.10.5': dependencies: undici-types: 6.20.0 '@types/resolve@1.20.2': {} + '@types/statuses@2.0.5': + optional: true + + '@types/tough-cookie@4.0.5': + optional: true + '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3473,6 +4039,11 @@ snapshots: '@typescript-eslint/types': 8.24.0 eslint-visitor-keys: 4.2.0 + '@vitejs/plugin-vue@5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2))(vue@3.5.13(typescript@5.7.2))': + dependencies: + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2) + vue: 3.5.13(typescript@5.7.2) + '@vitest/expect@2.1.9': dependencies: '@vitest/spy': 2.1.9 @@ -3480,12 +4051,13 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.9(vite@5.4.14(@types/node@22.10.5))': + '@vitest/mocker@2.1.9(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2))(vite@5.4.14(@types/node@22.10.5))': dependencies: '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: + msw: 2.7.0(@types/node@22.10.5)(typescript@5.7.2) vite: 5.4.14(@types/node@22.10.5) '@vitest/pretty-format@2.1.8': @@ -3520,12 +4092,12 @@ snapshots: sirv: 3.0.0 tinyglobby: 0.2.10 tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@22.10.5)(@vitest/ui@2.1.8) + vitest: 2.1.9(@types/node@22.10.5)(@vitest/ui@2.1.8)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)) '@vitest/utils@2.1.8': dependencies: '@vitest/pretty-format': 2.1.8 - loupe: 3.1.2 + loupe: 3.1.3 tinyrainbow: 1.2.0 '@vitest/utils@2.1.9': @@ -3559,11 +4131,30 @@ snapshots: '@vue/compiler-core': 3.5.13 '@vue/shared': 3.5.13 + '@vue/compiler-sfc@3.5.13': + dependencies: + '@babel/parser': 7.26.3 + '@vue/compiler-core': 3.5.13 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.4.49 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.13': + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 + '@vue/compiler-vue2@2.7.16': dependencies: de-indent: 1.0.2 he: 1.2.0 + '@vue/devtools-api@6.6.4': {} + '@vue/language-core@2.2.0(typescript@5.7.2)': dependencies: '@volar/language-core': 2.4.11 @@ -3577,8 +4168,35 @@ snapshots: optionalDependencies: typescript: 5.7.2 + '@vue/reactivity@3.5.13': + dependencies: + '@vue/shared': 3.5.13 + + '@vue/runtime-core@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/runtime-dom@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/runtime-core': 3.5.13 + '@vue/shared': 3.5.13 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.2))': + dependencies: + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + vue: 3.5.13(typescript@5.7.2) + '@vue/shared@3.5.13': {} + '@vue/tsconfig@0.7.0(typescript@5.7.2)(vue@3.5.13(typescript@5.7.2))': + optionalDependencies: + typescript: 5.7.2 + vue: 3.5.13(typescript@5.7.2) + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -3588,8 +4206,15 @@ snapshots: dependencies: acorn: 8.14.0 + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.0 + acorn@8.14.0: {} + agent-base@7.1.3: + optional: true + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3624,12 +4249,17 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.1: {} + argparse@2.0.1: {} array-ify@1.0.0: {} assertion-error@2.0.1: {} + asynckit@0.4.0: + optional: true + at-least-node@1.0.0: {} autoprefixer@10.4.20(postcss@8.4.49): @@ -3746,6 +4376,9 @@ snapshots: cli-width@3.0.0: {} + cli-width@4.1.0: + optional: true + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -3768,6 +4401,11 @@ snapshots: colord@2.9.3: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + optional: true + commander@7.2.0: {} commitizen@4.3.1(@types/node@22.10.5)(typescript@5.7.2): @@ -3822,6 +4460,9 @@ snapshots: convert-source-map@2.0.0: {} + cookie@0.7.2: + optional: true + cosmiconfig-typescript-loader@6.1.0(@types/node@22.10.5)(cosmiconfig@9.0.0(typescript@5.7.2))(typescript@5.7.2): dependencies: '@types/node': 22.10.5 @@ -3918,6 +4559,13 @@ snapshots: dependencies: css-tree: 2.2.1 + cssstyle@4.1.0: + dependencies: + rrweb-cssom: 0.7.1 + optional: true + + csstype@3.1.3: {} + cz-conventional-changelog@3.3.0(@types/node@22.10.5)(typescript@5.7.2): dependencies: chalk: 2.4.2 @@ -3934,12 +4582,21 @@ snapshots: dargs@8.1.0: {} + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + optional: true + de-indent@1.0.2: {} debug@4.4.0: dependencies: ms: 2.1.3 + decimal.js@10.4.3: + optional: true + dedent@0.7.0: {} deep-eql@5.0.2: {} @@ -3954,6 +4611,9 @@ snapshots: defu@6.1.4: {} + delayed-stream@1.0.0: + optional: true + detect-file@1.0.0: {} detect-indent@6.1.0: {} @@ -4298,8 +4958,21 @@ snapshots: flatted@3.3.2: {} + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + optional: true + fraction.js@4.3.7: {} + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -4377,6 +5050,9 @@ snapshots: graphemer@1.4.0: {} + graphql@16.10.0: + optional: true + has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -4387,16 +5063,45 @@ snapshots: he@1.2.0: {} + headers-polyfill@4.0.3: + optional: true + homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 hookable@5.5.3: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + optional: true + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + optional: true + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + optional: true + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -4475,10 +5180,16 @@ snapshots: is-module@1.0.0: {} + is-node-process@1.2.0: + optional: true + is-number@7.0.0: {} is-obj@2.0.0: {} + is-potential-custom-element-name@1.0.1: + optional: true + is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 @@ -4495,6 +5206,8 @@ snapshots: isexe@2.0.0: {} + isexe@3.1.1: {} + jiti@1.21.7: {} jiti@2.4.2: {} @@ -4505,12 +5218,43 @@ snapshots: dependencies: argparse: 2.0.1 + jsdom@25.0.1: + dependencies: + cssstyle: 4.1.0 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.1 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.16 + parse5: 7.2.1 + rrweb-cssom: 0.7.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + optional: true + jsesc@3.1.0: {} json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} + json-parse-even-better-errors@4.0.0: {} + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -4588,8 +5332,6 @@ snapshots: longest@2.0.1: {} - loupe@3.1.2: {} - loupe@3.1.3: {} lru-cache@5.1.1: @@ -4604,6 +5346,8 @@ snapshots: mdn-data@2.0.30: {} + memorystream@0.3.1: {} + meow@12.1.1: {} merge2@1.4.1: {} @@ -4615,6 +5359,14 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: + optional: true + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + optional: true + mimic-fn@2.1.0: {} minimatch@3.1.2: @@ -4629,7 +5381,7 @@ snapshots: minimist@1.2.8: {} - mkdist@2.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2)): + mkdist@2.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2))(vue@3.5.13(typescript@5.7.2)): dependencies: autoprefixer: 10.4.20(postcss@8.4.49) citty: 0.1.6 @@ -4646,6 +5398,7 @@ snapshots: tinyglobby: 0.2.10 optionalDependencies: typescript: 5.7.2 + vue: 3.5.13(typescript@5.7.2) vue-tsc: 2.2.0(typescript@5.7.2) mlly@1.7.3: @@ -4659,10 +5412,39 @@ snapshots: ms@2.1.3: {} + msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.1(@types/node@22.10.5) + '@mswjs/interceptors': 0.37.5 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + graphql: 16.10.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.31.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - '@types/node' + optional: true + muggle-string@0.4.1: {} mute-stream@0.0.8: {} + mute-stream@2.0.0: + optional: true + nanoid@3.3.8: {} natural-compare@1.4.0: {} @@ -4671,10 +5453,26 @@ snapshots: normalize-range@0.1.2: {} + npm-normalize-package-bin@4.0.0: {} + + npm-run-all2@7.0.2: + dependencies: + ansi-styles: 6.2.1 + cross-spawn: 7.0.6 + memorystream: 0.3.1 + minimatch: 9.0.5 + pidtree: 0.6.0 + read-package-json-fast: 4.0.0 + shell-quote: 1.8.2 + which: 5.0.0 + nth-check@2.1.1: dependencies: boolbase: 1.0.0 + nwsapi@2.2.16: + optional: true + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -4706,6 +5504,9 @@ snapshots: os-tmpdir@1.0.2: {} + outvariant@1.4.3: + optional: true + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -4735,6 +5536,11 @@ snapshots: parse-passwd@1.0.0: {} + parse5@7.2.1: + dependencies: + entities: 4.5.0 + optional: true + path-browserify@1.0.1: {} path-exists@4.0.0: {} @@ -4747,6 +5553,9 @@ snapshots: path-parse@1.0.7: {} + path-to-regexp@6.3.0: + optional: true + pathe@1.1.2: {} pathval@2.0.0: {} @@ -4757,12 +5566,20 @@ snapshots: picomatch@4.0.2: {} + pidtree@0.6.0: {} + pkg-types@1.3.0: dependencies: confbox: 0.1.8 mlly: 1.7.3 pathe: 1.1.2 + playwright-chromium@1.49.1: + dependencies: + playwright-core: 1.49.1 + + playwright-core@1.49.1: {} + postcss-calc@10.0.2(postcss@8.4.49): dependencies: postcss: 8.4.49 @@ -4945,10 +5762,23 @@ snapshots: pretty-bytes@6.1.1: {} + psl@1.15.0: + dependencies: + punycode: 2.3.1 + optional: true + punycode@2.3.1: {} + querystringify@2.2.0: + optional: true + queue-microtask@1.2.3: {} + read-package-json-fast@4.0.0: + dependencies: + json-parse-even-better-errors: 4.0.0 + npm-normalize-package-bin: 4.0.0 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -4959,6 +5789,9 @@ snapshots: require-from-string@2.0.2: {} + requires-port@1.0.0: + optional: true + resolve-dir@1.0.1: dependencies: expand-tilde: 2.0.2 @@ -5043,6 +5876,9 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.34.2 fsevents: 2.3.3 + rrweb-cssom@0.7.1: + optional: true + run-async@2.4.1: {} run-parallel@1.2.0: @@ -5057,6 +5893,11 @@ snapshots: safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + optional: true + scule@1.3.0: {} semver@6.3.1: {} @@ -5069,10 +5910,15 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.8.2: {} + siginfo@2.0.0: {} signal-exit@3.0.7: {} + signal-exit@4.1.0: + optional: true + sirv@3.0.0: dependencies: '@polka/url': 1.0.0-next.28 @@ -5085,8 +5931,14 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.1: + optional: true + std-env@3.8.0: {} + strict-event-emitter@0.5.1: + optional: true + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -5131,6 +5983,9 @@ snapshots: csso: 5.0.5 picocolors: 1.1.1 + symbol-tree@3.2.4: + optional: true + synckit@0.6.2: dependencies: tslib: 2.8.1 @@ -5156,6 +6011,14 @@ snapshots: tinyspy@3.0.2: {} + tldts-core@6.1.70: + optional: true + + tldts@6.1.70: + dependencies: + tldts-core: 6.1.70 + optional: true + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -5166,6 +6029,24 @@ snapshots: totalist@3.0.1: {} + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + optional: true + + tough-cookie@5.0.0: + dependencies: + tldts: 6.1.70 + optional: true + + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + optional: true + ts-api-utils@2.0.1(typescript@5.7.2): dependencies: typescript: 5.7.2 @@ -5180,6 +6061,9 @@ snapshots: type-fest@0.21.3: {} + type-fest@4.31.0: + optional: true + typescript-eslint@8.24.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2): dependencies: '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) @@ -5194,7 +6078,7 @@ snapshots: ufo@1.5.4: {} - unbuild@3.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2)): + unbuild@3.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2))(vue@3.5.13(typescript@5.7.2)): dependencies: '@rollup/plugin-alias': 5.1.1(rollup@4.29.1) '@rollup/plugin-commonjs': 28.0.2(rollup@4.29.1) @@ -5209,7 +6093,7 @@ snapshots: hookable: 5.5.3 jiti: 2.4.2 magic-string: 0.30.17 - mkdist: 2.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2)) + mkdist: 2.2.0(typescript@5.7.2)(vue-tsc@2.2.0(typescript@5.7.2))(vue@3.5.13(typescript@5.7.2)) mlly: 1.7.3 pathe: 1.1.2 pkg-types: 1.3.0 @@ -5232,6 +6116,9 @@ snapshots: unicorn-magic@0.1.0: {} + universalify@0.2.0: + optional: true + universalify@2.0.1: {} untyped@1.5.2: @@ -5257,6 +6144,12 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + optional: true + util-deprecate@1.0.2: {} vite-node@2.1.9(@types/node@22.10.5): @@ -5286,10 +6179,20 @@ snapshots: '@types/node': 22.10.5 fsevents: 2.3.3 - vitest@2.1.9(@types/node@22.10.5)(@vitest/ui@2.1.8): + vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2): + dependencies: + esbuild: 0.24.2 + postcss: 8.4.49 + rollup: 4.29.1 + optionalDependencies: + '@types/node': 22.10.5 + fsevents: 2.3.3 + jiti: 2.4.2 + + vitest@2.1.9(@types/node@22.10.5)(@vitest/ui@2.1.8)(jsdom@25.0.1)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)): dependencies: '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.14(@types/node@22.10.5)) + '@vitest/mocker': 2.1.9(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2))(vite@5.4.14(@types/node@22.10.5)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.9 '@vitest/snapshot': 2.1.9 @@ -5311,6 +6214,7 @@ snapshots: optionalDependencies: '@types/node': 22.10.5 '@vitest/ui': 2.1.8(vitest@2.1.9) + jsdom: 25.0.1 transitivePeerDependencies: - less - lightningcss @@ -5337,16 +6241,53 @@ snapshots: transitivePeerDependencies: - supports-color + vue-router@4.5.0(vue@3.5.13(typescript@5.7.2)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.13(typescript@5.7.2) + vue-tsc@2.2.0(typescript@5.7.2): dependencies: '@volar/typescript': 2.4.11 '@vue/language-core': 2.2.0(typescript@5.7.2) typescript: 5.7.2 + vue@3.5.13(typescript@5.7.2): + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-sfc': 3.5.13 + '@vue/runtime-dom': 3.5.13 + '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.2)) + '@vue/shared': 3.5.13 + optionalDependencies: + typescript: 5.7.2 + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + optional: true + wcwidth@1.0.1: dependencies: defaults: 1.0.4 + webidl-conversions@7.0.0: + optional: true + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + optional: true + + whatwg-mimetype@4.0.0: + optional: true + + whatwg-url@14.1.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + optional: true + which@1.3.1: dependencies: isexe: 2.0.0 @@ -5355,6 +6296,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@5.0.0: + dependencies: + isexe: 3.1.1 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -5380,8 +6325,17 @@ snapshots: wrappy@1.0.2: {} + ws@8.18.0: + optional: true + xml-name-validator@4.0.0: {} + xml-name-validator@5.0.0: + optional: true + + xmlchars@2.2.0: + optional: true + y18n@5.0.8: {} yallist@3.1.1: {} @@ -5401,3 +6355,6 @@ snapshots: yocto-queue@0.1.0: {} yocto-queue@1.1.1: {} + + yoctocolors-cjs@2.1.2: + optional: true diff --git a/src/plugin-dynamic-import-retry/README.md b/src/plugin-dynamic-import-retry/README.md new file mode 100644 index 0000000..f2da988 --- /dev/null +++ b/src/plugin-dynamic-import-retry/README.md @@ -0,0 +1,28 @@ +# plugin-dynamic-import-retry + +This plugin provides a retry mechanism for dynamic imports (both JS and CSS) in Vite. It helps to handle cases where dynamic imports might fail due to network issues or other transient problems by retrying the import a specified number of times. + +### Usage + +Import and use the plugin in your Vite configuration file: +```typescript +import { defineConfig } from 'vite' +import DynamicImportRetryPlugin from '@kong/vite-plugins/dynamic-import-retry' + +export default defineConfig({ + plugins: [ + DynamicImportRetryPlugin({ + include: /\.(js|ts|vue|tsx)$/, + retries: 3, + }), + ], +}) +``` + +### Configuration Options + +| Option | Type | Description | +|----------|-------------------------------------------|---------------------------------------------------------------| +| include | string \| RegExp \| (string \| RegExp)[] | Files to include, default is `\.(js\|ts\|vue\|tsx)$`. | +| exclude | string \| RegExp \| (string \| RegExp)[] | Files to exclude. | +| retries | number | Number of retry attempts, default is `3`. | diff --git a/src/plugin-dynamic-import-retry/css-preload-retry.ts b/src/plugin-dynamic-import-retry/css-preload-retry.ts new file mode 100644 index 0000000..8d76ed2 --- /dev/null +++ b/src/plugin-dynamic-import-retry/css-preload-retry.ts @@ -0,0 +1,57 @@ +interface VitePreloadErrorEvent extends Event { + payload: Error +} + +/** + * This function will be stringified and injected into html. + * Make sure everything is literally inlined, no imports or external references. + */ +export function createCSSPreloadRetry(options = { retries: 3 }) { + // error message: `Unable to preload CSS for /gateway-manager/assets/GatewayOverviewPage.BwFrm_fH.css` + // https://github.com/vitejs/vite/blob/21ec1ce7f041efa5cd781924f7bc536ab406a197/packages/vite/src/node/plugins/importAnalysisBuild.ts#L141 + const pathRegex = /\s([^\s]+\.css)/ + + // extract the css path from vite error message + function parseMessageBody(message: string) { + const [, cssPath] = message.match(pathRegex) || [] + return cssPath || null + } + + function loadCSS(path: string) { + return new Promise((resolve, reject) => { + const link = document.createElement('link') + link.rel = 'stylesheet' + link.href = path + link.crossOrigin = 'anonymous' + link.onload = resolve + link.onerror = reject + document.head.appendChild(link) + }) + } + + async function handleVitePreloadError(e: VitePreloadErrorEvent) { + if (!e.payload?.message || !e.payload.message.includes('Unable to preload CSS')) return + const cssPath = parseMessageBody(e.payload.message) + if (!cssPath) { + console.error('[preload-css-parse-failure]', e.payload.message) + return + } + e.preventDefault() // mute the original error + for (let i = 0; i < options.retries; i++) { + try { + const cacheBustedPath = `${cssPath}?t=${+new Date()}` + await loadCSS(cacheBustedPath) + return + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_) { + // avoid to create same timestamp for the next retry + await new Promise((resolve) => setTimeout(resolve, (1 + i) * 200)) + } + } + throw new Error(`[preload-css-retried] ${e.payload.message}`) + } + + // the event is emitted by vite + // https://github.com/vitejs/vite/blob/21ec1ce7f041efa5cd781924f7bc536ab406a197/packages/vite/src/node/plugins/importAnalysisBuild.ts#L150 + window.addEventListener('vite:preloadError', handleVitePreloadError as any) +} diff --git a/src/plugin-dynamic-import-retry/import-wrapper.ts b/src/plugin-dynamic-import-retry/import-wrapper.ts new file mode 100644 index 0000000..9014b8e --- /dev/null +++ b/src/plugin-dynamic-import-retry/import-wrapper.ts @@ -0,0 +1,71 @@ +/** + * This function will be stringified and injected into bundle. + * Make sure everything is literally inlined, no imports or external references. + */ +export function createImportWrapper(options = { retries: 3 }) { + type ImportFn = () => Promise + + const identity = (e: any) => e + const retry = createDynamicImportWithRetry(options.retries) + + /* + Copyright 2024 Carl-Erik Kopseng + https://github.com/fatso83/retry-dynamic-import + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + */ + type PositiveInteger = `${T}` extends + | '0' + | `-${any}` + | `${any}.${any}` + ? never + : T + + const uriOrRelativePathRegex = /["']((\w+:(\/?\/?))?[^\s]+)["']/ + function parseModulePathFromImporterBody(importer: () => any): string | null { + const fnString = importer.toString() + const match = fnString.match(uriOrRelativePathRegex) + if (!match) return null + return match.filter(identity)[1] + } + + function createDynamicImportWithRetry( + maxRetries: PositiveInteger, + importFunction: (path: string) => Promise = (path: string) => import(/* @vite-ignore */ path), + ) { + return async (importer: () => Promise) => { + try { + return await importer() + } catch (error) { + const modulePath = parseModulePathFromImporterBody(importer) + + if (!modulePath) { + throw error + } + + for (let i = 0; i < maxRetries; i++) { + // add a timestamp to the url to force a reload the module (and not use the cached version - cache busting) + const cacheBustedPath = `${modulePath}?t=${+new Date()}` + + try { + const result = await importFunction(cacheBustedPath) + return result + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_) { + // avoid to create same timestamp for the next retry + await new Promise((resolve) => setTimeout(resolve, (i + 1) * 200)) + } + } + throw new Error(`[dynamic-import-retry] ${error instanceof Error ? error.message = `[dynamic-import-retry] ${error.message}` : error}`) + } + } + } + + return function importWrapper(importFn: ImportFn): ImportFn { + return async function() { + return await retry(importFn) + } + } +} diff --git a/src/plugin-dynamic-import-retry/index.ts b/src/plugin-dynamic-import-retry/index.ts new file mode 100644 index 0000000..b80b19b --- /dev/null +++ b/src/plugin-dynamic-import-retry/index.ts @@ -0,0 +1,91 @@ +import type { HtmlTagDescriptor, Plugin } from 'vite' +import { simple } from 'acorn-walk' +import MagicString from 'magic-string' +import { createFilter } from '@rollup/pluginutils' + +import { createCSSPreloadRetry } from './css-preload-retry' +import { createImportWrapper } from './import-wrapper' + +const dynamicImportPrefixRE = /import\s*\(/ + +export type Options = { + include?: string | RegExp | (string | RegExp)[] + exclude?: string | RegExp | (string | RegExp)[] + retries?: number +} + +export const defaultOptions: Options = { + include: /\.(js|ts|vue|tsx)$/, + retries: 3, +} +export function DynamicImportRetryPlugin(opt?: Options): Plugin { + const options = { ...defaultOptions, ...opt } + const virtualModuleId = 'virtual:dynamic-import-retry' + const resolvedVirtualModuleId = '\0' + virtualModuleId + const filter = createFilter(options.include, options.exclude) + + return { + name: 'dynamic-import-retry', + apply: 'build', + + transformIndexHtml() { + const descriptor: HtmlTagDescriptor[] = [ + { + tag: 'script', + children: `(${createCSSPreloadRetry.toString()}({ retries: ${options.retries} }))`, + injectTo: 'head-prepend', + }, + ] + return descriptor + }, + + transform(code, id) { + if (!filter(id) || !dynamicImportPrefixRE.test(code)) { + return null + } + + const parsed = this.parse(code) + + let ms: MagicString | undefined + + simple(parsed, { + ImportExpression(node) { + ms = ms || new MagicString(code) + const start = node.start + const end = node.end + const path = code.slice(node.source.start, node.source.end) + ms.overwrite(start, end, `importWrapper(() => import(${path}))()`) + }, + }) + + if (ms) { + ms.prepend(`import { importWrapper } from '${virtualModuleId}'\n`) + + return { + code: ms.toString(), + map: ms.generateMap({ + file: id, + includeContent: true, + hires: true, + }), + } + } + + return null + }, + + resolveId(id) { + if (id === virtualModuleId) { + return resolvedVirtualModuleId + } + }, + + load(id) { + if (id === resolvedVirtualModuleId) { + return `${createImportWrapper.toString()} +const importWrapper = createImportWrapper({ retries: ${options.retries} }) +export { importWrapper }` + } + }, + } +} diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts new file mode 100644 index 0000000..e264dfc --- /dev/null +++ b/vitest.config.e2e.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'vitest/config' + +const timeout = process.env.CI ? 50000 : 30000 + +process.env.NODE_ENV = process.env.VITE_TEST_BUILD + ? 'production' + : 'development' + +export default defineConfig({ + test: { + include: ['./playground/**/*.spec.[tj]s'], + setupFiles: ['./playground/vitestSetup.ts'], + globalSetup: ['./playground/vitestGlobalSetup.ts'], + testTimeout: timeout, + hookTimeout: timeout, + reporters: 'dot', + onConsoleLog(log) { + if (log.match(/experimental|jit engine|emitted file|tailwind/i)) + return false + }, + }, +}) From d71f1138897832087bd68b6e362c09eb69f14b1f Mon Sep 17 00:00:00 2001 From: Zehao Zhang Date: Thu, 16 Jan 2025 11:13:11 +0800 Subject: [PATCH 02/14] feat: default exclude node_modules --- src/plugin-dynamic-import-retry/README.md | 30 ++++++++++++++----- .../import-wrapper.ts | 2 +- src/plugin-dynamic-import-retry/index.ts | 1 + 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/plugin-dynamic-import-retry/README.md b/src/plugin-dynamic-import-retry/README.md index f2da988..a4678f5 100644 --- a/src/plugin-dynamic-import-retry/README.md +++ b/src/plugin-dynamic-import-retry/README.md @@ -2,7 +2,9 @@ This plugin provides a retry mechanism for dynamic imports (both JS and CSS) in Vite. It helps to handle cases where dynamic imports might fail due to network issues or other transient problems by retrying the import a specified number of times. -### Usage +Inspired by [fatso83/retry-dynamic-import](https://github.com/fatso83/retry-dynamic-import). + +## Usage Import and use the plugin in your Vite configuration file: ```typescript @@ -11,18 +13,32 @@ import DynamicImportRetryPlugin from '@kong/vite-plugins/dynamic-import-retry' export default defineConfig({ plugins: [ - DynamicImportRetryPlugin({ - include: /\.(js|ts|vue|tsx)$/, - retries: 3, - }), + DynamicImportRetryPlugin(), ], }) ``` -### Configuration Options +## Configuration Options | Option | Type | Description | |----------|-------------------------------------------|---------------------------------------------------------------| | include | string \| RegExp \| (string \| RegExp)[] | Files to include, default is `\.(js\|ts\|vue\|tsx)$`. | -| exclude | string \| RegExp \| (string \| RegExp)[] | Files to exclude. | +| exclude | string \| RegExp \| (string \| RegExp)[] | Files to exclude. default is `/node_modules/` | | retries | number | Number of retry attempts, default is `3`. | + +## Limitations + +**Transitive imports:** This plugin only retries the top-level dynamic import. If the import itself contains other static imports, those will not be retried. +```ts +import('./a.js') // when you import `a.js`, it contains a static import for `b.js` + +// a.js +import './b.js' +// if `b.js` fails, even `a.js` itself is loaded successfully +// it still triggers a retry for `a.js` but it won't succeed because `b.js` is not retried. +``` + +**Variable import paths:** This plugin only supports static import paths. For example, the following will not work: +```ts +import(`./${path}`) +``` diff --git a/src/plugin-dynamic-import-retry/import-wrapper.ts b/src/plugin-dynamic-import-retry/import-wrapper.ts index 9014b8e..416ed5b 100644 --- a/src/plugin-dynamic-import-retry/import-wrapper.ts +++ b/src/plugin-dynamic-import-retry/import-wrapper.ts @@ -58,7 +58,7 @@ export function createImportWrapper(options = { retries: 3 }) { await new Promise((resolve) => setTimeout(resolve, (i + 1) * 200)) } } - throw new Error(`[dynamic-import-retry] ${error instanceof Error ? error.message = `[dynamic-import-retry] ${error.message}` : error}`) + throw new Error(`[dynamic-import-retry] ${error instanceof Error ? error.message : error}`) } } } diff --git a/src/plugin-dynamic-import-retry/index.ts b/src/plugin-dynamic-import-retry/index.ts index b80b19b..26d8a11 100644 --- a/src/plugin-dynamic-import-retry/index.ts +++ b/src/plugin-dynamic-import-retry/index.ts @@ -16,6 +16,7 @@ export type Options = { export const defaultOptions: Options = { include: /\.(js|ts|vue|tsx)$/, + exclude: /node_modules/, retries: 3, } export function DynamicImportRetryPlugin(opt?: Options): Plugin { From e6ed0b02abaf5d223c24d6eabbf0f27aead1ab56 Mon Sep 17 00:00:00 2001 From: Zehao Zhang Date: Fri, 17 Jan 2025 13:29:35 +0800 Subject: [PATCH 03/14] test: vue async component --- playground/README.md | 7 + playground/dynamic-import-retry/index.spec.ts | 78 ------------ .../vue-async-component/index.html | 12 ++ .../vue-async-component/src/App.vue | 31 +++++ .../src/AsyncComponent.vue | 29 +++++ .../src/ErrorComponent.vue | 3 + .../vue-async-component/src/HelloWorld.vue | 3 + .../src/LoadingComponent.vue | 3 + .../vue-async-component/src/main.ts | 5 + .../vue-async-component/tsconfig.json | 11 ++ .../vue-async-component/vite.config.ts | 19 +++ .../vue-async-component.spec.ts | 78 ++++++++++++ .../vue-router/src/AboutView.vue | 23 ---- .../vue-router/src/App.vue | 8 ++ .../vue-router/src/HomeView.vue | 9 -- .../vue-router/tsconfig.json | 2 +- .../vue-router/vite.config.ts | 2 - .../vue-router/vue-router.spec.ts | 120 ++++++++++++++++++ 18 files changed, 330 insertions(+), 113 deletions(-) delete mode 100644 playground/dynamic-import-retry/index.spec.ts create mode 100644 playground/dynamic-import-retry/vue-async-component/index.html create mode 100644 playground/dynamic-import-retry/vue-async-component/src/App.vue create mode 100644 playground/dynamic-import-retry/vue-async-component/src/AsyncComponent.vue create mode 100644 playground/dynamic-import-retry/vue-async-component/src/ErrorComponent.vue create mode 100644 playground/dynamic-import-retry/vue-async-component/src/HelloWorld.vue create mode 100644 playground/dynamic-import-retry/vue-async-component/src/LoadingComponent.vue create mode 100644 playground/dynamic-import-retry/vue-async-component/src/main.ts create mode 100644 playground/dynamic-import-retry/vue-async-component/tsconfig.json create mode 100644 playground/dynamic-import-retry/vue-async-component/vite.config.ts create mode 100644 playground/dynamic-import-retry/vue-async-component/vue-async-component.spec.ts create mode 100644 playground/dynamic-import-retry/vue-router/vue-router.spec.ts diff --git a/playground/README.md b/playground/README.md index 46959e5..96ce4d3 100644 --- a/playground/README.md +++ b/playground/README.md @@ -10,3 +10,10 @@ This directory serves as a testing environment for plugins. Each subdirectory co 2. Within this folder, set up one or more Vite projects for testing. 3. Write test files with the `.spec.ts` extension to validate your plugin’s functionality. 4. If browser-based testing is required, you can import `page` or `browser` from the `vitestSetup.ts` file to interact with the headless browser environment. + +## Run playground project manually +```bash +pnpm vite dev ./playground// +pnpm vite build ./playground// +pnpm vite preview ./playground// +``` diff --git a/playground/dynamic-import-retry/index.spec.ts b/playground/dynamic-import-retry/index.spec.ts deleted file mode 100644 index 2abfc3c..0000000 --- a/playground/dynamic-import-retry/index.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { beforeAll, expect, test } from 'vitest' -import { resolve } from 'node:path' -import type { InlineConfig } from 'vite' -import { build, preview } from 'vite' - -import { page, browserErrors, browserLogs } from '../vitestSetup' - -const root = resolve(__dirname, 'vue-router') - -let viteTestUrl: string - -beforeAll(async () => { - const testConfig: InlineConfig = { - configFile: resolve(root, 'vite.config.ts'), - } - - await build(testConfig) - const previewServer = await preview(testConfig) - - viteTestUrl = previewServer.resolvedUrls!.local[0] - await page.goto(viteTestUrl) - - return async () => { - previewServer.close() - } -}) - -test('should dynamic import module successful', async () => { - await page.click('#nav-about') - await page.waitForTimeout(100) - expect(await page.textContent('h2')).toMatch('AboutView') -}) - -const resources = ['css', 'js'] - -for (const resourceType of resources) { - const filter = (url: URL) => url.pathname.includes('AboutView') && url.pathname.includes(`.${resourceType}`) - - test('should recover by retrying import ' + resourceType, async () => { - await page.goto(viteTestUrl) - - let retries = 0 - await page.route(filter, route => { - retries++ - if (retries < 3) { - return route.fulfill({ status: 404, body: 'Not Found' }) - } - return route.continue() - }) - await page.click('#nav-about') - await page.waitForTimeout(1000) - expect(await page.textContent('h2')).toMatch('AboutView') - expect(retries).toBe(3) - }) - - test('should has an error when reach max attempts on loading ' + resourceType, async () => { - await page.goto(viteTestUrl) - - await page.route(filter, route => { - return route.fulfill({ status: 404, body: 'Not Found' }) - }) - await page.click('#nav-about') - await page.waitForTimeout(1500) - - if (resourceType === 'js') { - expect(await page.textContent('h2')).toMatch('HomeView') - } - - if (resourceType === 'css') { - const e = browserErrors.find(e => e.message.includes('[preload-css-retried]')) - expect(e).toBeDefined() - } else { - const e = browserLogs.find(l => l.includes('[dynamic-import-retry]')) - expect(e).toBeDefined() - } - }) -} - diff --git a/playground/dynamic-import-retry/vue-async-component/index.html b/playground/dynamic-import-retry/vue-async-component/index.html new file mode 100644 index 0000000..e763020 --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/index.html @@ -0,0 +1,12 @@ + + + + + + Vite + Vue + TS + + +
+ + + diff --git a/playground/dynamic-import-retry/vue-async-component/src/App.vue b/playground/dynamic-import-retry/vue-async-component/src/App.vue new file mode 100644 index 0000000..7e7e4eb --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/src/App.vue @@ -0,0 +1,31 @@ + + + diff --git a/playground/dynamic-import-retry/vue-async-component/src/AsyncComponent.vue b/playground/dynamic-import-retry/vue-async-component/src/AsyncComponent.vue new file mode 100644 index 0000000..dc79037 --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/src/AsyncComponent.vue @@ -0,0 +1,29 @@ + + + diff --git a/playground/dynamic-import-retry/vue-async-component/src/ErrorComponent.vue b/playground/dynamic-import-retry/vue-async-component/src/ErrorComponent.vue new file mode 100644 index 0000000..25cd2ef --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/src/ErrorComponent.vue @@ -0,0 +1,3 @@ + diff --git a/playground/dynamic-import-retry/vue-async-component/src/HelloWorld.vue b/playground/dynamic-import-retry/vue-async-component/src/HelloWorld.vue new file mode 100644 index 0000000..7a8044e --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/src/HelloWorld.vue @@ -0,0 +1,3 @@ + diff --git a/playground/dynamic-import-retry/vue-async-component/src/LoadingComponent.vue b/playground/dynamic-import-retry/vue-async-component/src/LoadingComponent.vue new file mode 100644 index 0000000..3df7615 --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/src/LoadingComponent.vue @@ -0,0 +1,3 @@ + diff --git a/playground/dynamic-import-retry/vue-async-component/src/main.ts b/playground/dynamic-import-retry/vue-async-component/src/main.ts new file mode 100644 index 0000000..e0742a6 --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App) + .mount('#app') diff --git a/playground/dynamic-import-retry/vue-async-component/tsconfig.json b/playground/dynamic-import-retry/vue-async-component/tsconfig.json new file mode 100644 index 0000000..2f16ef3 --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist", + "declaration": false, + "declarationDir": null, + "types": ["vite/client"] + }, + "include": ["vite.config.ts.bak"] +} diff --git a/playground/dynamic-import-retry/vue-async-component/vite.config.ts b/playground/dynamic-import-retry/vue-async-component/vite.config.ts new file mode 100644 index 0000000..9911dc6 --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'node:path' + +import { DynamicImportRetryPlugin } from '../../../src/plugin-dynamic-import-retry' + +export default defineConfig({ + root: resolve(__dirname), + plugins: [ + vue(), + DynamicImportRetryPlugin(), + ], + build: { + minify: false, + outDir: 'dist', + target: 'esnext', + emptyOutDir: false, + }, +}) diff --git a/playground/dynamic-import-retry/vue-async-component/vue-async-component.spec.ts b/playground/dynamic-import-retry/vue-async-component/vue-async-component.spec.ts new file mode 100644 index 0000000..9cb1bfa --- /dev/null +++ b/playground/dynamic-import-retry/vue-async-component/vue-async-component.spec.ts @@ -0,0 +1,78 @@ +import { beforeAll, expect, test } from 'vitest' +import { build, preview, loadConfigFromFile } from 'vite' +import { resolve } from 'node:path' + +import { page, browserErrors, browserLogs } from '../../vitestSetup' + +import type { InlineConfig } from 'vite' +import { describe } from 'vitest' + +let viteTestUrl: string + +beforeAll(async () => { + const res = await loadConfigFromFile( + { + command: 'build', + mode: 'production', + }, + undefined, + resolve(__dirname), + ) + if (!res) throw new Error('Failed to load config') + + const testConfig: InlineConfig = { + ...res.config, + logLevel: 'silent', + configFile: false, + } + + await build(testConfig) + const previewServer = await preview(testConfig) + + viteTestUrl = previewServer.resolvedUrls!.local[0] + + return async () => { + previewServer.close() + } +}) + +test('should loading component displayed', async () => { + await page.goto(viteTestUrl) + await page.waitForTimeout(200) + expect(await page.textContent('#app')).toMatch('Loading...') + await page.waitForTimeout(500) + expect(await page.textContent('#app')).toMatch('Hello, World!') +}) + +test('should error component displayed when timeout', async () => { + await page.goto(`${viteTestUrl}?timeout=1`) + expect(await page.textContent('#app')).toMatch('Error') +}) + +test('should recover when dynamic import failed', async () => { + await page.goto(`${viteTestUrl}?latency=0`) + let retries = 0 + await page.route( + (url) => url.pathname.includes('HelloWorld') && url.pathname.includes('.js'), + route => { + retries++ + if (retries < 2) { + return route.fulfill({ status: 404, body: 'Not Found' }) + } + return route.continue() + }, + ) + await page.waitForTimeout(1000) + expect(await page.textContent('#app')).toMatch('Hello, World!') + expect(retries).toBe(2) +}) + +test('should display error when reach max attempts on dynamic import', async () => { + await page.goto(`${viteTestUrl}?latency=0&timeout=5000`) + await page.route( + (url) => url.pathname.includes('HelloWorld') && url.pathname.includes('.js'), + route => route.fulfill({ status: 404, body: 'Not Found' }), + ) + await page.waitForTimeout(1800) + expect(await page.textContent('#app')).toMatch('Error') +}) diff --git a/playground/dynamic-import-retry/vue-router/src/AboutView.vue b/playground/dynamic-import-retry/vue-router/src/AboutView.vue index d5967fb..dec677f 100644 --- a/playground/dynamic-import-retry/vue-router/src/AboutView.vue +++ b/playground/dynamic-import-retry/vue-router/src/AboutView.vue @@ -1,28 +1,5 @@ - - diff --git a/playground/dynamic-import-retry/vue-router/src/HomeView.vue b/playground/dynamic-import-retry/vue-router/src/HomeView.vue index 7c54224..e9b06fe 100644 --- a/playground/dynamic-import-retry/vue-router/src/HomeView.vue +++ b/playground/dynamic-import-retry/vue-router/src/HomeView.vue @@ -1,14 +1,5 @@ - -