diff --git a/.changeset/bumpy-feet-bow.md b/.changeset/bumpy-feet-bow.md new file mode 100644 index 0000000..0a1a10c --- /dev/null +++ b/.changeset/bumpy-feet-bow.md @@ -0,0 +1,5 @@ +--- +"ui-test-visualizer": minor +--- + +Add "Record input as code" feature diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 633b564..4d9100f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -84,5 +84,7 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - run: pnpm run build + # Just run the "extension" tests to make sure the paths are detected correctly on Windows - run: pnpm run --filter ./packages/extension test diff --git a/.vscode/launch.json b/.vscode/launch.json index b264dba..7ae4d97 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -90,7 +90,7 @@ "sourceMaps": true }, { - "name": "Bun Test Runner Example", + "name": "Bun + React Example", "type": "extensionHost", "request": "launch", "args": [ @@ -100,6 +100,17 @@ "outFiles": ["${workspaceFolder}/build-dev/**/*.js"], "sourceMaps": true }, + { + "name": "Bun + Solid Example", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}/build-dev", + "${workspaceFolder}/examples/bun-solid" + ], + "outFiles": ["${workspaceFolder}/build-dev/**/*.js"], + "sourceMaps": true + }, { "name": "All examples", "type": "extensionHost", diff --git a/README.md b/README.md index 004bc01..a1cf380 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,41 @@ This is a VSCode extension: no code changes should be required to watch your UI - [Vitest](https://vitest.dev/) or [Jest v28+](https://jestjs.io/) or [Bun Test Runner](https://bun.com/docs/test/dom) - Your test framework is auto-detected by walking up the directories from your test file until it finds `vitest.config.{ts,js}`, `vite.config.{ts,js}`, `jest.config.{ts,js,json}`, or `bun.lock` + - The [Bun VSCode extension](https://marketplace.visualstudio.com/items?itemName=oven.bun-vscode) is required for Bun tests - [jsdom](https://github.com/jsdom/jsdom) or [happy-dom](https://github.com/capricorn86/happy-dom) test environment (what you're probably already using for UI in Vitest or Jest) ## Usage +### Visualizing tests + Click the "Visually Debug UI" button above your test. If there's no breakpoint in your test already, the extension will add one automatically. A side panel should open, and render your UI as you step through with the debugger. +### Record Input as Code + +You can also write tests by recording your input as you interact with your UI. + +**Project Requirements:** +- Test runner: Vitest, Jest, or Bun +- Testing libraries: + - `@testing-library/react` or `@solidjs/testing-library` + - `@testing-library/user-event` + +Steps: + +- *Optional*: Generate a starter test file for a React or Solid component by right-clicking the component name in the editor (e.g. MyForm) and clicking "Create UI test". + +- Click the "Visually Debug UI" button to start your test + +- Click "Step Over" until you get your UI into the state where you want to generate new code. + +- Click the "Record input as code" button in the side panel, and you'll see the recorder panel appear. + +- Click your UI elements, or change text inputs, and the extension will generate code for you to use in your test. + +- Right+click to generate `expect` statements or other mouse events. + +- The generated code is inserted into your test file when the test is ended or restarted. + ## UI Framework Compatibility Since this extension hooks into the DOM API directly, you should be able to use any DOM-based UI framework, including React, Solid, Angular, Vue, Svelte, jQuery, etc. @@ -50,14 +79,14 @@ This extension could fail to auto-build your source CSS files, in which case you - Vitest: - Adds a `--require` argument to the Vitest command. - Jest: - - Adds a `--setupFiles` argument to the Jest command in addition to any setupFiles you've already defined in your config. + - Adds a `--setupFiles` argument to the Jest command in addition to any setupFiles defined in your config. + - Bun: + - Adds a `--preload` argument to the `bun test` command in addition to any preloads defined in your bunfig.toml. - **Replicates the test DOM into a real DOM**: The extension then replicates those method calls and their arguments in a VSCode WebView (the side panel), which renders your UI in a real Chromium DOM. This panel only shows a **replica** of the test DOM without your UI's Javascript, so you can't interact with it using the mouse or keyboard. ## Caveats -- **Can be slow to run until your first breakpoint**: Because of the code inserted at startup for watching the DOM and optionally loading your styles, your test can be slightly slower to startup than when debugging your tests normally with other test extensions. - - **Possible de-synchronization**: The visual DOM replica gets updated incrementally as your test runs, but accurate synchronization relies on this extension's code to correctly handle every possible DOM mutation that happens in your UI. The DOM replication code is pretty thorough, even accounting for weird cases involving nested Shadow DOMs and Web Component lifecycles, but it's still possible for the visual replica to get out of sync with your actual test DOM. If this happens, you can click the panel's Refresh button to re-sync it. ## Contributing / Extension Development diff --git a/eslint.config.js b/eslint.config.js index 9766bea..894efd6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -16,5 +16,6 @@ module.exports = config({ // Convenient for this project 'node/prefer-global/process': 'off', 'antfu/no-import-node-modules-by-path': 'off', + 'test/consistent-test-it': 'off', }, }) diff --git a/examples/bun-react/bun.lock b/examples/bun-react/bun.lock index 3ba0289..81316bc 100644 --- a/examples/bun-react/bun.lock +++ b/examples/bun-react/bun.lock @@ -10,9 +10,9 @@ "react-dom": "^19.2.0", }, "devDependencies": { - "@types/bun": "^1.3.0", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.1", + "bun-types": "^1.3.0", }, }, }, @@ -31,9 +31,7 @@ "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], - "@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="], - - "@types/node": ["@types/node@20.19.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA=="], + "@types/node": ["@types/node@24.7.2", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA=="], "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], @@ -73,12 +71,16 @@ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], - "bun-types/@types/node": ["@types/node@24.7.2", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA=="], + "@happy-dom/global-registrator/@types/node": ["@types/node@20.19.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA=="], + + "happy-dom/@types/node": ["@types/node@20.19.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA=="], + + "@happy-dom/global-registrator/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "bun-types/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "happy-dom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], } } diff --git a/examples/bun-react/bunfig.toml b/examples/bun-react/bunfig.toml index ec27f0f..c08e690 100644 --- a/examples/bun-react/bunfig.toml +++ b/examples/bun-react/bunfig.toml @@ -1,6 +1,3 @@ -[serve.static] -env = "BUN_PUBLIC_*" - [install] linker = "isolated" diff --git a/examples/bun-react/package.json b/examples/bun-react/package.json index 079abf6..9df88fc 100644 --- a/examples/bun-react/package.json +++ b/examples/bun-react/package.json @@ -14,6 +14,6 @@ "devDependencies": { "@types/react": "^19.2.2", "@types/react-dom": "^19.2.1", - "@types/bun": "^1.3.0" + "bun-types": "^1.3.0" } } diff --git a/examples/bun-react/src/Counter.tsx b/examples/bun-react/src/Counter.tsx index c1c3883..e9c4449 100644 --- a/examples/bun-react/src/Counter.tsx +++ b/examples/bun-react/src/Counter.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' export function Counter() { const [count, setCount] = useState(0) @@ -14,7 +14,7 @@ export function Counter() { return (

Counter

-

Count: {count}

+
Count: {count}
diff --git a/examples/bun-react/test/basic.test.tsx b/examples/bun-react/test/basic.test.tsx index 59480b8..cf59e7b 100644 --- a/examples/bun-react/test/basic.test.tsx +++ b/examples/bun-react/test/basic.test.tsx @@ -9,5 +9,5 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) diff --git a/examples/bun-react/tsconfig.json b/examples/bun-react/tsconfig.json index 632a36f..08dec4b 100644 --- a/examples/bun-react/tsconfig.json +++ b/examples/bun-react/tsconfig.json @@ -29,7 +29,9 @@ // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false + "noPropertyAccessFromIndexSignature": false, + + "types": ["bun-types"] }, "exclude": ["dist", "node_modules"] diff --git a/examples/bun-solid/.gitignore b/examples/bun-solid/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/examples/bun-solid/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/examples/bun-solid/README.md b/examples/bun-solid/README.md new file mode 100644 index 0000000..54a7261 --- /dev/null +++ b/examples/bun-solid/README.md @@ -0,0 +1,15 @@ +# bun-solid + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/examples/bun-solid/bun.lock b/examples/bun-solid/bun.lock new file mode 100644 index 0000000..349ad68 --- /dev/null +++ b/examples/bun-solid/bun.lock @@ -0,0 +1,207 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "bun-solid", + "dependencies": { + "solid-js": "^1.9.9", + }, + "devDependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", + "@happy-dom/global-registrator": "^20.0.5", + "@solidjs/testing-library": "^0.8.10", + "@testing-library/user-event": "^14.6.1", + "@types/babel__core": "^7.20.5", + "babel-preset-solid": "^1.9.9", + "bun-types": "^1.3.0", + "happy-dom": "^20.0.5", + "typescript": "^5.9.3", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="], + + "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], + + "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], + + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], + + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + + "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], + + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="], + + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ=="], + + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], + + "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="], + + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg=="], + + "@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="], + + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], + + "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + + "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.5", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.5" } }, "sha512-RWQ85on4fiYKw5D1xk0nU2YGCYsJEJ5iXmZHTRyeYft4OCN4IRMFZ3uX91/Tncf/9K4tPOnU1HiEOtLs2Zxukw=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@solidjs/testing-library": ["@solidjs/testing-library@0.8.10", "", { "dependencies": { "@testing-library/dom": "^10.4.0" }, "peerDependencies": { "@solidjs/router": ">=0.9.0", "solid-js": ">=1.0.0" }, "optionalPeers": ["@solidjs/router"] }, "sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + + "@types/node": ["@types/node@20.19.22", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ=="], + + "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + + "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "babel-plugin-jsx-dom-expressions": ["babel-plugin-jsx-dom-expressions@0.40.1", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2", "validate-html-nesting": "^1.2.1" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-b4iHuirqK7RgaMzB2Lsl7MqrlDgQtVRSSazyrmx7wB3T759ggGjod5Rkok5MfHjQXhR7tRPmdwoeGPqBnW2KfA=="], + + "babel-preset-solid": ["babel-preset-solid@1.9.9", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.1" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.8" }, "optionalPeers": ["solid-js"] }, "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w=="], + + "browserslist": ["browserslist@4.26.3", "", { "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", "electron-to-chromium": "^1.5.227", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w=="], + + "bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.237", "", {}, "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "happy-dom": ["happy-dom@20.0.5", "", { "dependencies": { "@types/node": "^20.0.0", "@types/whatwg-mimetype": "^3.0.2", "whatwg-mimetype": "^3.0.0" } }, "sha512-AiqA0rfS7WR1kihXt9W9aA5LFLaOKzwiL+QoI7BkOQ0r21C7VHTOf4k8QNlnWYaHLhpI2tZzJPLV1lY1obDTmw=="], + + "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "node-releases": ["node-releases@2.0.25", "", {}, "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="], + + "seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], + + "solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], + + "validate-html-nesting": ["validate-html-nesting@1.2.3", "", {}, "sha512-kdkWdCl6eCeLlRShJKbjVOU2kFKxMF8Ghu50n+crEoyx+VKm3FxAxF9z4DCy6+bbTOqNW0+jcIYRnjoIRzigRw=="], + + "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], + + "bun-types/@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="], + + "bun-types/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + } +} diff --git a/examples/bun-solid/bunfig.toml b/examples/bun-solid/bunfig.toml new file mode 100644 index 0000000..81650dd --- /dev/null +++ b/examples/bun-solid/bunfig.toml @@ -0,0 +1,7 @@ +jsx = "solid" + +[install] +linker = "isolated" + +[test] +preload = "./test/test-setup.ts" diff --git a/examples/bun-solid/index.ts b/examples/bun-solid/index.ts new file mode 100644 index 0000000..f67b2c6 --- /dev/null +++ b/examples/bun-solid/index.ts @@ -0,0 +1 @@ +console.log("Hello via Bun!"); \ No newline at end of file diff --git a/examples/bun-solid/package.json b/examples/bun-solid/package.json new file mode 100644 index 0000000..988ab03 --- /dev/null +++ b/examples/bun-solid/package.json @@ -0,0 +1,24 @@ +{ + "name": "bun-solid", + "private": true, + "type": "module", + "scripts": { + "test": "bun test --conditions=browser" + }, + "devDependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", + "@happy-dom/global-registrator": "^20.0.5", + "@solidjs/testing-library": "^0.8.10", + "@testing-library/user-event": "^14.6.1", + "@types/babel__core": "^7.20.5", + "babel-preset-solid": "^1.9.9", + "bun-types": "^1.3.0", + "happy-dom": "^20.0.5", + "typescript": "^5.9.3" + }, + "dependencies": { + "solid-js": "^1.9.9" + } +} diff --git a/examples/bun-solid/src/Counter.tsx b/examples/bun-solid/src/Counter.tsx new file mode 100644 index 0000000..fc05b20 --- /dev/null +++ b/examples/bun-solid/src/Counter.tsx @@ -0,0 +1,22 @@ +import { createSignal } from 'solid-js' + +export function Counter() { + const [count, setCount] = createSignal(0) + + function increment() { + setCount(count() + 1) + }; + + function decrement() { + setCount(count() - 1) + }; + + return ( +
+

Counter

+
Count: {count()}
+ + +
+ ) +} diff --git a/examples/bun-solid/src/FormExample.tsx b/examples/bun-solid/src/FormExample.tsx new file mode 100644 index 0000000..21bcf6b --- /dev/null +++ b/examples/bun-solid/src/FormExample.tsx @@ -0,0 +1,54 @@ +import { createSignal } from 'solid-js' + +export function FormExample() { + const [submitCount, setSubmitCount] = createSignal(0) + return ( +
+
+ Submit Count: {submitCount()} +
+
+

Form Section

+
+
{ + e.preventDefault() + setSubmitCount(c => c + 1) + }} + > + + + + +
+
+
+
+ ) +} diff --git a/examples/bun-solid/src/blue-title.css b/examples/bun-solid/src/blue-title.css new file mode 100644 index 0000000..ccf22a0 --- /dev/null +++ b/examples/bun-solid/src/blue-title.css @@ -0,0 +1,3 @@ +h1 { + color: blue; +} diff --git a/examples/bun-solid/test/basic.test.tsx b/examples/bun-solid/test/basic.test.tsx new file mode 100644 index 0000000..a65db18 --- /dev/null +++ b/examples/bun-solid/test/basic.test.tsx @@ -0,0 +1,13 @@ +import { expect, it } from 'bun:test' +import { fireEvent, render, screen } from '@solidjs/testing-library' +import { Counter } from '../src/Counter' + +it('simple react testing library test', () => { + render(() => ) + + fireEvent.click(screen.getByText('Increment')) + fireEvent.click(screen.getByText('Increment')) + fireEvent.click(screen.getByText('Decrement')) + + expect(screen.getByText('Count: 1')) +}) diff --git a/examples/bun-solid/test/form-test-for-e2e.test.tsx b/examples/bun-solid/test/form-test-for-e2e.test.tsx new file mode 100644 index 0000000..ec0317d --- /dev/null +++ b/examples/bun-solid/test/form-test-for-e2e.test.tsx @@ -0,0 +1,10 @@ +import { render, screen } from '@solidjs/testing-library' +import { describe, expect, it } from 'bun:test' +import { FormExample } from '../src/FormExample' + +describe('form test for e2e test', () => { + it('basic usage', async () => { + render(() => ) + expect(screen.getByLabelText('First input')) + }) +}) diff --git a/examples/bun-solid/test/test-setup.ts b/examples/bun-solid/test/test-setup.ts new file mode 100644 index 0000000..41c193d --- /dev/null +++ b/examples/bun-solid/test/test-setup.ts @@ -0,0 +1,31 @@ +import { transformAsync } from '@babel/core' +import { GlobalRegistrator } from '@happy-dom/global-registrator' + +// @ts-expect-error no types +import solid from 'babel-preset-solid' +// @ts-expect-error no types +import ts from '@babel/preset-typescript' + +GlobalRegistrator.register() + +await Bun.plugin({ + name: 'bun-plugin-solid', + setup: (build) => { + build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => { + const code = await Bun.file(args.path).text() + const transforms = await transformAsync(code, { + filename: args.path, + presets: [ + [solid, { generate: 'dom', debug: true }], + [ts, {}], + ], + retainLines: true, + }) + + return { + contents: transforms!.code!, + loader: 'js', + } + }) + }, +}) diff --git a/examples/bun-solid/tsconfig.json b/examples/bun-solid/tsconfig.json new file mode 100644 index 0000000..b8f7753 --- /dev/null +++ b/examples/bun-solid/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "preserve", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + + "types": ["bun-types", "@babel/types"] + } +} diff --git a/examples/jest-babel-nextjs/package.json b/examples/jest-babel-nextjs/package.json index 68ef533..698ac70 100644 --- a/examples/jest-babel-nextjs/package.json +++ b/examples/jest-babel-nextjs/package.json @@ -5,13 +5,13 @@ "test": "jest" }, "dependencies": { - "next": "^15.5.4", + "next": "^15.5.6", "react": "^19.2.0", "react-dom": "^19.2.0" }, "devDependencies": { "@babel/runtime": "^7.28.4", - "@testing-library/jest-dom": "^6.9.0", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", @@ -20,6 +20,6 @@ "identity-obj-proxy": "^3.0.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", - "typescript": "^5.9.2" + "typescript": "^5.9.3" } } diff --git a/examples/jest-monorepo/packages/inner-package/test/basic.test.tsx b/examples/jest-monorepo/packages/inner-package/test/basic.test.tsx index cc68005..e098e49 100644 --- a/examples/jest-monorepo/packages/inner-package/test/basic.test.tsx +++ b/examples/jest-monorepo/packages/inner-package/test/basic.test.tsx @@ -9,5 +9,5 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) diff --git a/examples/jest-nextjs-minimal/components/TodoList.test.tsx b/examples/jest-nextjs-minimal/components/TodoList.test.tsx new file mode 100644 index 0000000..dcb5476 --- /dev/null +++ b/examples/jest-nextjs-minimal/components/TodoList.test.tsx @@ -0,0 +1,6 @@ +import { render } from '@testing-library/react' +import { TodoList } from './TodoList' + +test('basic usage', async () => { + (() => render())() +}) diff --git a/examples/jest-nextjs-minimal/components/TodoList.tsx b/examples/jest-nextjs-minimal/components/TodoList.tsx new file mode 100644 index 0000000..111e0fc --- /dev/null +++ b/examples/jest-nextjs-minimal/components/TodoList.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react' + +interface Todo { + id: number + text: string + completed: boolean +} + +export function TodoList() { + const [todos, setTodos] = useState([]) + const [input, setInput] = useState('') + + const addTodo = () => { + if (input.trim()) { + setTodos([...todos, { id: Date.now(), text: input, completed: false }]) + setInput('') + } + } + + const toggleTodo = (id: number) => { + setTodos(todos.map(todo => (todo.id === id ? { ...todo, completed: !todo.completed } : todo))) + } + + const deleteTodo = (id: number) => { + setTodos(todos.filter(todo => todo.id !== id)) + } + + const doneCount = todos.filter(todo => todo.completed).length + const notDoneCount = todos.filter(todo => !todo.completed).length + + return ( +
+
+
+

Todo List

+
+
{doneCount} done
+
{notDoneCount} in progress
+
+
+ +
+ setInput(e.target.value)} + onKeyDown={e => e.key === 'Enter' && addTodo()} + placeholder="Add a new todo" + className="flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-white placeholder:text-gray-500 focus:outline-none focus:ring-1 focus:ring-gray-600" + /> + +
+ +
+ {todos.map(todo => ( +
+ toggleTodo(todo.id)} + className="h-4 w-4 rounded border-gray-600 cursor-pointer" + aria-label="Toggle done" + /> + + {todo.text} + + +
+ ))} +
+ + {todos.length === 0 && ( +

No todos yet. Add one to get started.

+ )} +
+
+ ) +} diff --git a/examples/jest-nextjs-minimal/package.json b/examples/jest-nextjs-minimal/package.json index 81b7591..5fec0dd 100644 --- a/examples/jest-nextjs-minimal/package.json +++ b/examples/jest-nextjs-minimal/package.json @@ -5,18 +5,20 @@ "test": "jest" }, "dependencies": { - "next": "^15.5.4", + "next": "^15.5.6", "react": "^19.2.0", "react-dom": "^19.2.0", "server-only": "^0.0.1" }, "devDependencies": { - "@testing-library/jest-dom": "^6.9.0", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", "@types/react": "^19.2.2", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", - "typescript": "^5.9.2" + "tailwindcss": "^4.1.16", + "typescript": "^5.9.3" } } diff --git a/examples/jest-nextjs-minimal/styles/global.css b/examples/jest-nextjs-minimal/styles/global.css index 4d03bee..05a2710 100644 --- a/examples/jest-nextjs-minimal/styles/global.css +++ b/examples/jest-nextjs-minimal/styles/global.css @@ -1,3 +1,5 @@ +@import "tailwindcss"; + html, body { padding: 0; diff --git a/examples/jest-nextjs-ts-node/app/counter.test.tsx b/examples/jest-nextjs-ts-node/app/counter.test.tsx index 70dbe57..314b5b3 100644 --- a/examples/jest-nextjs-ts-node/app/counter.test.tsx +++ b/examples/jest-nextjs-ts-node/app/counter.test.tsx @@ -10,7 +10,7 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) // For some reason calling 'render' in the test block needs multiple diff --git a/examples/jest-nextjs-ts-node/package.json b/examples/jest-nextjs-ts-node/package.json index f50e020..893cb80 100644 --- a/examples/jest-nextjs-ts-node/package.json +++ b/examples/jest-nextjs-ts-node/package.json @@ -5,19 +5,19 @@ "test": "jest" }, "dependencies": { - "next": "^15.5.4", + "next": "^15.5.6", "react": "^19.2.0", "react-dom": "^19.2.0", "server-only": "^0.0.1" }, "devDependencies": { - "@testing-library/jest-dom": "^6.9.0", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@types/jest": "^30.0.0", "@types/react": "^19.2.2", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "ts-node": "^10.9.2", - "typescript": "^5.9.2" + "typescript": "^5.9.3" } } diff --git a/examples/jest-react-with-packagejson-config/test/basic.test.tsx b/examples/jest-react-with-packagejson-config/test/basic.test.tsx index cc68005..e098e49 100644 --- a/examples/jest-react-with-packagejson-config/test/basic.test.tsx +++ b/examples/jest-react-with-packagejson-config/test/basic.test.tsx @@ -9,5 +9,5 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) diff --git a/examples/jest-react/components/Counter.tsx b/examples/jest-react/components/Counter.tsx index c1c3883..54f7f73 100644 --- a/examples/jest-react/components/Counter.tsx +++ b/examples/jest-react/components/Counter.tsx @@ -14,7 +14,7 @@ export function Counter() { return (

Counter

-

Count: {count}

+

Count: {count}

diff --git a/examples/jest-react/components/FormExample.tsx b/examples/jest-react/components/FormExample.tsx new file mode 100644 index 0000000..d4762a4 --- /dev/null +++ b/examples/jest-react/components/FormExample.tsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react' + +export function FormExample() { + const [submitCount, setSubmitCount] = useState(0) + return ( +
+
+ Submit Count: {submitCount} +
+
+

Form Section

+
+
{ + e.preventDefault() + setSubmitCount(c => c + 1) + }} + > + + + + +
+
+
+
+ ) +} diff --git a/examples/jest-react/package.json b/examples/jest-react/package.json index 3afaa89..5016deb 100644 --- a/examples/jest-react/package.json +++ b/examples/jest-react/package.json @@ -12,6 +12,7 @@ "@babel/preset-react": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", "@types/react": "^19.2.2", "jest": "^30.2.0", diff --git a/examples/jest-react/test/basic.test.tsx b/examples/jest-react/test/basic.test.tsx index cc68005..e098e49 100644 --- a/examples/jest-react/test/basic.test.tsx +++ b/examples/jest-react/test/basic.test.tsx @@ -9,5 +9,5 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) diff --git a/examples/jest-react/test/form-test-for-e2e.test.tsx b/examples/jest-react/test/form-test-for-e2e.test.tsx new file mode 100644 index 0000000..c3ba43f --- /dev/null +++ b/examples/jest-react/test/form-test-for-e2e.test.tsx @@ -0,0 +1,10 @@ +import { render, screen } from '@testing-library/react' +import React from 'react' +import { FormExample } from '../components/FormExample' + +describe('form test for e2e test', () => { + it('basic usage', async () => { + render() + expect(screen.getByLabelText('First input')) + }) +}) diff --git a/examples/vitest-react-tailwind3/package.json b/examples/vitest-react-tailwind3/package.json index b044293..cee6479 100644 --- a/examples/vitest-react-tailwind3/package.json +++ b/examples/vitest-react-tailwind3/package.json @@ -13,7 +13,7 @@ "@vitejs/plugin-react": "^5.0.4", "autoprefixer": "^10.4.21", "happy-dom": "^14.11.4", - "less": "^4.4.1", + "less": "^4.4.2", "postcss": "^8.5.6", "sass": "^1.93.2", "stylus": "^0.64.0", diff --git a/examples/vitest-react-tailwind3/test/basic.test.tsx b/examples/vitest-react-tailwind3/test/basic.test.tsx index c8b5763..5bb8ee9 100644 --- a/examples/vitest-react-tailwind3/test/basic.test.tsx +++ b/examples/vitest-react-tailwind3/test/basic.test.tsx @@ -9,5 +9,5 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) diff --git a/examples/vitest-react-tailwind4/components/FormExample.tsx b/examples/vitest-react-tailwind4/components/FormExample.tsx new file mode 100644 index 0000000..cdf93b1 --- /dev/null +++ b/examples/vitest-react-tailwind4/components/FormExample.tsx @@ -0,0 +1,79 @@ +import { useState } from 'react' + +export function FormExample() { + const [submitCount, setSubmitCount] = useState(0) + return ( +
+
+ Submit Count: {submitCount} +
+
+

Form Section

+
+
{ + e.preventDefault() + setSubmitCount(c => c + 1) + }} + > + + + + +
+
+

Non-Form Section

+
+ + + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+
+
+
+ ) +} diff --git a/examples/vitest-react-tailwind4/package.json b/examples/vitest-react-tailwind4/package.json index 37526ae..d06845f 100644 --- a/examples/vitest-react-tailwind4/package.json +++ b/examples/vitest-react-tailwind4/package.json @@ -9,14 +9,15 @@ "react": "^19.2.0" }, "devDependencies": { - "@tailwindcss/vite": "^4.1.13", + "@tailwindcss/vite": "^4.1.14", "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/react": "^19.2.2", "@vitejs/plugin-react": "^5.0.4", "happy-dom": "^14.11.4", "sass": "^1.93.2", - "tailwindcss": "^4.1.13", - "vite": "^7.1.7", - "vitest": "^4.0.0-beta.8" + "tailwindcss": "^4.1.14", + "vite": "^7.1.10", + "vitest": "^4.0.0-beta.18" } } diff --git a/examples/vitest-react-tailwind4/test/basic.test.tsx b/examples/vitest-react-tailwind4/test/basic.test.tsx index a16124a..e4258ad 100644 --- a/examples/vitest-react-tailwind4/test/basic.test.tsx +++ b/examples/vitest-react-tailwind4/test/basic.test.tsx @@ -9,7 +9,7 @@ it('simple react testing library test', () => { fireEvent.click(screen.getByText('Increment')) fireEvent.click(screen.getByText('Decrement')) - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) function setupUi() { diff --git a/examples/vitest-react-tailwind4/test/form-test-for-e2e.test.tsx b/examples/vitest-react-tailwind4/test/form-test-for-e2e.test.tsx new file mode 100644 index 0000000..08b0168 --- /dev/null +++ b/examples/vitest-react-tailwind4/test/form-test-for-e2e.test.tsx @@ -0,0 +1,14 @@ +import { describe, it } from 'vitest' +import { render, screen } from '@testing-library/react' +import { FormExample } from '../components/FormExample' + +describe('form test for e2e test', () => { + it('basic usage', async () => { + setupUi() + expect(screen.getByLabelText('First input')) + }) +}) + +function setupUi() { + render() +} diff --git a/examples/vitest-react-tailwind4/test/form.test.tsx b/examples/vitest-react-tailwind4/test/form.test.tsx new file mode 100644 index 0000000..7412410 --- /dev/null +++ b/examples/vitest-react-tailwind4/test/form.test.tsx @@ -0,0 +1,18 @@ +import { expect } from 'vitest' +import { render, screen } from '@testing-library/react' +import { userEvent } from '@testing-library/user-event' +import { FormExample } from '../components/FormExample' + +it('renders FormExample with form and non-form sections', async () => { + (() => render())() + + // Fill form inputs and submit + const firstInput = screen.getByLabelText('First input') + await userEvent.type(firstInput, 'Test Value 1') + expect((firstInput as HTMLInputElement).value).toBe('Test Value 1') + + const secondInput = screen.getByPlaceholderText('Second input') + await userEvent.type(secondInput, 'Test Value 2') + + expect((secondInput as HTMLInputElement).value).toBe('Test Value 2') +}) diff --git a/examples/vitest-react-tailwind4/test/inspector-demo.test.tsx b/examples/vitest-react-tailwind4/test/inspector-demo.test.tsx index 00edef6..ac7f5cc 100644 --- a/examples/vitest-react-tailwind4/test/inspector-demo.test.tsx +++ b/examples/vitest-react-tailwind4/test/inspector-demo.test.tsx @@ -5,7 +5,7 @@ import { Counter } from '../../jest-react/components/Counter' it('example ui test to be run within the e2e test', async () => { setupUi() - expect(screen.getByText('Count: 1')).toBeTruthy() + expect(screen.getByText('Count: 1')) }) function setupUi() { diff --git a/examples/vitest-react-tailwind4/tsconfig.json b/examples/vitest-react-tailwind4/tsconfig.json index 0068949..06568a5 100644 --- a/examples/vitest-react-tailwind4/tsconfig.json +++ b/examples/vitest-react-tailwind4/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "jsx": "react", + "jsx": "react-jsx", "module": "Preserve", "moduleResolution": "bundler", "types": ["vitest/globals"], diff --git a/examples/vitest-react-tailwind4/vite.config.ts b/examples/vitest-react-tailwind4/vite.config.ts index 5066940..fa6a3f5 100644 --- a/examples/vitest-react-tailwind4/vite.config.ts +++ b/examples/vitest-react-tailwind4/vite.config.ts @@ -2,9 +2,10 @@ import { defineConfig } from 'vite' import tailwindcss from '@tailwindcss/vite' +import react from '@vitejs/plugin-react' export default defineConfig({ - plugins: [tailwindcss()], + plugins: [react(), tailwindcss()], test: { globals: true, environment: 'happy-dom', diff --git a/package.json b/package.json index 8a8e87b..5df3293 100644 --- a/package.json +++ b/package.json @@ -68,10 +68,24 @@ "description": "Test Framework: Vitest or Jest. Auto-detects by default by walking up directories from your test file." } } - } + }, + "menus": { + "editor/context": [ + { + "command": "ui-test-visualizer.createUiTest", + "when": "editorTextFocus && editorLangId =~ /typescript|javascript/" + } + ] + }, + "commands": [ + { + "command": "ui-test-visualizer.createUiTest", + "title": "Create UI test (UI Test Visualizer)" + } + ] }, "scripts": { - "prepare": "which bun && bun install --cwd ./examples/bun-react || true", + "prepare": "which bun && (bun install --cwd ./examples/bun-react) && (bun install --cwd ./examples/bun-solid)", "dev": "rm -rf build-dev && pnpm run -r dev", "build": "rm -rf build-prod && pnpm run -r build && (cd build-prod && npx vsce package --allow-missing-repository --skip-license --out ./extension.vsix)", "lint": "tsc", @@ -86,16 +100,17 @@ "devDependencies": { "@antfu/eslint-config": "^2.19.1", "@changesets/cli": "^2.29.7", - "@playwright/test": "^1.55.1", + "@playwright/test": "^1.56.1", "@total-typescript/ts-reset": "^0.6.1", - "@types/node": "^24.6.0", + "@types/node": "^24.8.1", "@vitest/expect": "^3.2.4", "@vscode/vsce": "^3.6.2", - "eslint": "^9.36.0", + "eslint": "^9.38.0", "ovsx": "^0.10.6", + "pathe": "^2.0.3", "tsup": "^8.5.0", "type-fest": "^5.0.1", - "typescript": "^5.9.2", + "typescript": "^5.9.3", "vitest": "^3.2.4" }, "pnpm": { @@ -103,5 +118,5 @@ "jest-config@30.2.0": "patches/jest-config@30.2.0.patch" } }, - "packageManager": "pnpm@10.17.1" + "packageManager": "pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8" } diff --git a/packages/extension/package.json b/packages/extension/package.json index ac4d82a..b80d2e1 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -9,11 +9,11 @@ "build": "tsup --minify" }, "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.5", - "@oxc-parser/binding-wasm32-wasi": "^0.93.0", - "@tailwindcss/oxide-wasm32-wasi": "^4.1.13", + "@napi-rs/wasm-runtime": "^1.0.7", + "@oxc-parser/binding-wasm32-wasi": "^0.95.0", + "@tailwindcss/oxide-wasm32-wasi": "^4.1.14", "@trpc/server": "^11.6.0", - "@vscode/extension-telemetry": "^1.0.0", + "@vscode/extension-telemetry": "^1.1.0", "enhanced-resolve": "^5.18.3", "estree-walker": "^3.0.3", "find-up": "^8.0.0", @@ -24,20 +24,22 @@ "postcss": "^8.5.6", "postcss-load-config": "^6.0.1", "replicate-dom": "workspace:*", - "zod": "^4.1.11" + "zod": "^4.1.12" }, "devDependencies": { + "@testing-library/user-event": "^13.5.0", "@types/less": "^3.0.8", "@types/lodash": "^4.17.20", - "@types/node": "^24.6.0", + "@types/node": "^24.8.1", "@types/stylus": "^0.48.43", - "@types/vscode": "^1.104.0", + "@types/vscode": "^1.105.0", "@types/ws": "^8.18.1", "@vitest/expect": "^3.2.4", - "@vscode/codicons": "^0.0.40", - "esbuild": "^0.25.10", + "@vscode/codicons": "^0.0.41", + "esbuild": "^0.25.11", "execa": "^9.6.0", - "less": "^4.4.1", + "less": "^4.4.2", + "oxc-parser": "^0.95.0", "sass": "^1.93.2", "stylus": "^0.64.0", "vitest": "^3.2.4", diff --git a/packages/extension/src/code-lens-provider.ts b/packages/extension/src/code-lens-provider.ts index 7dcec54..7fc91f9 100644 --- a/packages/extension/src/code-lens-provider.ts +++ b/packages/extension/src/code-lens-provider.ts @@ -46,8 +46,16 @@ export const codeLensProvider: vscode.CodeLensProvider = { walk(program, { enter(node) { if (node.type === 'CallExpression' && ['it', 'fit', 'test'].includes(node?.callee.name)) { + const functionNode = (() => { + for (const argNode of node.arguments) { + if (['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(argNode?.type ?? '')) { + return argNode + } + } + })() + const name = node?.arguments?.[0]?.value - let firstStatementStartChar: number | null = Number(node?.arguments?.[1]?.body?.body?.[0]?.start ?? undefined) + let firstStatementStartChar: number | null = Number(functionNode?.body?.body?.[0]?.start ?? undefined) if (Number.isNaN(firstStatementStartChar)) { firstStatementStartChar = null } diff --git a/packages/extension/src/debug-config.ts b/packages/extension/src/debug-config.ts index 0df2d30..8a508ea 100644 --- a/packages/extension/src/debug-config.ts +++ b/packages/extension/src/debug-config.ts @@ -1,9 +1,7 @@ +import { findUp } from 'find-up' import path from 'pathe' import type vscode from 'vscode' -import type { z } from 'zod/mini' -import { findUp } from 'find-up' -import type { zFrameworkSetting } from './extension' -import { detectTestFramework } from './framework-support/detect' +import type { TestFrameworkInfo } from './framework-support/detect-test-framework' import { jestDebugConfig } from './framework-support/jest-support' import { vitestDebugConfig } from './framework-support/vitest-support' import { bunDebugConfig } from './framework-support/bun-support' @@ -11,14 +9,12 @@ import { bunDebugConfig } from './framework-support/bun-support' const DEBUG_NAME = 'Visually Debug UI' export async function makeDebugConfig( + fwInfo: TestFrameworkInfo, testFile: string, testName: string, - frameworkSetting: z.infer, htmlUpdaterPort: number, testCssFiles?: string[], ) { - const fwInfo = await detectTestFramework(testFile, frameworkSetting) - const pkgPath = await findUp('package.json', { cwd: testFile }) if (!pkgPath) { throw new Error(`Could not find related package.json for test file ${testFile}`) diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 586a44c..93be27a 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -3,16 +3,24 @@ import '@total-typescript/ts-reset' import { TelemetryReporter } from '@vscode/extension-telemetry' import * as vscode from 'vscode' import * as z from 'zod/mini' +import zodEn from 'zod/v4/locales/en.js' import getPort from 'get-port' import { autoSetFirstBreakpoint } from './auto-set-first-breakpoint' import { codeLensProvider } from './code-lens-provider' import { makeDebugConfig } from './debug-config' +import { detectTestFramework } from './framework-support/detect-test-framework' +import { detectTestLibrary } from './framework-support/detect-test-library' import { myExtensionStorage } from './my-extension-storage' import { startPanelController } from './panel-controller/panel-controller' +import { createUiTestFile } from './recorder/create-new-test-file/create-ui-test-file' +import { startRecorderCodeGenSession } from './recorder/recorder-codegen-session' import { startDebuggerTracker } from './util/debugger-tracker' import { extensionSetting } from './util/extension-setting' +import { onceWithPeek } from './util/util' import { enableHotReload } from './util/hot-reload' +z.config(zodEn()) + const reporter = (() => { try { return new TelemetryReporter('InstrumentationKey=ffa9ad6b-9974-4d5c-9247-9ff01c0a4cc8;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/;ApplicationId=55f0d25f-492a-4af3-8487-d8abce8a41f6') @@ -80,6 +88,11 @@ export async function activate(extensionContext: vscode.ExtensionContext) { }), ) } + + // Right-click a component name and click "Create UI test" -> create a test file with the name of the component + extensionContext.subscriptions.push( + vscode.commands.registerCommand('ui-test-visualizer.createUiTest', () => createUiTestFile()), + ) } export function deactivate() { @@ -115,21 +128,23 @@ async function startTest( throw new TypeError(`Expected number or null argument \"firstStatementStartLine\", received ${firstStatementStartLineRaw}`) } - // Save the test file before starting the debug session - await vscode.window.activeTextEditor?.document.save() - - const htmlUpdaterPort = await getPort() - const frameworkSetting = (() => { const parsed = zFrameworkSetting .safeParse(extensionSetting('ui-test-visualizer.testFramework')) return parsed.success ? parsed.data : 'autodetect' })() + const frameworkInfo = await detectTestFramework(testFile, frameworkSetting) + + // Save the test file before starting the debug session + await vscode.window.activeTextEditor?.document.save() + + const htmlUpdaterPort = await getPort() + const debugConfig = await makeDebugConfig( + frameworkInfo, testFile, testName, - frameworkSetting, htmlUpdaterPort, await storage.get('enabledCssFiles'), ) @@ -158,17 +173,28 @@ async function startWebView(extensionContext: vscode.ExtensionContext, currentSe const storage = myExtensionStorage(extensionContext) + const testLibrary = await detectTestLibrary(testFile) + + // Only initialize the recorder state once per debug session + const recorderCodeGenSession = onceWithPeek( + () => testLibrary + ? startRecorderCodeGenSession(testFile, env.TEST_FRAMEWORK, testLibrary, panelController) + : null, + ) + const sessionTracker = startDebuggerTracker( currentSession, { onFrameChange: () => panelController.flushPatches(), onDebugRestarted: () => panelController.notifyDebuggerRestarted(), + onDebugTerminated: async () => recorderCodeGenSession.peek()?.performEdit(), }, ) const panelController = await startPanelController( extensionContext, storage, + recorderCodeGenSession, sessionTracker, htmlUpdaterPort, ) @@ -193,6 +219,7 @@ async function startWebView(extensionContext: vscode.ExtensionContext, currentSe autoBreakpoint?.dispose() sessionTracker.dispose() panelController.dispose() + recorderCodeGenSession.peek()?.dispose() onTerminate.dispose() }, ) diff --git a/packages/extension/src/framework-support/detect.ts b/packages/extension/src/framework-support/detect-test-framework.ts similarity index 96% rename from packages/extension/src/framework-support/detect.ts rename to packages/extension/src/framework-support/detect-test-framework.ts index 4cbb4d3..eb65803 100644 --- a/packages/extension/src/framework-support/detect.ts +++ b/packages/extension/src/framework-support/detect-test-framework.ts @@ -2,14 +2,16 @@ import path from 'pathe' import { findUp, findUpMultiple } from 'find-up' import { readInitialOptions } from 'jest-config' +export type SupportedFramework = 'vitest' | 'jest' | 'bun' + export interface TestFrameworkInfo { - framework: 'jest' | 'vitest' | 'bun' + framework: SupportedFramework configPath: string } export async function detectTestFramework( testFilePath: string, - frameworkSetting: 'autodetect' | 'jest' | 'vitest' | 'bun', + frameworkSetting: 'autodetect' | SupportedFramework, ): Promise { // auto detect test config files diff --git a/packages/extension/src/framework-support/detect-test-library.ts b/packages/extension/src/framework-support/detect-test-library.ts new file mode 100644 index 0000000..19fcaf5 --- /dev/null +++ b/packages/extension/src/framework-support/detect-test-library.ts @@ -0,0 +1,30 @@ +import type { SupportedFramework } from './detect-test-framework' + +export const SUPPORTED_TESTING_LIBRARIES = [ + '@solidjs/testing-library', + '@testing-library/react', + '@testing-library/dom', +] as const + +export type TestingLibrary = typeof SUPPORTED_TESTING_LIBRARIES[number] + +export async function detectTestLibrary(testFilePath: string): Promise { + // Lookup the test framework in node_modules + const detectedLibrary = (() => { + for (const testingLibrary of SUPPORTED_TESTING_LIBRARIES) { + const resolved = (() => { + try { + return require.resolve(testingLibrary, { paths: [testFilePath] }) + } + catch { + return undefined + } + })() + if (resolved) { + return testingLibrary + } + } + })() ?? null + + return detectedLibrary +} diff --git a/packages/extension/src/framework-support/jest-support.ts b/packages/extension/src/framework-support/jest-support.ts index 1187742..cccc4ad 100644 --- a/packages/extension/src/framework-support/jest-support.ts +++ b/packages/extension/src/framework-support/jest-support.ts @@ -2,7 +2,7 @@ import { findUp, findUpSync } from 'find-up' import { readInitialOptions } from 'jest-config' import path from 'pathe' import type * as vscode from 'vscode' -import type { TestFrameworkInfo } from './detect' +import type { TestFrameworkInfo } from './detect-test-framework' import { cleanTestNameForTerminal } from './util' function buildPath() { @@ -59,6 +59,7 @@ export async function jestDebugConfig( '--detectOpenHandles', // TODO find out why Jest doesn't exit on its own '--forceExit', + '--colors', // Show green text for the 'pass' text. TODO find out why the rest of the output is red text ], autoAttachChildProcesses: true, } diff --git a/packages/extension/src/framework-support/vitest-support.ts b/packages/extension/src/framework-support/vitest-support.ts index 21686a7..152b26a 100644 --- a/packages/extension/src/framework-support/vitest-support.ts +++ b/packages/extension/src/framework-support/vitest-support.ts @@ -24,6 +24,7 @@ export async function vitestDebugConfig( filePath, '-t', cleanTestNameForTerminal(testName), + '--globals', // Needed for the recorder's generated 'expect' statements // Use child process instead of the default threads. '--pool', 'forks', diff --git a/packages/extension/src/panel-controller/panel-controller.ts b/packages/extension/src/panel-controller/panel-controller.ts index 377ae98..668f9d1 100644 --- a/packages/extension/src/panel-controller/panel-controller.ts +++ b/packages/extension/src/panel-controller/panel-controller.ts @@ -4,6 +4,7 @@ import type { HTMLPatch } from 'replicate-dom' import * as vscode from 'vscode' import type { Server as WsServer } from 'ws' import type { MyStorageType } from '../my-extension-storage' +import type { RecorderCodeGenSession } from '../recorder/recorder-codegen-session' import type { DebuggerTracker } from '../util/debugger-tracker' import { type PanelRouterCtx, panelRouter } from './panel-router' @@ -11,10 +12,13 @@ import { type PanelRouterCtx, panelRouter } from './panel-router' // eslint-disable-next-line ts/no-var-requires, ts/no-require-imports const Server = require('../../node_modules/ws/lib/websocket-server') as typeof WsServer +export type PanelController = Awaited> + export async function startPanelController( extensionContext: vscode.ExtensionContext, storage: MyStorageType, - sessionTracker: DebuggerTracker, + recorderCodeGenSession: () => RecorderCodeGenSession | null, + debuggerTracker: DebuggerTracker, htmlUpdaterPort: number, ) { let setWebviewIsReady = () => {} @@ -80,6 +84,7 @@ export async function startPanelController( + @@ -109,11 +114,11 @@ export async function startPanelController( } catch (error) { console.log(error) - if (error instanceof TRPCError) { - vscode.window.showErrorMessage(String(error.cause)) + if (error instanceof TRPCError && error.cause) { + vscode.window.showErrorMessage(error.cause.message) } else if (error instanceof Error) { - vscode.window.showErrorMessage(String(error)) + vscode.window.showErrorMessage(error.message) } panel.webview.postMessage({ id, @@ -135,6 +140,10 @@ export async function startPanelController( notifyDebuggerRestarted: () => { panel.webview.postMessage({ debuggerRestarted: true }) }, + notifyRecorderEditPerformed: () => { + panel?.webview.postMessage({ recorderEditPerformed: true }) + }, + dispose() { htmlUpdaterServer.close() panel.dispose() @@ -143,10 +152,11 @@ export async function startPanelController( } const ctx: PanelRouterCtx = { - sessionTracker, + debuggerTracker, storage, flushPatches: controller.flushPatches, setWebviewIsReady, + recorderCodeGenSession, } return controller diff --git a/packages/extension/src/panel-controller/panel-router.ts b/packages/extension/src/panel-controller/panel-router.ts index 14a3779..7c2eeff 100644 --- a/packages/extension/src/panel-controller/panel-router.ts +++ b/packages/extension/src/panel-controller/panel-router.ts @@ -1,20 +1,47 @@ +import { initTRPC } from '@trpc/server' import path from 'pathe' import * as vscode from 'vscode' -import { initTRPC } from '@trpc/server' import * as z from 'zod/mini' -import { workspaceCssFiles } from '../util/workspace-css-files' import type { MyStorageType } from '../my-extension-storage' +import { type RecorderCodeGenSession, zSerializedRegexp } from '../recorder/recorder-codegen-session' import type { DebuggerTracker } from '../util/debugger-tracker' +import { workspaceCssFiles } from '../util/workspace-css-files' export interface PanelRouterCtx { - sessionTracker: DebuggerTracker + debuggerTracker: DebuggerTracker storage: MyStorageType flushPatches: () => void + recorderCodeGenSession: () => RecorderCodeGenSession | null setWebviewIsReady: () => void } const t = initTRPC.context().create() +const zTestingLibraryQueryArgs = z.tuple([ + z.string(), + z.tuple([ + z.union([z.string(), zSerializedRegexp]), + z.optional(z.record( + z.string(), + z.union([z.string(), z.boolean(), zSerializedRegexp]), + )), + ]), +]) +export type TestingLibraryQueryArgs = z.infer + +export const zRecordedEventData = z.object({ + text: z.optional(z.string()), // Used for change events + options: z.optional(z.array(z.string())), // Used for selectOptions events + enterKeyPressed: z.optional(z.boolean()), // Used for Enter keydown events + indexIfMultipleFound: z.optional(z.number()), // Used when there are multiple elements for the same query + clearBeforeType: z.optional(z.boolean()), // Clear the input before typing + checked: z.optional(z.boolean()), // Used for expect/toBeChecked statements + enabled: z.optional(z.boolean()), // Used for expect/toBeEnabled statements +}) + +const zExpectStatementType = z.enum(['minimal', 'toHaveValue', 'toBeEnabled', 'toHaveTextContent', 'toBeChecked']) +export type ExpectStatementType = z.infer + /** Defines RPCs callable from the WebView to the VSCode Extension. */ export const panelRouter = t.router({ setWebviewIsReady: t.procedure @@ -24,7 +51,7 @@ export const panelRouter = t.router({ serializeHtml: t.procedure .query(async ({ ctx }) => { - const html = await ctx.sessionTracker.runDebugExpression('globalThis.__serializeHtml()') + const html = await ctx.debuggerTracker.runDebugExpression('globalThis.__serializeHtml()') return html }), @@ -111,7 +138,7 @@ export const panelRouter = t.router({ .mutation(async ({ ctx }) => { const files = await ctx.storage.get('enabledCssFiles') ?? [] const filesAsString = JSON.stringify(files) - const resultStr = await ctx.sessionTracker.runDebugExpression( + const resultStr = await ctx.debuggerTracker.runDebugExpression( `globalThis.__replaceStyles(${filesAsString})`, ) ctx.flushPatches() @@ -179,6 +206,50 @@ export const panelRouter = t.router({ .mutation(async ({ ctx }) => { ctx.storage.set('stylePromptDismissed', false) }), + + recordInputAsCode: t.procedure + .input( + z.object({ + event: z.string(), + eventData: zRecordedEventData, + query: zTestingLibraryQueryArgs, + // Whether to generate an 'expect' statement + useExpect: z.optional(zExpectStatementType), + // Whether to use fireEvent instead of userEvent + useFireEvent: z.optional(z.boolean()), + }), + ) + .mutation(async ({ ctx, input }) => { + const { event, eventData, query: [findMethod, [queryArg0, queryOptions]], useExpect, useFireEvent } = input + const recorderCodeGenSession = ctx.recorderCodeGenSession() + const _insertion = await recorderCodeGenSession?.recordInputAsCode( + ctx.debuggerTracker, + { + event, + eventData, + findMethod, + queryArg0, + queryOptions, + useExpect, + useFireEvent, + }, + ) + return recorderCodeGenSession?.insertions + }), + + removeRecorderInsertion: t.procedure + .input( + z.object({ + line: z.number(), + idx: z.optional(z.number()), + }), + ) + .mutation(async ({ ctx, input }) => { + const { line, idx } = input + const session = ctx.recorderCodeGenSession() + session?.removeInsertion(line, idx) + return session?.insertions + }), }) export type PanelRouter = typeof panelRouter diff --git a/packages/extension/src/recorder/create-new-test-file/create-ui-test-code.ts b/packages/extension/src/recorder/create-new-test-file/create-ui-test-code.ts new file mode 100644 index 0000000..1fde14d --- /dev/null +++ b/packages/extension/src/recorder/create-new-test-file/create-ui-test-code.ts @@ -0,0 +1,60 @@ +import { walk } from 'estree-walker' +import type { ParseResult } from 'oxc-parser' +import type { TestFrameworkInfo } from '../../framework-support/detect-test-framework' +import type { TestingLibrary } from '../../framework-support/detect-test-library' + +export function createUITestCode( + { program, word, frameworkInfo, testingLibrary, relativePathToSrc }: + { + program: ParseResult + word: string + frameworkInfo: TestFrameworkInfo + testingLibrary: TestingLibrary + relativePathToSrc: string + }, +): [Error, null] | [null, { exportName: string, testContent: string }] { + const { exportName, isDefaultExport } = (() => { + let result: string | null = null + let isDefaultExport = false + walk(program, { + enter(node) { + if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') { + const nodeName = node?.declaration?.name + ?? node?.declaration?.id?.name + ?? node?.declaration?.declarations?.[0]?.id?.name + ?? node?.declaration?.declarations?.[0]?.name + if (nodeName === word) { + result = nodeName ?? null + isDefaultExport = node.type === 'ExportDefaultDeclaration' + } + } + }, + }) + + return { exportName: result as string | null, isDefaultExport } + })() + + if (!exportName) { + return [new Error(`No valid export found. Must be an exported capitalized function name. Got ${word}`), null] + } + + // React/Solid component convention: starts with capital letter + if (!/^[A-Z]/.test(exportName)) { + return [new Error(`Selection must be a capitalized identifier, e.g. a React component name. Got "${word}".`), null] + } + + const fwImport = frameworkInfo.framework === 'bun' ? 'bun:test' : frameworkInfo.framework + const testImport = frameworkInfo.framework === 'jest' ? '' : `import { test } from '${fwImport}'\n` + + const isArrowRender = testingLibrary === '@solidjs/testing-library' + + // Create basic test content + const testContent = `${testImport}import { render } from '${testingLibrary}' +import ${isDefaultExport ? exportName : `{ ${exportName} }`} from './${relativePathToSrc}' + +test('basic usage', async () => { + (() => render(${isArrowRender ? `() => <${exportName} />` : `<${exportName} />`}))() +}) +` + return [null, { exportName, testContent }] +} diff --git a/packages/extension/src/recorder/create-new-test-file/create-ui-test-file.ts b/packages/extension/src/recorder/create-new-test-file/create-ui-test-file.ts new file mode 100644 index 0000000..bf19e9a --- /dev/null +++ b/packages/extension/src/recorder/create-new-test-file/create-ui-test-file.ts @@ -0,0 +1,84 @@ +import { Buffer } from 'node:buffer' +import type { ParseResult } from 'oxc-parser' +import * as path from 'pathe' +import * as vscode from 'vscode' +import { zFrameworkSetting } from '../../extension' +import { detectTestFramework } from '../../framework-support/detect-test-framework' +import { SUPPORTED_TESTING_LIBRARIES, detectTestLibrary } from '../../framework-support/detect-test-library' +import { extensionSetting } from '../../util/extension-setting' +import { createUITestCode } from './create-ui-test-code' + +export async function createUiTestFile() { + const editor = vscode.window.activeTextEditor + if (!editor) { + return + } + + const doc = editor.document + + const selection = editor.selection + const wordRange = doc.getWordRangeAtPosition(selection.active, /\w+/) + const word = doc.getText(wordRange) + + if (!wordRange || !word) { + vscode.window.showErrorMessage( + `No valid identifier found. Must be an exported capitalized function name. Got ${word}`, + ) + return + } + + const docFileName = doc.fileName + const docText = doc.getText() + + const frameworkSetting = (() => { + const parsed = zFrameworkSetting + .safeParse(extensionSetting('ui-test-visualizer.testFramework')) + return parsed.success ? parsed.data : 'autodetect' + })() + + const frameworkInfo = await detectTestFramework(editor.document.uri.fsPath, frameworkSetting) + const testingLibrary = await detectTestLibrary(editor.document.uri.fsPath) + + if (!testingLibrary) { + vscode.window.showErrorMessage(`Could not detect a testing library for ${editor.document.uri.fsPath}. Supported testing libraries are ${SUPPORTED_TESTING_LIBRARIES.join(', ')}.`) + return + } + + const currentDir = path.dirname(editor.document.uri.fsPath) + + const relativePathToSrc = path.relative(currentDir, doc.fileName).replace(/\.[jt]sx?$/, '') + + // @ts-expect-error import the wasm file directly + const { parseSync } = (await import('@oxc-parser/binding-wasm32-wasi')) as typeof import('oxc-parser') + + let program: ParseResult = parseSync(docFileName, docText, {}).program + // @ts-expect-error needs to be JSON.parse'd when using the wasm parser + program = JSON.parse(program) + + const [error, result] = await createUITestCode({ program, word, frameworkInfo, testingLibrary, relativePathToSrc }) + if (error) { + vscode.window.showErrorMessage(error.message) + return + } + const { exportName, testContent } = result + + const testFileName = `${exportName}.test.tsx` + const testFileUri = vscode.Uri.file(path.join(currentDir, testFileName)) + + // Check if the test file already exists + try { + await vscode.workspace.fs.stat(testFileUri) + vscode.window.showErrorMessage(`Test file ${testFileName} already exists.`) + return + } + catch { + // File does not exist, proceed to create + } + + // Write the file + await vscode.workspace.fs.writeFile(testFileUri, Buffer.from(testContent, 'utf8')) + + // Open the new test file + const newDocument = await vscode.workspace.openTextDocument(testFileUri) + await vscode.window.showTextDocument(newDocument) +} diff --git a/packages/extension/src/recorder/generate-code-from-input.ts b/packages/extension/src/recorder/generate-code-from-input.ts new file mode 100644 index 0000000..3a715de --- /dev/null +++ b/packages/extension/src/recorder/generate-code-from-input.ts @@ -0,0 +1,232 @@ +import type { SupportedFramework } from '../framework-support/detect-test-framework' +import type { TestingLibrary } from '../framework-support/detect-test-library' +import type { RecordInputAsCodeParams } from './recorder-codegen-session' + +export interface RecorderGeneratedCode { + code: string[] + debugExpression: string + requiredImports: Record +} + +export function generateCodeFromInput( + hasUserEventLib: boolean, + testLibrary: TestingLibrary, + testFramework: SupportedFramework, + userEventLibPath: string, + { + event, + eventData, + findMethod, + queryArg0, + queryOptions, + useExpect, + useFireEvent, + }: RecordInputAsCodeParams, +): RecorderGeneratedCode { + const parsedQueryOptions = queryOptions && Object.entries(queryOptions).reduce( + (result, entry) => { + const [key, val] = entry + result[key] = (() => { + if (typeof val === 'string') { + return `'${val.replace(/'/g, '\\\'')}'` + } + else if (typeof val === 'boolean') { + return val + } + else if (val.type === 'regexp') { + return val.value + } + else { + return '' + } + })() + + return result + }, + {} as Record, + ) + + const queryArgsStr = (() => { + let result = typeof queryArg0 === 'string' ? `'${queryArg0}'` : queryArg0.value + if (!parsedQueryOptions) { + return result + } + const entries = Object.entries(parsedQueryOptions).map(([key, val]) => { + return `${key}: ${String(val)}` + }) + let optionsStr = entries.join(', ') + if (entries.length > 0) { + optionsStr = `{ ${optionsStr} }` + result += `, ${optionsStr}` + } + return result + })() + + const screen = 'screen' + const fireEvent = 'fireEvent' + const userEvent = 'userEvent' + const expect = 'expect' + + /** + * e.g. `screen.getByRole('button', { name: 'Submit' })` + * or just `document` + */ + const selector = (() => { + if (queryArg0 === 'document') { + return 'document' + } + let index = '' + if (eventData.indexIfMultipleFound !== undefined) { + findMethod = findMethod.replace(/^getBy/, 'getAllBy') + index = `[${eventData.indexIfMultipleFound}]` + } + return `${screen}.${findMethod}(${queryArgsStr})${index}` + })() + + let mainLine = '' + const requiredImports: Record = { + [screen]: testLibrary, + } + + // When 'useExpect' is defined, generate an 'expect' statement + if (useExpect) { + // Generate an 'expect' statement based on the selected type + mainLine = (() => { + function escapeQuotes(str: string) { + return str.replaceAll(/['\\]/g, (match) => { + return `\\${match}` + }) + } + switch (useExpect) { + case 'toHaveTextContent': { + const textContent = escapeQuotes(eventData.text ?? '') + return `${expect}(${selector}).toHaveTextContent('${textContent}')` + } + case 'toHaveValue': { + const value = escapeQuotes(eventData.text ?? '') + return `${expect}(${selector}).toHaveValue('${value}')` + } + case 'toBeEnabled': { + const not = eventData.enabled ? '' : 'not.' + return `${expect}(${selector}).${not}toBeEnabled()` + } + case 'toBeChecked': { + const not = eventData.checked ? '' : 'not.' + return `${expect}(${selector}).${not}toBeChecked()` + } + case 'minimal': + default: + return `${expect}(${selector})` + } + })() + + if (testFramework === 'vitest') { + requiredImports[expect] = testFramework + } + if (testFramework === 'bun') { + requiredImports[expect] = 'bun:test' + } + } + // Otherwise, generate a userEvent call + else if (useFireEvent) { + const fireEventArgs = (() => { + if (event === 'change' && eventData.text) { + const value = eventData.text.replace(/'/g, '\\\'') + return `, { target: { value: '${value}' } }` + } + if (event === 'selectOptions' && eventData.options) { + return `, { target: { value: '${eventData.options[0]}' } }` + } + return '' + })() + + mainLine = `${fireEvent}.${event}(${selector}${fireEventArgs})` + + requiredImports[fireEvent] = testLibrary + } + else if (!hasUserEventLib) { + throw new Error('Cannot use userEvent without @testing-library/user-event installed') + } + else { + const userEventArgs = (() => { + // When using userEvent with text inputs, the method is either 'type' or 'clear' + if (event === 'change' && typeof eventData.text === 'string') { + // If the text is empty, use 'clear' instead of 'type' + if (eventData.text.length === 0) { + event = 'clear' + return '' + } + + // Otherwise 'type' the text + event = 'type' + const value = eventData.text.replace(/'/g, '\\\'') + return `, '${value}'` + } + if (event === 'selectOptions' && eventData.options) { + return `, [${eventData.options.map(it => `'${it.replace(/'/g, '\\\'')}'`).join(', ')}]` + } + return '' + })() + + mainLine = `await ${userEvent}.${event}(${selector}${userEventArgs})` + + if (event === 'keydown' && eventData.enterKeyPressed) { + mainLine = `await ${userEvent}.keyboard('{enter}')` + } + + requiredImports[userEvent] = '@testing-library/user-event' + } + + const importCode = Object.entries(requiredImports).map(([importName, from]) => { + if (from === 'vitest') { + // Workaround: we can't import vitest in the debug expressions because it can't be imported with 'require', + // so run the global 'expect' function (using `vitest --globals` flag), + // but still generate the import code after the recording session. + return '' + } + + // user-event v13 is used when running the debug expressions, because it's the last version + // where the methods were synchronous e.g. `userEvent.click(...);`. + // Newer versions use async code e.g. `await userEvent.click(...);`, + // which fail when running through the debugger's 'evaluate' request. + if (from === '@testing-library/user-event') { + from = userEventLibPath + return `const ${importName} = globalThis.require('${from}').default;` + } + return `const { ${importName} } = globalThis.require('${from}');` + }).filter(Boolean).join('\n') + + const code = [mainLine] + + // Clear the input before typing when the 'clearBeforeType' flag is set + if (event === 'type' && eventData.clearBeforeType) { + code.unshift(`await ${userEvent}.clear(${selector})`) + } + + // The fireEvent or userEvent statement to run in the debugger. + // May be slightly different than the code that is actually generated for the user. + const debugEventStatement = (() => { + const result = code.map(line => line.replace(/\s*await\s*/, '')).join('\n') // No 'awaits' allowed in debug expressions + + // When writing react tests with userEvent v13, wrap in 'act'. + if (testLibrary === '@testing-library/react' && !useFireEvent && !useExpect) { + return `globalThis.require('react').act(() => { ${result} });` + } + return result + })() + + // Replicate the input event from the webview to the test runtime. + // We do this through a debug expression, which is the same as running code through vscode's 'Debug Terminal'. + const debugExpression = ` +(() => { +${importCode} +${debugEventStatement} +})() +` + + return { + code, + debugExpression, + requiredImports, + } +} diff --git a/packages/extension/src/recorder/perform-edit.ts b/packages/extension/src/recorder/perform-edit.ts new file mode 100644 index 0000000..8e19d64 --- /dev/null +++ b/packages/extension/src/recorder/perform-edit.ts @@ -0,0 +1,156 @@ +import { walk } from 'estree-walker' +import * as vscode from 'vscode' +import type { PanelController } from '../panel-controller/panel-controller' +import { getOrOpenEditor } from '../util/util' +import type { RecorderCodeInsertions } from './recorder-codegen-session' + +export async function performEdit( + filePath: string, + insertions: RecorderCodeInsertions, + panelController: PanelController, +) { + if (Object.keys(insertions).length === 0) { + // No edits to perform + return + } + + const editor = await getOrOpenEditor(filePath) + + if (!editor) { + vscode.window.showErrorMessage(`No editor found for ${filePath.split('/').pop()}`) + return + } + + // eslint-disable-next-line ts/no-var-requires, ts/no-require-imports + const { parseSync } = require('@oxc-parser/binding-wasm32-wasi') + + // Convert insertion line numbers to character indexes + const insertionCharIndexes: number[] = [] + for (const line in insertions) { + const lineNumber = Number.parseInt(line) - 1 // 0-based + const position = new vscode.Position(lineNumber, 0) + const offset = editor.document.offsetAt(position) + insertionCharIndexes.push(offset) + } + + const requiresAsync = Object.keys(insertions) + .some(lineNum => insertions[Number(lineNum)]?.some(line => /\s*await*/.test(line[0]))) + + const reverseOrderInsertions = Object.entries(insertions) + .sort((a, b) => Number(b[0]) - Number(a[0])) + + await editor.edit((editBuilder) => { + for (const [line, lines] of reverseOrderInsertions) { + const lineNumber = Number.parseInt(line) + const position = new vscode.Position(lineNumber - 1, 0) + for (let [code, _requiredImports] of lines) { + const indent: string = (() => { + // Indent should be the longest indentation between the paused line and the previous line. + // This is to make sure the right indent is used when paused at the end of the test. + // e.g. + // test('click button', () => { + // const button = screen.getByRole('button') + // button.click() <-- when paused here, use this line's indent. + // }) <-- when paused here, use previous line's indent. + + const line1 = editor.document.lineAt(lineNumber - 1) + const line2 = editor.document.lineAt(lineNumber - 2) + return [line1, line2].map(it => it.text.match(/^\s*/)?.[0] || '').sort((a, b) => b.length - a.length)[0] || '' + })() + + code = `${indent}${code}\n` + + editBuilder.insert(position, code) + } + } + + // Figure out where to put each import statement + // Either add a new import, or edit an existing one to import something else + const importInsertionPoints = new Map() + const existingImports = new Set() + { + const parsed = parseSync(editor.document.fileName, editor.document.getText(), {}) + const programJson = parsed.program + const program = JSON.parse(programJson) + + walk(program, { + enter(node) { + if (node.type === 'ImportDeclaration') { + if (node.source.type === 'Literal') { + const endOfLastSpecifier = node.specifiers.at(-1)?.end + if (endOfLastSpecifier) { + importInsertionPoints.set(node.source.value, endOfLastSpecifier) + } + } + for (const specifier of node.specifiers) { + if (['ImportSpecifier', 'ImportDefaultSpecifier', 'ImportNamespaceSpecifier'].includes(specifier.type)) { + existingImports.add(specifier.local.name) + } + } + } + + // When the test needs to be async, insert the 'async' keyword if it's missing + if ( + requiresAsync + // Find the running test + && node.type === 'CallExpression' && ['it', 'fit', 'test'].includes(node?.callee.name) + ) { + for (const argNode of node.arguments) { + // Find the arrow function containing the insertion char indexes + if ( + ['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(argNode?.type ?? '') + && argNode?.async === false + && insertionCharIndexes.some(charIndex => charIndex >= argNode.start && charIndex <= argNode.end) + ) { + // Check if the function node contains any insertion char indexes + for (const charIndex of insertionCharIndexes) { + if (charIndex >= argNode.start && charIndex <= argNode.end) { + const position = editor.document.positionAt(argNode.start) + editBuilder.insert(position, 'async ') + } + } + } + } + } + }, + }) + } + + // Get the list of imports to add + const importList: { importName: string, from: string }[] = [] + for (const [_lineNum, insertionGroup] of Object.entries(insertions)) { + for (const [_code, requiredImports] of insertionGroup) { + for (const [importName, from] of Object.entries(requiredImports)) { + if (existingImports.has(importName)) { + continue + } + + importList.push({ importName, from }) + + existingImports.add(importName) + } + } + } + + // Edit the imports into the file + for (const { importName, from } of importList) { + const insertionPoint = importInsertionPoints.get(from) + if (insertionPoint) { + const position = editor.document.positionAt(insertionPoint) + editBuilder.insert(position, `, ${importName}`) + } + else { + editBuilder.insert(new vscode.Position(0, 0), `import { ${importName} } from '${from}'\n`) + } + } + }) + + // Remove the insertions from the state after they've been inserted into the code + for (const line in insertions) { + delete insertions[Number(line)] + } + + panelController.notifyRecorderEditPerformed() + + await editor.document.save() +} diff --git a/packages/extension/src/recorder/recorder-codegen-session.ts b/packages/extension/src/recorder/recorder-codegen-session.ts new file mode 100644 index 0000000..ae6fcfe --- /dev/null +++ b/packages/extension/src/recorder/recorder-codegen-session.ts @@ -0,0 +1,145 @@ +import path from 'pathe' +import * as vscode from 'vscode' +import * as z from 'zod/mini' +import type { SupportedFramework } from '../framework-support/detect-test-framework' +import type { TestingLibrary } from '../framework-support/detect-test-library' +import type { PanelController } from '../panel-controller/panel-controller' +import type { ExpectStatementType, zRecordedEventData } from '../panel-controller/panel-router' +import type { DebuggerTracker } from '../util/debugger-tracker' +import { generateCodeFromInput } from './generate-code-from-input' +import { performEdit } from './perform-edit' + +export type RecorderCodeGenSession = Awaited> + +export type RecorderCodeInsertions = Record][]> + +export type SerializedRegexp = z.infer +export const zSerializedRegexp = z.object({ + type: z.literal('regexp'), + value: z.string(), +}) + +export interface RecordInputAsCodeParams { + event: string + eventData: z.infer + findMethod: string + queryArg0: string | SerializedRegexp + queryOptions?: Record + useExpect?: ExpectStatementType + useFireEvent?: boolean +} + +export function startRecorderCodeGenSession( + testFile: string, + testFramework: SupportedFramework, + testLibrary: TestingLibrary, + panelController: PanelController, +) { + const insertions: RecorderCodeInsertions = {} + function addInsertion(line: number, text: string, requiredImports: Record) { + const lines = insertions[line] ?? (insertions[line] = []) + lines.push([text, requiredImports]) + updateCodeLens.fire() + } + function removeInsertion(line: number, idx?: number) { + if (idx === undefined) { + delete insertions[line] + } + else { + insertions[line]?.splice(idx, 1) + if (insertions[line]?.length === 0) { + delete insertions[line] + } + } + updateCodeLens.fire() + } + + const disposables = new Set() + + // Show Code Lens in between lines of code to indicate where the generated code will go. + const updateCodeLens = new vscode.EventEmitter() + disposables.add(vscode.languages.registerCodeLensProvider( + [{ pattern: testFile }], + { + provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] { + const lenses: vscode.CodeLens[] = [] + for (const [lineStr, _codeLines] of Object.entries(insertions)) { + const line = Number(lineStr) - 1 + if (line >= 0 && line < document.lineCount) { + const lineText = document.lineAt(line).text + const range = new vscode.Range(line, 0, line, lineText.length) + const title = 'Code will be inserted here after the test' + lenses.push(new vscode.CodeLens(range, { title, command: '' })) + } + } + return lenses + }, + + // Weird trick to manually trigger the code lens update. + onDidChangeCodeLenses: updateCodeLens.event, + }, + )) + + async function runPerformEdit() { + await performEdit( + testFile, + insertions, + panelController, + ) + } + + disposables.add(vscode.debug.onDidChangeActiveStackItem((stackItem) => { + if (stackItem === undefined) { + runPerformEdit() + } + })) + + const hasUserEventLib = (() => { + try { + require.resolve('@testing-library/user-event', { paths: [path.dirname(testFile)] }) + return true + } + catch { + return false + } + })() + + return { + recordInputAsCode: async ( + debuggerTracker: DebuggerTracker, + recordInputAsCodeParams: RecordInputAsCodeParams, + ): Promise<[number, string[]] | null> => { + const pausedLocation = await debuggerTracker.getPausedLocation() + if (!pausedLocation) { + return null + } + + const { code, debugExpression, requiredImports } = generateCodeFromInput( + hasUserEventLib, + testLibrary, + testFramework, + path.join(__dirname, 'user-event-13.js'), + recordInputAsCodeParams, + ) + + await debuggerTracker.runDebugExpression(debugExpression) + panelController.flushPatches() + + // Add the fireEvent code + for (const line of code) { + addInsertion(pausedLocation.lineNumber, line, requiredImports) + } + + return [pausedLocation.lineNumber, code] as const + }, + performEdit: runPerformEdit, + insertions, + removeInsertion, + hasUserEventLib, + dispose: () => { + for (const disposable of disposables) { + disposable.dispose() + } + }, + } +} diff --git a/packages/extension/src/util/debugger-tracker.ts b/packages/extension/src/util/debugger-tracker.ts index 30d3738..4e5269d 100644 --- a/packages/extension/src/util/debugger-tracker.ts +++ b/packages/extension/src/util/debugger-tracker.ts @@ -2,6 +2,11 @@ import * as vscode from 'vscode' export type DebuggerTracker = ReturnType +export interface DebugPauseLocation { + filePath: string + lineNumber: number +} + /** * Wrapper around the VSCode debug session with convenience methods. * Emits debugger-related events e.g. onFrameChange (Step Over) and onDebugRestarted (the Restart button is clicked). @@ -12,7 +17,12 @@ export function startDebuggerTracker( { onFrameChange, onDebugRestarted, - }: { onFrameChange: () => void, onDebugRestarted: () => void }, + onDebugTerminated, + }: { + onFrameChange: () => void + onDebugRestarted: () => void + onDebugTerminated: () => void + }, ) { const disposables = new Set() @@ -21,7 +31,13 @@ export function startDebuggerTracker( let hasHitFirstBreakpoint = false const startedSessions = new WeakSet() disposables.add(vscode.debug.onDidStartDebugSession(session => startedSessions.add(session))) - disposables.add(vscode.debug.onDidTerminateDebugSession(session => startedSessions.delete(session))) + disposables.add(vscode.debug.onDidTerminateDebugSession((session) => { + startedSessions.delete(session) + + if (session === rootSession) { + onDebugTerminated() + } + })) disposables.add(vscode.debug.onDidChangeActiveStackItem((stackItem) => { if (stackItem && startedSessions.has(stackItem?.session)) { if (hasHitFirstBreakpoint) { @@ -79,8 +95,38 @@ export function startDebuggerTracker( return result } + async function getPausedLocation(): Promise { + const session = vscode.debug.activeDebugSession + if (!session) { + return null + } + try { + const response = await session.customRequest('stackTrace', { + threadId: 1, // TODO is this always the right threadId? + startFrame: 0, + levels: 1, + }) + if (response.stackFrames && response.stackFrames.length > 0) { + const frame = response.stackFrames[0] + const fileUri = frame.source?.path + const lineNumber = frame.line + const filePath = vscode.Uri.parse(fileUri).fsPath + return { filePath, lineNumber } + } + else { + vscode.window.showErrorMessage('No stack frames') + return null + } + } + catch (error) { + vscode.window.showErrorMessage(`Error: ${error}`) + return null + } + } + return { runDebugExpression, + getPausedLocation, dispose: () => { for (const disposable of disposables) { disposable.dispose() diff --git a/packages/extension/src/util/util.ts b/packages/extension/src/util/util.ts new file mode 100644 index 0000000..8b06621 --- /dev/null +++ b/packages/extension/src/util/util.ts @@ -0,0 +1,46 @@ +import * as vscode from 'vscode' + +/** + * Creates a function that is restricted to invoking func once. + * Repeat calls to the function return the value of the first call. + * The func is invoked with the this binding and arguments of the created function. + * + * You can peek at the result of the first call to the function by calling the peek() method. + * + * Copied and adapted from lodash's 'once' function. + */ +export function onceWithPeek any>(fn: T): { + (): ReturnType + peek: () => ReturnType | undefined +} { + let called = false + let result: ReturnType + + const wrapper = (...args: Parameters): ReturnType => { + if (!called) { + result = fn(...args) + called = true + } + return result + } + + wrapper.peek = (): ReturnType | undefined => { + return called ? result : undefined + } + + return wrapper +} + +/** + * Returns the editor for the given file path, or opens a new editor if it doesn't exist. + */ +export async function getOrOpenEditor(filePath: string) { + let editor = vscode.window.visibleTextEditors.find( + editor => editor.document.uri.path === filePath, + ) + if (!editor) { + const document = await vscode.workspace.openTextDocument(vscode.Uri.file(filePath)) + editor = await vscode.window.showTextDocument(document, vscode.ViewColumn.One) + } + return editor +} diff --git a/packages/extension/test/compat.test.ts b/packages/extension/test/compat.test.ts index 7bad98e..d2c7460 100644 --- a/packages/extension/test/compat.test.ts +++ b/packages/extension/test/compat.test.ts @@ -1,10 +1,10 @@ import { execa } from 'execa' -import { findUp } from 'find-up' import getPort from 'get-port' import path from 'pathe' import { beforeAll, describe, expect, it } from 'vitest' import type { Server as WsServer } from 'ws' import { makeDebugConfig } from '../src/debug-config' +import { detectTestFramework } from '../src/framework-support/detect-test-framework' describe('tool compatibility', async () => { it('works with Jest + Nextjs + SWC minimal/default setup', async () => { @@ -89,28 +89,6 @@ describe('tool compatibility', async () => { let fakeHtmlUpdaterServer: WsServer beforeAll(async () => { - // These tests require the "build-prod" folder to exist - { - const buildDir = await findUp( - 'build-prod', - { cwd: examplesPath, type: 'directory' }, - ) - if (!buildDir) { - const pnpmLock = await findUp( - 'pnpm-lock.yaml', - { cwd: __dirname }, - ) - if (!pnpmLock) { - throw new Error(`Could not find project root from ${__dirname}`) - } - console.log('These tests requires the "build-prod" folder to exist. Building it...') - await execa({ - cwd: path.dirname(pnpmLock), - stdout: ['pipe', 'inherit'], - })`pnpm run build` - } - } - htmlUpdaterPort = await getPort() fakeHtmlUpdaterServer = new Server({ port: htmlUpdaterPort }) @@ -123,10 +101,11 @@ describe('tool compatibility', async () => { testFile: string, testName: string, ) { + const fwInfo = await detectTestFramework(path.join(examplesPath, testFile), 'autodetect') const cfg = await makeDebugConfig( + fwInfo, path.join(examplesPath, testFile), testName, - 'autodetect', htmlUpdaterPort, [], ) diff --git a/packages/extension/test/detect-test-framework.test.ts b/packages/extension/test/detect-test-framework.test.ts index 0f3c564..369f76e 100644 --- a/packages/extension/test/detect-test-framework.test.ts +++ b/packages/extension/test/detect-test-framework.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import path from 'pathe' -import { detectTestFramework } from '../src/framework-support/detect' +import { detectTestFramework } from '../src/framework-support/detect-test-framework' const examplesPath = path.join(__dirname, '../../../examples') diff --git a/packages/extension/test/recorder/create-ui-test-code.test.ts b/packages/extension/test/recorder/create-ui-test-code.test.ts new file mode 100644 index 0000000..6c3efb6 --- /dev/null +++ b/packages/extension/test/recorder/create-ui-test-code.test.ts @@ -0,0 +1,69 @@ +import { parseSync } from 'oxc-parser' +import { describe, expect, it } from 'vitest' +import { createUITestCode } from '../../src/recorder/create-new-test-file/create-ui-test-code' +import type { SupportedFramework } from '../../src/framework-support/detect-test-framework' +import type { TestingLibrary } from '../../src/framework-support/detect-test-library' + +const program = parseSync('my-component.ts', 'export function MyComponent() {}', {}).program + +function input( + framework: SupportedFramework, + testingLibrary: TestingLibrary, +): Parameters[0] { + return { + program, + word: 'MyComponent', + frameworkInfo: { + framework, + configPath: '', + }, + relativePathToSrc: 'my-component', + testingLibrary, + } +} + +describe('createUITestCode', () => { + it('jest+react', () => { + const [_, result] = createUITestCode(input('jest', '@testing-library/react')) + expect(result).toEqual({ + exportName: 'MyComponent', + testContent: `import { render } from '@testing-library/react' +import { MyComponent } from './my-component' + +test('basic usage', async () => { + (() => render())() +}) +`, + }) + }) + + it('vitest+solid', () => { + const [_, result] = createUITestCode(input('vitest', '@solidjs/testing-library')) + expect(result).toEqual({ + exportName: 'MyComponent', + testContent: `import { test } from 'vitest' +import { render } from '@solidjs/testing-library' +import { MyComponent } from './my-component' + +test('basic usage', async () => { + (() => render(() => ))() +}) +`, + }) + }) + + it('bun+react', () => { + const [_, result] = createUITestCode(input('bun', '@testing-library/react')) + expect(result).toEqual({ + exportName: 'MyComponent', + testContent: `import { test } from 'bun:test' +import { render } from '@testing-library/react' +import { MyComponent } from './my-component' + +test('basic usage', async () => { + (() => render())() +}) +`, + }) + }) +}) diff --git a/packages/extension/test/recorder/generate-code-from-input.test.ts b/packages/extension/test/recorder/generate-code-from-input.test.ts new file mode 100644 index 0000000..165d3fe --- /dev/null +++ b/packages/extension/test/recorder/generate-code-from-input.test.ts @@ -0,0 +1,428 @@ +import { expect, test } from 'vitest' +import type { RecorderGeneratedCode } from '../../src/recorder/generate-code-from-input' +import { generateCodeFromInput } from '../../src/recorder/generate-code-from-input' + +const userEventLibPath = '/fakepath/user-event-13.js' + +test('change an empty text input: vitest + react', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'change', + eventData: { text: 'test-input' }, + findMethod: 'getByRole', + queryArg0: 'textbox', + queryOptions: { name: { type: 'regexp', value: '/^my-input$/i' } }, + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + `await userEvent.type(screen.getByRole('textbox', { name: /^my-input$/i }), 'test-input')`, + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +const userEvent = globalThis.require('/fakepath/user-event-13.js').default; +globalThis.require('react').act(() => { userEvent.type(screen.getByRole('textbox', { name: /^my-input$/i }), 'test-input') }); +})() +`, + requiredImports: { + screen: '@testing-library/react', + userEvent: '@testing-library/user-event', + }, + } + + expect(result).toEqual(expected) +}) + +test('change an empty text input: vitest + solid', () => { + const result = generateCodeFromInput( + true, + '@solidjs/testing-library', + 'vitest', + userEventLibPath, + { + event: 'change', + eventData: { text: 'test-input' }, + findMethod: 'getByRole', + queryArg0: 'textbox', + queryOptions: { name: { type: 'regexp', value: '/^my-input$/i' } }, + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + `await userEvent.type(screen.getByRole('textbox', { name: /^my-input$/i }), 'test-input')`, + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@solidjs/testing-library'); +const userEvent = globalThis.require('/fakepath/user-event-13.js').default; +userEvent.type(screen.getByRole('textbox', { name: /^my-input$/i }), 'test-input') +})() +`, + requiredImports: { + screen: '@solidjs/testing-library', + userEvent: '@testing-library/user-event', + }, + } + + expect(result).toEqual(expected) +}) + +test('change a pre-filled text input', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'change', + eventData: { text: 'test-input', clearBeforeType: true }, + findMethod: 'getByRole', + queryArg0: 'textbox', + queryOptions: { name: { type: 'regexp', value: '/^my-input$/i' } }, + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + `await userEvent.clear(screen.getByRole('textbox', { name: /^my-input$/i }))`, + `await userEvent.type(screen.getByRole('textbox', { name: /^my-input$/i }), 'test-input')`, + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +const userEvent = globalThis.require('/fakepath/user-event-13.js').default; +globalThis.require('react').act(() => { userEvent.clear(screen.getByRole('textbox', { name: /^my-input$/i })) +userEvent.type(screen.getByRole('textbox', { name: /^my-input$/i }), 'test-input') }); +})() +`, + requiredImports: { + screen: '@testing-library/react', + userEvent: '@testing-library/user-event', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate an expect statement: vitest', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'click', + eventData: { enabled: true }, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useExpect: 'toBeEnabled', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'button\', { name: \'my-button\' })).toBeEnabled()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +expect(screen.getByRole('button', { name: 'my-button' })).toBeEnabled() +})() +`, + requiredImports: { + expect: 'vitest', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate an expect statement: vitest + solid', () => { + const result = generateCodeFromInput( + true, + '@solidjs/testing-library', + 'vitest', + userEventLibPath, + { + event: 'click', + eventData: { enabled: true }, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useExpect: 'toBeEnabled', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'button\', { name: \'my-button\' })).toBeEnabled()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@solidjs/testing-library'); +expect(screen.getByRole('button', { name: 'my-button' })).toBeEnabled() +})() +`, + requiredImports: { + expect: 'vitest', + screen: '@solidjs/testing-library', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate an expect statement: jest', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'jest', + userEventLibPath, + { + event: 'click', + eventData: { enabled: true }, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useExpect: 'toBeEnabled', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'button\', { name: \'my-button\' })).toBeEnabled()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +expect(screen.getByRole('button', { name: 'my-button' })).toBeEnabled() +})() +`, + requiredImports: { + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate an expect statement: bun test api', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'bun', + userEventLibPath, + { + event: 'click', + eventData: { enabled: true }, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useExpect: 'toBeEnabled', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'button\', { name: \'my-button\' })).toBeEnabled()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +const { expect } = globalThis.require('bun:test'); +expect(screen.getByRole('button', { name: 'my-button' })).toBeEnabled() +})() +`, + requiredImports: { + expect: 'bun:test', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate a mouseup event with fireEvent', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'mouseUp', + eventData: {}, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useFireEvent: true, + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'fireEvent.mouseUp(screen.getByRole(\'button\', { name: \'my-button\' }))', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +const { fireEvent } = globalThis.require('@testing-library/react'); +fireEvent.mouseUp(screen.getByRole('button', { name: 'my-button' })) +})() +`, + requiredImports: { + fireEvent: '@testing-library/react', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate toBeChecked expect statement', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'click', + eventData: { checked: true }, + findMethod: 'getByRole', + queryArg0: 'checkbox', + queryOptions: { name: 'my-checkbox' }, + useExpect: 'toBeChecked', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'checkbox\', { name: \'my-checkbox\' })).toBeChecked()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +expect(screen.getByRole('checkbox', { name: 'my-checkbox' })).toBeChecked() +})() +`, + requiredImports: { + expect: 'vitest', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate not.toBeChecked expect statement', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'click', + eventData: { checked: false }, + findMethod: 'getByRole', + queryArg0: 'checkbox', + queryOptions: { name: 'my-checkbox' }, + useExpect: 'toBeChecked', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'checkbox\', { name: \'my-checkbox\' })).not.toBeChecked()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +expect(screen.getByRole('checkbox', { name: 'my-checkbox' })).not.toBeChecked() +})() +`, + requiredImports: { + expect: 'vitest', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate toBeEnabled expect statement', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'click', + eventData: { enabled: true }, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useExpect: 'toBeEnabled', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'button\', { name: \'my-button\' })).toBeEnabled()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +expect(screen.getByRole('button', { name: 'my-button' })).toBeEnabled() +})() +`, + requiredImports: { + expect: 'vitest', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) + +test('generate not.toBeEnabled expect statement', () => { + const result = generateCodeFromInput( + true, + '@testing-library/react', + 'vitest', + userEventLibPath, + { + event: 'click', + eventData: { enabled: false }, + findMethod: 'getByRole', + queryArg0: 'button', + queryOptions: { name: 'my-button' }, + useExpect: 'toBeEnabled', + }, + ) + + const expected: RecorderGeneratedCode = { + code: [ + 'expect(screen.getByRole(\'button\', { name: \'my-button\' })).not.toBeEnabled()', + ], + debugExpression: ` +(() => { +const { screen } = globalThis.require('@testing-library/react'); +expect(screen.getByRole('button', { name: 'my-button' })).not.toBeEnabled() +})() +`, + requiredImports: { + expect: 'vitest', + screen: '@testing-library/react', + }, + } + + expect(result).toEqual(expected) +}) diff --git a/packages/extension/tsup.config.ts b/packages/extension/tsup.config.ts index c79e26c..b197e45 100644 --- a/packages/extension/tsup.config.ts +++ b/packages/extension/tsup.config.ts @@ -23,6 +23,11 @@ export default defineConfig((options) => { 'vitest-cli-setup': '../test-setup/src/vitest-cli-setup.ts', 'test-runtime-setup': '../test-setup/src/test-runtime-setup.ts', 'transform-css': './src/transform-css/transform-css.ts', + + // user-event v13 is used when running the recorder's generated code as debug expressions, because it's the last version + // where the methods were synchronous e.g. `userEvent.click(...);`. Newer versions use async code like `await userEvent.click(...);` + // which fail when running through the debugger's 'evaluate' request. + 'user-event-13': './node_modules/@testing-library/user-event/dist/index.js', }, format: ['cjs'], outExtension: () => ({ diff --git a/packages/replicate-dom/src/primary/ignored-node-methods.ts b/packages/replicate-dom/src/primary/ignored-node-methods.ts index d85f56e..da6fb10 100644 --- a/packages/replicate-dom/src/primary/ignored-node-methods.ts +++ b/packages/replicate-dom/src/primary/ignored-node-methods.ts @@ -227,6 +227,7 @@ export const IGNORED_NODE_METHODS: { getEnclosureList: true, getIntersectionList: true, getScreenCTM: true, + getHTML: true, querySelector: true, querySelectorAll: true, diff --git a/packages/test-setup/src/test-runtime-setup.ts b/packages/test-setup/src/test-runtime-setup.ts index faf2fd0..5c9fc9f 100644 --- a/packages/test-setup/src/test-runtime-setup.ts +++ b/packages/test-setup/src/test-runtime-setup.ts @@ -1,6 +1,7 @@ // Run this script at the beginning of the test process import { log, error as logError } from 'node:console' +import { createRequire } from 'node:module' import { initPrimaryDom, serializeDomNode } from 'replicate-dom' import difference from 'lodash/difference' import shadowCss from './shadow.css.txt' @@ -178,6 +179,10 @@ if (process.env.TEST_FRAMEWORK === 'jest') { module.exports = preTest } +// Make 'require' available in debug expressions for the recorder. +// May be undefined when running in Bun and Jest. +globalThis.require ??= createRequire(process.env.TEST_FILE_PATH!) + function validateStringArray(value: unknown): string[] | null { if (!Array.isArray(value)) { logError(`Invalid CSS files: ${JSON.stringify(value)} ( ${String(value)} )`) diff --git a/packages/web-view-vite/index.html b/packages/web-view-vite/index.html index 110d016..d792321 100644 --- a/packages/web-view-vite/index.html +++ b/packages/web-view-vite/index.html @@ -2,6 +2,7 @@ + diff --git a/packages/web-view-vite/package.json b/packages/web-view-vite/package.json index 94c8aff..d142902 100644 --- a/packages/web-view-vite/package.json +++ b/packages/web-view-vite/package.json @@ -7,33 +7,43 @@ "build": "vite build" }, "dependencies": { + "@corvu/resizable": "^0.2.5", + "@happy-dom/global-registrator": "^20.0.8", "@kobalte/core": "0.13.11", "@kobalte/utils": "^0.9.1", + "@shikijs/core": "^3.13.0", + "@shikijs/langs": "^3.13.0", + "@shikijs/themes": "^3.13.0", "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/map": "^0.7.2", "@solid-primitives/mutation-observer": "^1.2.2", + "@testing-library/dom": "^10.4.1", + "@testing-library/user-event": "^14.6.1", "@trpc/client": "^11.6.0", "@trpc/server": "^11.6.0", "@vscode/webview-ui-toolkit": "^1.4.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "csstype": "^3.1.3", + "happy-dom": "^20.0.8", "kagekiri": "^2.0.0", "lodash": "^4.17.21", - "lucide-solid": "^0.544.0", + "lucide-solid": "^0.546.0", "replicate-dom": "workspace:*", + "shiki": "^3.13.0", "solid-js": "^1.9.9", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", - "ws": "^8.18.3" + "ws": "^8.18.3", + "zod": "^4.1.12" }, "devDependencies": { - "@tailwindcss/vite": "^4.1.13", + "@tailwindcss/vite": "^4.1.14", "@types/lodash": "^4.17.20", "@types/vscode-webview": "^1.57.5", "@types/ws": "^8.18.1", - "tailwindcss": "^4.1.13", - "vite": "^7.1.7", - "vite-plugin-solid": "^2.11.8" + "tailwindcss": "^4.1.14", + "vite": "^7.1.10", + "vite-plugin-solid": "^2.11.9" } } diff --git a/packages/web-view-vite/src/App.tsx b/packages/web-view-vite/src/App.tsx index 06822d0..4f26e6f 100644 --- a/packages/web-view-vite/src/App.tsx +++ b/packages/web-view-vite/src/App.tsx @@ -1,29 +1,17 @@ -import * as webviewToolkit from '@vscode/webview-ui-toolkit' -import { ErrorBoundary, Show, createSignal } from 'solid-js' -import { createColorTheme } from './lib/color-theme' -import { createDomReplica } from './lib/create-dom-replica' -import { createInspectorHeight } from './inspector/inspector-height' +import clamp from 'lodash/clamp' +import debounce from 'lodash/debounce' +import { ErrorBoundary, Match, Show, Switch, createSignal } from 'solid-js' +import { Resizable, ResizableHandle, ResizablePanel } from './components/solid-ui/resizable' import { Toolbar } from './components/Toolbar' import { Inspector } from './inspector/Inspector' -import { Resizer } from './inspector/Resizer' - -// Importing the router type from the server file - -// In order to use the Webview UI Toolkit web components they -// must be registered with the browser (i.e. webview) using the -// syntax below. -{ - const prefix = 'ui-test-visualizer' - webviewToolkit - .provideVSCodeDesignSystem() - .register(webviewToolkit.vsCodeButton({ prefix })) - .register(webviewToolkit.vsCodeCheckbox({ prefix })) - .register(webviewToolkit.vsCodeProgressRing({ prefix })) - .register(webviewToolkit.vsCodeTextField({ prefix })) -} +import { createColorTheme } from './lib/color-theme' +import { createDomReplica } from './lib/create-dom-replica' +import { createRecorder } from './recorder/recorder' +import { RecorderPanel } from './recorder/recorder-panel' +import { ContextMenu } from './components/solid-ui/context-menu' +import { RecorderContextMenuProvider } from './recorder/recorder-context-menu' // TODO put these into a context provider - export const [replicaHtmlEl, setReplicaHtmlEl] = createSignal() export const [theme, toggleTheme] = createColorTheme( replicaHtmlEl, @@ -36,9 +24,27 @@ export const { stylesAreLoading, } = createDomReplica() -export const inspector = createInspectorHeight() +export const STORAGE_KEY_INSPECTOR_IS_OPEN = 'ui-test-visualizer.inspector-is-open' +const inspectorWasOpen = localStorage.getItem(STORAGE_KEY_INSPECTOR_IS_OPEN) === 'true' + +export type Panel = 'inspector' | 'recorder' +const [openPanel, _setOpenPanel] = createSignal( + inspectorWasOpen ? 'inspector' : null, +) +export { openPanel } + +export function updateOpenPanel(newPanel: Panel | null) { + _setOpenPanel(newPanel) + if (newPanel === 'inspector' || newPanel === null) { + localStorage.setItem(STORAGE_KEY_INSPECTOR_IS_OPEN, newPanel === 'inspector' ? 'true' : 'false') + } +} + +export const recorder = createRecorder(shadowHost) export function App() { + const { panelSizes, updateBottomPanelHeight } = createBottomPanelHeight() + return (
@@ -53,30 +59,74 @@ export function App() {
-
sizes[1] && updateBottomPanelHeight(sizes[1])} > - {shadowHost} -
- -
- - ( -
- Error showing the inspector{error instanceof Error ? `: ${error.message}` : ''} -
- )} - > - -
-
-
+ + + {shadowHost} + + + + + + + + ( +
+ Error showing the recorder UI{error instanceof Error ? `: ${error.message}` : ''} +
+ )} + > + +
+
+ + ( +
+ Error showing the inspector{error instanceof Error ? `: ${error.message}` : ''} +
+ )} + > + +
+
+
+
+
+
) } + +/** Store the bottom panel size in localStorage and return a signal for the panel layout sizes. */ +function createBottomPanelHeight() { + const BOTTOMPANEL_SIZE_KEY = 'ui-test-visualizer.bottom-panel-size' + const BOTTOMPANEL_DEFAULT_SIZE = 0.4 + const persistPanelSize = debounce((size: number) => { + localStorage.setItem(BOTTOMPANEL_SIZE_KEY, size.toString()) + }, 200) + const storedBottomPanelSize = Number(localStorage.getItem(BOTTOMPANEL_SIZE_KEY)) || BOTTOMPANEL_DEFAULT_SIZE + + const [bottomPanelHeight, _setBottomPanelHeight] = createSignal(BOTTOMPANEL_DEFAULT_SIZE) + function updateBottomPanelHeight(newHeight: number) { + newHeight = clamp(newHeight, 0.15, 0.8) + _setBottomPanelHeight(newHeight) + persistPanelSize(newHeight) + } + updateBottomPanelHeight(storedBottomPanelSize) + + function panelSizes() { + return openPanel() + ? [1 - bottomPanelHeight(), bottomPanelHeight()] + : [1, 0] + } + + return { panelSizes, updateBottomPanelHeight } +} diff --git a/packages/web-view-vite/src/components/HighlightedNode.tsx b/packages/web-view-vite/src/components/HighlightedNode.tsx new file mode 100644 index 0000000..fa4d043 --- /dev/null +++ b/packages/web-view-vite/src/components/HighlightedNode.tsx @@ -0,0 +1,39 @@ +import { makeEventListener } from '@solid-primitives/event-listener' +import { createSignal } from 'solid-js' + +/** Shows a highlight overlay over the given node */ +export function HighlightedNode(props: { node: Node }) { + function newRect() { + return props.node instanceof Text + ? (() => { + const range = document.createRange() + range.selectNodeContents(props.node) + return range.getBoundingClientRect() + })() + : props.node instanceof Element + ? props.node.getBoundingClientRect() + : props.node.parentElement?.getBoundingClientRect() + } + + const [rect, setRect] = createSignal(newRect()) + + // Update the rect on scroll, click, etc + makeEventListener(window, 'resize', () => setRect(newRect())) + makeEventListener(window, 'scroll', () => setRect(newRect())) + makeEventListener(window, 'wheel', () => setRect(newRect())) + makeEventListener(window, 'mousedown', () => setRect(newRect())) + makeEventListener(window, 'mouseup', () => setRect(newRect())) + makeEventListener(window, 'click', () => setRect(newRect())) + + return ( +
+ ) +} diff --git a/packages/web-view-vite/src/components/Toolbar.tsx b/packages/web-view-vite/src/components/Toolbar.tsx index e6e39ef..7ffc153 100644 --- a/packages/web-view-vite/src/components/Toolbar.tsx +++ b/packages/web-view-vite/src/components/Toolbar.tsx @@ -1,23 +1,28 @@ -import Sun from 'lucide-solid/icons/sun' +import Code from 'lucide-solid/icons/code' +import Info from 'lucide-solid/icons/info' import Moon from 'lucide-solid/icons/moon' import RefreshCw from 'lucide-solid/icons/refresh-cw' -import Code from 'lucide-solid/icons/code' -import type { ParentProps } from 'solid-js' -import { firstPatchReceived, inspector, refreshShadow, theme, toggleTheme } from '../App' +import Sun from 'lucide-solid/icons/sun' +import { type ParentProps, Show, createSignal } from 'solid-js' +import { firstPatchReceived, openPanel, recorder, refreshShadow, theme, toggleTheme, updateOpenPanel } from '../App' import { StyleIcon, StylePicker } from './StylePicker' +import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from './solid-ui/popover' import { Tooltip, TooltipContent, TooltipTrigger } from './solid-ui/tooltip' export function Toolbar() { + const [recorderPopoverOpen, setRecorderPopoverOpen] = createSignal(false) return (
updateOpenPanel(openPanel() === 'inspector' ? null : 'inspector')} label="Toggle Inspector" > - + )} /> +
+ recorder.toggle(!recorder.isRecording())} + label={recorder.isRecording() ? 'Stop recording' : 'Record input as code'} + > +
+
REC
+
+
+
+
+ + setRecorderPopoverOpen(open)} + > + + + + + + + +

Test Recorder Info

+
    +
  • 'Step Over' with the debugger to where you want to generate new code.
  • +
  • Left-click an element or change a text input to generate code.
  • +
  • Right-click an element to generate 'expect' statements or other mouse events.
  • +
  • When you end or restart the test, the generated code is inserted into the test file.
  • +
  • + Note: Your UI is not actually running in this panel, so certain interactions might not behave as expected. + Your UI is running in your test process as usual, and this panel shows a live replica. + When you interact here, the generated testing-library code is executed in the test process. +
  • +
+
+ setRecorderPopoverOpen(false)} + > + OK + +
+
+
+
+
) } @@ -49,6 +111,7 @@ export function Toolbar() { interface ToolbarButtonProps extends ParentProps { label: string onClick?: () => void + appearance?: string } function ToolbarButton(props: ToolbarButtonProps) { @@ -57,7 +120,7 @@ function ToolbarButton(props: ToolbarButtonProps) { diff --git a/packages/web-view-vite/src/components/solid-ui/context-menu.tsx b/packages/web-view-vite/src/components/solid-ui/context-menu.tsx new file mode 100644 index 0000000..ab95072 --- /dev/null +++ b/packages/web-view-vite/src/components/solid-ui/context-menu.tsx @@ -0,0 +1,234 @@ +import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js' +import { splitProps } from 'solid-js' + +import * as ContextMenuPrimitive from '@kobalte/core/context-menu' +import type { PolymorphicProps } from '@kobalte/core/polymorphic' + +import { cn } from './utils' + +const ContextMenuTrigger = ContextMenuPrimitive.Trigger +const ContextMenuPortal = ContextMenuPrimitive.Portal +const ContextMenuSub = ContextMenuPrimitive.Sub +const ContextMenuGroup = ContextMenuPrimitive.Group +const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup + +const ContextMenu: Component = (props) => { + return +} + +type ContextMenuContentProps = + ContextMenuPrimitive.ContextMenuContentProps & { + class?: string | undefined + } + +function ContextMenuContent(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuContentProps, ['class']) + return ( + + + + ) +} + +type ContextMenuItemProps = + ContextMenuPrimitive.ContextMenuItemProps & { + class?: string | undefined + } + +function ContextMenuItem(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuItemProps, ['class']) + return ( + + ) +} + +const ContextMenuShortcut: Component> = (props) => { + const [local, others] = splitProps(props, ['class']) + return +} + +type ContextMenuSeparatorProps = + ContextMenuPrimitive.ContextMenuSeparatorProps & { + class?: string | undefined + } + +function ContextMenuSeparator(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuSeparatorProps, ['class']) + return ( + + ) +} + +type ContextMenuSubTriggerProps = + ContextMenuPrimitive.ContextMenuSubTriggerProps & { + class?: string | undefined + children?: JSX.Element + } + +function ContextMenuSubTrigger(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuSubTriggerProps, ['class', 'children']) + return ( + + {local.children} + + + + + ) +} + +type ContextMenuSubContentProps = + ContextMenuPrimitive.ContextMenuSubContentProps & { + class?: string | undefined + } + +function ContextMenuSubContent(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuSubContentProps, ['class']) + return ( + + ) +} + +type ContextMenuCheckboxItemProps = + ContextMenuPrimitive.ContextMenuCheckboxItemProps & { + class?: string | undefined + children?: JSX.Element + } + +function ContextMenuCheckboxItem(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuCheckboxItemProps, ['class', 'children']) + return ( + + + + + + + + + {local.children} + + ) +} + +type ContextMenuGroupLabelProps = + ContextMenuPrimitive.ContextMenuGroupLabelProps & { + class?: string | undefined + } + +function ContextMenuGroupLabel(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuGroupLabelProps, ['class']) + return ( + + ) +} + +type ContextMenuRadioItemProps = + ContextMenuPrimitive.ContextMenuRadioItemProps & { + class?: string | undefined + children?: JSX.Element + } + +function ContextMenuRadioItem(props: PolymorphicProps>) { + const [local, others] = splitProps(props as ContextMenuRadioItemProps, ['class', 'children']) + return ( + + + + + + + + + {local.children} + + ) +} + +export { + ContextMenu, + ContextMenuTrigger, + ContextMenuPortal, + ContextMenuContent, + ContextMenuItem, + ContextMenuShortcut, + ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubTrigger, + ContextMenuSubContent, + ContextMenuCheckboxItem, + ContextMenuGroup, + ContextMenuGroupLabel, + ContextMenuRadioGroup, + ContextMenuRadioItem, +} diff --git a/packages/web-view-vite/src/components/solid-ui/resizable.tsx b/packages/web-view-vite/src/components/solid-ui/resizable.tsx new file mode 100644 index 0000000..b34884f --- /dev/null +++ b/packages/web-view-vite/src/components/solid-ui/resizable.tsx @@ -0,0 +1,63 @@ +import type { ValidComponent } from 'solid-js' +import { Show, splitProps } from 'solid-js' + +import type { DynamicProps, HandleProps, RootProps } from '@corvu/resizable' +import ResizablePrimitive from '@corvu/resizable' + +import { cn } from './utils' + +type ResizableProps = RootProps & { class?: string } + +function Resizable(props: DynamicProps>) { + const [, rest] = splitProps(props as ResizableProps, ['class']) + return ( + + ) +} + +const ResizablePanel = ResizablePrimitive.Panel + +type ResizableHandleProps = HandleProps & { + class?: string + withHandle?: boolean +} + +function ResizableHandle(props: DynamicProps>) { + const [, rest] = splitProps(props as ResizableHandleProps, ['class', 'withHandle']) + return ( + div]:rotate-90 z-70', + props.class, + )} + {...rest} + > + +
+ + + + + + + + +
+
+
+ ) +} + +export { Resizable, ResizablePanel, ResizableHandle } diff --git a/packages/web-view-vite/src/index.css b/packages/web-view-vite/src/index.css index f88ff51..5beb6c7 100644 --- a/packages/web-view-vite/src/index.css +++ b/packages/web-view-vite/src/index.css @@ -44,7 +44,7 @@ --success-foreground: 160 84% 39%; --warning: 48 96% 89%; - --warning-foreground: 25 95% 53%; + --warning-foreground: var(--vscode-editorWarning-foreground); --error: 0 93% 94%; --error-foreground: var(--vscode-list-errorForeground); diff --git a/packages/web-view-vite/src/index.tsx b/packages/web-view-vite/src/index.tsx index 0305992..20d30d9 100644 --- a/packages/web-view-vite/src/index.tsx +++ b/packages/web-view-vite/src/index.tsx @@ -1,8 +1,24 @@ /* @refresh reload */ + +import * as webviewToolkit from '@vscode/webview-ui-toolkit' import { render } from 'solid-js/web' +import { App } from './App' import './index.css' -import { App } from './App' + +// In order to use the Webview UI Toolkit web components they +// must be registered with the browser (i.e. webview) using the +// syntax below. +{ + const prefix = 'ui-test-visualizer' + webviewToolkit + .provideVSCodeDesignSystem() + .register(webviewToolkit.vsCodeButton({ prefix })) + .register(webviewToolkit.vsCodeCheckbox({ prefix })) + .register(webviewToolkit.vsCodeProgressRing({ prefix })) + .register(webviewToolkit.vsCodeTextField({ prefix })) + .register(webviewToolkit.vsCodeRadio({ prefix })) +} const root = document.getElementById('root') diff --git a/packages/web-view-vite/src/inspector/Inspector.tsx b/packages/web-view-vite/src/inspector/Inspector.tsx index 2201242..92a5ceb 100644 --- a/packages/web-view-vite/src/inspector/Inspector.tsx +++ b/packages/web-view-vite/src/inspector/Inspector.tsx @@ -2,7 +2,8 @@ import { makeEventListener } from '@solid-primitives/event-listener' import { ReactiveWeakMap } from '@solid-primitives/map' import { createMutationObserver } from '@solid-primitives/mutation-observer' import { Show, createEffect, createSignal, onCleanup, onMount } from 'solid-js' -import { shadowHost } from '../App' +import { recorder, shadowHost } from '../App' +import { HighlightedNode } from '../components/HighlightedNode' import { type InspectedNode, getNewDomTree } from './inspector-dom-tree' import { createInspectorSearch } from './inspector-search' import { SearchToolbar } from './SearchToolbar' @@ -101,42 +102,10 @@ export function Inspector() {
)} - - {(node) => { - function newRect() { - return node instanceof Text - ? (() => { - const range = document.createRange() - range.selectNodeContents(node) - return range.getBoundingClientRect() - })() - : node instanceof Element - ? node.getBoundingClientRect() - : node.parentElement?.getBoundingClientRect() - } - - const [rect, setRect] = createSignal(newRect()) - - // Update the rect on scroll, click, etc - makeEventListener(window, 'resize', () => setRect(newRect())) - makeEventListener(window, 'scroll', () => setRect(newRect())) - makeEventListener(window, 'wheel', () => setRect(newRect())) - makeEventListener(window, 'mousedown', () => setRect(newRect())) - makeEventListener(window, 'mouseup', () => setRect(newRect())) - makeEventListener(window, 'click', () => setRect(newRect())) - - return ( -
- ) - }} + + {node => ( + + )}
) diff --git a/packages/web-view-vite/src/inspector/Resizer.tsx b/packages/web-view-vite/src/inspector/Resizer.tsx deleted file mode 100644 index 9f4a787..0000000 --- a/packages/web-view-vite/src/inspector/Resizer.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { createSignal } from 'solid-js' -import { makeEventListener } from '@solid-primitives/event-listener' - -interface ResizerProps { - onResize: (height: number) => void -} - -export function Resizer(props: ResizerProps) { - const [isDragging, setIsDragging] = createSignal(false) - - function handleMouseDown(e: MouseEvent) { - setIsDragging(true) - e.preventDefault() - } - - // Use makeEventListener for mousemove - makeEventListener(document, 'mousemove', (e) => { - if (!isDragging()) { return } - props.onResize(window.innerHeight - e.clientY) - }) - - // Use makeEventListener for mouseup - makeEventListener(document, 'mouseup', () => { - setIsDragging(false) - }) - - makeEventListener(document, 'mouseleave', () => { - setIsDragging(false) - }) - - return ( - // invisible line to divide the 2 sections - + ) +} + +/** When a new code line is added, scroll to it. */ +function setupCodePanel(el: HTMLDivElement) { + queueMicrotask(() => { + el.scrollTop = el.scrollHeight + }) + + createEffect(on(recorder.codeInsertions, (current, prev) => { + if (current && prev) { + for (const lineNum of Object.keys(current)) { + const currentLen = current[Number(lineNum)]?.length ?? 0 + const prevLen = prev[Number(lineNum)]?.length ?? 0 + if (currentLen > prevLen) { + queueMicrotask(() => { + el.scrollTop = el.scrollHeight + }) + } + } + } + })) +} diff --git a/packages/web-view-vite/src/recorder/recorder.ts b/packages/web-view-vite/src/recorder/recorder.ts new file mode 100644 index 0000000..c1e47a7 --- /dev/null +++ b/packages/web-view-vite/src/recorder/recorder.ts @@ -0,0 +1,356 @@ +import { makeEventListener } from '@solid-primitives/event-listener' +import { ReactiveMap } from '@solid-primitives/map' +import { createMutationObserver } from '@solid-primitives/mutation-observer' +import type { EventType, QueryArgs, Suggestion } from '@testing-library/dom' +import { getSuggestedQuery, queries } from '@testing-library/dom' +import type { userEvent } from '@testing-library/user-event' +import type { inferProcedureInput } from '@trpc/server' +import { createEffect, createSignal } from 'solid-js' +import type { z } from 'zod/mini' +import type { ExpectStatementType, PanelRouter, TestingLibraryQueryArgs, zRecordedEventData } from '../../../extension/src/panel-controller/panel-router' +import type { RecorderCodeInsertions } from '../../../extension/src/recorder/recorder-codegen-session' +import { openPanel, updateOpenPanel } from '../App' +import { deepElementFromPoint } from '../inspector/util' +import { client } from '../lib/panel-client' + +export const USEREVENT_MOUSE_EVENT_TYPES: (keyof typeof userEvent)[] = [ + 'click', + 'dblClick', + 'hover', + 'unhover', + 'clear', +] + +export const FIREEVENT_MOUSE_EVENT_TYPES: EventType[] = [ + 'click', + 'dblClick', + 'mouseDown', + 'mouseUp', + 'mouseEnter', + 'mouseLeave', + 'mouseMove', + 'mouseOver', + 'mouseOut', + 'contextMenu', +] + +export function createRecorder(shadowHost: HTMLDivElement) { + function isRecording() { + return openPanel() === 'recorder' + } + const [codeInsertions, setCodeInsertions] = createSignal() + + // When the edit is performed, clear the recorder UI's insertions state + makeEventListener(window, 'message', (event) => { + if (event.data.recorderEditPerformed) { + setCodeInsertions(undefined) + } + }) + + const { hasPendingInputChange } = trackPendingInputChanges(shadowHost, isRecording) + + const [rootElement, setRootElement] = createSignal( + shadowHost.shadowRoot?.children[0] ?? null, + ) + createMutationObserver(shadowHost.shadowRoot!, { childList: true }, () => { + setRootElement(shadowHost.shadowRoot?.children[0] ?? null) + }) + + /** Generate a line of test code for the given user input event. */ + async function submitRecorderInputEvent( + target: Element, + eventType: string, + { useExpect, enterKeyPressed, useFireEvent, processInput }: { + useExpect?: ExpectStatementType + enterKeyPressed?: boolean + useFireEvent?: boolean + processInput?: (input: inferProcedureInput) => inferProcedureInput + } = {}, + ) { + let suggestedQuery: Suggestion | undefined + + /** + * Sometimes testing-library can't find a good query to use. + * This fn checks if testing-library generated a useful query. + */ + function hasQuery() { + if (!suggestedQuery) { + return false + } + if (suggestedQuery.queryArgs[0] === 'document') { + return false + } + return true + } + + // Generate the selector using the closest HTMLElement. + // e.g. if you click on an SVG, that doesn't count as an HTMLElement, + // so step up to the parent. + while (!hasQuery() && target) { + if (target instanceof HTMLElement) { + suggestedQuery = getSuggestedQuery(target) + } + if (!hasQuery()) { + if (target instanceof Element && target.parentElement) { + target = target.parentElement + } + else { + return + } + } + } + + if (!hasQuery() || !suggestedQuery) { + return + } + + const eventData: z.infer = {} + if (enterKeyPressed) { + eventData.enterKeyPressed = true + } + + // On text input change + if (eventType === 'change') { + if (target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement) { + if (target.type === 'text') { + const text = target.value + eventData.text = text + eventData.clearBeforeType = focusedInputHadText + } + } + } + + const root = rootElement() + if (!root) { + return + } + + // Check if the query returns multiple elements + await (async () => { + try { + const method = `findAllBy${suggestedQuery.queryName}` + // @ts-expect-error Method should exist + const results = await queries[method]( + root, + ...suggestedQuery.queryArgs, + ) + if (results.length > 1) { + eventData.indexIfMultipleFound = results.indexOf(target) + } + } + catch (error) { + // Do nothing? + } + })() + + const recordedEventType = (() => { + if (eventType === 'change' && target instanceof HTMLSelectElement) { + eventData.options = [target.value] + return 'selectOptions' + } + + return eventType + })() + + const queryArgs: TestingLibraryQueryArgs = [ + suggestedQuery.queryMethod, + serializeQueryArgs(suggestedQuery.queryArgs), + ] + + let input: inferProcedureInput = { + event: recordedEventType, + query: queryArgs, + eventData, + useExpect, + useFireEvent, + } + + input = processInput?.(input) ?? input + + // Send the selector to the extension process to record as code + const insertions = await client.recordInputAsCode.mutate(input) + setCodeInsertions(insertions) + } + + let focusedInputHadText = false + + // When recording, listen to events in the shadow root and generate code for them. + createEffect(() => { + const root = rootElement() + if (!root) { + return + } + if (isRecording()) { + // Check if a focused input has text before you start typing into it + makeEventListener(root, 'focus', (e: Event) => { + const target = e.target + if ( + (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) + && target.value + ) { + focusedInputHadText = true + } + }, { capture: true }) + + // Capture input events to be recorded as test code + for (const eventType of ['click', 'submit', 'change', 'keydown'] as const) { + makeEventListener(root, eventType, async (e: Event) => { + let target = e.target + + // When clicking, use deepElementFromPoint to get the right element if it's inside a shadow root. + if (e instanceof MouseEvent) { + const clickedEl = (shadowHost.shadowRoot && deepElementFromPoint(shadowHost.shadowRoot, e.clientX, e.clientY)) ?? e.target + if (!(clickedEl instanceof Element)) { + return + } + target = clickedEl + } + + if (!(target instanceof Element)) { + return + } + + if ( + (target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement) + && (target.type === 'checkbox' || target.type === 'radio') + && eventType === 'change' + ) { + e.preventDefault() + return // generate code for the 'click' event, not 'change' + } + + let enterKeyPressed = false + if (eventType === 'keydown') { + if (e instanceof KeyboardEvent && target instanceof HTMLInputElement && e.key === 'Enter') { + enterKeyPressed = true + target.blur() + await new Promise(resolve => setTimeout(resolve, 0)) + target.focus() + } + else { + // Ignore other key presses + return + } + } + + // On alt-click, generate the 'minimal' expect statement + // e.g. `expect(screen.getByRole('button'))` + const useExpect = (eventType === 'click' && e instanceof MouseEvent && e.altKey) + ? 'minimal' + : undefined + // When the 'alt' key is held while clicking, generate an 'expect' statement + if (useExpect) { + e.preventDefault() + } + + await submitRecorderInputEvent(target, eventType, { useExpect, enterKeyPressed }) + }) + } + } + else { + makeEventListener(root, 'beforeinput', (e) => { + // Block changing inputs when not recording + e.preventDefault() + }) + makeEventListener(root, 'click', (e) => { + if (e.target instanceof HTMLInputElement) { + // Block changing inputs like checkboxes and radios when not recording + e.preventDefault() + } + }) + } + }) + + return { + isRecording, + toggle: (recording: boolean) => { + updateOpenPanel(recording ? 'recorder' : null) + }, + removeInsertion: async (line: number, idx?: number) => { + const newInsertions = await client.removeRecorderInsertion.mutate({ line, idx }) + setCodeInsertions(newInsertions) + }, + codeInsertions, + hasPendingInputChange, + submitRecorderInputEvent, + } +} + +/** + * Convert the queryArgs from testing-library to JSON to be sent to the extension process. + * Mainly to convert the RegExp to a string before sending it. + */ +export function serializeQueryArgs(queryArgs: QueryArgs): [string | SerializedRegexp, { [key: string]: string | boolean | SerializedRegexp }?] { + const [query, options] = queryArgs + if (!options) { + // @ts-expect-error Not declared the testing library, but the query could be a RegExp: + const result = query instanceof RegExp ? processRegexp(query) : query + return [result] + } + const serializedOptions = Object.entries(options).reduce((prev, curr) => { + const val = curr[1] + if (val !== undefined) { + prev[curr[0]] = val instanceof RegExp ? processRegexp(val) : val + } + return prev + }, {} as Record) + + return [query, serializedOptions] +} + +export interface SerializedRegexp { type: 'regexp', value: string } + +function processRegexp(regexp: RegExp): SerializedRegexp { + // Make the regexp exact, to avoid multiple matches. + // e.g. when you have buttons aria-labeled "right" and "top right", then the regex /right/ would match both. + regexp = makeRegexpExact(regexp) + + return { type: 'regexp', value: regexp.toString() } +} + +function makeRegexpExact(regexp: RegExp) { + const { source, flags } = regexp + + // Wrap with ^ and $ + return new RegExp(`^${source}$`, flags) +} + +function trackPendingInputChanges(shadowHost: HTMLDivElement, isRecording: () => boolean) { + const isPendingInputChange = new ReactiveMap() + function hasPendingInputChange() { + return isPendingInputChange.size > 0 + } + + makeEventListener(shadowHost.shadowRoot!, 'input', (e) => { + if (!(e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)) { + return + } + isPendingInputChange.set(e.target, true) + }) + makeEventListener(shadowHost.shadowRoot!, 'change', (e) => { + if (!(e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)) { + return + } + isPendingInputChange.delete(e.target) + }) + + const shadowHtml = shadowHost.shadowRoot?.children[0] + if (shadowHtml) { + createMutationObserver(shadowHtml, { subtree: true, childList: true }, (mutations) => { + for (const mutation of mutations) { + for (const removedNode of mutation.removedNodes) { + isPendingInputChange.delete(removedNode) + } + } + }) + } + createEffect(() => { + if (!isRecording()) { + isPendingInputChange.clear() + } + }) + + return { + hasPendingInputChange, + } +} diff --git a/packages/web-view-vite/ui.preset.js b/packages/web-view-vite/ui.preset.js index 133efcb..ebd696c 100644 --- a/packages/web-view-vite/ui.preset.js +++ b/packages/web-view-vite/ui.preset.js @@ -41,7 +41,7 @@ module.exports = { }, warning: { DEFAULT: 'hsl(var(--warning))', - foreground: 'hsl(var(--warning-foreground))', + foreground: 'var(--warning-foreground)', }, error: { DEFAULT: 'hsl(var(--error))', diff --git a/playwright.config.ts b/playwright.config.ts index 35ff30e..46a9d46 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -16,6 +16,6 @@ export default defineConfig({ headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS, viewport: { width: 1600, height: 800 }, }, - timeout: 60_000, + timeout: 90_000, retries: process.env.CI ? 1 : 0, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4342f2..ff9a349 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,19 +15,19 @@ importers: devDependencies: '@antfu/eslint-config': specifier: ^2.19.1 - version: 2.19.1(@vue/compiler-sfc@3.4.34)(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)(vitest@3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + version: 2.19.1(@vue/compiler-sfc@3.4.34)(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) '@changesets/cli': specifier: ^2.29.7 - version: 2.29.7(@types/node@24.6.0) + version: 2.29.7(@types/node@24.8.1) '@playwright/test': - specifier: ^1.55.1 - version: 1.55.1 + specifier: ^1.56.1 + version: 1.56.1 '@total-typescript/ts-reset': specifier: ^0.6.1 version: 0.6.1 '@types/node': - specifier: ^24.6.0 - version: 24.6.0 + specifier: ^24.8.1 + version: 24.8.1 '@vitest/expect': specifier: ^3.2.4 version: 3.2.4 @@ -35,29 +35,32 @@ importers: specifier: ^3.6.2 version: 3.6.2 eslint: - specifier: ^9.36.0 - version: 9.36.0(jiti@2.6.0) + specifier: ^9.38.0 + version: 9.38.0(jiti@2.6.1) ovsx: specifier: ^0.10.6 version: 0.10.6 + pathe: + specifier: ^2.0.3 + version: 2.0.3 tsup: specifier: ^8.5.0 - version: 8.5.0(jiti@2.6.0)(postcss@8.5.6)(typescript@5.9.2)(yaml@2.4.2) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(typescript@5.9.3)(yaml@2.4.2) type-fest: specifier: ^5.0.1 version: 5.0.1 typescript: - specifier: ^5.9.2 - version: 5.9.2 + specifier: ^5.9.3 + version: 5.9.3 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + version: 3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) examples/jest-babel-nextjs: dependencies: next: - specifier: ^15.5.4 - version: 15.5.4(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2) + specifier: ^15.5.6 + version: 15.5.6(@babel/core@7.28.4)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2) react: specifier: ^19.2.0 version: 19.2.0 @@ -69,14 +72,14 @@ importers: specifier: ^7.28.4 version: 7.28.4 '@testing-library/jest-dom': - specifier: ^6.9.0 - version: 6.9.0 + specifier: ^6.9.1 + version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@testing-library/user-event': specifier: ^14.6.1 - version: 14.6.1(@testing-library/dom@10.4.0) + version: 14.6.1(@testing-library/dom@10.4.1) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -91,13 +94,13 @@ importers: version: 3.0.0 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-environment-jsdom: specifier: ^30.2.0 version: 30.2.0 typescript: - specifier: ^5.9.2 - version: 5.9.2 + specifier: ^5.9.3 + version: 5.9.3 examples/jest-monorepo: dependencies: @@ -107,7 +110,7 @@ importers: devDependencies: '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -116,7 +119,7 @@ importers: version: 19.2.2 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-environment-jsdom: specifier: ^30.2.0 version: 30.2.0 @@ -124,8 +127,8 @@ importers: examples/jest-nextjs-minimal: dependencies: next: - specifier: ^15.5.4 - version: 15.5.4(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2) + specifier: ^15.5.6 + version: 15.5.6(@babel/core@7.28.4)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2) react: specifier: ^19.2.0 version: 19.2.0 @@ -137,11 +140,14 @@ importers: version: 0.0.1 devDependencies: '@testing-library/jest-dom': - specifier: ^6.9.0 - version: 6.9.0 + specifier: ^6.9.1 + version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -150,19 +156,22 @@ importers: version: 19.2.2 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-environment-jsdom: specifier: ^30.2.0 version: 30.2.0 + tailwindcss: + specifier: ^4.1.16 + version: 4.1.16 typescript: - specifier: ^5.9.2 - version: 5.9.2 + specifier: ^5.9.3 + version: 5.9.3 examples/jest-nextjs-ts-node: dependencies: next: - specifier: ^15.5.4 - version: 15.5.4(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2) + specifier: ^15.5.6 + version: 15.5.6(@babel/core@7.28.4)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2) react: specifier: ^19.2.0 version: 19.2.0 @@ -174,11 +183,11 @@ importers: version: 0.0.1 devDependencies: '@testing-library/jest-dom': - specifier: ^6.9.0 - version: 6.9.0 + specifier: ^6.9.1 + version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -187,16 +196,16 @@ importers: version: 19.2.2 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-environment-jsdom: specifier: ^30.2.0 version: 30.2.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@24.6.0)(typescript@5.9.2) + version: 10.9.2(@types/node@24.8.1)(typescript@5.9.3) typescript: - specifier: ^5.9.2 - version: 5.9.2 + specifier: ^5.9.3 + version: 5.9.3 examples/jest-react: dependencies: @@ -215,7 +224,10 @@ importers: version: 7.27.1(@babel/core@7.28.4) '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -224,7 +236,7 @@ importers: version: 19.2.2 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-canvas-mock: specifier: ^2.5.2 version: 2.5.2 @@ -249,7 +261,7 @@ importers: version: 7.27.1(@babel/core@7.28.4) '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -258,7 +270,7 @@ importers: version: 19.2.2 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-canvas-mock: specifier: ^2.5.2 version: 2.5.2 @@ -274,13 +286,13 @@ importers: devDependencies: '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@types/react': specifier: ^19.2.2 version: 19.2.2 '@vitejs/plugin-react': specifier: ^5.0.4 - version: 5.0.4(vite@5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)) + version: 5.0.4(vite@5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) @@ -288,8 +300,8 @@ importers: specifier: ^14.11.4 version: 14.12.3 less: - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^4.4.2 + version: 4.4.2 postcss: specifier: ^8.5.6 version: 8.5.6 @@ -301,13 +313,13 @@ importers: version: 0.64.0 tailwindcss: specifier: 3.4.17 - version: 3.4.17(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 3.4.17(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) vite: specifier: ^5.4.19 - version: 5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + version: 5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) vitest: specifier: 2.1.8 - version: 2.1.8(@types/node@24.6.0)(happy-dom@14.12.3)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + version: 2.1.8(@types/node@24.8.1)(happy-dom@14.12.3)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) examples/vitest-react-tailwind4: dependencies: @@ -316,17 +328,20 @@ importers: version: 19.2.0 devDependencies: '@tailwindcss/vite': - specifier: ^4.1.13 - version: 4.1.13(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + specifier: ^4.1.14 + version: 4.1.14(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) '@types/react': specifier: ^19.2.2 version: 19.2.2 '@vitejs/plugin-react': specifier: ^5.0.4 - version: 5.0.4(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + version: 5.0.4(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) happy-dom: specifier: ^14.11.4 version: 14.12.3 @@ -334,32 +349,32 @@ importers: specifier: ^1.93.2 version: 1.93.2 tailwindcss: - specifier: ^4.1.13 - version: 4.1.13 + specifier: ^4.1.14 + version: 4.1.14 vite: - specifier: ^7.1.7 - version: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + specifier: ^7.1.10 + version: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) vitest: - specifier: ^4.0.0-beta.8 - version: 4.0.0-beta.8(@types/node@24.6.0)(happy-dom@14.12.3)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + specifier: ^4.0.0-beta.18 + version: 4.0.0-beta.18(@types/node@24.8.1)(happy-dom@14.12.3)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) packages/extension: dependencies: '@napi-rs/wasm-runtime': - specifier: ^1.0.5 - version: 1.0.5 + specifier: ^1.0.7 + version: 1.0.7 '@oxc-parser/binding-wasm32-wasi': - specifier: ^0.93.0 - version: 0.93.0 + specifier: ^0.95.0 + version: 0.95.0 '@tailwindcss/oxide-wasm32-wasi': - specifier: ^4.1.13 - version: 4.1.13 + specifier: ^4.1.14 + version: 4.1.14 '@trpc/server': specifier: ^11.6.0 - version: 11.6.0(typescript@5.9.2) + version: 11.6.0(typescript@5.9.3) '@vscode/extension-telemetry': - specifier: ^1.0.0 - version: 1.0.0(tslib@2.8.1) + specifier: ^1.1.0 + version: 1.1.0(tslib@2.8.1) enhanced-resolve: specifier: ^5.18.3 version: 5.18.3 @@ -374,7 +389,7 @@ importers: version: 7.1.0 jest-config: specifier: 30.2.0 - version: 30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + version: 30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) lodash: specifier: ^4.17.21 version: 4.17.21 @@ -386,14 +401,17 @@ importers: version: 8.5.6 postcss-load-config: specifier: ^6.0.1 - version: 6.0.1(jiti@2.6.0)(postcss@8.5.6)(yaml@2.4.2) + version: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.4.2) replicate-dom: specifier: workspace:* version: link:../replicate-dom zod: - specifier: ^4.1.11 - version: 4.1.11 + specifier: ^4.1.12 + version: 4.1.12 devDependencies: + '@testing-library/user-event': + specifier: ^13.5.0 + version: 13.5.0(@testing-library/dom@10.4.1) '@types/less': specifier: ^3.0.8 version: 3.0.8 @@ -401,14 +419,14 @@ importers: specifier: ^4.17.20 version: 4.17.20 '@types/node': - specifier: ^24.6.0 - version: 24.6.0 + specifier: ^24.8.1 + version: 24.8.1 '@types/stylus': specifier: ^0.48.43 version: 0.48.43 '@types/vscode': - specifier: ^1.104.0 - version: 1.104.0 + specifier: ^1.105.0 + version: 1.105.0 '@types/ws': specifier: ^8.18.1 version: 8.18.1 @@ -416,17 +434,20 @@ importers: specifier: ^3.2.4 version: 3.2.4 '@vscode/codicons': - specifier: ^0.0.40 - version: 0.0.40 + specifier: ^0.0.41 + version: 0.0.41 esbuild: - specifier: ^0.25.10 - version: 0.25.10 + specifier: ^0.25.11 + version: 0.25.11 execa: specifier: ^9.6.0 version: 9.6.0 less: - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^4.4.2 + version: 4.4.2 + oxc-parser: + specifier: ^0.95.0 + version: 0.95.0 sass: specifier: ^1.93.2 version: 1.93.2 @@ -435,7 +456,7 @@ importers: version: 0.64.0 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + version: 3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) ws: specifier: ^8.18.3 version: 8.18.3 @@ -463,7 +484,7 @@ importers: version: 26.1.0 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@24.6.0)(happy-dom@14.6.2)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + version: 3.2.4(@types/node@24.8.1)(happy-dom@14.6.2)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) packages/test-setup: dependencies: @@ -489,12 +510,27 @@ importers: packages/web-view-vite: dependencies: + '@corvu/resizable': + specifier: ^0.2.5 + version: 0.2.5(solid-js@1.9.9) + '@happy-dom/global-registrator': + specifier: ^20.0.8 + version: 20.0.8 '@kobalte/core': specifier: 0.13.11 version: 0.13.11(solid-js@1.9.9) '@kobalte/utils': specifier: ^0.9.1 version: 0.9.1(solid-js@1.9.9) + '@shikijs/core': + specifier: ^3.13.0 + version: 3.13.0 + '@shikijs/langs': + specifier: ^3.13.0 + version: 3.13.0 + '@shikijs/themes': + specifier: ^3.13.0 + version: 3.13.0 '@solid-primitives/event-listener': specifier: ^2.4.3 version: 2.4.3(solid-js@1.9.9) @@ -504,12 +540,18 @@ importers: '@solid-primitives/mutation-observer': specifier: ^1.2.2 version: 1.2.2(solid-js@1.9.9) + '@testing-library/dom': + specifier: ^10.4.1 + version: 10.4.1 + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) '@trpc/client': specifier: ^11.6.0 - version: 11.6.0(@trpc/server@11.6.0(typescript@5.9.2))(typescript@5.9.2) + version: 11.6.0(@trpc/server@11.6.0(typescript@5.9.3))(typescript@5.9.3) '@trpc/server': specifier: ^11.6.0 - version: 11.6.0(typescript@5.9.2) + version: 11.6.0(typescript@5.9.3) '@vscode/webview-ui-toolkit': specifier: ^1.4.0 version: 1.4.0(react@19.2.0) @@ -522,6 +564,9 @@ importers: csstype: specifier: ^3.1.3 version: 3.1.3 + happy-dom: + specifier: ^20.0.8 + version: 20.0.8 kagekiri: specifier: ^2.0.0 version: 2.0.0 @@ -529,11 +574,14 @@ importers: specifier: ^4.17.21 version: 4.17.21 lucide-solid: - specifier: ^0.544.0 - version: 0.544.0(solid-js@1.9.9) + specifier: ^0.546.0 + version: 0.546.0(solid-js@1.9.9) replicate-dom: specifier: workspace:* version: link:../replicate-dom + shiki: + specifier: ^3.13.0 + version: 3.13.0 solid-js: specifier: ^1.9.9 version: 1.9.9 @@ -542,14 +590,17 @@ importers: version: 3.3.1 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@4.1.13) + version: 1.0.7(tailwindcss@4.1.14) ws: specifier: ^8.18.3 version: 8.18.3 + zod: + specifier: ^4.1.12 + version: 4.1.12 devDependencies: '@tailwindcss/vite': - specifier: ^4.1.13 - version: 4.1.13(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + specifier: ^4.1.14 + version: 4.1.14(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) '@types/lodash': specifier: ^4.17.20 version: 4.17.20 @@ -560,14 +611,14 @@ importers: specifier: ^8.18.1 version: 8.18.1 tailwindcss: - specifier: ^4.1.13 - version: 4.1.13 + specifier: ^4.1.14 + version: 4.1.14 vite: - specifier: ^7.1.7 - version: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + specifier: ^7.1.10 + version: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) vite-plugin-solid: - specifier: ^2.11.8 - version: 2.11.8(@testing-library/jest-dom@6.9.0)(solid-js@1.9.9)(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + specifier: ^2.11.9 + version: 2.11.9(@testing-library/jest-dom@6.9.1)(solid-js@1.9.9)(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) packages: @@ -581,10 +632,6 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@antfu/eslint-config@2.19.1': resolution: {integrity: sha512-gtiyWxoBnk39Vgf23xJiSQrq3esEGYesv7Q4IZXEJJaYSpyiJWHMYxlC6dFr4V9tAczDa7bZjR9E6XWEiU7VEA==} hasBin: true @@ -698,18 +745,10 @@ packages: resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.0': - resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} - engines: {node: '>=6.9.0'} - '@babel/core@7.28.4': resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.0': - resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} @@ -817,24 +856,10 @@ packages: resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.6': - resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} - engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.4': resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.5': - resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/parser@7.28.0': - resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.28.3': resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} engines: {node: '>=6.0.0'} @@ -1355,6 +1380,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.28.3': + resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + engines: {node: '>=6.9.0'} + '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} @@ -1367,10 +1396,6 @@ packages: resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.0': - resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} - engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.3': resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} engines: {node: '>=6.9.0'} @@ -1383,10 +1408,6 @@ packages: resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.0': - resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.2': resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} @@ -1461,6 +1482,11 @@ packages: bundledDependencies: - is-unicode-supported + '@corvu/resizable@0.2.5': + resolution: {integrity: sha512-psax38DraM9SXtaNh/sfvizGO6xLCL4sqT4rWYlMZP5ZsUM05Q30tvXgnTo2roQFNOz2e06WofhDsBXTjY6g4A==} + peerDependencies: + solid-js: ^1.8 + '@corvu/utils@0.4.2': resolution: {integrity: sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA==} peerDependencies: @@ -1517,8 +1543,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.10': - resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1535,8 +1561,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.10': - resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1553,8 +1579,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.10': - resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1571,8 +1597,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.10': - resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1589,8 +1615,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.10': - resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1607,8 +1633,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.10': - resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1625,8 +1651,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.10': - resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1643,8 +1669,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.10': - resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1661,8 +1687,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.10': - resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1679,8 +1705,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.10': - resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1697,8 +1723,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.10': - resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1715,8 +1741,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.10': - resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1733,8 +1759,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.10': - resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1751,8 +1777,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.10': - resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1769,8 +1795,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.10': - resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1787,8 +1813,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.10': - resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1805,8 +1831,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.10': - resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -1817,8 +1843,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.10': - resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1835,8 +1861,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.10': - resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1847,8 +1873,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.10': - resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1865,8 +1891,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.10': - resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1877,8 +1903,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.10': - resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1895,8 +1921,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.10': - resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1913,8 +1939,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.10': - resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1931,8 +1957,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.10': - resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1949,8 +1975,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.10': - resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1981,16 +2007,16 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.1': - resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + '@eslint/config-helpers@0.4.1': + resolution: {integrity: sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.2': - resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.1.0': @@ -2001,16 +2027,16 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.36.0': - resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} + '@eslint/js@9.38.0': + resolution: {integrity: sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.5': - resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + '@eslint/plugin-kit@0.4.0': + resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@floating-ui/core@1.7.3': @@ -2022,6 +2048,10 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@happy-dom/global-registrator@20.0.8': + resolution: {integrity: sha512-6XJ7BGO3tz3yLZcrQF1JIQAEZ6u3mJ2EYfLSZDuZPC5EVdGtz5O2R/RlWgLYATc2tbkPnuxb9qHWHdT4TlzeOw==} + engines: {node: '>=20.0.0'} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -2311,9 +2341,6 @@ packages: resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.12': - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -2321,10 +2348,6 @@ packages: resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} - '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} @@ -2351,9 +2374,6 @@ packages: '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} - '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -2380,32 +2400,32 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@microsoft/1ds-core-js@4.3.8': - resolution: {integrity: sha512-5y7pmcqoYmh+461xyUog4aQAZ2ZIoYlAg7VkHU0G+dpOwAmwN3WECnsO/8RNagNiVwsFOBeN5e2OxKm3vruJWw==} + '@microsoft/1ds-core-js@4.3.10': + resolution: {integrity: sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==} - '@microsoft/1ds-post-js@4.3.8': - resolution: {integrity: sha512-9Jq6PqEs/wImx246xhOU6WyrhBVMLZPp1MGCKKGexxOQKhHSbK6RzOguSYKT15nfO7JVDde3+nCGc6EPxAagaA==} + '@microsoft/1ds-post-js@4.3.10': + resolution: {integrity: sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==} - '@microsoft/applicationinsights-channel-js@3.3.8': - resolution: {integrity: sha512-uj60YhHTxcHLjLUHRPe2Y9VVdDOcam4/pdBBCbJ6/hkBWh6KZznnM3vb6JRbBVwD69Iq6tdGNWJRSCWNpZhLaA==} + '@microsoft/applicationinsights-channel-js@3.3.10': + resolution: {integrity: sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==} peerDependencies: tslib: '>= 1.0.0' - '@microsoft/applicationinsights-common@3.3.8': - resolution: {integrity: sha512-yfcU3g05Z36S3r4SDtV+LGkoubT3px6Yt4fwINIGDixbTJB6VZXQxLwkTAzWxKwxuvBX2L8PP9O1LY4D7gGrrA==} + '@microsoft/applicationinsights-common@3.3.10': + resolution: {integrity: sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==} peerDependencies: tslib: '>= 1.0.0' - '@microsoft/applicationinsights-core-js@3.3.8': - resolution: {integrity: sha512-k1uQCRSbV0aUF4DxgTFAbbk1IlsihLoldgQCMIVwHdnS3X80NjtSDWQPy0n+Hbw1XNlpky9p8jxNDJlZ9PoFDg==} + '@microsoft/applicationinsights-core-js@3.3.10': + resolution: {integrity: sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==} peerDependencies: tslib: '>= 1.0.0' '@microsoft/applicationinsights-shims@3.0.1': resolution: {integrity: sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==} - '@microsoft/applicationinsights-web-basic@3.3.8': - resolution: {integrity: sha512-etHqbg6zwDxObY7GUF1CZZtoNIzzfjzvJyOEvixwnio4ehHyxZLQXBTavoUL424/A2MgWTBQjj6AV2ezayoz4A==} + '@microsoft/applicationinsights-web-basic@3.3.10': + resolution: {integrity: sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==} peerDependencies: tslib: '>= 1.0.0' @@ -2429,8 +2449,8 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@napi-rs/wasm-runtime@1.0.5': - resolution: {integrity: sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==} + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} '@nevware21/ts-async@0.5.4': resolution: {integrity: sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==} @@ -2438,53 +2458,53 @@ packages: '@nevware21/ts-utils@0.12.5': resolution: {integrity: sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==} - '@next/env@15.5.4': - resolution: {integrity: sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==} + '@next/env@15.5.6': + resolution: {integrity: sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q==} - '@next/swc-darwin-arm64@15.5.4': - resolution: {integrity: sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==} + '@next/swc-darwin-arm64@15.5.6': + resolution: {integrity: sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.5.4': - resolution: {integrity: sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==} + '@next/swc-darwin-x64@15.5.6': + resolution: {integrity: sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.5.4': - resolution: {integrity: sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==} + '@next/swc-linux-arm64-gnu@15.5.6': + resolution: {integrity: sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.5.4': - resolution: {integrity: sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==} + '@next/swc-linux-arm64-musl@15.5.6': + resolution: {integrity: sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.5.4': - resolution: {integrity: sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==} + '@next/swc-linux-x64-gnu@15.5.6': + resolution: {integrity: sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.5.4': - resolution: {integrity: sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==} + '@next/swc-linux-x64-musl@15.5.6': + resolution: {integrity: sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.5.4': - resolution: {integrity: sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==} + '@next/swc-win32-arm64-msvc@15.5.6': + resolution: {integrity: sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.5.4': - resolution: {integrity: sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==} + '@next/swc-win32-x64-msvc@15.5.6': + resolution: {integrity: sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2588,11 +2608,98 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-parser/binding-wasm32-wasi@0.93.0': - resolution: {integrity: sha512-EXyCyY4GJO+SNTQJPPmJJwYbPkPOzw2nxSRMmUlwG19WKO7QHzHyL6u+4hXpp5IwgIWvgQgoix2/pB9JF+EA7w==} + '@oxc-parser/binding-android-arm64@0.95.0': + resolution: {integrity: sha512-dZyxhhvJigwWL1wgnLlqyEiSeuqO0WdDH9H+if0dPcBM4fKa5fjVkaUcJT1jBMcBTnkjxMwTXYZy5TK60N0fjg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.95.0': + resolution: {integrity: sha512-zun9+V33kyCtNec9oUSWwb0qi3fB8pXwum1yGFECPEr55g/CrWju6/Jv4hwwNBeW2tK9Ch/PRstEtYmOLMhHpg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.95.0': + resolution: {integrity: sha512-9djMQ/t6Ns/UXtziwUe562uVJMbhtuLtCj+Xav+HMVT/rhV9gWO8PQOG7AwDLUBjJanItsrfqrGtqhNxtZ701w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.95.0': + resolution: {integrity: sha512-GK6k0PgCVkkeRZtHgcosCYbXIRySpJpuPw/OInfLGFh8f3x9gp2l8Fbcfx+YO+ZOHFBCd2NNedGqw8wMgouxfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.95.0': + resolution: {integrity: sha512-+g/lFITtyHHEk69cunOHuiT5cX+mpUTcbGYNe8suguZ7FqgNwai+PnGv0ctCvtgxBPVfckfUK8c3RvFKo+vi/w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.95.0': + resolution: {integrity: sha512-SXNasDtPw8ycNV7VEvFxb4LETmykvWKUhHR7K3us818coXYpDj54P8WEx8hJobP/9skuuiFuKHmtYLdjX8wntA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.95.0': + resolution: {integrity: sha512-0LzebARTU0ROfD6pDK4h1pFn+09meErCZ0MA2TaW08G72+GNneEsksPufOuI+9AxVSRa+jKE3fu0wavvhZgSkg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.95.0': + resolution: {integrity: sha512-Pvi1lGe/G+mJZ3hUojMP/aAHAzHA25AEtVr8/iuz7UV72t/15NOgJYr9kELMUMNjPqpr3vKUgXTFmTtAxp11Qw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.95.0': + resolution: {integrity: sha512-pUEVHIOVNDfhk4sTlLhn6mrNENhE4/dAwemxIfqpcSyBlYG0xYZND1F3jjR2yWY6DakXZ6VSuDbtiv1LPNlOLw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.95.0': + resolution: {integrity: sha512-5+olaepHTE3J/+w7g0tr3nocvv5BKilAJnzj4L8tWBCLEZbL6olJcGVoldUO+3cgg1SO1xJywP5BuLhT0mDUDw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.95.0': + resolution: {integrity: sha512-8huzHlK/N98wrnYKxIcYsK8ZGBWomQchu/Mzi6m+CtbhjWOv9DmK0jQ2fUWImtluQVpTwS0uZT06d3g7XIkJrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.95.0': + resolution: {integrity: sha512-bWnrLfGDcx/fab0+UQnFbVFbiykof/btImbYf+cI2pU/1Egb2x+OKSmM5Qt0nEUiIpM5fgJmYXxTopybSZOKYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-wasm32-wasi@0.95.0': + resolution: {integrity: sha512-0JLyqkZu1HnQIZ4e5LBGOtzqua1QwFEUOoMSycdoerXqayd4LK2b7WMfAx8eCIf+jGm1Uj6f3R00nlsx8g1faQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] + '@oxc-parser/binding-win32-arm64-msvc@0.95.0': + resolution: {integrity: sha512-RWvaA6s1SYlBj9CxwHfNn0CRlkPdv9fEUAXfZkGQPdP5e1ppIaO2KYE0sUov/zzp9hPTMMsTMHl4dcIbb+pHCQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.95.0': + resolution: {integrity: sha512-BQpgl7rDjFvCIHudmUR0dCwc4ylBYZl4CPVinlD3NhkMif4WD5dADckoo5ES/KOpFyvwcbKZX+grP63cjHi26g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.95.0': + resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -2683,8 +2790,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.55.1': - resolution: {integrity: sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==} + '@playwright/test@1.56.1': + resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==} engines: {node: '>=18'} hasBin: true @@ -3034,6 +3141,27 @@ packages: resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==} engines: {node: '>=20.0.0'} + '@shikijs/core@3.13.0': + resolution: {integrity: sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==} + + '@shikijs/engine-javascript@3.13.0': + resolution: {integrity: sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==} + + '@shikijs/engine-oniguruma@3.13.0': + resolution: {integrity: sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==} + + '@shikijs/langs@3.13.0': + resolution: {integrity: sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==} + + '@shikijs/themes@3.13.0': + resolution: {integrity: sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==} + + '@shikijs/types@3.13.0': + resolution: {integrity: sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sinclair/typebox@0.34.37': resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==} @@ -3144,6 +3272,9 @@ packages: peerDependencies: solid-js: ^1.6.12 + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@stylistic/eslint-plugin-js@2.1.0': resolution: {integrity: sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3179,65 +3310,65 @@ packages: '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} - '@tailwindcss/node@4.1.13': - resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + '@tailwindcss/node@4.1.14': + resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==} - '@tailwindcss/oxide-android-arm64@4.1.13': - resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + '@tailwindcss/oxide-android-arm64@4.1.14': + resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.13': - resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + '@tailwindcss/oxide-darwin-arm64@4.1.14': + resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.13': - resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + '@tailwindcss/oxide-darwin-x64@4.1.14': + resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.13': - resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + '@tailwindcss/oxide-freebsd-x64@4.1.14': + resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': - resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': + resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': - resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': + resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.13': - resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': + resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.13': - resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': + resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.13': - resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + '@tailwindcss/oxide-linux-x64-musl@4.1.14': + resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.13': - resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + '@tailwindcss/oxide-wasm32-wasi@4.1.14': + resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3248,33 +3379,33 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': - resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': + resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.13': - resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': + resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.13': - resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + '@tailwindcss/oxide@4.1.14': + resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==} engines: {node: '>= 10'} - '@tailwindcss/vite@4.1.13': - resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} + '@tailwindcss/vite@4.1.14': + resolution: {integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 - '@testing-library/dom@10.4.0': - resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.9.0': - resolution: {integrity: sha512-QHdxYMJ0YPGKYofMc6zYvo7LOViVhdc6nPg/OtM2cf9MQrwEcTxFCs7d/GJ5eSyPkHzOiBkc/KfLdFJBHzldtQ==} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} '@testing-library/react@16.3.0': @@ -3292,6 +3423,12 @@ packages: '@types/react-dom': optional: true + '@testing-library/user-event@13.5.0': + resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==} + engines: {node: '>=10', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@testing-library/user-event@14.6.1': resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} engines: {node: '>=12', npm: '>=6'} @@ -3375,6 +3512,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -3402,14 +3542,17 @@ packages: '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.19.21': - resolution: {integrity: sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==} + '@types/node@20.19.22': + resolution: {integrity: sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==} - '@types/node@24.6.0': - resolution: {integrity: sha512-F1CBxgqwOMc4GKJ7eY22hWhBVQuMYTtqI8L0FcszYcpYX0fzfDGpez22Xau8Mgm7O9fI+zA/TYIdq3tGWfweBA==} + '@types/node@24.8.1': + resolution: {integrity: sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3437,11 +3580,14 @@ packages: '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/vscode-webview@1.57.5': resolution: {integrity: sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==} - '@types/vscode@1.104.0': - resolution: {integrity: sha512-0KwoU2rZ2ecsTGFxo4K1+f+AErRsYW0fsp6A0zufzGuhyczc2IoKqYqcwXidKXmy2u8YB2GsYsOtiI9Izx3Tig==} + '@types/vscode@1.105.0': + resolution: {integrity: sha512-Lotk3CTFlGZN8ray4VxJE7axIyLZZETQJVWi/lYoUVQuqfRxlQhVOfoejsD2V3dVXPSbS15ov5ZyowMAzgUqcw==} '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} @@ -3627,8 +3773,8 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@4.0.0-beta.8': - resolution: {integrity: sha512-40DZ7nl5AkASkDNaVR7TqsbeJCs5D+dyQNM5cIvgjG3KK+ATeWxtXJbmRNqgdbq+FL3v/pchnrJM1R9BFkTdUQ==} + '@vitest/expect@4.0.0-beta.18': + resolution: {integrity: sha512-dP38ctyRhGj4DTz4azK7sKR7BULMdVdgmR4Flzmul9wE3GdKUSr4zNd2RVNHhrb7l0NK0GN5/kRquaQmv9krGQ==} '@vitest/mocker@2.1.8': resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} @@ -3652,8 +3798,8 @@ packages: vite: optional: true - '@vitest/mocker@4.0.0-beta.8': - resolution: {integrity: sha512-a5ynR/Fsrciuq17i8lzS5NH3ICxwFZMlQ4bXPzGV+KlIcVHu20a/8q6KekqUaVBxCbmohORLFXFx9IptHS9gXA==} + '@vitest/mocker@4.0.0-beta.18': + resolution: {integrity: sha512-vwvvqj4zNaV+uQSBJHhGP72UL4fluU2gLI1Q+hT4e4ruJOF5TWD/UuWnWCpzHjGotfDTNSztypYkZ3ZottPFvA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -3672,8 +3818,8 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.0.0-beta.8': - resolution: {integrity: sha512-sr5HPeeRff4gTpDwI2Kvz8dS2CmDCCZ1PRu3IOeLTcSJjhEWmk3IJILjqaA8yyj+QzWjnqAxr2rmZNpO0h/5Vw==} + '@vitest/pretty-format@4.0.0-beta.18': + resolution: {integrity: sha512-LzgQxcQ6QxhjDfYGMT/fFH3hdzJaq2KsG0R2CGkhYUNFvAml2nvFAxzQKYtxDDk0olOxk3j29QPvv3j8D4hONg==} '@vitest/runner@2.1.8': resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} @@ -3681,8 +3827,8 @@ packages: '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/runner@4.0.0-beta.8': - resolution: {integrity: sha512-m7jrT+KMEgONclBI7y3ptUD+/uhRzzHLblws84fo+WesCjS8WT8q7RoPMgqymj5kmzIF5sTh6NOvrNEE5LaLCQ==} + '@vitest/runner@4.0.0-beta.18': + resolution: {integrity: sha512-HpEaHsxNKJYeKApkxbrGT6OZA9Ty+BLXIc4rxo6xzo+f4zlUGluy4RjQs9GQIzEpQSPP5ehUIcUZbOi7thB49g==} '@vitest/snapshot@2.1.8': resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} @@ -3690,8 +3836,8 @@ packages: '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/snapshot@4.0.0-beta.8': - resolution: {integrity: sha512-2hzc/ksGlZ8Rcg11VD/AhTwSaPEsdtrNA+TCV4w6tuZ7I2X+XJXimfk/Cehz5zMgfFuV8tFmGimb/BpyIbNiMg==} + '@vitest/snapshot@4.0.0-beta.18': + resolution: {integrity: sha512-ruWnM+5xVR5mhiTW5c66JRwxni6riPxupaXNPqdkOHzBuxxz79Cf56yzuYapT/TSRHVwkIyldfKLcZTY18CWig==} '@vitest/spy@2.1.8': resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} @@ -3699,8 +3845,8 @@ packages: '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.0.0-beta.8': - resolution: {integrity: sha512-WfHF35GCf5xx3B1oRrhWMVAfcBBYO4WHAYLbeeaZ1ZSW5/VBXJ/M37bLxRRKnXcgffwcsWA7xpjWnL0dQ1q5NA==} + '@vitest/spy@4.0.0-beta.18': + resolution: {integrity: sha512-KHxVrn/e1PhcylP3waDajDZ7o5ut9BnN+QDCgz6uMev1cqVHLE1EBaz8qUcxaRH6qFNKcTm8T4x+FIIYSGS/xw==} '@vitest/utils@2.1.8': resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} @@ -3708,14 +3854,14 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.0.0-beta.8': - resolution: {integrity: sha512-+eV3yrDooGMnHHVQ1aqzPJIBlxEsaQg/5BVFRSkbBgfiKqM9HZbqYK736s0D8tfdMLOjeB+7u7Tw0emd/h3MlQ==} + '@vitest/utils@4.0.0-beta.18': + resolution: {integrity: sha512-Z7r82xwG8G6J755DqWpoP/XEuKMhxVFlIPVunD609iH8wjLJ6VD+vd9cojalhrW/tqHfdnaBpS+hxDLwSrfw3Q==} - '@vscode/codicons@0.0.40': - resolution: {integrity: sha512-R8sEDXthD86JHsk3xERrMcTN6sMovbk1AXYB5/tGoEYCE8DWwya6al5VLrAmQYXC1bQhUHIfHALj8ijQUs11cQ==} + '@vscode/codicons@0.0.41': + resolution: {integrity: sha512-v6/8nx76zau3Joxjzi3eN/FVw+7jKBq4j7LTZY5FhFhq2g0OoFebZ3vRZbv/pUopGpbCnJJ4FOz+NzbjVsmoiw==} - '@vscode/extension-telemetry@1.0.0': - resolution: {integrity: sha512-vaTZE65zigWwSWYB6yaZUAyVC/Ux+6U82hnzy/ejuS/KpFifO+0oORNd5yAoPeIRnYjvllM6ES3YlX4K5tUuww==} + '@vscode/extension-telemetry@1.1.0': + resolution: {integrity: sha512-y3cFCDell/xVn3b2/FdpJSwpspfUzBvVtjAQnD94Z38RXHCsYRZXJOJLZNuFxFgcSihjYZNnL8VPgOzWNiE9/g==} engines: {vscode: ^1.75.0} '@vscode/vsce-sign-alpine-arm64@2.0.6': @@ -4085,15 +4231,18 @@ packages: caniuse-lite@1.0.30001735: resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} - caniuse-lite@1.0.30001746: - resolution: {integrity: sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==} + caniuse-lite@1.0.30001751: + resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} - chai@5.2.1: - resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + chai@6.2.0: + resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} engines: {node: '>=18'} chalk@4.1.2: @@ -4108,9 +4257,15 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + character-entities-legacy@1.1.4: resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@1.2.4: resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} @@ -4204,6 +4359,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -4391,10 +4549,17 @@ packages: resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==} engines: {node: '>=8'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -4526,8 +4691,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.25.10: - resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} engines: {node: '>=18'} hasBin: true @@ -4733,8 +4898,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.36.0: - resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==} + eslint@9.38.0: + resolution: {integrity: sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -5048,8 +5213,8 @@ packages: resolution: {integrity: sha512-yiaVI0Da/tGrBD6QwmXkJZH9RPW1/TCxqadbwkp0MgwRTTDNyBC09Ow+nppVLhbG4Ws2Z6t29Vzbbd6C0y7LCQ==} engines: {node: '>=16.0.0'} - happy-dom@20.0.0: - resolution: {integrity: sha512-GkWnwIFxVGCf2raNrxImLo397RdGhLapj5cT3R2PT7FwL62Ze1DROhzmYW7+J3p9105DYMVenEejEbnq5wA37w==} + happy-dom@20.0.8: + resolution: {integrity: sha512-TlYaNQNtzsZ97rNMBAm8U+e2cUQXNithgfCizkDgc11lgmN4j9CKMhO3FPGKWQYPwwkFcPpoXYF/CqEPLgzfOg==} engines: {node: '>=20.0.0'} harmony-reflect@1.6.2: @@ -5078,6 +5243,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -5099,6 +5270,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} @@ -5496,8 +5670,8 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.6.0: - resolution: {integrity: sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true joycon@3.1.1: @@ -5597,8 +5771,8 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - less@4.4.1: - resolution: {integrity: sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==} + less@4.4.2: + resolution: {integrity: sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==} engines: {node: '>=14'} hasBin: true @@ -5754,9 +5928,6 @@ packages: loupe@3.1.4: resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} - loupe@3.2.0: - resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -5771,8 +5942,8 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lucide-solid@0.544.0: - resolution: {integrity: sha512-BQ4hysggShgAFE66U2AStAyOdD0U2Oraec1P35+m5b+SDuh2mWf8kTGbtMmZEmy0MKKQoKP5PEU77FrADbAQTw==} + lucide-solid@0.546.0: + resolution: {integrity: sha512-zJX9x7rlOwqsB6eNMWOrzS+vuvZThPFksPVryiXMmC6QGAD6vRihEZHe+r4JSppAGhOaPRBnMhg8XNZ/qLslvA==} peerDependencies: solid-js: ^1.4.7 @@ -5811,6 +5982,9 @@ packages: mdast-util-from-markdown@0.8.5: resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-string@2.0.0: resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} @@ -5828,6 +6002,21 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + micromark@2.11.4: resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} @@ -5935,8 +6124,8 @@ packages: engines: {node: '>= 4.4.x'} hasBin: true - next@15.5.4: - resolution: {integrity: sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==} + next@15.5.6: + resolution: {integrity: sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -6031,6 +6220,12 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + open@10.2.0: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} @@ -6047,6 +6242,10 @@ packages: engines: {node: '>= 20'} hasBin: true + oxc-parser@0.95.0: + resolution: {integrity: sha512-Te8fE/SmiiKWIrwBwxz5Dod87uYvsbcZ9JAL5ylPg1DevyKgTkxCXnPEaewk1Su2qpfNmry5RHoN+NywWFCG+A==} + engines: {node: ^20.19.0 || >=22.12.0} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -6228,13 +6427,13 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - playwright-core@1.55.1: - resolution: {integrity: sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==} + playwright-core@1.56.1: + resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} engines: {node: '>=18'} hasBin: true - playwright@1.55.1: - resolution: {integrity: sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==} + playwright@1.56.1: + resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} engines: {node: '>=18'} hasBin: true @@ -6342,6 +6541,9 @@ packages: resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} engines: {node: '>=18'} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -6445,6 +6647,15 @@ packages: regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + regexp-ast-analysis@0.7.1: resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -6572,6 +6783,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + seroval-plugins@1.3.2: resolution: {integrity: sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==} engines: {node: '>=10'} @@ -6597,6 +6813,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shiki@3.13.0: + resolution: {integrity: sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -6686,6 +6905,9 @@ packages: engines: {node: '>= 8'} deprecated: The work that was done in this beta branch won't be included in future versions + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} @@ -6736,6 +6958,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -6856,8 +7081,11 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.13: - resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} + tailwindcss@4.1.14: + resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} + + tailwindcss@4.1.16: + resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==} tapable@2.2.2: resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} @@ -6918,6 +7146,10 @@ packages: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} + tinypool@2.0.0: + resolution: {integrity: sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg==} + engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@1.2.0: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} @@ -6926,6 +7158,10 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} @@ -6971,6 +7207,9 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -7061,8 +7300,8 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -7081,8 +7320,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.13.0: - resolution: {integrity: sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==} + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} undici@7.16.0: resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} @@ -7112,9 +7351,24 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -7174,6 +7428,12 @@ packages: resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} engines: {node: '>=4'} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-node@2.1.8: resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} engines: {node: ^18.0.0 || >=20.0.0} @@ -7184,8 +7444,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite-plugin-solid@2.11.8: - resolution: {integrity: sha512-hFrCxBfv3B1BmFqnJF4JOCYpjrmi/zwyeKjcomQ0khh8HFyQ8SbuBWQ7zGojfrz6HUOBFrJBNySDi/JgAHytWg==} + vite-plugin-solid@2.11.9: + resolution: {integrity: sha512-bTA6p+bspXZsuulSd2y6aTzegF8xGaJYcq1Uyh/mv+W4DQtzCgL9nN6n2fsTaxp/dMk+ZHHKgGndlNeooqHLKw==} peerDependencies: '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* solid-js: ^1.7.2 @@ -7225,8 +7485,8 @@ packages: terser: optional: true - vite@7.1.3: - resolution: {integrity: sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==} + vite@7.1.10: + resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7265,8 +7525,8 @@ packages: yaml: optional: true - vite@7.1.7: - resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} + vite@7.1.3: + resolution: {integrity: sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7366,16 +7626,18 @@ packages: jsdom: optional: true - vitest@4.0.0-beta.8: - resolution: {integrity: sha512-wN/RDeCd5uXHV6tELw4AJzeP5rxR4YWXN3ems+59ZummmiovNjlfwG+CEZp5GitlxDQu7muoY4VPrSUxPzzKiQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@4.0.0-beta.18: + resolution: {integrity: sha512-zWvKMoebACjaOZADoHugNLC2GO8rnY4ERj052BunaJ9u/re6RmdIu4xu3mQ7yz97a1jmpSjeGr2tUz4kF1TrLA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 4.0.0-beta.8 - '@vitest/ui': 4.0.0-beta.8 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.0-beta.18 + '@vitest/browser-preview': 4.0.0-beta.18 + '@vitest/browser-webdriverio': 4.0.0-beta.18 + '@vitest/ui': 4.0.0-beta.18 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -7385,7 +7647,11 @@ packages: optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': optional: true '@vitest/ui': optional: true @@ -7554,8 +7820,11 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} - zod@4.1.11: - resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: @@ -7565,47 +7834,42 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.29 - - '@antfu/eslint-config@2.19.1(@vue/compiler-sfc@3.4.34)(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)(vitest@3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': + '@antfu/eslint-config@2.19.1(@vue/compiler-sfc@3.4.34)(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': dependencies: '@antfu/install-pkg': 0.3.3 '@clack/prompts': 0.7.0 - '@stylistic/eslint-plugin': 2.1.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - '@typescript-eslint/parser': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - eslint: 9.36.0(jiti@2.6.0) + '@stylistic/eslint-plugin': 2.1.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) eslint-config-flat-gitignore: 0.1.5 eslint-flat-config-utils: 0.2.5 - eslint-merge-processors: 0.1.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-antfu: 2.3.3(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-command: 0.2.3(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-import-x: 0.5.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - eslint-plugin-jsdoc: 48.2.7(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-jsonc: 2.16.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-markdown: 5.0.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-n: 17.7.0(eslint@9.36.0(jiti@2.6.0)) + eslint-merge-processors: 0.1.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-antfu: 2.3.3(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-command: 0.2.3(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-import-x: 0.5.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint-plugin-jsdoc: 48.2.7(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-jsonc: 2.16.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-markdown: 5.0.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-n: 17.7.0(eslint@9.38.0(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-perfectionist: 2.10.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)(vue-eslint-parser@9.4.2(eslint@9.36.0(jiti@2.6.0))) - eslint-plugin-regexp: 2.6.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-toml: 0.11.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-unicorn: 53.0.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-vitest: 0.5.4(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)(vitest@3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) - eslint-plugin-vue: 9.26.0(eslint@9.36.0(jiti@2.6.0)) - eslint-plugin-yml: 1.14.0(eslint@9.36.0(jiti@2.6.0)) - eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.4.34)(eslint@9.36.0(jiti@2.6.0)) + eslint-plugin-perfectionist: 2.10.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)(vue-eslint-parser@9.4.2(eslint@9.38.0(jiti@2.6.1))) + eslint-plugin-regexp: 2.6.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-toml: 0.11.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-unicorn: 53.0.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-vitest: 0.5.4(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + eslint-plugin-vue: 9.26.0(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-yml: 1.14.0(eslint@9.38.0(jiti@2.6.1)) + eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.4.34)(eslint@9.38.0(jiti@2.6.1)) globals: 15.3.0 jsonc-eslint-parser: 2.4.0 local-pkg: 0.5.0 parse-gitignore: 2.0.0 picocolors: 1.1.1 toml-eslint-parser: 0.9.3 - vue-eslint-parser: 9.4.2(eslint@9.36.0(jiti@2.6.0)) + vue-eslint-parser: 9.4.2(eslint@9.38.0(jiti@2.6.1)) yaml-eslint-parser: 1.2.3 yargs: 17.7.2 transitivePeerDependencies: @@ -7726,26 +7990,6 @@ snapshots: '@babel/compat-data@7.28.0': {} - '@babel/core@7.28.0': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.0 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) - '@babel/helpers': 7.27.6 - '@babel/parser': 7.28.0 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 - convert-source-map: 2.0.0 - debug: 4.4.1 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.28.4': dependencies: '@babel/code-frame': 7.27.1 @@ -7766,25 +8010,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.0': - dependencies: - '@babel/parser': 7.28.0 - '@babel/types': 7.28.0 - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 - jsesc: 3.1.0 - '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.4 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -7842,28 +8078,19 @@ snapshots: '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color @@ -7887,7 +8114,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@babel/helper-plugin-utils@7.27.1': {} @@ -7927,32 +8154,19 @@ snapshots: '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helpers@7.27.6': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.27.6 - '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 '@babel/types': 7.28.4 - '@babel/parser@7.27.5': - dependencies: - '@babel/types': 7.27.6 - - '@babel/parser@7.28.0': - dependencies: - '@babel/types': 7.28.0 - '@babel/parser@7.28.3': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@babel/parser@7.28.4': dependencies: @@ -8037,11 +8251,6 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -8393,7 +8602,7 @@ snapshots: '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) - '@babel/types': 7.27.6 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color @@ -8587,38 +8796,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/runtime@7.28.3': {} + '@babel/runtime@7.28.4': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@babel/traverse@7.27.4': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/types': 7.27.6 - debug: 4.4.1 + '@babel/types': 7.28.4 + debug: 4.4.3 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/traverse@7.28.0': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.0 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.0 - '@babel/template': 7.27.2 - '@babel/types': 7.28.0 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - '@babel/traverse@7.28.3': dependencies: '@babel/code-frame': 7.27.1 @@ -8648,11 +8847,6 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.28.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.28.2': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -8694,7 +8888,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.7(@types/node@24.6.0)': + '@changesets/cli@2.29.7(@types/node@24.8.1)': dependencies: '@changesets/apply-release-plan': 7.0.13 '@changesets/assemble-release-plan': 6.0.9 @@ -8710,7 +8904,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.2(@types/node@24.6.0) + '@inquirer/external-editor': 1.0.2(@types/node@24.8.1) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -8820,6 +9014,11 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 + '@corvu/resizable@0.2.5(solid-js@1.9.9)': + dependencies: + '@corvu/utils': 0.4.2(solid-js@1.9.9) + solid-js: 1.9.9 + '@corvu/utils@0.4.2(solid-js@1.9.9)': dependencies: '@floating-ui/dom': 1.7.3 @@ -8874,7 +9073,7 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.25.10': + '@esbuild/aix-ppc64@0.25.11': optional: true '@esbuild/aix-ppc64@0.25.6': @@ -8883,7 +9082,7 @@ snapshots: '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.25.10': + '@esbuild/android-arm64@0.25.11': optional: true '@esbuild/android-arm64@0.25.6': @@ -8892,7 +9091,7 @@ snapshots: '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.25.10': + '@esbuild/android-arm@0.25.11': optional: true '@esbuild/android-arm@0.25.6': @@ -8901,7 +9100,7 @@ snapshots: '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.25.10': + '@esbuild/android-x64@0.25.11': optional: true '@esbuild/android-x64@0.25.6': @@ -8910,7 +9109,7 @@ snapshots: '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.25.10': + '@esbuild/darwin-arm64@0.25.11': optional: true '@esbuild/darwin-arm64@0.25.6': @@ -8919,7 +9118,7 @@ snapshots: '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.25.10': + '@esbuild/darwin-x64@0.25.11': optional: true '@esbuild/darwin-x64@0.25.6': @@ -8928,7 +9127,7 @@ snapshots: '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.25.10': + '@esbuild/freebsd-arm64@0.25.11': optional: true '@esbuild/freebsd-arm64@0.25.6': @@ -8937,7 +9136,7 @@ snapshots: '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.25.10': + '@esbuild/freebsd-x64@0.25.11': optional: true '@esbuild/freebsd-x64@0.25.6': @@ -8946,7 +9145,7 @@ snapshots: '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.25.10': + '@esbuild/linux-arm64@0.25.11': optional: true '@esbuild/linux-arm64@0.25.6': @@ -8955,7 +9154,7 @@ snapshots: '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.25.10': + '@esbuild/linux-arm@0.25.11': optional: true '@esbuild/linux-arm@0.25.6': @@ -8964,7 +9163,7 @@ snapshots: '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.25.10': + '@esbuild/linux-ia32@0.25.11': optional: true '@esbuild/linux-ia32@0.25.6': @@ -8973,7 +9172,7 @@ snapshots: '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.25.10': + '@esbuild/linux-loong64@0.25.11': optional: true '@esbuild/linux-loong64@0.25.6': @@ -8982,7 +9181,7 @@ snapshots: '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.25.10': + '@esbuild/linux-mips64el@0.25.11': optional: true '@esbuild/linux-mips64el@0.25.6': @@ -8991,7 +9190,7 @@ snapshots: '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.25.10': + '@esbuild/linux-ppc64@0.25.11': optional: true '@esbuild/linux-ppc64@0.25.6': @@ -9000,7 +9199,7 @@ snapshots: '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.25.10': + '@esbuild/linux-riscv64@0.25.11': optional: true '@esbuild/linux-riscv64@0.25.6': @@ -9009,7 +9208,7 @@ snapshots: '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.25.10': + '@esbuild/linux-s390x@0.25.11': optional: true '@esbuild/linux-s390x@0.25.6': @@ -9018,13 +9217,13 @@ snapshots: '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.25.10': + '@esbuild/linux-x64@0.25.11': optional: true '@esbuild/linux-x64@0.25.6': optional: true - '@esbuild/netbsd-arm64@0.25.10': + '@esbuild/netbsd-arm64@0.25.11': optional: true '@esbuild/netbsd-arm64@0.25.6': @@ -9033,13 +9232,13 @@ snapshots: '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.25.10': + '@esbuild/netbsd-x64@0.25.11': optional: true '@esbuild/netbsd-x64@0.25.6': optional: true - '@esbuild/openbsd-arm64@0.25.10': + '@esbuild/openbsd-arm64@0.25.11': optional: true '@esbuild/openbsd-arm64@0.25.6': @@ -9048,13 +9247,13 @@ snapshots: '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.25.10': + '@esbuild/openbsd-x64@0.25.11': optional: true '@esbuild/openbsd-x64@0.25.6': optional: true - '@esbuild/openharmony-arm64@0.25.10': + '@esbuild/openharmony-arm64@0.25.11': optional: true '@esbuild/openharmony-arm64@0.25.6': @@ -9063,7 +9262,7 @@ snapshots: '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.25.10': + '@esbuild/sunos-x64@0.25.11': optional: true '@esbuild/sunos-x64@0.25.6': @@ -9072,7 +9271,7 @@ snapshots: '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.25.10': + '@esbuild/win32-arm64@0.25.11': optional: true '@esbuild/win32-arm64@0.25.6': @@ -9081,7 +9280,7 @@ snapshots: '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.25.10': + '@esbuild/win32-ia32@0.25.11': optional: true '@esbuild/win32-ia32@0.25.6': @@ -9090,37 +9289,39 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.25.10': + '@esbuild/win32-x64@0.25.11': optional: true '@esbuild/win32-x64@0.25.6': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.36.0(jiti@2.6.0))': + '@eslint-community/eslint-utils@4.4.0(eslint@9.38.0(jiti@2.6.1))': dependencies: - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0(jiti@2.6.0))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.38.0(jiti@2.6.1))': dependencies: - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.10.0': {} '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 + '@eslint/object-schema': 2.1.7 debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.4.1': + dependencies: + '@eslint/core': 0.16.0 - '@eslint/core@0.15.2': + '@eslint/core@0.16.0': dependencies: '@types/json-schema': 7.0.15 @@ -9152,13 +9353,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.36.0': {} + '@eslint/js@9.38.0': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.5': + '@eslint/plugin-kit@0.4.0': dependencies: - '@eslint/core': 0.15.2 + '@eslint/core': 0.16.0 levn: 0.4.1 '@floating-ui/core@1.7.3': @@ -9172,6 +9373,11 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@happy-dom/global-registrator@20.0.8': + dependencies: + '@types/node': 20.19.22 + happy-dom: 20.0.8 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -9272,12 +9478,12 @@ snapshots: '@img/sharp-win32-x64@0.34.4': optional: true - '@inquirer/external-editor@1.0.2(@types/node@24.6.0)': + '@inquirer/external-editor@1.0.2(@types/node@24.8.1)': dependencies: chardet: 2.1.0 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@internationalized/date@3.8.2': dependencies: @@ -9319,13 +9525,13 @@ snapshots: '@jest/console@30.2.0': dependencies: '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 jest-message-util: 30.2.0 jest-util: 30.2.0 slash: 3.0.0 - '@jest/core@30.2.0(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2))': + '@jest/core@30.2.0(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3))': dependencies: '@jest/console': 30.2.0 '@jest/pattern': 30.0.1 @@ -9333,14 +9539,14 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.0 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.2.0 - jest-config: 30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + jest-config: 30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-haste-map: 30.2.0 jest-message-util: 30.2.0 jest-regex-util: 30.0.1 @@ -9369,7 +9575,7 @@ snapshots: '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 '@types/jsdom': 21.1.7 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-mock: 30.2.0 jest-util: 30.2.0 jsdom: 26.1.0 @@ -9378,7 +9584,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-mock: 30.2.0 '@jest/expect-utils@30.0.4': @@ -9400,7 +9606,7 @@ snapshots: dependencies: '@jest/types': 30.2.0 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-message-util: 30.2.0 jest-mock: 30.2.0 jest-util: 30.2.0 @@ -9420,7 +9626,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-regex-util: 30.0.1 '@jest/reporters@30.2.0': @@ -9431,7 +9637,7 @@ snapshots: '@jest/transform': 30.2.0 '@jest/types': 30.2.0 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit-x: 0.2.2 @@ -9512,7 +9718,7 @@ snapshots: '@jest/schemas': 30.0.1 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -9522,19 +9728,14 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@types/yargs': 17.0.33 chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.12': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/gen-mapping@0.3.5': dependencies: @@ -9542,12 +9743,6 @@ snapshots: '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -9570,11 +9765,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping@0.3.30': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -9631,9 +9821,9 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@microsoft/1ds-core-js@4.3.8(tslib@2.8.1)': + '@microsoft/1ds-core-js@4.3.10(tslib@2.8.1)': dependencies: - '@microsoft/applicationinsights-core-js': 3.3.8(tslib@2.8.1) + '@microsoft/applicationinsights-core-js': 3.3.10(tslib@2.8.1) '@microsoft/applicationinsights-shims': 3.0.1 '@microsoft/dynamicproto-js': 2.0.3 '@nevware21/ts-async': 0.5.4 @@ -9641,9 +9831,9 @@ snapshots: transitivePeerDependencies: - tslib - '@microsoft/1ds-post-js@4.3.8(tslib@2.8.1)': + '@microsoft/1ds-post-js@4.3.10(tslib@2.8.1)': dependencies: - '@microsoft/1ds-core-js': 4.3.8(tslib@2.8.1) + '@microsoft/1ds-core-js': 4.3.10(tslib@2.8.1) '@microsoft/applicationinsights-shims': 3.0.1 '@microsoft/dynamicproto-js': 2.0.3 '@nevware21/ts-async': 0.5.4 @@ -9651,25 +9841,25 @@ snapshots: transitivePeerDependencies: - tslib - '@microsoft/applicationinsights-channel-js@3.3.8(tslib@2.8.1)': + '@microsoft/applicationinsights-channel-js@3.3.10(tslib@2.8.1)': dependencies: - '@microsoft/applicationinsights-common': 3.3.8(tslib@2.8.1) - '@microsoft/applicationinsights-core-js': 3.3.8(tslib@2.8.1) + '@microsoft/applicationinsights-common': 3.3.10(tslib@2.8.1) + '@microsoft/applicationinsights-core-js': 3.3.10(tslib@2.8.1) '@microsoft/applicationinsights-shims': 3.0.1 '@microsoft/dynamicproto-js': 2.0.3 '@nevware21/ts-async': 0.5.4 '@nevware21/ts-utils': 0.12.5 tslib: 2.8.1 - '@microsoft/applicationinsights-common@3.3.8(tslib@2.8.1)': + '@microsoft/applicationinsights-common@3.3.10(tslib@2.8.1)': dependencies: - '@microsoft/applicationinsights-core-js': 3.3.8(tslib@2.8.1) + '@microsoft/applicationinsights-core-js': 3.3.10(tslib@2.8.1) '@microsoft/applicationinsights-shims': 3.0.1 '@microsoft/dynamicproto-js': 2.0.3 '@nevware21/ts-utils': 0.12.5 tslib: 2.8.1 - '@microsoft/applicationinsights-core-js@3.3.8(tslib@2.8.1)': + '@microsoft/applicationinsights-core-js@3.3.10(tslib@2.8.1)': dependencies: '@microsoft/applicationinsights-shims': 3.0.1 '@microsoft/dynamicproto-js': 2.0.3 @@ -9681,11 +9871,11 @@ snapshots: dependencies: '@nevware21/ts-utils': 0.12.5 - '@microsoft/applicationinsights-web-basic@3.3.8(tslib@2.8.1)': + '@microsoft/applicationinsights-web-basic@3.3.10(tslib@2.8.1)': dependencies: - '@microsoft/applicationinsights-channel-js': 3.3.8(tslib@2.8.1) - '@microsoft/applicationinsights-common': 3.3.8(tslib@2.8.1) - '@microsoft/applicationinsights-core-js': 3.3.8(tslib@2.8.1) + '@microsoft/applicationinsights-channel-js': 3.3.10(tslib@2.8.1) + '@microsoft/applicationinsights-common': 3.3.10(tslib@2.8.1) + '@microsoft/applicationinsights-core-js': 3.3.10(tslib@2.8.1) '@microsoft/applicationinsights-shims': 3.0.1 '@microsoft/dynamicproto-js': 2.0.3 '@nevware21/ts-async': 0.5.4 @@ -9722,7 +9912,7 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.0.5': + '@napi-rs/wasm-runtime@1.0.7': dependencies: '@emnapi/core': 1.5.0 '@emnapi/runtime': 1.5.0 @@ -9734,30 +9924,30 @@ snapshots: '@nevware21/ts-utils@0.12.5': {} - '@next/env@15.5.4': {} + '@next/env@15.5.6': {} - '@next/swc-darwin-arm64@15.5.4': + '@next/swc-darwin-arm64@15.5.6': optional: true - '@next/swc-darwin-x64@15.5.4': + '@next/swc-darwin-x64@15.5.6': optional: true - '@next/swc-linux-arm64-gnu@15.5.4': + '@next/swc-linux-arm64-gnu@15.5.6': optional: true - '@next/swc-linux-arm64-musl@15.5.4': + '@next/swc-linux-arm64-musl@15.5.6': optional: true - '@next/swc-linux-x64-gnu@15.5.4': + '@next/swc-linux-x64-gnu@15.5.6': optional: true - '@next/swc-linux-x64-musl@15.5.4': + '@next/swc-linux-x64-musl@15.5.6': optional: true - '@next/swc-win32-arm64-msvc@15.5.4': + '@next/swc-win32-arm64-msvc@15.5.6': optional: true - '@next/swc-win32-x64-msvc@15.5.4': + '@next/swc-win32-x64-msvc@15.5.6': optional: true '@node-rs/crc32-android-arm-eabi@1.10.6': @@ -9833,9 +10023,53 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@oxc-parser/binding-wasm32-wasi@0.93.0': + '@oxc-parser/binding-android-arm64@0.95.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.95.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.95.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.95.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.95.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.95.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.95.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.95.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.95.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.95.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.95.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.95.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.95.0': dependencies: - '@napi-rs/wasm-runtime': 1.0.5 + '@napi-rs/wasm-runtime': 1.0.7 + + '@oxc-parser/binding-win32-arm64-msvc@0.95.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.95.0': + optional: true + + '@oxc-project/types@0.95.0': {} '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -9903,9 +10137,9 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/test@1.55.1': + '@playwright/test@1.56.1': dependencies: - playwright: 1.55.1 + playwright: 1.56.1 '@rolldown/pluginutils@1.0.0-beta.38': {} @@ -10162,6 +10396,39 @@ snapshots: '@secretlint/types@10.2.2': {} + '@shikijs/core@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + + '@shikijs/themes@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + + '@shikijs/types@3.13.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@sinclair/typebox@0.34.37': {} '@sinclair/typebox@0.34.41': {} @@ -10271,49 +10538,51 @@ snapshots: dependencies: solid-js: 1.9.9 - '@stylistic/eslint-plugin-js@2.1.0(eslint@9.36.0(jiti@2.6.0))': + '@standard-schema/spec@1.0.0': {} + + '@stylistic/eslint-plugin-js@2.1.0(eslint@9.38.0(jiti@2.6.1))': dependencies: '@types/eslint': 8.56.10 acorn: 8.15.0 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-visitor-keys: 4.0.0 espree: 10.0.1 - '@stylistic/eslint-plugin-jsx@2.1.0(eslint@9.36.0(jiti@2.6.0))': + '@stylistic/eslint-plugin-jsx@2.1.0(eslint@9.38.0(jiti@2.6.1))': dependencies: - '@stylistic/eslint-plugin-js': 2.1.0(eslint@9.36.0(jiti@2.6.0)) + '@stylistic/eslint-plugin-js': 2.1.0(eslint@9.38.0(jiti@2.6.1)) '@types/eslint': 8.56.10 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) estraverse: 5.3.0 picomatch: 4.0.2 - '@stylistic/eslint-plugin-plus@2.1.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@stylistic/eslint-plugin-plus@2.1.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@types/eslint': 8.56.10 - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - eslint: 9.36.0(jiti@2.6.0) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript - '@stylistic/eslint-plugin-ts@2.1.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@stylistic/eslint-plugin-ts@2.1.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@stylistic/eslint-plugin-js': 2.1.0(eslint@9.36.0(jiti@2.6.0)) + '@stylistic/eslint-plugin-js': 2.1.0(eslint@9.38.0(jiti@2.6.1)) '@types/eslint': 8.56.10 - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - eslint: 9.36.0(jiti@2.6.0) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript - '@stylistic/eslint-plugin@2.1.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@stylistic/eslint-plugin@2.1.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@stylistic/eslint-plugin-js': 2.1.0(eslint@9.36.0(jiti@2.6.0)) - '@stylistic/eslint-plugin-jsx': 2.1.0(eslint@9.36.0(jiti@2.6.0)) - '@stylistic/eslint-plugin-plus': 2.1.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - '@stylistic/eslint-plugin-ts': 2.1.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@stylistic/eslint-plugin-js': 2.1.0(eslint@9.38.0(jiti@2.6.1)) + '@stylistic/eslint-plugin-jsx': 2.1.0(eslint@9.38.0(jiti@2.6.1)) + '@stylistic/eslint-plugin-plus': 2.1.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@stylistic/eslint-plugin-ts': 2.1.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@types/eslint': 8.56.10 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript @@ -10326,88 +10595,88 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.1.13': + '@tailwindcss/node@4.1.14': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.18.3 - jiti: 2.6.0 + jiti: 2.6.1 lightningcss: 1.30.1 magic-string: 0.30.19 source-map-js: 1.2.1 - tailwindcss: 4.1.13 + tailwindcss: 4.1.14 - '@tailwindcss/oxide-android-arm64@4.1.13': + '@tailwindcss/oxide-android-arm64@4.1.14': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.13': + '@tailwindcss/oxide-darwin-arm64@4.1.14': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.13': + '@tailwindcss/oxide-darwin-x64@4.1.14': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.13': + '@tailwindcss/oxide-freebsd-x64@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.13': + '@tailwindcss/oxide-linux-x64-musl@4.1.14': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.13': {} + '@tailwindcss/oxide-wasm32-wasi@4.1.14': {} - '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': optional: true - '@tailwindcss/oxide@4.1.13': + '@tailwindcss/oxide@4.1.14': dependencies: - detect-libc: 2.1.1 + detect-libc: 2.1.2 tar: 7.5.1 optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.13 - '@tailwindcss/oxide-darwin-arm64': 4.1.13 - '@tailwindcss/oxide-darwin-x64': 4.1.13 - '@tailwindcss/oxide-freebsd-x64': 4.1.13 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 - '@tailwindcss/oxide-linux-x64-musl': 4.1.13 - '@tailwindcss/oxide-wasm32-wasi': 4.1.13 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 - - '@tailwindcss/vite@4.1.13(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': - dependencies: - '@tailwindcss/node': 4.1.13 - '@tailwindcss/oxide': 4.1.13 - tailwindcss: 4.1.13 - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) - - '@testing-library/dom@10.4.0': + '@tailwindcss/oxide-android-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-x64': 4.1.14 + '@tailwindcss/oxide-freebsd-x64': 4.1.14 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.14 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-x64-musl': 4.1.14 + '@tailwindcss/oxide-wasm32-wasi': 4.1.14 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.14 + + '@tailwindcss/vite@4.1.14(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': + dependencies: + '@tailwindcss/node': 4.1.14 + '@tailwindcss/oxide': 4.1.14 + tailwindcss: 4.1.14 + vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + + '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.27.1 '@babel/runtime': 7.28.4 '@types/aria-query': 5.0.4 aria-query: 5.3.0 - chalk: 4.1.2 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 + picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.0': + '@testing-library/jest-dom@6.9.1': dependencies: '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 @@ -10416,19 +10685,24 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@testing-library/dom': 10.4.0 + '@testing-library/dom': 10.4.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: '@types/react': 19.2.2 '@types/react-dom': 19.2.1(@types/react@19.2.2) - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': + '@testing-library/user-event@13.5.0(@testing-library/dom@10.4.1)': dependencies: - '@testing-library/dom': 10.4.0 + '@babel/runtime': 7.28.3 + '@testing-library/dom': 10.4.1 + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 '@textlint/ast-node-types@15.2.2': {} @@ -10461,14 +10735,14 @@ snapshots: '@total-typescript/ts-reset@0.6.1': {} - '@trpc/client@11.6.0(@trpc/server@11.6.0(typescript@5.9.2))(typescript@5.9.2)': + '@trpc/client@11.6.0(@trpc/server@11.6.0(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@trpc/server': 11.6.0(typescript@5.9.2) - typescript: 5.9.2 + '@trpc/server': 11.6.0(typescript@5.9.3) + typescript: 5.9.3 - '@trpc/server@11.6.0(typescript@5.9.2)': + '@trpc/server@11.6.0(typescript@5.9.3)': dependencies: - typescript: 5.9.2 + typescript: 5.9.3 '@tsconfig/node10@1.0.9': {} @@ -10486,24 +10760,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/chai@5.2.2': dependencies: @@ -10522,6 +10796,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -10539,7 +10817,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@types/tough-cookie': 4.0.5 parse5: 7.2.1 @@ -10553,16 +10831,19 @@ snapshots: dependencies: '@types/unist': 2.0.10 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/node@12.20.55': {} - '@types/node@20.19.21': + '@types/node@20.19.22': dependencies: undici-types: 6.21.0 - optional: true - '@types/node@24.6.0': + '@types/node@24.8.1': dependencies: - undici-types: 7.13.0 + undici-types: 7.14.0 '@types/normalize-package-data@2.4.4': {} @@ -10581,22 +10862,23 @@ snapshots: '@types/stylus@0.48.43': dependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@types/tough-cookie@4.0.5': {} '@types/unist@2.0.10': {} + '@types/unist@3.0.3': {} + '@types/vscode-webview@1.57.5': {} - '@types/vscode@1.104.0': {} + '@types/vscode@1.105.0': {} - '@types/whatwg-mimetype@3.0.2': - optional: true + '@types/whatwg-mimetype@3.0.2': {} '@types/ws@8.18.1': dependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@types/yargs-parser@21.0.3': {} @@ -10604,34 +10886,34 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/parser': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/scope-manager': 7.11.0 - '@typescript-eslint/type-utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/type-utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 7.11.0 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.9.2) + ts-api-utils: 1.3.0(typescript@5.9.3) optionalDependencies: - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 7.11.0 '@typescript-eslint/types': 7.11.0 - '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 7.11.0 debug: 4.4.1 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) optionalDependencies: - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -10640,21 +10922,21 @@ snapshots: '@typescript-eslint/types': 7.11.0 '@typescript-eslint/visitor-keys': 7.11.0 - '@typescript-eslint/type-utils@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@typescript-eslint/type-utils@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.9.2) - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.9.3) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.1 - eslint: 9.36.0(jiti@2.6.0) - ts-api-utils: 1.3.0(typescript@5.9.2) + eslint: 9.38.0(jiti@2.6.1) + ts-api-utils: 1.3.0(typescript@5.9.3) optionalDependencies: - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@7.11.0': {} - '@typescript-eslint/typescript-estree@7.11.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@7.11.0(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 7.11.0 '@typescript-eslint/visitor-keys': 7.11.0 @@ -10663,19 +10945,19 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 1.3.0(typescript@5.9.2) + ts-api-utils: 1.3.0(typescript@5.9.3) optionalDependencies: - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + '@typescript-eslint/utils@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) '@typescript-eslint/scope-manager': 7.11.0 '@typescript-eslint/types': 7.11.0 - '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.9.2) - eslint: 9.36.0(jiti@2.6.0) + '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript @@ -10754,7 +11036,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@5.0.4(vite@5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0))': + '@vitejs/plugin-react@5.0.4(vite@5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0))': dependencies: '@babel/core': 7.28.4 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) @@ -10762,11 +11044,11 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.38 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + vite: 5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@5.0.4(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': + '@vitejs/plugin-react@5.0.4(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': dependencies: '@babel/core': 7.28.4 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) @@ -10774,7 +11056,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.38 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) transitivePeerDependencies: - supports-color @@ -10793,37 +11075,38 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/expect@4.0.0-beta.8': + '@vitest/expect@4.0.0-beta.18': dependencies: + '@standard-schema/spec': 1.0.0 '@types/chai': 5.2.2 - '@vitest/spy': 4.0.0-beta.8 - '@vitest/utils': 4.0.0-beta.8 - chai: 5.2.1 - tinyrainbow: 2.0.0 + '@vitest/spy': 4.0.0-beta.18 + '@vitest/utils': 4.0.0-beta.18 + chai: 6.2.0 + tinyrainbow: 3.0.3 - '@vitest/mocker@2.1.8(vite@5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0))': + '@vitest/mocker@2.1.8(vite@5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0))': dependencies: '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + vite: 5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) - '@vitest/mocker@3.2.4(vite@7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': + '@vitest/mocker@3.2.4(vite@7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) - '@vitest/mocker@4.0.0-beta.8(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': + '@vitest/mocker@4.0.0-beta.18(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2))': dependencies: - '@vitest/spy': 4.0.0-beta.8 + '@vitest/spy': 4.0.0-beta.18 estree-walker: 3.0.3 - magic-string: 0.30.17 + magic-string: 0.30.19 optionalDependencies: - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) '@vitest/pretty-format@2.1.8': dependencies: @@ -10837,9 +11120,9 @@ snapshots: dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.0.0-beta.8': + '@vitest/pretty-format@4.0.0-beta.18': dependencies: - tinyrainbow: 2.0.0 + tinyrainbow: 3.0.3 '@vitest/runner@2.1.8': dependencies: @@ -10852,11 +11135,10 @@ snapshots: pathe: 2.0.3 strip-literal: 3.0.0 - '@vitest/runner@4.0.0-beta.8': + '@vitest/runner@4.0.0-beta.18': dependencies: - '@vitest/utils': 4.0.0-beta.8 + '@vitest/utils': 4.0.0-beta.18 pathe: 2.0.3 - strip-literal: 3.0.0 '@vitest/snapshot@2.1.8': dependencies: @@ -10870,10 +11152,10 @@ snapshots: magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/snapshot@4.0.0-beta.8': + '@vitest/snapshot@4.0.0-beta.18': dependencies: - '@vitest/pretty-format': 4.0.0-beta.8 - magic-string: 0.30.17 + '@vitest/pretty-format': 4.0.0-beta.18 + magic-string: 0.30.19 pathe: 2.0.3 '@vitest/spy@2.1.8': @@ -10884,7 +11166,7 @@ snapshots: dependencies: tinyspy: 4.0.3 - '@vitest/spy@4.0.0-beta.8': {} + '@vitest/spy@4.0.0-beta.18': {} '@vitest/utils@2.1.8': dependencies: @@ -10898,19 +11180,18 @@ snapshots: loupe: 3.1.4 tinyrainbow: 2.0.0 - '@vitest/utils@4.0.0-beta.8': + '@vitest/utils@4.0.0-beta.18': dependencies: - '@vitest/pretty-format': 4.0.0-beta.8 - loupe: 3.2.0 - tinyrainbow: 2.0.0 + '@vitest/pretty-format': 4.0.0-beta.18 + tinyrainbow: 3.0.3 - '@vscode/codicons@0.0.40': {} + '@vscode/codicons@0.0.41': {} - '@vscode/extension-telemetry@1.0.0(tslib@2.8.1)': + '@vscode/extension-telemetry@1.1.0(tslib@2.8.1)': dependencies: - '@microsoft/1ds-core-js': 4.3.8(tslib@2.8.1) - '@microsoft/1ds-post-js': 4.3.8(tslib@2.8.1) - '@microsoft/applicationinsights-web-basic': 3.3.8(tslib@2.8.1) + '@microsoft/1ds-core-js': 4.3.10(tslib@2.8.1) + '@microsoft/1ds-post-js': 4.3.10(tslib@2.8.1) + '@microsoft/applicationinsights-web-basic': 3.3.10(tslib@2.8.1) transitivePeerDependencies: - tslib @@ -11152,12 +11433,12 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 - babel-plugin-jsx-dom-expressions@0.40.1(@babel/core@7.28.0): + babel-plugin-jsx-dom-expressions@0.40.1(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) - '@babel/types': 7.28.2 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 html-entities: 2.3.3 parse5: 7.3.0 validate-html-nesting: 1.2.3 @@ -11211,10 +11492,10 @@ snapshots: babel-plugin-jest-hoist: 30.2.0 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) - babel-preset-solid@1.9.9(@babel/core@7.28.0)(solid-js@1.9.9): + babel-preset-solid@1.9.9(@babel/core@7.28.4)(solid-js@1.9.9): dependencies: - '@babel/core': 7.28.0 - babel-plugin-jsx-dom-expressions: 0.40.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + babel-plugin-jsx-dom-expressions: 0.40.1(@babel/core@7.28.4) optionalDependencies: solid-js: 1.9.9 @@ -11337,7 +11618,9 @@ snapshots: caniuse-lite@1.0.30001735: {} - caniuse-lite@1.0.30001746: {} + caniuse-lite@1.0.30001751: {} + + ccount@2.0.1: {} chai@5.2.0: dependencies: @@ -11347,13 +11630,7 @@ snapshots: loupe: 3.1.4 pathval: 2.0.1 - chai@5.2.1: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.2.0 - pathval: 2.0.1 + chai@6.2.0: {} chalk@4.1.2: dependencies: @@ -11364,8 +11641,12 @@ snapshots: char-regex@1.0.2: {} + character-entities-html4@2.1.0: {} + character-entities-legacy@1.1.4: {} + character-entities-legacy@3.0.0: {} + character-entities@1.2.4: {} character-reference-invalid@1.1.4: {} @@ -11462,6 +11743,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + commander@12.1.0: {} commander@4.1.1: {} @@ -11598,10 +11881,17 @@ snapshots: detect-libc@1.0.3: optional: true - detect-libc@2.1.1: {} + detect-libc@2.1.1: + optional: true + + detect-libc@2.1.2: {} detect-newline@3.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + didyoumean@1.2.2: {} diff@4.0.2: {} @@ -11749,34 +12039,34 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.25.10: + esbuild@0.25.11: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.10 - '@esbuild/android-arm': 0.25.10 - '@esbuild/android-arm64': 0.25.10 - '@esbuild/android-x64': 0.25.10 - '@esbuild/darwin-arm64': 0.25.10 - '@esbuild/darwin-x64': 0.25.10 - '@esbuild/freebsd-arm64': 0.25.10 - '@esbuild/freebsd-x64': 0.25.10 - '@esbuild/linux-arm': 0.25.10 - '@esbuild/linux-arm64': 0.25.10 - '@esbuild/linux-ia32': 0.25.10 - '@esbuild/linux-loong64': 0.25.10 - '@esbuild/linux-mips64el': 0.25.10 - '@esbuild/linux-ppc64': 0.25.10 - '@esbuild/linux-riscv64': 0.25.10 - '@esbuild/linux-s390x': 0.25.10 - '@esbuild/linux-x64': 0.25.10 - '@esbuild/netbsd-arm64': 0.25.10 - '@esbuild/netbsd-x64': 0.25.10 - '@esbuild/openbsd-arm64': 0.25.10 - '@esbuild/openbsd-x64': 0.25.10 - '@esbuild/openharmony-arm64': 0.25.10 - '@esbuild/sunos-x64': 0.25.10 - '@esbuild/win32-arm64': 0.25.10 - '@esbuild/win32-ia32': 0.25.10 - '@esbuild/win32-x64': 0.25.10 + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 esbuild@0.25.6: optionalDependencies: @@ -11817,9 +12107,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.5.0(eslint@9.36.0(jiti@2.6.0)): + eslint-compat-utils@0.5.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) semver: 7.7.2 eslint-config-flat-gitignore@0.1.5: @@ -11840,39 +12130,39 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-merge-processors@0.1.0(eslint@9.36.0(jiti@2.6.0)): + eslint-merge-processors@0.1.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) - eslint-plugin-antfu@2.3.3(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-antfu@2.3.3(eslint@9.38.0(jiti@2.6.1)): dependencies: '@antfu/utils': 0.7.8 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) - eslint-plugin-command@0.2.3(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-command@0.2.3(eslint@9.38.0(jiti@2.6.1)): dependencies: '@es-joy/jsdoccomment': 0.43.1 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) - eslint-plugin-es-x@7.6.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-es-x@7.6.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.10.0 - eslint: 9.36.0(jiti@2.6.0) - eslint-compat-utils: 0.5.0(eslint@9.36.0(jiti@2.6.0)) + eslint: 9.38.0(jiti@2.6.1) + eslint-compat-utils: 0.5.0(eslint@9.38.0(jiti@2.6.1)) - eslint-plugin-eslint-comments@3.2.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-eslint-comments@3.2.0(eslint@9.38.0(jiti@2.6.1)): dependencies: escape-string-regexp: 1.0.5 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) ignore: 5.3.1 - eslint-plugin-import-x@0.5.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2): + eslint-plugin-import-x@0.5.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.1 doctrine: 3.0.0 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 get-tsconfig: 4.7.5 is-glob: 4.0.3 @@ -11883,44 +12173,44 @@ snapshots: - supports-color - typescript - eslint-plugin-jsdoc@48.2.7(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-jsdoc@48.2.7(eslint@9.38.0(jiti@2.6.1)): dependencies: '@es-joy/jsdoccomment': 0.43.1 are-docs-informative: 0.0.2 comment-parser: 1.4.1 debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) esquery: 1.5.0 semver: 7.7.2 spdx-expression-parse: 4.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@2.16.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-jsonc@2.16.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) - eslint: 9.36.0(jiti@2.6.0) - eslint-compat-utils: 0.5.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) + eslint: 9.38.0(jiti@2.6.1) + eslint-compat-utils: 0.5.0(eslint@9.38.0(jiti@2.6.1)) espree: 9.6.1 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.0 natural-compare: 1.4.0 synckit: 0.6.2 - eslint-plugin-markdown@5.0.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-markdown@5.0.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color - eslint-plugin-n@17.7.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-n@17.7.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) enhanced-resolve: 5.18.2 - eslint: 9.36.0(jiti@2.6.0) - eslint-plugin-es-x: 7.6.0(eslint@9.36.0(jiti@2.6.0)) + eslint: 9.38.0(jiti@2.6.1) + eslint-plugin-es-x: 7.6.0(eslint@9.38.0(jiti@2.6.1)) get-tsconfig: 4.7.5 globals: 15.3.0 ignore: 5.3.1 @@ -11929,48 +12219,48 @@ snapshots: eslint-plugin-no-only-tests@3.1.0: {} - eslint-plugin-perfectionist@2.10.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)(vue-eslint-parser@9.4.2(eslint@9.36.0(jiti@2.6.0))): + eslint-plugin-perfectionist@2.10.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)(vue-eslint-parser@9.4.2(eslint@9.38.0(jiti@2.6.1))): dependencies: - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - eslint: 9.36.0(jiti@2.6.0) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) minimatch: 9.0.4 natural-compare-lite: 1.4.0 optionalDependencies: - vue-eslint-parser: 9.4.2(eslint@9.36.0(jiti@2.6.0)) + vue-eslint-parser: 9.4.2(eslint@9.38.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-regexp@2.6.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-regexp@2.6.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.10.0 comment-parser: 1.4.1 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) jsdoc-type-pratt-parser: 4.0.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-toml@0.11.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-toml@0.11.0(eslint@9.38.0(jiti@2.6.1)): dependencies: debug: 4.4.1 - eslint: 9.36.0(jiti@2.6.0) - eslint-compat-utils: 0.5.0(eslint@9.36.0(jiti@2.6.0)) + eslint: 9.38.0(jiti@2.6.1) + eslint-compat-utils: 0.5.0(eslint@9.38.0(jiti@2.6.1)) lodash: 4.17.21 toml-eslint-parser: 0.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@53.0.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-unicorn@53.0.0(eslint@9.38.0(jiti@2.6.1)): dependencies: '@babel/helper-validator-identifier': 7.24.6 - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) '@eslint/eslintrc': 3.1.0 ci-info: 4.0.0 clean-regexp: 1.0.0 core-js-compat: 3.37.1 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -11984,53 +12274,53 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1)): dependencies: - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-rule-composer: 0.3.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)(vitest@3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)): + eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)): dependencies: - '@typescript-eslint/utils': 7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - eslint: 9.36.0(jiti@2.6.0) + '@typescript-eslint/utils': 7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) - vitest: 3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + '@typescript-eslint/eslint-plugin': 7.11.0(@typescript-eslint/parser@7.11.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + vitest: 3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-vue@9.26.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-vue@9.26.0(eslint@9.38.0(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.36.0(jiti@2.6.0)) - eslint: 9.36.0(jiti@2.6.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.38.0(jiti@2.6.1)) + eslint: 9.38.0(jiti@2.6.1) globals: 13.24.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.0 semver: 7.7.2 - vue-eslint-parser: 9.4.2(eslint@9.36.0(jiti@2.6.0)) + vue-eslint-parser: 9.4.2(eslint@9.38.0(jiti@2.6.1)) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-yml@1.14.0(eslint@9.36.0(jiti@2.6.0)): + eslint-plugin-yml@1.14.0(eslint@9.38.0(jiti@2.6.1)): dependencies: debug: 4.4.1 - eslint: 9.36.0(jiti@2.6.0) - eslint-compat-utils: 0.5.0(eslint@9.36.0(jiti@2.6.0)) + eslint: 9.38.0(jiti@2.6.1) + eslint-compat-utils: 0.5.0(eslint@9.38.0(jiti@2.6.1)) lodash: 4.17.21 natural-compare: 1.4.0 yaml-eslint-parser: 1.2.3 transitivePeerDependencies: - supports-color - eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.4.34)(eslint@9.36.0(jiti@2.6.0)): + eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.4.34)(eslint@9.38.0(jiti@2.6.1)): dependencies: '@vue/compiler-sfc': 3.4.34 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-rule-composer@0.3.0: {} @@ -12050,21 +12340,20 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.36.0(jiti@2.6.0): + eslint@9.38.0(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.1 - '@eslint/core': 0.15.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.1 + '@eslint/core': 0.16.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.36.0 - '@eslint/plugin-kit': 0.3.5 + '@eslint/js': 9.38.0 + '@eslint/plugin-kit': 0.4.0 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -12088,7 +12377,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.6.0 + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -12442,12 +12731,11 @@ snapshots: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 - happy-dom@20.0.0: + happy-dom@20.0.8: dependencies: - '@types/node': 20.19.21 + '@types/node': 20.19.22 '@types/whatwg-mimetype': 3.0.2 whatwg-mimetype: 3.0.0 - optional: true harmony-reflect@1.6.2: {} @@ -12471,6 +12759,24 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hosted-git-info@2.8.9: {} hosted-git-info@4.1.0: @@ -12489,6 +12795,8 @@ snapshots: html-escaper@2.0.2: {} + html-void-elements@3.0.0: {} + htmlparser2@10.0.0: dependencies: domelementtype: 2.3.0 @@ -12718,7 +13026,7 @@ snapshots: '@jest/expect': 30.2.0 '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.0 @@ -12738,15 +13046,15 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)): + jest-cli@30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + '@jest/core': 30.2.0(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + jest-config: 30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -12757,7 +13065,7 @@ snapshots: - supports-color - ts-node - jest-config@30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)): + jest-config@30.2.0(patch_hash=7183ebeb461331a42f7b4be95c553b6304fa99af85998b66f7f8c91aa96e8d46)(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.4 '@jest/get-type': 30.1.0 @@ -12784,8 +13092,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 24.6.0 - ts-node: 10.9.2(@types/node@24.6.0)(typescript@5.9.2) + '@types/node': 24.8.1 + ts-node: 10.9.2(@types/node@24.8.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -12821,7 +13129,7 @@ snapshots: '@jest/environment': 30.2.0 '@jest/environment-jsdom-abstract': 30.2.0(jsdom@26.1.0) '@types/jsdom': 21.1.7 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jsdom: 26.1.0 transitivePeerDependencies: - bufferutil @@ -12833,7 +13141,7 @@ snapshots: '@jest/environment': 30.2.0 '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-mock: 30.2.0 jest-util: 30.2.0 jest-validate: 30.2.0 @@ -12841,7 +13149,7 @@ snapshots: jest-haste-map@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -12899,13 +13207,13 @@ snapshots: jest-mock@30.0.2: dependencies: '@jest/types': 30.0.1 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-util: 30.0.2 jest-mock@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 jest-util: 30.2.0 jest-pnp-resolver@1.2.3(jest-resolve@30.2.0): @@ -12939,7 +13247,7 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -12968,7 +13276,7 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 cjs-module-lexer: 2.1.0 collect-v8-coverage: 1.0.2 @@ -13015,7 +13323,7 @@ snapshots: jest-util@30.0.2: dependencies: '@jest/types': 30.0.1 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 ci-info: 4.3.0 graceful-fs: 4.2.11 @@ -13024,7 +13332,7 @@ snapshots: jest-util@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 chalk: 4.1.2 ci-info: 4.3.0 graceful-fs: 4.2.11 @@ -13043,7 +13351,7 @@ snapshots: dependencies: '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.6.0 + '@types/node': 24.8.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -13052,18 +13360,18 @@ snapshots: jest-worker@30.2.0: dependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 '@ungap/structured-clone': 1.3.0 jest-util: 30.2.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)): + jest@30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + '@jest/core': 30.2.0(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@24.6.0)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + jest-cli: 30.2.0(@types/node@24.8.1)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13073,7 +13381,7 @@ snapshots: jiti@1.21.7: {} - jiti@2.6.0: {} + jiti@2.6.1: {} joycon@3.1.1: {} @@ -13192,7 +13500,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - less@4.4.1: + less@4.4.2: dependencies: copy-anything: 2.0.6 parse-node-version: 1.0.1 @@ -13245,7 +13553,7 @@ snapshots: lightningcss@1.30.1: dependencies: - detect-libc: 2.1.1 + detect-libc: 2.1.2 optionalDependencies: lightningcss-darwin-arm64: 1.30.1 lightningcss-darwin-x64: 1.30.1 @@ -13319,8 +13627,6 @@ snapshots: loupe@3.1.4: {} - loupe@3.2.0: {} - lru-cache@10.4.3: {} lru-cache@11.2.2: {} @@ -13333,7 +13639,7 @@ snapshots: dependencies: yallist: 4.0.0 - lucide-solid@0.544.0(solid-js@1.9.9): + lucide-solid@0.546.0(solid-js@1.9.9): dependencies: solid-js: 1.9.9 @@ -13355,7 +13661,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 make-error@1.3.6: {} @@ -13384,6 +13690,18 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + mdast-util-to-string@2.0.0: {} mdurl@2.0.0: {} @@ -13396,6 +13714,23 @@ snapshots: merge2@1.4.1: {} + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + micromark@2.11.4: dependencies: debug: 4.4.1 @@ -13498,25 +13833,25 @@ snapshots: sax: 1.4.1 optional: true - next@15.5.4(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2): + next@15.5.6(@babel/core@7.28.4)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2): dependencies: - '@next/env': 15.5.4 + '@next/env': 15.5.6 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001746 + caniuse-lite: 1.0.30001751 postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.5.4 - '@next/swc-darwin-x64': 15.5.4 - '@next/swc-linux-arm64-gnu': 15.5.4 - '@next/swc-linux-arm64-musl': 15.5.4 - '@next/swc-linux-x64-gnu': 15.5.4 - '@next/swc-linux-x64-musl': 15.5.4 - '@next/swc-win32-arm64-msvc': 15.5.4 - '@next/swc-win32-x64-msvc': 15.5.4 - '@playwright/test': 1.55.1 + '@next/swc-darwin-arm64': 15.5.6 + '@next/swc-darwin-x64': 15.5.6 + '@next/swc-linux-arm64-gnu': 15.5.6 + '@next/swc-linux-arm64-musl': 15.5.6 + '@next/swc-linux-x64-gnu': 15.5.6 + '@next/swc-linux-x64-musl': 15.5.6 + '@next/swc-win32-arm64-msvc': 15.5.6 + '@next/swc-win32-x64-msvc': 15.5.6 + '@playwright/test': 1.56.1 sass: 1.93.2 sharp: 0.34.4 transitivePeerDependencies: @@ -13593,6 +13928,14 @@ snapshots: dependencies: mimic-fn: 2.1.0 + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + open@10.2.0: dependencies: default-browser: 5.2.1 @@ -13625,6 +13968,26 @@ snapshots: - debug - supports-color + oxc-parser@0.95.0: + dependencies: + '@oxc-project/types': 0.95.0 + optionalDependencies: + '@oxc-parser/binding-android-arm64': 0.95.0 + '@oxc-parser/binding-darwin-arm64': 0.95.0 + '@oxc-parser/binding-darwin-x64': 0.95.0 + '@oxc-parser/binding-freebsd-x64': 0.95.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.95.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.95.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.95.0 + '@oxc-parser/binding-linux-arm64-musl': 0.95.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.95.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.95.0 + '@oxc-parser/binding-linux-x64-gnu': 0.95.0 + '@oxc-parser/binding-linux-x64-musl': 0.95.0 + '@oxc-parser/binding-wasm32-wasi': 0.95.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.95.0 + '@oxc-parser/binding-win32-x64-msvc': 0.95.0 + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -13784,11 +14147,11 @@ snapshots: mlly: 1.7.4 pathe: 2.0.3 - playwright-core@1.55.1: {} + playwright-core@1.56.1: {} - playwright@1.55.1: + playwright@1.56.1: dependencies: - playwright-core: 1.55.1 + playwright-core: 1.56.1 optionalDependencies: fsevents: 2.3.2 @@ -13808,19 +14171,19 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)): + postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 yaml: 2.4.2 optionalDependencies: postcss: 8.5.6 - ts-node: 10.9.2(@types/node@24.6.0)(typescript@5.9.2) + ts-node: 10.9.2(@types/node@24.8.1)(typescript@5.9.3) - postcss-load-config@6.0.1(jiti@2.6.0)(postcss@8.5.6)(yaml@2.4.2): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.4.2): dependencies: lilconfig: 3.1.1 optionalDependencies: - jiti: 2.6.0 + jiti: 2.6.1 postcss: 8.5.6 yaml: 2.4.2 @@ -13895,6 +14258,8 @@ snapshots: dependencies: parse-ms: 4.0.0 + property-information@7.1.0: {} + prr@1.0.1: optional: true @@ -14012,6 +14377,16 @@ snapshots: regenerate@1.4.2: {} + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + regexp-ast-analysis@0.7.1: dependencies: '@eslint-community/regexpp': 4.10.0 @@ -14195,6 +14570,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: {} + seroval-plugins@1.3.2(seroval@1.3.2): dependencies: seroval: 1.3.2 @@ -14206,8 +14583,8 @@ snapshots: sharp@0.34.4: dependencies: '@img/colour': 1.0.0 - detect-libc: 2.1.1 - semver: 7.7.2 + detect-libc: 2.1.2 + semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.4 '@img/sharp-darwin-x64': 0.34.4 @@ -14239,6 +14616,17 @@ snapshots: shebang-regex@3.0.0: {} + shiki@3.13.0: + dependencies: + '@shikijs/core': 3.13.0 + '@shikijs/engine-javascript': 3.13.0 + '@shikijs/engine-oniguruma': 3.13.0 + '@shikijs/langs': 3.13.0 + '@shikijs/themes': 3.13.0 + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -14315,9 +14703,9 @@ snapshots: solid-refresh@0.6.3(solid-js@1.9.9): dependencies: - '@babel/generator': 7.28.0 + '@babel/generator': 7.28.3 '@babel/helper-module-imports': 7.27.1 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 solid-js: 1.9.9 transitivePeerDependencies: - supports-color @@ -14337,6 +14725,8 @@ snapshots: dependencies: whatwg-url: 7.1.0 + space-separated-tokens@2.0.2: {} + spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 @@ -14395,6 +14785,11 @@ snapshots: safe-buffer: 5.2.1 optional: true + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -14498,11 +14893,11 @@ snapshots: tailwind-merge@3.3.1: {} - tailwindcss-animate@1.0.7(tailwindcss@4.1.13): + tailwindcss-animate@1.0.7(tailwindcss@4.1.14): dependencies: - tailwindcss: 4.1.13 + tailwindcss: 4.1.14 - tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)): + tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -14521,7 +14916,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.0.1(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2)) + postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3)) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 @@ -14529,7 +14924,9 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@4.1.13: {} + tailwindcss@4.1.14: {} + + tailwindcss@4.1.16: {} tapable@2.2.2: {} @@ -14601,10 +14998,14 @@ snapshots: tinypool@1.1.1: {} + tinypool@2.0.0: {} + tinyrainbow@1.2.0: {} tinyrainbow@2.0.0: {} + tinyrainbow@3.0.3: {} + tinyspy@3.0.2: {} tinyspy@4.0.3: {} @@ -14641,27 +15042,29 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.3.0(typescript@5.9.2): + trim-lines@3.0.1: {} + + ts-api-utils@1.3.0(typescript@5.9.3): dependencies: - typescript: 5.9.2 + typescript: 5.9.3 ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@24.6.0)(typescript@5.9.2): + ts-node@10.9.2(@types/node@24.8.1)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.6.0 + '@types/node': 24.8.1 acorn: 8.15.0 acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.2 + typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 @@ -14669,7 +15072,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.0(jiti@2.6.0)(postcss@8.5.6)(typescript@5.9.2)(yaml@2.4.2): + tsup@8.5.0(jiti@2.6.1)(postcss@8.5.6)(typescript@5.9.3)(yaml@2.4.2): dependencies: bundle-require: 5.1.0(esbuild@0.25.6) cac: 6.7.14 @@ -14680,7 +15083,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.0)(postcss@8.5.6)(yaml@2.4.2) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.4.2) resolve-from: 5.0.0 rollup: 4.42.0 source-map: 0.8.0-beta.0 @@ -14690,7 +15093,7 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: postcss: 8.5.6 - typescript: 5.9.2 + typescript: 5.9.3 transitivePeerDependencies: - jiti - supports-color @@ -14730,7 +15133,7 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.7 - typescript@5.9.2: {} + typescript@5.9.3: {} uc.micro@2.1.0: {} @@ -14740,10 +15143,9 @@ snapshots: underscore@1.13.7: {} - undici-types@6.21.0: - optional: true + undici-types@6.21.0: {} - undici-types@7.13.0: {} + undici-types@7.14.0: {} undici@7.16.0: {} @@ -14762,10 +15164,33 @@ snapshots: unicorn-magic@0.3.0: {} + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position@2.0.3: dependencies: '@types/unist': 2.0.10 + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universalify@0.1.2: {} universalify@2.0.1: {} @@ -14839,13 +15264,23 @@ snapshots: version-range@4.15.0: {} - vite-node@2.1.8(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite-node@2.1.8(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 1.1.2 - vite: 5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + vite: 5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) transitivePeerDependencies: - '@types/node' - less @@ -14857,16 +15292,15 @@ snapshots: - supports-color - terser - vite-node@3.2.4(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): + vite-node@3.2.4(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) transitivePeerDependencies: - '@types/node' - - jiti - less - lightningcss - sass @@ -14875,81 +15309,79 @@ snapshots: - sugarss - supports-color - terser - - tsx - - yaml - vite-plugin-solid@2.11.8(@testing-library/jest-dom@6.9.0)(solid-js@1.9.9)(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)): + vite-plugin-solid@2.11.9(@testing-library/jest-dom@6.9.1)(solid-js@1.9.9)(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@types/babel__core': 7.20.5 - babel-preset-solid: 1.9.9(@babel/core@7.28.0)(solid-js@1.9.9) + babel-preset-solid: 1.9.9(@babel/core@7.28.4)(solid-js@1.9.9) merge-anything: 5.1.7 solid-js: 1.9.9 solid-refresh: 0.6.3(solid-js@1.9.9) - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) - vitefu: 1.1.1(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vitefu: 1.1.1(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) optionalDependencies: - '@testing-library/jest-dom': 6.9.0 + '@testing-library/jest-dom': 6.9.1 transitivePeerDependencies: - supports-color - vite@5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): + vite@5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): dependencies: esbuild: 0.21.5 postcss: 8.5.6 rollup: 4.32.0 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 fsevents: 2.3.3 - less: 4.4.1 + less: 4.4.2 lightningcss: 1.30.1 sass: 1.93.2 stylus: 0.64.0 - vite@7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): + vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): dependencies: - esbuild: 0.25.10 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.49.0 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 fsevents: 2.3.3 - jiti: 2.6.0 - less: 4.4.1 + jiti: 2.6.1 + less: 4.4.2 lightningcss: 1.30.1 sass: 1.93.2 stylus: 0.64.0 yaml: 2.4.2 - vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): + vite@7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): dependencies: - esbuild: 0.25.10 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.49.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 fsevents: 2.3.3 - jiti: 2.6.0 - less: 4.4.1 + jiti: 2.6.1 + less: 4.4.2 lightningcss: 1.30.1 sass: 1.93.2 stylus: 0.64.0 yaml: 2.4.2 - vitefu@1.1.1(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)): + vitefu@1.1.1(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)): optionalDependencies: - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) - vitest@2.1.8(@types/node@24.6.0)(happy-dom@14.12.3)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): + vitest@2.1.8(@types/node@24.8.1)(happy-dom@14.12.3)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0): dependencies: '@vitest/expect': 2.1.8 - '@vitest/mocker': 2.1.8(vite@5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)) + '@vitest/mocker': 2.1.8(vite@5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.8 '@vitest/snapshot': 2.1.8 @@ -14965,11 +15397,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.1.1 tinyrainbow: 1.2.0 - vite: 5.4.19(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) - vite-node: 2.1.8(@types/node@24.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + vite: 5.4.19(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) + vite-node: 2.1.8(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 happy-dom: 14.12.3 jsdom: 26.1.0 transitivePeerDependencies: @@ -14983,11 +15415,11 @@ snapshots: - supports-color - terser - vitest@3.2.4(@types/node@24.6.0)(happy-dom@14.6.2)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): + vitest@3.2.4(@types/node@24.8.1)(happy-dom@14.6.2)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + '@vitest/mocker': 3.2.4(vite@7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -15005,11 +15437,11 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) - vite-node: 3.2.4(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite-node: 3.2.4(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 happy-dom: 14.6.2 jsdom: 26.1.0 transitivePeerDependencies: @@ -15026,11 +15458,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/node@24.6.0)(happy-dom@20.0.0)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): + vitest@3.2.4(@types/node@24.8.1)(happy-dom@20.0.8)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + '@vitest/mocker': 3.2.4(vite@7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -15048,12 +15480,12 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.3(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) - vite-node: 3.2.4(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite: 7.1.3(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + vite-node: 3.2.4(@types/node@24.8.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.6.0 - happy-dom: 20.0.0 + '@types/node': 24.8.1 + happy-dom: 20.0.8 jsdom: 26.1.0 transitivePeerDependencies: - jiti @@ -15069,33 +15501,31 @@ snapshots: - tsx - yaml - vitest@4.0.0-beta.8(@types/node@24.6.0)(happy-dom@14.12.3)(jiti@2.6.0)(jsdom@26.1.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): + vitest@4.0.0-beta.18(@types/node@24.8.1)(happy-dom@14.12.3)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2): dependencies: - '@types/chai': 5.2.2 - '@vitest/expect': 4.0.0-beta.8 - '@vitest/mocker': 4.0.0-beta.8(vite@7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) - '@vitest/pretty-format': 4.0.0-beta.8 - '@vitest/runner': 4.0.0-beta.8 - '@vitest/snapshot': 4.0.0-beta.8 - '@vitest/spy': 4.0.0-beta.8 - '@vitest/utils': 4.0.0-beta.8 - chai: 5.2.1 - debug: 4.4.1 + '@vitest/expect': 4.0.0-beta.18 + '@vitest/mocker': 4.0.0-beta.18(vite@7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2)) + '@vitest/pretty-format': 4.0.0-beta.18 + '@vitest/runner': 4.0.0-beta.18 + '@vitest/snapshot': 4.0.0-beta.18 + '@vitest/spy': 4.0.0-beta.18 + '@vitest/utils': 4.0.0-beta.18 + debug: 4.4.3 es-module-lexer: 1.7.0 expect-type: 1.2.2 - magic-string: 0.30.17 + magic-string: 0.30.19 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.4.1)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) + tinyglobby: 0.2.15 + tinypool: 2.0.0 + tinyrainbow: 3.0.3 + vite: 7.1.10(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.1)(sass@1.93.2)(stylus@0.64.0)(yaml@2.4.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.6.0 + '@types/node': 24.8.1 happy-dom: 14.12.3 jsdom: 26.1.0 transitivePeerDependencies: @@ -15112,10 +15542,10 @@ snapshots: - tsx - yaml - vue-eslint-parser@9.4.2(eslint@9.36.0(jiti@2.6.0)): + vue-eslint-parser@9.4.2(eslint@9.38.0(jiti@2.6.1)): dependencies: debug: 4.4.1 - eslint: 9.36.0(jiti@2.6.0) + eslint: 9.38.0(jiti@2.6.1) eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 @@ -15256,4 +15686,6 @@ snapshots: yoctocolors@2.1.1: {} - zod@4.1.11: {} + zod@4.1.12: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e7ae1eb..b1088d5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,4 @@ packages: - . - 'packages/*' - - examples/!(bun-react) + - examples/!(bun-react|bun-solid) diff --git a/test/Dockerfile b/test/Dockerfile index f456894..00b2986 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -1,15 +1,15 @@ +FROM node:22.20.0 AS node-stage +FROM oven/bun:1.3.0 AS bun-stage + FROM codercom/code-server:4.104.2 -ENV NODE_VERSION=22.20.0 -# installs NVM (Node Version Manager) -RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash -ENV NVM_DIR=/home/coder/.nvm +# Copy Node and Bun from their stages +COPY --from=bun-stage /usr/local/bin/bun /usr/local/bin/bun +COPY --from=node-stage /usr/local /usr/local USER coder -# download and install Node.js -RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} -RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} -RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} +# Install Bun VSCode extension +RUN code-server --install-extension oven.bun-vscode@0.0.31 COPY --chown=coder ./.vscode/settings.json /home/coder/.local/share/code-server/User/settings.json diff --git a/test/docker-compose.yml b/test/docker-compose.yml index 39eafcd..ec7b3a8 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -1,7 +1,8 @@ services: code-server: # Use the pushed image instead of building it locally, to save time in the CI pipeline - image: poffm/vscode-server-with-node:4.104.2-22.20.0 + # The version numbers refer to the versions of (code-server)-(node)-(bun) + image: poffm/vscode-server-with-node:4.104.2-22.20.0-1.3.0 # build: # context: .. # dockerfile: ./test/Dockerfile diff --git a/test/e2e-core.test.ts b/test/e2e-core.test.ts index fa7a076..52975e9 100644 --- a/test/e2e-core.test.ts +++ b/test/e2e-core.test.ts @@ -48,6 +48,7 @@ test('Steps through the Vitest+React+Tailwind@4 Counter example', async ({ brows .evaluate( button => window.getComputedStyle(button).getPropertyValue('background-color'), ), + { timeout: 20_000 }, ).toBe('oklch(0.448 0.119 151.328)') // green await debugHelper.debugStep() @@ -111,6 +112,7 @@ test('Steps through the Vitest+React+Tailwind@3 Counter example', async ({ brows .evaluate( button => window.getComputedStyle(button).getPropertyValue('background-color'), ), + { timeout: 20_000 }, ).toBe('rgb(22, 101, 52)') // green await debugHelper.debugStep() diff --git a/test/e2e-misc-features.test.ts b/test/e2e-misc-features.test.ts index 4658aad..919806a 100644 --- a/test/e2e-misc-features.test.ts +++ b/test/e2e-misc-features.test.ts @@ -59,7 +59,7 @@ test('Load an external css file', async () => { .evaluate( button => window.getComputedStyle(button).getPropertyValue('background-color'), ), - { timeout: 20_000 }, + { timeout: 40_000 }, ).toBe('rgb(128, 0, 128)') // Disable the external css file @@ -73,7 +73,7 @@ test('Load an external css file', async () => { .evaluate( button => window.getComputedStyle(button).getPropertyValue('background-color'), ), - { timeout: 20_000 }, + { timeout: 40_000 }, ).toBe('rgb(239, 239, 239)') }) diff --git a/test/e2e-recorder.test.ts b/test/e2e-recorder.test.ts new file mode 100644 index 0000000..e59fe43 --- /dev/null +++ b/test/e2e-recorder.test.ts @@ -0,0 +1,240 @@ +import fs from 'node:fs/promises' +import type { Browser } from '@playwright/test' +import { test } from '@playwright/test' +import path from 'pathe' +import { DebuggerHelper } from './debugger-helper' + +// Undo changes made to the e2e test file that gets edited +for (const exampleFolder of ['vitest-react-tailwind4', 'jest-react', 'bun-solid']) { + const e2eTestTestPath = path.join(__dirname, `../examples/${exampleFolder}/test/form-test-for-e2e.test.tsx`) + let e2eTestTestOriginalContent: string + test.beforeAll(async () => { + e2eTestTestOriginalContent = await fs.readFile(e2eTestTestPath, 'utf8') + }) + test.afterEach(async () => { + // Undo changes made to the e2e test file + await fs.writeFile(e2eTestTestPath, e2eTestTestOriginalContent, 'utf8') + }) +} + +test.afterEach(async () => { + // Delete the generated test file + const generatedFilePath = path.join(__dirname, '../examples/vitest-react-tailwind4/components/FormExample.test.tsx') + try { + await fs.access(generatedFilePath) + await fs.unlink(generatedFilePath) + } + catch { + // ignore + } +}) + +test('Create a UI test file', async ({ browser }) => { + const page = await browser.newPage() + + await page.goto('http://localhost:8080/?folder=/source/examples/vitest-react-tailwind4') + + // Open FormExample.tsx + await page.locator('.search-label').click() + await page.locator('.monaco-findInput > .monaco-inputbox > .ibwrapper > .input').clear() + await page.locator('.monaco-findInput > .monaco-inputbox > .ibwrapper > .input').type('FormExample.tsx') + await page.locator('.highlight').first().click() + + // Create the test file + await page.getByText('FormExample', { exact: true }).click({ + button: 'right', + }) + await page.getByRole('menuitem', { name: 'Create UI test (UI Test' }).click() + + // FormExample.test.tsx should open on its own here + + // Hide the bottom panel to get more space + const hideButton = page.getByRole('button', { name: 'Hide Panel' }) + if (await hideButton.isVisible()) { + await hideButton.click() + } + + // Wait for the text "render()" to appear anywhere on the page + await page.getByText('render()').waitFor() +}) + +test('Record a UI test, Vitest + React + Tailwind4', async ({ browser }) => { + await recordFormTest(browser, 'vitest-react-tailwind4', 'form-test-for-e2e.test.tsx', true) +}) + +test('Record a UI test, Jest + React', async ({ browser }) => { + test.skip(!!process.env.CI, 'Rely on the other Jest + React recorder test to save time in CI') + await recordFormTest(browser, 'jest-react', 'form-test-for-e2e.test.tsx') +}) + +test('Record a UI test, Bun + Solid', async ({ browser }) => { + await recordFormTest(browser, 'bun-solid', 'form-test-for-e2e.test.tsx', true) +}) + +test.describe('Record TodoList test, Jest + React', () => { + // Undo changes made to the TodoList.test.tsx file that gets edited + { + const todoListTestPath = path.join(__dirname, '../examples/jest-nextjs-minimal/components/TodoList.test.tsx') + let todoListTestOriginalContent: string = '' + test.beforeAll(async () => { + todoListTestOriginalContent = await fs.readFile(todoListTestPath, 'utf8') + }) + test.afterEach(async () => { + // Undo changes made to the TodoList.test.tsx file + await fs.writeFile(todoListTestPath, todoListTestOriginalContent, 'utf8') + }) + } + + test('Record TodoList test, Jest + React', async ({ browser }) => { + const { page, replicaPanel } = await startRecorderTest(browser, 'jest-nextjs-minimal', 'TodoList.test.tsx', true) + + // Generate an 'expect/toHaveTextContent' statement for the initial no-todos-message

tag + await replicaPanel.getByText('No todos yet. Add one to get started.').click({ + button: 'right', + }) + await replicaPanel.getByText('.toHaveTextContent(...)').click() + + // Add todos + await replicaPanel.getByRole('textbox', { name: 'Add a new todo' }).click() + await replicaPanel.getByRole('textbox', { name: 'Add a new todo' }).fill('todo 1') + await page.keyboard.press('Enter') + await replicaPanel.getByRole('textbox', { name: 'Add a new todo' }).fill('todo 2') + await page.keyboard.press('Enter') + await replicaPanel.getByRole('textbox', { name: 'Add a new todo' }).fill('todo 3') + await page.keyboard.press('Enter') + + // Text after adding 3 todos + await replicaPanel.getByText('0 done').waitFor() + await replicaPanel.getByText('3 in progress').waitFor() + + // Toggle todos + await replicaPanel.getByRole('checkbox', { name: 'Toggle done' }).nth(1).check() + await replicaPanel.getByRole('checkbox', { name: 'Toggle done' }).nth(2).check() + await replicaPanel.getByText('2 done').waitFor() + await replicaPanel.getByText('1 in progress').waitFor() + + // Delete todos + await replicaPanel.getByRole('button', { name: 'Delete' }).first().click() + await replicaPanel.getByRole('button', { name: 'Delete' }).nth(1).click() + await replicaPanel.getByText('1 done').waitFor() + await replicaPanel.getByText('0 in progress').waitFor() + + // Generate 'expect' statements + await replicaPanel.getByText(/^1 done$/).first().click({ + modifiers: ['Alt'], + }) + await replicaPanel.getByText(/^0 in progress$/).first().click({ + modifiers: ['Alt'], + }) + + // End the test + await page.getByRole('button', { name: 'Continue (F5)' }).click() + + // Wait for replica panel to be closed + await page.frameLocator('iframe.webview.ready').locator('iframe[title="Tested UI"]').waitFor({ state: 'detached' }) + + // Check for the generated code in the editor; Make sure indexes are used for the checkboxes and buttons + await page.getByText(`await userEvent.click(screen.getAllByRole('checkbox', { name: /^toggle done$/i })[1])`).waitFor() + await page.getByText(`await userEvent.click(screen.getAllByRole('button', { name: /^delete$/i })[0])`).first().waitFor() + await page.getByText(`expect(screen.getByRole('paragraph')).toHaveTextContent('No todos yet. Add one to get started.')`).first().waitFor() + await page.getByText(`expect(screen.getByText(/^1 done$/i))`).waitFor() + }) +}) + +async function startRecorderTest(browser: Browser, exampleFolder: string, testFileSearch: string, dismissStylePrompt = false) { + const page = await browser.newPage() + + await page.goto(`http://localhost:8080/?folder=/source/examples/${exampleFolder}`) + + // Open form-test-for-e2e.test.tsx + await page.locator('.search-label').click() + await page.locator('.monaco-findInput > .monaco-inputbox > .ibwrapper > .input').clear() + await page.locator('.monaco-findInput > .monaco-inputbox > .ibwrapper > .input').type(testFileSearch) + await page.locator('.highlight').first().click() + + // Hide the bottom panel to get more space + const hideButton = page.getByRole('button', { name: 'Hide Panel' }) + if (await hideButton.isVisible()) { + await hideButton.click() + } + + // Start the visual debug + await page.getByRole('button', { name: 'Visually Debug UI' }).click() + await page.getByText('Tested UI').waitFor() + + const replicaPanel = page + .frameLocator('iframe.webview.ready') + .frameLocator('iframe[title="Tested UI"]') + + if (dismissStylePrompt) { + // Click the OK button on the initial style prompt + await replicaPanel.locator('ui-test-visualizer-button[title="Dismiss style prompt"]').click() + } + + // Wait until the debugger is paused on breakpoint + await page.getByText('Paused on breakpoint').waitFor() + + const debugHelper = new DebuggerHelper(page) + await debugHelper.debugStep() + + // Start recording + await replicaPanel.getByRole('button', { name: 'Record input as code' }).first().click() + + return { page, replicaPanel, debugHelper } +} + +async function recordFormTest( + browser: Browser, + exampleFolder: string, + testFileSearch: string, + dismissStylePrompt = false, +) { + const { page, replicaPanel } = await startRecorderTest( + browser, + exampleFolder, + testFileSearch, + dismissStylePrompt, + ) + + // Check that the submission count starts at 0 + await replicaPanel.getByText(`Submit Count: 0`).waitFor() + + // Fill in the first input + await replicaPanel.getByRole('textbox', { name: 'First input' }).click() + await replicaPanel.getByRole('textbox', { name: 'First input' }).fill('test input 1') + + // Fill in the second input + await replicaPanel.getByRole('textbox', { name: 'Second input' }).click() + await replicaPanel.getByRole('textbox', { name: 'Second input' }).fill('test input 2') + + // Select an option from the select menu + await replicaPanel.getByRole('combobox', { name: 'Select menu' }).click() + await replicaPanel.getByRole('combobox', { name: 'Select menu' }).selectOption('option2') + + // Check the code is generated for the userEvent calls + await replicaPanel.getByText(`await userEvent.type(screen.getByRole('textbox', { name: /^first input$/i }), 'test input 1')`).waitFor() + await replicaPanel.getByText(`await userEvent.type(screen.getByRole('textbox', { name: /^second input$/i }), 'test input 2')`).waitFor() + await replicaPanel.getByText(`await userEvent.selectOptions(screen.getByRole('combobox', { name: /^select menu$/i }), ['option2'])`).waitFor() + + // Submit the form + await replicaPanel.getByRole('button', { name: 'Submit' }).click() + await replicaPanel.getByText(`await userEvent.click(screen.getByRole('button', { name: /^submit$/i }))`).waitFor() + + // Alt-click the submit count to generate the `expect` statement + await replicaPanel.getByText('Submit Count:').click({ + modifiers: ['Alt'], + }) + // Check that the submission count increased due to the click, which causes the userEvent code to run in the debugger + await replicaPanel.getByText(`Submit Count: 1`).waitFor() + await replicaPanel.getByText(`expect(screen.getByText(/^submit count: 1$/i))`).waitFor() + + // Finish the test + await page.getByRole('button', { name: 'Continue (F5)' }).click() + + // Wait for replica panel to be closed + await page.frameLocator('iframe.webview.ready').locator('iframe[title="Tested UI"]').waitFor({ state: 'detached' }) + + // Check for the generated code in the editor + await page.getByText(`await userEvent.click(screen.getByRole('button', { name: /^submit$/i }))`).waitFor() + await page.getByText(`expect(screen.getByText(/^submit count: 1$/i))`).waitFor() +}