diff --git a/deno.lock b/deno.lock index 8e316d4ba..d63f3c014 100644 --- a/deno.lock +++ b/deno.lock @@ -10194,6 +10194,7 @@ "npm:@types/ssh2-sftp-client@^9.0.6", "npm:@types/yargs@^17.0.35", "npm:@typescript-eslint/parser@^8.53.0", + "npm:@vitejs/plugin-legacy@^7.2.1", "npm:@vitejs/plugin-react@^5.1.1", "npm:@vitest/browser-playwright@^4.0.15", "npm:@vitest/browser@^4.0.15", @@ -10205,6 +10206,7 @@ "npm:buffer@^6.0.3", "npm:class-variance-authority@~0.7.1", "npm:clsx@^2.1.1", + "npm:core-js@^3.48.0", "npm:detect-port@^2.1.0", "npm:dotenv@^17.2.3", "npm:ed2curve@0.3", @@ -10241,6 +10243,7 @@ "npm:swiper@^12.0.3", "npm:tailwind-merge@^3.4.0", "npm:tailwindcss@4", + "npm:terser@^5.46.0", "npm:turbo@^2.7.1", "npm:tw-animate-css@^1.4.0", "npm:tweetnacl@^1.0.3", @@ -10353,6 +10356,15 @@ ] } }, + "packages/android-focus-blur-guard": { + "packageJson": { + "dependencies": [ + "npm:jsdom@^27.2.0", + "npm:typescript@^5.9.3", + "npm:vitest@4" + ] + } + }, "packages/bio-sdk": { "packageJson": { "dependencies": [ diff --git a/package.json b/package.json index b656e2395..570765efd 100644 --- a/package.json +++ b/package.json @@ -71,10 +71,10 @@ "miniapps:dev:biobridge": "pnpm --filter @biochain/miniapp-biobridge dev" }, "dependencies": { - "@biochain/android-focus-blur-guard": "workspace:*", "@base-ui/react": "^1.0.0", "@bfchain/util": "^5.0.0", "@bfmeta/sign-util": "^1.3.10", + "@biochain/android-focus-blur-guard": "workspace:*", "@biochain/bio-sdk": "workspace:*", "@biochain/chain-effect": "workspace:*", "@biochain/ecosystem-native": "workspace:*", @@ -149,12 +149,12 @@ "@inquirer/prompts": "^8.0.2", "@plaoc/cli": "^1.2.2", "@playwright/test": "^1.57.0", + "@stackflow/config": "^1.0.1-canary.0", "@storybook/addon-docs": "^10.1.4", "@storybook/addon-vitest": "^10.1.4", "@storybook/react": "^10.1.4", "@storybook/react-vite": "^10.1.4", "@storybook/test": "^8.6.14", - "@stackflow/config": "^1.0.1-canary.0", "@tailwindcss/vite": "^4.0.0", "@tanstack/ai": "^0.0.3", "@testing-library/jest-dom": "^6.9.1", @@ -171,11 +171,13 @@ "@types/semver": "^7.7.1", "@types/ssh2-sftp-client": "^9.0.6", "@typescript-eslint/parser": "^8.53.0", + "@vitejs/plugin-legacy": "^7.2.1", "@vitejs/plugin-react": "^5.1.1", "@vitest/browser": "^4.0.15", "@vitest/browser-playwright": "^4.0.15", "@vitest/coverage-istanbul": "^4.0.15", "@vitest/coverage-v8": "^4.0.15", + "core-js": "^3.48.0", "detect-port": "^2.1.0", "dotenv": "^17.2.3", "eslint-plugin-file-component-constraints": "workspace:*", @@ -198,6 +200,7 @@ "ssh2-sftp-client": "^12.0.1", "storybook": "^10.1.4", "tailwindcss": "^4.0.0", + "terser": "^5.46.0", "turbo": "^2.7.1", "typescript": "~5.9.3", "vite": "^7.2.7", diff --git a/packages/android-focus-blur-guard/package.json b/packages/android-focus-blur-guard/package.json index c92bc7e50..b5726c751 100644 --- a/packages/android-focus-blur-guard/package.json +++ b/packages/android-focus-blur-guard/package.json @@ -1,5 +1,6 @@ { "name": "@biochain/android-focus-blur-guard", + "private": false, "version": "0.1.0", "description": "Android focus/blur loop guard for WebView and iframe contexts", "type": "module", @@ -9,7 +10,8 @@ "exports": { ".": { "import": "./src/index.ts", - "types": "./src/index.ts" + "types": "./src/index.ts", + "default": "./src/index.ts" } }, "files": [ diff --git a/packages/bio-sdk/package.json b/packages/bio-sdk/package.json index f842fa7f8..2f5142478 100644 --- a/packages/bio-sdk/package.json +++ b/packages/bio-sdk/package.json @@ -1,11 +1,16 @@ { "name": "@biochain/bio-sdk", + "private": false, "version": "0.2.0", "description": "Bio Ecosystem SDK for MiniApps - window.bio provider", "type": "module", + "main": "./src/index.ts", "module": "./src/index.ts", + "types": "./src/index.ts", "exports": { ".": { + "import": "./src/index.ts", + "types": "./src/index.ts", "default": "./src/index.ts" } }, diff --git a/packages/chain-effect/package.json b/packages/chain-effect/package.json index 7423bce8a..50bd78b5f 100644 --- a/packages/chain-effect/package.json +++ b/packages/chain-effect/package.json @@ -1,5 +1,6 @@ { "name": "@biochain/chain-effect", + "private": true, "version": "0.1.0", "description": "Effect TS based reactive chain data fetching", "type": "module", diff --git a/packages/chain-effect/src/http.test.ts b/packages/chain-effect/src/http.test.ts new file mode 100644 index 000000000..5759da845 --- /dev/null +++ b/packages/chain-effect/src/http.test.ts @@ -0,0 +1,27 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { Effect } from 'effect' +import { httpFetch } from './http' + +describe('httpFetch', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + + it('serializes bigint body values before POST', async () => { + const fetchSpy = vi + .spyOn(globalThis, 'fetch') + .mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 })) + + await Effect.runPromise( + httpFetch({ + url: 'https://example.test/api', + method: 'POST', + body: { amount: 1000000000n }, + }), + ) + + expect(fetchSpy).toHaveBeenCalledTimes(1) + const requestInit = fetchSpy.mock.calls[0]?.[1] + expect(requestInit?.body).toBe('{"amount":"1000000000"}') + }) +}) diff --git a/packages/chain-effect/src/http.ts b/packages/chain-effect/src/http.ts index 390e84e8b..befed1f6b 100644 --- a/packages/chain-effect/src/http.ts +++ b/packages/chain-effect/src/http.ts @@ -147,7 +147,7 @@ export function httpFetch(options: FetchOptions): Effect.Effect=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} @@ -1184,6 +1201,10 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -1192,12 +1213,33 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.5': resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.6': + resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} @@ -1210,12 +1252,22 @@ packages: resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.3': resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.27.1': resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} @@ -1224,12 +1276,28 @@ packages: resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.27.1': resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} @@ -1246,6 +1314,10 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.4': resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} @@ -1255,6 +1327,59 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-jsx@7.27.1': resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} @@ -1267,164 +1392,499 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.27.1': - resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.5': - resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.28.5': - resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.10': - resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@base-ui/react@1.0.0': - resolution: {integrity: sha512-4USBWz++DUSLTuIYpbYkSgy1F9ZmNG9S/lXvlUN6qMK0P0RlW+6eQmDUB4DgZ7HVvtXl4pvi4z5J2fv6Z3+9hg==} - engines: {node: '>=14.0.0'} + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true + '@babel/core': ^7.0.0-0 - '@base-ui/utils@0.2.3': - resolution: {integrity: sha512-/CguQ2PDaOzeVOkllQR8nocJ0FFIDqsWIcURsVmm53QGo8NhFNpePjNlyPIB41luxfOqnG7PU0xicMEw3ls7XQ==} + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true + '@babel/core': ^7.0.0-0 - '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} - engines: {node: '>=18'} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@bfchain/util-aborter@5.0.0': - resolution: {integrity: sha512-w1F1aWAKwNx/hbDbkItJ6Kfov+KS5/yCHyGVeuPq6bq6fh4cn7TOBQxLTLDMfdYXwU+Ams8vkptPhxk6gPe66w==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-buffer@5.0.0': - resolution: {integrity: sha512-XfsEPIyZx/cHXdODiy5vbb3BuLelRAlISv0OoTWfpr1QY2KhmVhvBgX2pFvweX3JzOysf3mktw+3iSFFVskg1g==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-decorator@5.0.0': - resolution: {integrity: sha512-6vdVEibvxoiulNF995SW9WYmKtDcL3p6nZz2EpCK2J3Bhd8rn3qpbi/2V8rIlL0yL4YmEFHnkD2YNjpJdUnKXQ==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-deepcopy@5.0.0': - resolution: {integrity: sha512-/XYBG4nndPk+Bszemgi8As0bir6ZNs15tNK0jcGjQIxymxPSpzeCGAFG1a5/wgBSEkVjEEcUSe8TSgovShrNSw==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-deepmix@5.0.0': - resolution: {integrity: sha512-/53TT+rmOKGnAhEM9FDWSOB0FCB5OpbZrT7jF/5R+HlwIHi+Eras8/ZRuuO8at/2a1O57zzcWoelbQP2w+ZUtQ==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-dep-inject@5.0.0': - resolution: {integrity: sha512-hK2+07fz2DVPB7lsFwnf+0TN3DF+eSJ3QcTwhDhNYPggx4RzGpzmxlJ2BJlnHdCRLTo6bJG1KmxIgdtyBbDEGw==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-encoding-binary@5.0.0': - resolution: {integrity: sha512-i2rfq3EGUvUj8/DYAso1A9sCRuh8Xv253Uk9SoOLX1XLrW+rVwG8PW3qNMJCEMW/m8cSKCkVlboK9Nyy64WCZw==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-encoding-hex@5.0.0': - resolution: {integrity: sha512-6bkJ61N2jLZOfbASmb4TQe9+U2APJRFMxOJJKwQVtXiMQybUkw/2euY2o4VKXsXWvacYdZhKTQgcRUO85i+tiA==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-encoding-utf8@5.0.0': - resolution: {integrity: sha512-3HggkUnTr2QWItSUXR/DZHFJ6+wI7VpCcBDFkVuz5fhidwv3p/lNA26j4vnW/pANSm20y6IaRWbfbJb1j7I1BQ==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-env@5.0.0': - resolution: {integrity: sha512-U9S2J/F3DEp+U0T4wvQNFHVHtYAA1LyNqrtIONDi5sXIKQnAoVQ5M1Vr8HweEEERA07H9jt5Vi4ek4wOxDBn6w==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-event-base@5.0.0': - resolution: {integrity: sha512-cG4kqdhzwYccrYaLcr6HDBISgFBqXwiCEujvI5TcP4qSsd7XM1G31KZtMQn2ZG4ukeT6CDNNPqJNj34RKgZT8g==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-event-map-emitter@5.0.0': - resolution: {integrity: sha512-0zdmvv+R1oJR/eiHK2K+aLvEm65me9qsuQC/yKSLcno+KQ3XHIjxz3NfvVYI8AGneruHh0FFuhf+uIYSG1dE2w==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-event-quene-emitter@5.0.0': - resolution: {integrity: sha512-MK5d59Wksg28dbDmz4n4C5St9VgI4vza1iOaVUeYPtk+aYcqqtwmzleyMavMlNC+vYgPVLJGb+OVn2iFH7MJjw==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-event@5.0.0': - resolution: {integrity: sha512-XEg2ilqy1osvU6ZMqIHmJNEHG9knk7zHedDDmGnbeRGbe7pI7Lt9uOeg1d1C20yHFHCMqlx3weXZsNIda8dXQw==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-exception-error-code@5.0.0': - resolution: {integrity: sha512-B+9Njc3s0GQyQL4YLdZtciXWoPiBKumzdJCFyaz+98bKDG2UuDdwsxQhNJwbYrQp+01wtYqsy+2Pa4lJuK+eZg==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-exception-generator@5.0.0': - resolution: {integrity: sha512-UWf07tbEW1Gg18DABEiTRnMemGfi+BBUCdPI7yMnV4t1QxSYUo0iit9cQrjkmX3ndMuP+OiwU5MaZPj+Z5QIGQ==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@bfchain/util-exception-logger@5.0.0': - resolution: {integrity: sha512-uFgnbqn1sPMTd2FvIrlSTsZEkvwdk5SL13lL+PuwiUzKfhXDnEp6JL8AWBbL56WX/RZiFfcynpuXiCg5N51etA==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-exception@5.0.0': - resolution: {integrity: sha512-o6P3RMH5v6Pg7j5EnDmXRjd9z8wymH4axfbKcpdDeXSZWMeSTvLyPXOL349asOmD0BV7MSaTGSNFydBFj3BmjQ==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-extends-array@5.0.0': - resolution: {integrity: sha512-TzFTrDxReWMeup7BS5kwESW3tRa7heGcg0rT1CwkNwtrkda/V29Fwa/SZ8Hkz3BsuFuhDpOukDCcm/Gw1K/5Ug==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-extends-async-iterator@5.0.0': - resolution: {integrity: sha512-C+JzJOWqPceV5pcdzpvhF5FLg88WjjIuXG6eHnRPCpbumzBsqtnGUUmpk7Rx37SBxo0can1URA2gGsEJghBDNg==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bfchain/util-extends-error@5.0.0': - resolution: {integrity: sha512-MxKGcfBhWqwc2b/hw+B6I5QGOa/aJXUY8VkKVZWv0c5kr6caBFhUyCyCYARpzUWUAD+iOVWDQjwwP5FsDJu1qA==} - engines: {node: '>=14.19.0'} + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.0': + resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.26.10': + resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@base-ui/react@1.0.0': + resolution: {integrity: sha512-4USBWz++DUSLTuIYpbYkSgy1F9ZmNG9S/lXvlUN6qMK0P0RlW+6eQmDUB4DgZ7HVvtXl4pvi4z5J2fv6Z3+9hg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@base-ui/utils@0.2.3': + resolution: {integrity: sha512-/CguQ2PDaOzeVOkllQR8nocJ0FFIDqsWIcURsVmm53QGo8NhFNpePjNlyPIB41luxfOqnG7PU0xicMEw3ls7XQ==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@bfchain/util-aborter@5.0.0': + resolution: {integrity: sha512-w1F1aWAKwNx/hbDbkItJ6Kfov+KS5/yCHyGVeuPq6bq6fh4cn7TOBQxLTLDMfdYXwU+Ams8vkptPhxk6gPe66w==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-buffer@5.0.0': + resolution: {integrity: sha512-XfsEPIyZx/cHXdODiy5vbb3BuLelRAlISv0OoTWfpr1QY2KhmVhvBgX2pFvweX3JzOysf3mktw+3iSFFVskg1g==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-decorator@5.0.0': + resolution: {integrity: sha512-6vdVEibvxoiulNF995SW9WYmKtDcL3p6nZz2EpCK2J3Bhd8rn3qpbi/2V8rIlL0yL4YmEFHnkD2YNjpJdUnKXQ==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-deepcopy@5.0.0': + resolution: {integrity: sha512-/XYBG4nndPk+Bszemgi8As0bir6ZNs15tNK0jcGjQIxymxPSpzeCGAFG1a5/wgBSEkVjEEcUSe8TSgovShrNSw==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-deepmix@5.0.0': + resolution: {integrity: sha512-/53TT+rmOKGnAhEM9FDWSOB0FCB5OpbZrT7jF/5R+HlwIHi+Eras8/ZRuuO8at/2a1O57zzcWoelbQP2w+ZUtQ==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-dep-inject@5.0.0': + resolution: {integrity: sha512-hK2+07fz2DVPB7lsFwnf+0TN3DF+eSJ3QcTwhDhNYPggx4RzGpzmxlJ2BJlnHdCRLTo6bJG1KmxIgdtyBbDEGw==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-encoding-binary@5.0.0': + resolution: {integrity: sha512-i2rfq3EGUvUj8/DYAso1A9sCRuh8Xv253Uk9SoOLX1XLrW+rVwG8PW3qNMJCEMW/m8cSKCkVlboK9Nyy64WCZw==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-encoding-hex@5.0.0': + resolution: {integrity: sha512-6bkJ61N2jLZOfbASmb4TQe9+U2APJRFMxOJJKwQVtXiMQybUkw/2euY2o4VKXsXWvacYdZhKTQgcRUO85i+tiA==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-encoding-utf8@5.0.0': + resolution: {integrity: sha512-3HggkUnTr2QWItSUXR/DZHFJ6+wI7VpCcBDFkVuz5fhidwv3p/lNA26j4vnW/pANSm20y6IaRWbfbJb1j7I1BQ==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-env@5.0.0': + resolution: {integrity: sha512-U9S2J/F3DEp+U0T4wvQNFHVHtYAA1LyNqrtIONDi5sXIKQnAoVQ5M1Vr8HweEEERA07H9jt5Vi4ek4wOxDBn6w==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-event-base@5.0.0': + resolution: {integrity: sha512-cG4kqdhzwYccrYaLcr6HDBISgFBqXwiCEujvI5TcP4qSsd7XM1G31KZtMQn2ZG4ukeT6CDNNPqJNj34RKgZT8g==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-event-map-emitter@5.0.0': + resolution: {integrity: sha512-0zdmvv+R1oJR/eiHK2K+aLvEm65me9qsuQC/yKSLcno+KQ3XHIjxz3NfvVYI8AGneruHh0FFuhf+uIYSG1dE2w==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-event-quene-emitter@5.0.0': + resolution: {integrity: sha512-MK5d59Wksg28dbDmz4n4C5St9VgI4vza1iOaVUeYPtk+aYcqqtwmzleyMavMlNC+vYgPVLJGb+OVn2iFH7MJjw==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-event@5.0.0': + resolution: {integrity: sha512-XEg2ilqy1osvU6ZMqIHmJNEHG9knk7zHedDDmGnbeRGbe7pI7Lt9uOeg1d1C20yHFHCMqlx3weXZsNIda8dXQw==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-exception-error-code@5.0.0': + resolution: {integrity: sha512-B+9Njc3s0GQyQL4YLdZtciXWoPiBKumzdJCFyaz+98bKDG2UuDdwsxQhNJwbYrQp+01wtYqsy+2Pa4lJuK+eZg==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-exception-generator@5.0.0': + resolution: {integrity: sha512-UWf07tbEW1Gg18DABEiTRnMemGfi+BBUCdPI7yMnV4t1QxSYUo0iit9cQrjkmX3ndMuP+OiwU5MaZPj+Z5QIGQ==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-exception-logger@5.0.0': + resolution: {integrity: sha512-uFgnbqn1sPMTd2FvIrlSTsZEkvwdk5SL13lL+PuwiUzKfhXDnEp6JL8AWBbL56WX/RZiFfcynpuXiCg5N51etA==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-exception@5.0.0': + resolution: {integrity: sha512-o6P3RMH5v6Pg7j5EnDmXRjd9z8wymH4axfbKcpdDeXSZWMeSTvLyPXOL349asOmD0BV7MSaTGSNFydBFj3BmjQ==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-extends-array@5.0.0': + resolution: {integrity: sha512-TzFTrDxReWMeup7BS5kwESW3tRa7heGcg0rT1CwkNwtrkda/V29Fwa/SZ8Hkz3BsuFuhDpOukDCcm/Gw1K/5Ug==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-extends-async-iterator@5.0.0': + resolution: {integrity: sha512-C+JzJOWqPceV5pcdzpvhF5FLg88WjjIuXG6eHnRPCpbumzBsqtnGUUmpk7Rx37SBxo0can1URA2gGsEJghBDNg==} + engines: {node: '>=14.19.0'} + + '@bfchain/util-extends-error@5.0.0': + resolution: {integrity: sha512-MxKGcfBhWqwc2b/hw+B6I5QGOa/aJXUY8VkKVZWv0c5kr6caBFhUyCyCYARpzUWUAD+iOVWDQjwwP5FsDJu1qA==} + engines: {node: '>=14.19.0'} '@bfchain/util-extends-function@5.0.0': resolution: {integrity: sha512-cMoZWIc8h01Qy/FRMnahz/vzM1BE3hV5bB5xFsbQYcT0IHrE3Pdvwz09uemW9YgcIqI0LP+8Rn3Dwqi8euspGw==} @@ -2663,6 +3123,9 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -4148,6 +4611,13 @@ packages: peerDependencies: '@vanilla-extract/css': ^1.0.0 + '@vitejs/plugin-legacy@7.2.1': + resolution: {integrity: sha512-CaXb/y0mlfu7jQRELEJJc2/5w2bX2m1JraARgFnvSB2yfvnCNJVWWlqAo6WjnKoepOwKx8gs0ugJThPLKCOXIg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + terser: ^5.16.0 + vite: ^7.0.0 + '@vitejs/plugin-react@5.1.2': resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4571,6 +5041,26 @@ packages: axios@1.12.2: resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + babel-plugin-polyfill-corejs2@0.4.15: + resolution: {integrity: sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.0: + resolution: {integrity: sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.6: + resolution: {integrity: sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + backo2@1.0.2: resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==} @@ -4647,9 +5137,16 @@ packages: browserify-aes@1.2.0: resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + browserslist-to-esbuild@2.1.1: + resolution: {integrity: sha512-KN+mty6C3e9AN8Z5dI1xeN15ExcRNeISoC3g7V0Kax/MMF9MSoYA2G7lkTTcVUFntiEjkpI0HNgqJC1NjdyNUw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + browserslist: '*' + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bs58@4.0.1: @@ -4853,6 +5350,9 @@ packages: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -4923,6 +5423,12 @@ packages: resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} engines: {node: '>=18'} + core-js-compat@3.48.0: + resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} + + core-js@3.48.0: + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -6287,6 +6793,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -6372,6 +6881,10 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -7226,6 +7739,13 @@ packages: reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -7238,6 +7758,17 @@ packages: regex@6.1.0: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + request@2.88.2: resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} engines: {node: '>= 6'} @@ -7497,6 +8028,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -7665,6 +8199,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + systemjs@6.15.1: + resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} + tabbable@6.3.0: resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} @@ -7682,6 +8219,11 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + terser@5.46.0: + resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + engines: {node: '>=10'} + hasBin: true + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -8015,6 +8557,22 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -8824,171 +9382,639 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 11.2.4 - '@asamuzakjp/dom-selector@6.7.6': + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.4 + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@axe-core/playwright@4.11.0(playwright-core@1.57.0)': + dependencies: + axe-core: 4.11.0 + playwright-core: 1.57.0 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.5 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': dependencies: - '@asamuzakjp/nwsapi': 2.3.9 - bidi-js: 1.0.3 - css-tree: 3.1.0 - is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.4 + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color - '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@axe-core/playwright@4.11.0(playwright-core@1.57.0)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': dependencies: - axe-core: 4.11.0 - playwright-core: 1.57.0 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/code-frame@7.27.1': + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/compat-data@7.28.5': {} + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/core@7.28.5': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/core': 7.28.5 '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/generator@7.28.5': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color - '@babel/helper-annotate-as-pure@7.27.3': + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - '@babel/helper-compilation-targets@7.27.2': + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.28.5)': dependencies: - '@babel/compat-data': 7.28.5 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 - semver: 6.3.1 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-globals@7.28.0': {} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-member-expression-to-functions@7.28.5': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.27.1': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.27.1': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': dependencies: - '@babel/types': 7.28.5 - - '@babel/helper-plugin-utils@7.27.1': {} + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-option@7.27.1': {} + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/helpers@7.28.4': + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.28.5)': dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/parser@7.28.5': + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': dependencies: @@ -9001,6 +10027,112 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.28.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) + babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.14.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.5) + core-js-compat: 3.48.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.28.5 + esutils: 2.0.3 + '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -9024,6 +10156,12 @@ snapshots: '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -9036,11 +10174,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@base-ui/react@1.0.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 @@ -10553,11 +11708,11 @@ snapshots: '@istanbuljs/schema@0.1.3': {} - '@joshwooding/vite-plugin-react-docgen-typescript@0.6.3(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.3(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: glob: 11.1.0 react-docgen-typescript: 2.4.0(typescript@5.9.3) - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) optionalDependencies: typescript: 5.9.3 @@ -10573,6 +11728,11 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': @@ -11534,10 +12694,10 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addon-docs@10.1.10(@types/react@19.2.7)(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@storybook/addon-docs@10.1.10(@types/react@19.2.7)(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3) - '@storybook/csf-plugin': 10.1.10(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@storybook/csf-plugin': 10.1.10(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@storybook/react-dom-shim': 10.1.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) react: 19.2.3 @@ -11551,41 +12711,41 @@ snapshots: - vite - webpack - '@storybook/addon-vitest@10.1.10(@vitest/browser-playwright@4.0.16)(@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16))(@vitest/runner@4.0.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vitest@4.0.16)': + '@storybook/addon-vitest@10.1.10(@vitest/browser-playwright@4.0.16)(@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16))(@vitest/runner@4.0.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vitest@4.0.16)': dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) storybook: 10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: - '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) - '@vitest/browser-playwright': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/browser-playwright': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) '@vitest/runner': 4.0.16 - vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - react - react-dom - '@storybook/builder-vite@10.1.10(esbuild@0.27.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@storybook/builder-vite@10.1.10(esbuild@0.27.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@storybook/csf-plugin': 10.1.10(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) - '@vitest/mocker': 3.2.4(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@storybook/csf-plugin': 10.1.10(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) storybook: 10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - esbuild - msw - rollup - webpack - '@storybook/csf-plugin@10.1.10(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@storybook/csf-plugin@10.1.10(esbuild@0.27.2)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: storybook: 10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.2 rollup: 4.54.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) '@storybook/global@5.0.0': {} @@ -11606,11 +12766,11 @@ snapshots: react-dom: 19.2.3(react@19.2.3) storybook: 10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@storybook/react-vite@10.1.10(esbuild@0.27.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@storybook/react-vite@10.1.10(esbuild@0.27.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@rollup/pluginutils': 5.3.0(rollup@4.54.0) - '@storybook/builder-vite': 10.1.10(esbuild@0.27.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@storybook/builder-vite': 10.1.10(esbuild@0.27.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(rollup@4.54.0)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@storybook/react': 10.1.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) empathic: 2.0.0 magic-string: 0.30.21 @@ -11620,7 +12780,7 @@ snapshots: resolve: 1.22.11 storybook: 10.1.10(@testing-library/dom@10.4.0)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tsconfig-paths: 4.2.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - esbuild - msw @@ -11773,12 +12933,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 - '@tailwindcss/vite@4.1.18(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) '@tanstack/ai@0.0.3(@alcyone-labs/zod-to-json-schema@4.0.10(zod@4.2.1))(zod@4.2.1)': dependencies: @@ -12115,7 +13275,26 @@ snapshots: dependencies: '@vanilla-extract/css': 1.18.0 - '@vitejs/plugin-react@5.1.2(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@vitejs/plugin-legacy@7.2.1(terser@5.46.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.28.5) + '@babel/preset-env': 7.29.0(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.5) + browserslist: 4.28.1 + browserslist-to-esbuild: 2.1.1(browserslist@4.28.1) + core-js: 3.48.0 + magic-string: 0.30.21 + regenerator-runtime: 0.14.1 + systemjs: 6.15.1 + terser: 5.46.0 + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-react@5.1.2(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) @@ -12123,38 +13302,38 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.53 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@24.10.4)(lightningcss@1.30.2))(vue@3.5.26(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@24.10.4)(lightningcss@1.30.2)(terser@5.46.0))(vue@3.5.26(typescript@5.9.3))': dependencies: - vite: 5.4.21(@types/node@24.10.4)(lightningcss@1.30.2) + vite: 5.4.21(@types/node@24.10.4)(lightningcss@1.30.2)(terser@5.46.0) vue: 3.5.26(typescript@5.9.3) - '@vitest/browser-playwright@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16)': + '@vitest/browser-playwright@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16)': dependencies: - '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) - '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.0.3 - vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16)': + '@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16)': dependencies: - '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@vitest/utils': 4.0.16 magic-string: 0.30.21 pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2) ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -12175,11 +13354,11 @@ snapshots: magicast: 0.5.1 obug: 2.1.1 tinyrainbow: 3.0.3 - vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@4.0.16(@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16))(vitest@4.0.16)': + '@vitest/coverage-v8@4.0.16(@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16))(vitest@4.0.16)': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.16 @@ -12192,9 +13371,9 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2) optionalDependencies: - '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) transitivePeerDependencies: - supports-color @@ -12222,23 +13401,23 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@3.2.4(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.4(@types/node@24.10.4)(typescript@5.9.3) - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) - '@vitest/mocker@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@vitest/mocker@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.16 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.4(@types/node@24.10.4)(typescript@5.9.3) - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) '@vitest/pretty-format@2.0.5': dependencies: @@ -12633,6 +13812,38 @@ snapshots: transitivePeerDependencies: - debug + babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.28.5): + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + backo2@1.0.2: {} balanced-match@1.0.2: {} @@ -12715,6 +13926,11 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + browserslist-to-esbuild@2.1.1(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + meow: 13.2.0 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.11 @@ -12928,6 +14144,8 @@ snapshots: commander@14.0.2: {} + commander@2.20.3: {} + commander@4.1.1: {} compare-versions@6.1.1: {} @@ -12987,6 +14205,12 @@ snapshots: dependencies: is-what: 5.5.0 + core-js-compat@3.48.0: + dependencies: + browserslist: 4.28.1 + + core-js@3.48.0: {} + core-util-is@1.0.2: {} core-util-is@1.0.3: {} @@ -14496,6 +15720,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.debounce@4.0.8: {} + lodash.merge@4.6.2: {} lodash@4.17.21: {} @@ -14592,6 +15818,8 @@ snapshots: media-typer@1.1.0: {} + meow@13.2.0: {} + merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} @@ -15353,6 +16581,12 @@ snapshots: reflect-metadata@0.2.2: {} + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + regenerator-runtime@0.14.1: {} regex-recursion@6.0.2: @@ -15365,6 +16599,21 @@ snapshots: dependencies: regex-utilities: 2.3.0 + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + request@2.88.2: dependencies: aws-sign2: 0.7.0 @@ -15761,6 +17010,11 @@ snapshots: source-map-js@1.2.1: {} + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + source-map@0.6.1: {} source-map@0.7.6: {} @@ -15945,6 +17199,8 @@ snapshots: symbol-tree@3.2.4: {} + systemjs@6.15.1: {} + tabbable@6.3.0: {} tagged-tag@1.0.0: {} @@ -15955,6 +17211,13 @@ snapshots: tapable@2.3.0: {} + terser@5.46.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -16225,6 +17488,17 @@ snapshots: undici-types@7.16.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + unicorn-magic@0.3.0: {} unist-util-is@6.0.1: @@ -16367,13 +17641,13 @@ snapshots: - utf-8-validate - zod - vite-node@3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -16394,7 +17668,7 @@ snapshots: magic-string: 0.30.21 vite-plugin-dynamic-import: 1.6.0 - vite-plugin-dts@4.5.4(@types/node@24.10.4)(rollup@4.54.0)(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)): + vite-plugin-dts@4.5.4(@types/node@24.10.4)(rollup@4.54.0)(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)): dependencies: '@microsoft/api-extractor': 7.55.2(@types/node@24.10.4) '@rollup/pluginutils': 5.3.0(rollup@4.54.0) @@ -16407,7 +17681,7 @@ snapshots: magic-string: 0.30.21 typescript: 5.9.3 optionalDependencies: - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - rollup @@ -16420,27 +17694,27 @@ snapshots: fast-glob: 3.3.3 magic-string: 0.30.21 - vite-plugin-mkcert@1.17.9(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)): + vite-plugin-mkcert@1.17.9(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)): dependencies: axios: 1.12.2(debug@4.4.3) debug: 4.4.3 picocolors: 1.1.1 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - typescript - vite@5.4.21(@types/node@24.10.4)(lightningcss@1.30.2): + vite@5.4.21(@types/node@24.10.4)(lightningcss@1.30.2)(terser@5.46.0): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -16449,8 +17723,9 @@ snapshots: '@types/node': 24.10.4 fsevents: 2.3.3 lightningcss: 1.30.2 + terser: 5.46.0 - vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): + vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -16463,9 +17738,10 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 + terser: 5.46.0 yaml: 2.8.2 - vitepress@1.6.4(@algolia/client-search@5.46.2)(@types/node@24.10.4)(@types/react@19.2.7)(axios@1.12.2)(lightningcss@1.30.2)(postcss@8.5.6)(qrcode@1.5.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3)(typescript@5.9.3): + vitepress@1.6.4(@algolia/client-search@5.46.2)(@types/node@24.10.4)(@types/react@19.2.7)(axios@1.12.2)(lightningcss@1.30.2)(postcss@8.5.6)(qrcode@1.5.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3)(terser@5.46.0)(typescript@5.9.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.46.2)(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3) @@ -16474,7 +17750,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@24.10.4)(lightningcss@1.30.2))(vue@3.5.26(typescript@5.9.3)) + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@24.10.4)(lightningcss@1.30.2)(terser@5.46.0))(vue@3.5.26(typescript@5.9.3)) '@vue/devtools-api': 7.7.9 '@vue/shared': 3.5.26 '@vueuse/core': 12.8.2(typescript@5.9.3) @@ -16483,7 +17759,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 2.5.0 - vite: 5.4.21(@types/node@24.10.4)(lightningcss@1.30.2) + vite: 5.4.21(@types/node@24.10.4)(lightningcss@1.30.2)(terser@5.46.0) vue: 3.5.26(typescript@5.9.3) optionalDependencies: postcss: 8.5.6 @@ -16514,11 +17790,11 @@ snapshots: - typescript - universal-cookie - vitest@3.2.4(@types/node@24.10.4)(@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16))(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2): + vitest@3.2.4(@types/node@24.10.4)(@vitest/browser@4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16))(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -16536,12 +17812,12 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.4 - '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/browser': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) jsdom: 27.3.0 transitivePeerDependencies: - jiti @@ -16557,10 +17833,10 @@ snapshots: - tsx - yaml - vitest@4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2): + vitest@4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.16 - '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.16 '@vitest/runner': 4.0.16 '@vitest/snapshot': 4.0.16 @@ -16577,11 +17853,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.4 - '@vitest/browser-playwright': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/browser-playwright': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) jsdom: 26.1.0 transitivePeerDependencies: - jiti @@ -16596,10 +17872,10 @@ snapshots: - tsx - yaml - vitest@4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2): + vitest@4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.16 - '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.16 '@vitest/runner': 4.0.16 '@vitest/snapshot': 4.0.16 @@ -16616,11 +17892,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.4 - '@vitest/browser-playwright': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/browser-playwright': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2))(vitest@4.0.16) jsdom: 27.3.0 transitivePeerDependencies: - jiti diff --git a/scripts/css-compat-features.ts b/scripts/css-compat-features.ts new file mode 100644 index 000000000..0112de08d --- /dev/null +++ b/scripts/css-compat-features.ts @@ -0,0 +1,120 @@ +export type CssCompatFeatureId = + | 'field-sizing' + | 'scroll-driven-animations' + | 'css-scrollbar-standard' + | 'color-mix' + | 'container-queries' + | 'property-at-rule' + | 'backdrop-filter' + | 'dynamic-viewport-units' + | 'has-selector' + +export type CssCompatSupport = 'unsupported' | 'partial' | 'supported' +export type CssCompatRisk = 'high' | 'medium' | 'low' + +export interface CssCompatFeatureDefinition { + id: CssCompatFeatureId + api: string + support: CssCompatSupport + risk: CssCompatRisk + fallback: string + patterns: readonly RegExp[] +} + +export interface CssCompatDetectedFeature { + feature: CssCompatFeatureDefinition + matchCount: number +} + +function countPatternMatches(code: string, pattern: RegExp): number { + const normalized = pattern.global ? pattern : new RegExp(pattern.source, `${pattern.flags}g`) + let count = 0 + let match = normalized.exec(code) + while (match) { + count += 1 + match = normalized.exec(code) + } + return count +} + +export const CSS_COMPAT_FEATURES: readonly CssCompatFeatureDefinition[] = [ + { + id: 'field-sizing', + api: 'field-sizing', + support: 'unsupported', + risk: 'high', + fallback: '为输入组件提供 rows/min-height 或 JS autosize,避免依赖 field-sizing。', + patterns: [/\bfield-sizing\s*:/g], + }, + { + id: 'scroll-driven-animations', + api: 'animation-timeline / scroll-timeline / view()', + support: 'unsupported', + risk: 'high', + fallback: '必须保留无滚动驱动动画时的静态样式,并用 @supports 做增强。', + patterns: [/\banimation-timeline\s*:/g, /\bscroll-timeline(?:-name|-axis)?\s*:/g, /@supports\s*\(\s*animation-timeline\s*:/g], + }, + { + id: 'css-scrollbar-standard', + api: 'scrollbar-width', + support: 'partial', + risk: 'medium', + fallback: '保留 ::-webkit-scrollbar 回退样式,不依赖单一标准滚动条属性。', + patterns: [/\bscrollbar-width\s*:/g], + }, + { + id: 'color-mix', + api: 'color-mix()', + support: 'supported', + risk: 'low', + fallback: '关键颜色需保留普通色值兜底,避免表达式解析失败导致不可读。', + patterns: [/\bcolor-mix\s*\(/g], + }, + { + id: 'container-queries', + api: '@container / container-type / container-name', + support: 'supported', + risk: 'low', + fallback: '关键布局在不支持容器查询时需有基础块级布局兜底。', + patterns: [/@container\b/g, /\bcontainer-type\s*:/g, /\bcontainer-name\s*:/g], + }, + { + id: 'property-at-rule', + api: '@property', + support: 'supported', + risk: 'low', + fallback: '保持未注册 CSS 变量也可用的默认值路径。', + patterns: [/@property\s+--/g], + }, + { + id: 'backdrop-filter', + api: 'backdrop-filter', + support: 'supported', + risk: 'medium', + fallback: '关键可读性不能依赖模糊效果,需有纯色/透明度兜底。', + patterns: [/\bbackdrop-filter\s*:/g, /\b-webkit-backdrop-filter\s*:/g], + }, + { + id: 'dynamic-viewport-units', + api: 'dvh / svh / lvh', + support: 'supported', + risk: 'low', + fallback: '保留 vh/min-height 兜底以降低宿主 WebView 差异影响。', + patterns: [/\b(?:\d*\.?\d+)?(?:dvh|svh|lvh)\b/g], + }, + { + id: 'has-selector', + api: ':has()', + support: 'supported', + risk: 'low', + fallback: '不要让核心交互仅依赖 :has 选择器触发。', + patterns: [/:has\(/g], + }, +] as const + +export function detectCssCompatFeaturesInCode(code: string): CssCompatDetectedFeature[] { + return CSS_COMPAT_FEATURES.map((feature) => { + const matchCount = feature.patterns.reduce((sum, pattern) => sum + countPatternMatches(code, pattern), 0) + return { feature, matchCount } + }).filter((item) => item.matchCount > 0) +} diff --git a/scripts/mobile-compat-features.ts b/scripts/mobile-compat-features.ts new file mode 100644 index 000000000..41aac32ad --- /dev/null +++ b/scripts/mobile-compat-features.ts @@ -0,0 +1,262 @@ +import { readdirSync, readFileSync, statSync } from 'node:fs' +import { join, relative } from 'node:path' + +export type MobileCompatFeatureId = + | 'promise.withResolvers' + | 'object.groupBy' + | 'map.groupBy' + | 'array.fromAsync' + | 'url.canParse' + | 'regexp.escape' + | 'iterator.from' + | 'set.union' + | 'set.intersection' + | 'set.difference' + | 'set.symmetricDifference' + | 'set.isSubsetOf' + | 'set.isSupersetOf' + | 'set.isDisjointFrom' + | 'atomics.waitAsync' + +export interface MobileCompatFeatureDefinition { + id: MobileCompatFeatureId + api: string + polyfillable: boolean + modernPolyfills: readonly string[] + detector: (code: string) => boolean +} + +const SET_METHODS = [ + 'union', + 'intersection', + 'difference', + 'symmetricDifference', + 'isSubsetOf', + 'isSupersetOf', + 'isDisjointFrom', +] as const + +function createSimpleDetector(pattern: RegExp): (code: string) => boolean { + return (code: string) => pattern.test(code) +} + +function collectDeclaredSetVariables(code: string): string[] { + const names: string[] = [] + const reg = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*new\s+Set\b/g + let match = reg.exec(code) + while (match) { + names.push(match[1]) + match = reg.exec(code) + } + return names +} + +function hasSetMethodUsage(code: string, method: (typeof SET_METHODS)[number]): boolean { + if (new RegExp(`\\bSet\\.prototype\\.${method}\\b`).test(code)) { + return true + } + + if (new RegExp(`\\bnew\\s+Set\\s*\\([^)]*\\)\\s*\\.${method}\\s*\\(`).test(code)) { + return true + } + + const setVars = collectDeclaredSetVariables(code) + if (setVars.length === 0) { + return false + } + + const escaped = setVars.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) + return new RegExp(`\\b(?:${escaped.join('|')})\\.${method}\\s*\\(`).test(code) +} + +export const MOBILE_COMPAT_FEATURES: readonly MobileCompatFeatureDefinition[] = [ + { + id: 'promise.withResolvers', + api: 'Promise.withResolvers', + polyfillable: true, + modernPolyfills: ['es.promise.with-resolvers'], + detector: createSimpleDetector(/\bPromise\.withResolvers\b/), + }, + { + id: 'object.groupBy', + api: 'Object.groupBy', + polyfillable: true, + modernPolyfills: ['es.object.group-by'], + detector: createSimpleDetector(/\bObject\.groupBy\b/), + }, + { + id: 'map.groupBy', + api: 'Map.groupBy', + polyfillable: true, + modernPolyfills: ['es.map.group-by'], + detector: createSimpleDetector(/\bMap\.groupBy\b/), + }, + { + id: 'array.fromAsync', + api: 'Array.fromAsync', + polyfillable: true, + modernPolyfills: ['es.array.from-async'], + detector: createSimpleDetector(/\bArray\.fromAsync\b/), + }, + { + id: 'url.canParse', + api: 'URL.canParse', + polyfillable: true, + modernPolyfills: ['web.url.can-parse'], + detector: createSimpleDetector(/\bURL\.canParse\b/), + }, + { + id: 'regexp.escape', + api: 'RegExp.escape', + polyfillable: true, + modernPolyfills: ['es.regexp.escape'], + detector: createSimpleDetector(/\bRegExp\.escape\b/), + }, + { + id: 'iterator.from', + api: 'Iterator.from', + polyfillable: true, + modernPolyfills: ['es.iterator.from'], + detector: createSimpleDetector(/\bIterator\.from\b/), + }, + { + id: 'set.union', + api: 'Set.prototype.union', + polyfillable: true, + modernPolyfills: ['es.set.union.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'union'), + }, + { + id: 'set.intersection', + api: 'Set.prototype.intersection', + polyfillable: true, + modernPolyfills: ['es.set.intersection.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'intersection'), + }, + { + id: 'set.difference', + api: 'Set.prototype.difference', + polyfillable: true, + modernPolyfills: ['es.set.difference.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'difference'), + }, + { + id: 'set.symmetricDifference', + api: 'Set.prototype.symmetricDifference', + polyfillable: true, + modernPolyfills: ['es.set.symmetric-difference.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'symmetricDifference'), + }, + { + id: 'set.isSubsetOf', + api: 'Set.prototype.isSubsetOf', + polyfillable: true, + modernPolyfills: ['es.set.is-subset-of.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'isSubsetOf'), + }, + { + id: 'set.isSupersetOf', + api: 'Set.prototype.isSupersetOf', + polyfillable: true, + modernPolyfills: ['es.set.is-superset-of.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'isSupersetOf'), + }, + { + id: 'set.isDisjointFrom', + api: 'Set.prototype.isDisjointFrom', + polyfillable: true, + modernPolyfills: ['es.set.is-disjoint-from.v2'], + detector: (code: string) => hasSetMethodUsage(code, 'isDisjointFrom'), + }, + { + id: 'atomics.waitAsync', + api: 'Atomics.waitAsync', + polyfillable: false, + modernPolyfills: [], + detector: createSimpleDetector(/\bAtomics\.waitAsync\b/), + }, +] as const + +export function detectMobileCompatFeaturesInCode(code: string): MobileCompatFeatureDefinition[] { + return MOBILE_COMPAT_FEATURES.filter((feature) => feature.detector(code)) +} + +export function collectModernPolyfills(features: readonly MobileCompatFeatureDefinition[]): string[] { + const set = new Set() + for (const feature of features) { + if (!feature.polyfillable) { + continue + } + for (const item of feature.modernPolyfills) { + set.add(item) + } + } + return [...set].sort() +} + +export function scanMobileCompatFeaturesInFiles( + rootDir: string, + includeExtensions: readonly string[] = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts'], +): Map> { + const hits = new Map>() + const ignoredDirs = new Set([ + '.git', + '.turbo', + '.tmp', + 'coverage', + 'dist', + 'dist-dweb', + 'dist-web', + 'node_modules', + 'playwright-report', + 'storybook-static', + 'test', + 'tests', + 'test-results', + ]) + const ignoredFilePatterns = [/\.test\./, /\.spec\./, /\.stories\./] + + function addHit(featureId: MobileCompatFeatureId, filePath: string): void { + let set = hits.get(featureId) + if (!set) { + set = new Set() + hits.set(featureId, set) + } + set.add(relative(process.cwd(), filePath)) + } + + function walk(dir: string): void { + const entries = readdirSync(dir, { withFileTypes: true }) + for (const entry of entries) { + const fullPath = join(dir, entry.name) + if (entry.isDirectory()) { + if (ignoredDirs.has(entry.name)) { + continue + } + walk(fullPath) + continue + } + + const matchedExt = includeExtensions.some((ext) => entry.name.endsWith(ext)) + if (!matchedExt) { + continue + } + if (ignoredFilePatterns.some((pattern) => pattern.test(entry.name))) { + continue + } + + const source = readFileSync(fullPath, 'utf-8') + const features = detectMobileCompatFeaturesInCode(source) + for (const feature of features) { + addHit(feature.id, fullPath) + } + } + } + + const stats = statSync(rootDir) + if (stats.isDirectory()) { + walk(rootDir) + } + + return hits +} diff --git a/scripts/vite-plugin-css-compat-report.ts b/scripts/vite-plugin-css-compat-report.ts new file mode 100644 index 000000000..b1357da16 --- /dev/null +++ b/scripts/vite-plugin-css-compat-report.ts @@ -0,0 +1,154 @@ +import { mkdirSync, writeFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import type { OutputAsset, OutputBundle, Plugin, ResolvedConfig } from 'vite' +import { + CSS_COMPAT_FEATURES, + detectCssCompatFeaturesInCode, + type CssCompatFeatureId, + type CssCompatRisk, + type CssCompatSupport, +} from './css-compat-features' + +interface CssCompatReportPluginOptions { + baseline?: string +} + +interface CssCompatReportItem { + feature: CssCompatFeatureId + api: string + support: CssCompatSupport + risk: CssCompatRisk + assets: string[] + matchCount: number + fallback: string +} + +interface CssCompatReport { + baseline: string + detectedFeatures: CssCompatReportItem[] + suggestions: string[] + generatedAt: string +} + +const DEFAULT_BASELINE = 'chrome>=114' + +function toAssetSource(source: OutputAsset['source']): string { + if (typeof source === 'string') { + return source + } + return Buffer.from(source).toString('utf-8') +} + +function addFeatureHit( + featureHits: Map; matchCount: number }>, + featureId: CssCompatFeatureId, + assetName: string, + count: number, +): void { + let hit = featureHits.get(featureId) + if (!hit) { + hit = { + assets: new Set(), + matchCount: 0, + } + featureHits.set(featureId, hit) + } + hit.assets.add(assetName) + hit.matchCount += count +} + +export function cssCompatReportPlugin(options: CssCompatReportPluginOptions = {}): Plugin { + const baseline = options.baseline ?? DEFAULT_BASELINE + + let config: ResolvedConfig | undefined + let outDir = 'dist' + const featureHits = new Map; matchCount: number }>() + + return { + name: 'vite-plugin-css-compat-report', + apply: 'build', + + configResolved(resolvedConfig) { + config = resolvedConfig + outDir = resolvedConfig.build.outDir + }, + + generateBundle(_outputOptions, bundle: OutputBundle) { + for (const [fileName, output] of Object.entries(bundle)) { + if (output.type !== 'asset' || !fileName.endsWith('.css')) { + continue + } + + const cssCode = toAssetSource(output.source) + const detected = detectCssCompatFeaturesInCode(cssCode) + for (const item of detected) { + addFeatureHit(featureHits, item.feature.id, fileName, item.matchCount) + } + } + }, + + writeBundle(outputOptions) { + outDir = outputOptions.dir ?? outDir + }, + + closeBundle() { + const detectedFeatures = CSS_COMPAT_FEATURES.filter((feature) => featureHits.has(feature.id)).map((feature) => { + const hit = featureHits.get(feature.id) + return { + feature: feature.id, + api: feature.api, + support: feature.support, + risk: feature.risk, + assets: [...(hit?.assets ?? [])].sort(), + matchCount: hit?.matchCount ?? 0, + fallback: feature.fallback, + } + }) + + const suggestions: string[] = [] + for (const item of detectedFeatures) { + if (item.support === 'unsupported') { + suggestions.push(`[fallback-required] ${item.api}: ${item.fallback}`) + continue + } + if (item.support === 'partial') { + suggestions.push(`[fallback-recommended] ${item.api}: ${item.fallback}`) + } + } + + const report: CssCompatReport = { + baseline, + detectedFeatures, + suggestions, + generatedAt: new Date().toISOString(), + } + + const reportPath = resolve(config?.root ?? process.cwd(), outDir, 'css-compat-report.json') + mkdirSync(dirname(reportPath), { recursive: true }) + writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, 'utf-8') + + console.log(`\n[css-compat] Baseline: ${baseline}`) + if (detectedFeatures.length === 0) { + console.log('[css-compat] Feature hits: none') + } else { + console.log('[css-compat] Feature hits:') + for (const item of detectedFeatures) { + console.log(` - ${item.api}: ${item.assets.join(', ')} (${item.matchCount})`) + } + } + + if (suggestions.length === 0) { + console.log('[css-compat] Suggestions: none') + } else { + console.log('[css-compat] Suggestions:') + for (const suggestion of suggestions) { + console.log(` - ${suggestion}`) + } + } + + console.log(`[css-compat] Report generated: ${reportPath}\n`) + }, + } +} + +export default cssCompatReportPlugin diff --git a/scripts/vite-plugin-mobile-compat-report.ts b/scripts/vite-plugin-mobile-compat-report.ts new file mode 100644 index 000000000..f31a585e6 --- /dev/null +++ b/scripts/vite-plugin-mobile-compat-report.ts @@ -0,0 +1,168 @@ +import { mkdirSync, writeFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import type { OutputBundle, OutputChunk, Plugin, ResolvedConfig } from 'vite' +import { + MOBILE_COMPAT_FEATURES, + detectMobileCompatFeaturesInCode, + type MobileCompatFeatureId, +} from './mobile-compat-features' + +interface MobileCompatReportPluginOptions { + baseline?: string + configuredPolyfillsMode?: 'auto' | 'manual' + configuredPolyfills?: readonly string[] +} + +interface MobileCompatReportItem { + feature: MobileCompatFeatureId + api: string + chunks: string[] + polyfillable: boolean + modernPolyfills: string[] +} + +interface MobileCompatReport { + baseline: string + detectedFeatures: MobileCompatReportItem[] + configuredPolyfills: string[] + suggestions: string[] + generatedAt: string +} + +const DEFAULT_BASELINE = 'chrome>=114' + +function addHit(map: Map>, featureId: MobileCompatFeatureId, chunk: string): void { + let bucket = map.get(featureId) + if (!bucket) { + bucket = new Set() + map.set(featureId, bucket) + } + bucket.add(chunk) +} + +function isGeneratedPolyfillChunk(fileName: string): boolean { + return /(^|\/)polyfills?-.*\.js$/i.test(fileName) +} + +export function mobileCompatReportPlugin(options: MobileCompatReportPluginOptions = {}): Plugin { + const baseline = options.baseline ?? DEFAULT_BASELINE + const configuredPolyfillsMode = options.configuredPolyfillsMode ?? 'manual' + const configuredPolyfills = [...new Set(options.configuredPolyfills ?? [])].sort() + + let config: ResolvedConfig | undefined + let outDir = 'dist' + const chunkFeatureHits = new Map>() + + return { + name: 'vite-plugin-mobile-compat-report', + apply: 'build', + + configResolved(resolvedConfig) { + config = resolvedConfig + outDir = resolvedConfig.build.outDir + }, + + generateBundle(_outputOptions, bundle: OutputBundle) { + for (const [fileName, output] of Object.entries(bundle)) { + if (output.type !== 'chunk') { + continue + } + if (isGeneratedPolyfillChunk(fileName)) { + continue + } + const chunk = output as OutputChunk + const features = detectMobileCompatFeaturesInCode(chunk.code) + for (const feature of features) { + addHit(chunkFeatureHits, feature.id, fileName) + } + } + }, + + writeBundle(outputOptions) { + outDir = outputOptions.dir ?? outDir + }, + + closeBundle() { + const detectedFeatures = MOBILE_COMPAT_FEATURES.filter((feature) => chunkFeatureHits.has(feature.id)).map( + (feature) => ({ + feature: feature.id, + api: feature.api, + chunks: [...(chunkFeatureHits.get(feature.id) ?? [])].sort(), + polyfillable: feature.polyfillable, + modernPolyfills: [...feature.modernPolyfills], + }), + ) + + const suggestions: string[] = [] + const mappedPolyfills = new Set() + + for (const feature of detectedFeatures) { + if (!feature.polyfillable) { + suggestions.push( + `[manual-fallback] ${feature.api} is detected but cannot be polyfilled reliably (runtime guard required)`, + ) + continue + } + + for (const moduleName of feature.modernPolyfills) { + mappedPolyfills.add(moduleName) + } + + if ( + configuredPolyfillsMode === 'manual' && + (configuredPolyfills.length === 0 || + feature.modernPolyfills.every((moduleName) => !configuredPolyfills.includes(moduleName))) + ) { + suggestions.push( + `[missing-config] ${feature.api} detected but matching modern polyfill is not configured in plugin-legacy`, + ) + } + } + + if (configuredPolyfillsMode === 'manual') { + for (const moduleName of configuredPolyfills) { + if (!mappedPolyfills.has(moduleName)) { + suggestions.push(`[redundant-config] ${moduleName} is configured but not detected in current bundle`) + } + } + } + + const reportConfiguredPolyfills = + configuredPolyfillsMode === 'auto' ? [''] : configuredPolyfills + + const report: MobileCompatReport = { + baseline, + detectedFeatures, + configuredPolyfills: reportConfiguredPolyfills, + suggestions, + generatedAt: new Date().toISOString(), + } + + const reportPath = resolve(config?.root ?? process.cwd(), outDir, 'mobile-compat-report.json') + mkdirSync(dirname(reportPath), { recursive: true }) + writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, 'utf-8') + + console.log(`\n[mobile-compat] Baseline: ${baseline}`) + if (detectedFeatures.length === 0) { + console.log('[mobile-compat] Feature hits: none') + } else { + console.log('[mobile-compat] Feature hits:') + for (const item of detectedFeatures) { + console.log(` - ${item.api}: ${item.chunks.join(', ')}`) + } + } + + if (suggestions.length === 0) { + console.log('[mobile-compat] Suggestions: none') + } else { + console.log('[mobile-compat] Suggestions:') + for (const suggestion of suggestions) { + console.warn(` - ${suggestion}`) + } + } + console.log(`[mobile-compat] Report generated: ${reportPath}\n`) + }, + } +} + +export default mobileCompatReportPlugin diff --git a/src/hooks/use-send.submit-message.test.ts b/src/hooks/use-send.submit-message.test.ts new file mode 100644 index 000000000..70c039f03 --- /dev/null +++ b/src/hooks/use-send.submit-message.test.ts @@ -0,0 +1,83 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { renderHook, act, waitFor } from '@testing-library/react'; +import { Amount } from '@/types/amount'; +import type { AssetInfo } from '@/types/asset'; +import type { ChainConfig } from '@/services/chain-config'; + +const { mockSubmitWeb3Transfer, mockFetchWeb3Fee } = vi.hoisted(() => ({ + mockSubmitWeb3Transfer: vi.fn(), + mockFetchWeb3Fee: vi.fn(), +})); + +vi.mock('./use-send.web3', async () => { + const actual = await vi.importActual('./use-send.web3'); + return { + ...actual, + fetchWeb3Fee: mockFetchWeb3Fee, + submitWeb3Transfer: mockSubmitWeb3Transfer, + validateWeb3Address: vi.fn(() => null), + }; +}); + +import { useSend } from './use-send'; + +const mockAsset: AssetInfo = { + assetType: 'TRX', + name: 'Tron', + amount: Amount.fromRaw('100000000', 6, 'TRX'), + decimals: 6, +}; + +const mockChainConfig = { + id: 'tron', + name: 'Tron', + symbol: 'TRX', + decimals: 6, + chainKind: 'tron', +} as ChainConfig; + +describe('useSend submit message propagation', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockFetchWeb3Fee.mockResolvedValue({ + amount: Amount.fromRaw('1000', 6, 'TRX'), + symbol: 'TRX', + }); + }); + + it('returns and stores detailed submit error message for web3 transfer', async () => { + const detailedMessage = 'Broadcast failed: SIGERROR'; + mockSubmitWeb3Transfer.mockResolvedValue({ + status: 'error', + message: detailedMessage, + }); + + const { result } = renderHook(() => + useSend({ + initialAsset: mockAsset, + useMock: false, + walletId: 'wallet-1', + fromAddress: 'TFromAddress', + chainConfig: mockChainConfig, + }), + ); + + act(() => { + result.current.setToAddress('TToAddress'); + result.current.setAmount(Amount.fromRaw('100000', 6, 'TRX')); + }); + + let submitResult: Awaited>; + await act(async () => { + submitResult = await result.current.submit('wallet-lock'); + }); + + expect(submitResult).toEqual({ + status: 'error', + message: detailedMessage, + }); + await waitFor(() => { + expect(result.current.state.errorMessage).toBe(detailedMessage); + }); + }); +}); diff --git a/src/hooks/use-send.test.ts b/src/hooks/use-send.test.ts index 673360369..64ddb82e7 100644 --- a/src/hooks/use-send.test.ts +++ b/src/hooks/use-send.test.ts @@ -93,25 +93,69 @@ describe('useSend', () => { }) describe('setAsset', () => { - it('updates asset and estimates fee', async () => { + it('updates asset without estimating fee before form completion', async () => { const { result } = renderHook(() => useSend()) act(() => { result.current.setAsset(mockAsset) }) expect(result.current.state.asset).toEqual(mockAsset) + expect(result.current.state.feeLoading).toBe(false) + expect(result.current.state.feeAmount).toBeNull() + expect(result.current.state.feeSymbol).toBe('') + }) + + it('estimates fee after form becomes complete with debounce', async () => { + const { result } = renderHook(() => useSend({ initialAsset: mockAsset })) + + act(() => { + result.current.setToAddress('0x1234567890abcdef1234567890abcdef12345678') + result.current.setAmount(Amount.fromFormatted('0.5', 18, 'ETH')) + }) + + act(() => { + vi.advanceTimersByTime(299) + }) + expect(result.current.state.feeAmount).toBeNull() expect(result.current.state.feeLoading).toBe(true) - // Wait for fee estimation act(() => { - vi.advanceTimersByTime(300) + vi.advanceTimersByTime(1) }) expect(result.current.state.feeLoading).toBe(false) - expect(result.current.state.feeAmount).not.toBeNull() expect(result.current.state.feeAmount?.toFormatted()).toBe('0.002') expect(result.current.state.feeSymbol).toBe('ETH') }) + + it('re-estimates fee after amount change with debounce', async () => { + const { result } = renderHook(() => useSend({ initialAsset: mockAsset })) + + act(() => { + result.current.setToAddress('0x1234567890abcdef1234567890abcdef12345678') + result.current.setAmount(Amount.fromFormatted('0.5', 18, 'ETH')) + }) + act(() => { + vi.advanceTimersByTime(300) + }) + expect(result.current.state.feeAmount?.toFormatted()).toBe('0.002') + + act(() => { + result.current.setAmount(Amount.fromFormatted('0.6', 18, 'ETH')) + }) + expect(result.current.state.feeLoading).toBe(true) + + act(() => { + vi.advanceTimersByTime(299) + }) + expect(result.current.state.feeAmount).toBeNull() + + act(() => { + vi.advanceTimersByTime(1) + }) + expect(result.current.state.feeLoading).toBe(false) + expect(result.current.state.feeAmount?.toFormatted()).toBe('0.002') + }) }) describe('canProceed', () => { diff --git a/src/hooks/use-send.ts b/src/hooks/use-send.ts index d836d455b..0ecee4fa4 100644 --- a/src/hooks/use-send.ts +++ b/src/hooks/use-send.ts @@ -22,7 +22,8 @@ export function useSend(options: UseSendOptions = {}): UseSendReturn { asset: initialAsset ?? null, }); - const feeInitKeyRef = useRef(null); + const feeEstimateDebounceRef = useRef | null>(null); + const feeEstimateSeqRef = useRef(0); const isBioforestChain = chainConfig?.chainKind === 'bioforest'; const isWeb3Chain = @@ -81,33 +82,83 @@ export function useSend(options: UseSendOptions = {}): UseSendReturn { setState((prev) => ({ ...prev, asset, - feeLoading: true, + feeAmount: null, + feeMinAmount: null, + feeSymbol: '', + feeLoading: false, })); + }, + [], + ); - const shouldUseMock = useMock || (!isBioforestChain && !isWeb3Chain) || !chainConfig || !fromAddress; + useEffect(() => { + if (feeEstimateDebounceRef.current) { + clearTimeout(feeEstimateDebounceRef.current); + feeEstimateDebounceRef.current = null; + } - if (shouldUseMock) { - // Mock fee estimation delay - setTimeout(() => { - const fee = MOCK_FEES[asset.assetType] ?? { amount: '0.001', symbol: asset.assetType }; - const feeAmount = Amount.fromFormatted(fee.amount, asset.decimals, fee.symbol); - setState((prev) => ({ - ...prev, - feeAmount: feeAmount, - feeMinAmount: feeAmount, - feeSymbol: fee.symbol, - feeLoading: false, - })); - }, 300); - return; - } + if (!state.asset) { + return; + } + + const shouldUseMock = useMock || (!isBioforestChain && !isWeb3Chain) || !chainConfig || !fromAddress; + const toAddress = state.toAddress.trim(); + const hasValidAmount = state.amount?.isPositive() === true; + const isTronSelfTransfer = + chainConfig?.chainKind === 'tron' && + fromAddress !== undefined && + fromAddress.trim().length > 0 && + fromAddress.trim() === toAddress; + const canEstimateFee = toAddress.length > 0 && hasValidAmount && !isTronSelfTransfer; + + if (!canEstimateFee) { + setState((prev) => { + if (!prev.feeLoading && prev.feeAmount === null && prev.feeMinAmount === null && prev.feeSymbol === '') { + return prev; + } + return { + ...prev, + feeLoading: false, + feeAmount: null, + feeMinAmount: null, + feeSymbol: '', + }; + }); + return; + } + setState((prev) => ({ + ...prev, + feeLoading: true, + feeAmount: null, + feeMinAmount: null, + feeSymbol: '', + })); + + const requestSeq = ++feeEstimateSeqRef.current; + feeEstimateDebounceRef.current = setTimeout(() => { void (async () => { try { - // Use appropriate fee fetcher based on chain type - const feeEstimate = isWeb3Chain - ? await fetchWeb3Fee(chainConfig, fromAddress) - : await fetchBioforestFee(chainConfig, fromAddress); + const feeEstimate = shouldUseMock + ? (() => { + const fee = MOCK_FEES[state.asset!.assetType] ?? { amount: '0.001', symbol: state.asset!.assetType }; + return { + amount: Amount.fromFormatted(fee.amount, state.asset!.decimals, fee.symbol), + symbol: fee.symbol, + }; + })() + : isWeb3Chain && chainConfig && fromAddress + ? await fetchWeb3Fee({ + chainConfig, + fromAddress, + toAddress, + amount: state.amount ?? undefined, + }) + : await fetchBioforestFee(chainConfig!, fromAddress!); + + if (requestSeq !== feeEstimateSeqRef.current) { + return; + } setState((prev) => ({ ...prev, @@ -117,32 +168,37 @@ export function useSend(options: UseSendOptions = {}): UseSendReturn { feeLoading: false, })); } catch (error) { + if (requestSeq !== feeEstimateSeqRef.current) { + return; + } setState((prev) => ({ ...prev, + feeAmount: null, + feeMinAmount: null, + feeSymbol: '', feeLoading: false, errorMessage: error instanceof Error ? error.message : t('error:transaction.feeEstimateFailed'), })); } })(); - }, - [chainConfig, fromAddress, isBioforestChain, isWeb3Chain, useMock], - ); + }, 300); - useEffect(() => { - if (!state.asset) return; - if (state.feeLoading) return; - - const feeKey = `${chainConfig?.id ?? 'unknown'}:${fromAddress ?? ''}:${state.asset.assetType}`; - const feeKeyChanged = feeInitKeyRef.current !== feeKey; - if (feeKeyChanged) { - feeInitKeyRef.current = feeKey; - } - - if (!feeKeyChanged && state.feeAmount) return; - if (!feeKeyChanged && !state.feeAmount) return; - - setAsset(state.asset); - }, [chainConfig?.id, fromAddress, setAsset, state.asset, state.feeAmount, state.feeLoading]); + return () => { + if (feeEstimateDebounceRef.current) { + clearTimeout(feeEstimateDebounceRef.current); + feeEstimateDebounceRef.current = null; + } + }; + }, [ + chainConfig, + fromAddress, + isBioforestChain, + isWeb3Chain, + state.amount, + state.asset, + state.toAddress, + useMock, + ]); // Get current balance from external source (single source of truth) const currentBalance = useMemo(() => { @@ -291,7 +347,7 @@ export function useSend(options: UseSendOptions = {}): UseSendReturn { txHash: null, errorMessage: result.message, })); - return { status: 'error' as const }; + return { status: 'error' as const, message: result.message }; } setState((prev) => ({ diff --git a/src/hooks/use-send.web3.test.ts b/src/hooks/use-send.web3.test.ts new file mode 100644 index 000000000..8c3ccda91 --- /dev/null +++ b/src/hooks/use-send.web3.test.ts @@ -0,0 +1,154 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import type { ChainConfig } from '@/services/chain-config'; +import { Amount } from '@/types/amount'; +import { ChainErrorCodes, ChainServiceError } from '@/services/chain-adapter/types'; +import i18n from '@/i18n'; + +const { mockGetMnemonic, mockGetChainProvider } = vi.hoisted(() => ({ + mockGetMnemonic: vi.fn(), + mockGetChainProvider: vi.fn(), +})); + +vi.mock('@/services/wallet-storage', async () => { + const actual = await vi.importActual('@/services/wallet-storage'); + return { + ...actual, + walletStorageService: { + ...actual.walletStorageService, + getMnemonic: mockGetMnemonic, + }, + }; +}); + +vi.mock('@/services/chain-adapter/providers', async () => { + const actual = await vi.importActual( + '@/services/chain-adapter/providers', + ); + return { + ...actual, + getChainProvider: mockGetChainProvider, + }; +}); + +import { submitWeb3Transfer } from './use-send.web3'; + +type MockChainProvider = { + supportsFullTransaction: boolean; + buildTransaction: (intent: unknown) => Promise; + signTransaction: (unsignedTx: unknown, options: { privateKey: Uint8Array }) => Promise; + broadcastTransaction: (signedTx: unknown) => Promise; +}; + +function createChainConfig(): ChainConfig { + return { + id: 'tron', + name: 'Tron', + symbol: 'TRX', + decimals: 6, + chainKind: 'tron', + } as ChainConfig; +} + +function createMockProvider(overrides?: Partial): MockChainProvider { + const buildTransaction = vi.fn(async () => ({ data: { txID: 'mock-tx' } })); + const signTransaction = vi.fn(async () => ({ data: { txID: 'mock-tx', signature: ['sig'] } })); + const broadcastTransaction = vi.fn(async () => 'mock-hash'); + + return { + supportsFullTransaction: true, + buildTransaction, + signTransaction, + broadcastTransaction, + ...overrides, + }; +} + +describe('submitWeb3Transfer', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetMnemonic.mockResolvedValue( + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', + ); + }); + + it('maps tx build self-transfer error to localized copy', async () => { + const provider = createMockProvider({ + buildTransaction: vi.fn(async () => { + throw new ChainServiceError( + ChainErrorCodes.TX_BUILD_FAILED, + 'Failed to create Tron transaction: Cannot transfer TRX to yourself.', + { reason: 'Cannot transfer TRX to yourself.' }, + ); + }), + }); + mockGetChainProvider.mockReturnValue(provider); + + const result = await submitWeb3Transfer({ + chainConfig: createChainConfig(), + walletId: 'wallet-1', + password: 'pwd', + fromAddress: 'TFromAddress', + toAddress: 'TFromAddress', + amount: Amount.fromRaw('1000000', 6, 'TRX'), + }); + + expect(result).toEqual({ + status: 'error', + message: i18n.t('error:validation.cannotTransferToSelf'), + }); + }); + + it('returns tx build reason for non-self-transfer errors', async () => { + const reason = 'account does not exist'; + const provider = createMockProvider({ + buildTransaction: vi.fn(async () => { + throw new ChainServiceError( + ChainErrorCodes.TX_BUILD_FAILED, + `Failed to create Tron transaction: ${reason}`, + { reason }, + ); + }), + }); + mockGetChainProvider.mockReturnValue(provider); + + const result = await submitWeb3Transfer({ + chainConfig: createChainConfig(), + walletId: 'wallet-1', + password: 'pwd', + fromAddress: 'TFromAddress', + toAddress: 'TAnotherAddress', + amount: Amount.fromRaw('1000000', 6, 'TRX'), + }); + + expect(result).toEqual({ + status: 'error', + message: reason, + }); + }); + + it('uses 32-byte private key for tron signing', async () => { + const signTransaction = vi.fn(async (_unsignedTx: unknown, options: { privateKey: Uint8Array }) => { + expect(options.privateKey).toBeInstanceOf(Uint8Array); + expect(options.privateKey.length).toBe(32); + return { data: { txID: 'signed-tx', signature: ['sig'] } }; + }); + + const provider = createMockProvider({ + buildTransaction: vi.fn(async () => ({ data: { txID: 'mock-tx' } })), + signTransaction, + broadcastTransaction: vi.fn(async () => 'tx-hash'), + }); + mockGetChainProvider.mockReturnValue(provider); + + const result = await submitWeb3Transfer({ + chainConfig: createChainConfig(), + walletId: 'wallet-1', + password: 'pwd', + fromAddress: 'TFromAddress', + toAddress: 'TToAddress', + amount: Amount.fromRaw('1000000', 6, 'TRX'), + }); + + expect(result).toEqual({ status: 'ok', txHash: 'tx-hash' }); + }); +}); diff --git a/src/hooks/use-send.web3.ts b/src/hooks/use-send.web3.ts index 03472ba00..8c714fa91 100644 --- a/src/hooks/use-send.web3.ts +++ b/src/hooks/use-send.web3.ts @@ -9,29 +9,148 @@ import type { ChainConfig } from '@/services/chain-config'; import { Amount } from '@/types/amount'; import { walletStorageService, WalletStorageError, WalletStorageErrorCode } from '@/services/wallet-storage'; import { getChainProvider } from '@/services/chain-adapter/providers'; -import { mnemonicToSeedSync } from '@scure/bip39'; +import { ChainErrorCodes, ChainServiceError } from '@/services/chain-adapter/types'; +import { deriveKey } from '@/lib/crypto/derivation'; +import { hexToBytes } from '@noble/hashes/utils.js'; import i18n from '@/i18n'; const t = i18n.t.bind(i18n); +function collectErrorMessages(error: unknown): string[] { + const messages: string[] = []; + const visited = new Set(); + let current: unknown = error; + + while (current instanceof Error && !visited.has(current)) { + visited.add(current); + if (current.message) { + messages.push(current.message); + } + current = (current as Error & { cause?: unknown }).cause; + } + + return messages; +} + +function isTimeoutMessage(message: string): boolean { + return /timeout|timed out|etimedout|aborterror|aborted/i.test(message); +} + +function isBroadcastFailureMessage(message: string): boolean { + return /failed to broadcast transaction|broadcast failed|tx_broadcast_failed/i.test(message); +} + +function isSelfTransferMessage(message: string): boolean { + return /cannot transfer(?:\s+\w+)*\s+to yourself|不能转账给自己|不能给自己转账/i.test(message); +} + +function isGenericBroadcastFailureMessage(message: string): boolean { + return /^broadcast failed:?$/i.test(message.trim()) + || /^failed to broadcast transaction:?$/i.test(message.trim()); +} + +function extractBuildFailureReason(error: unknown): string | null { + if (error instanceof ChainServiceError && typeof error.details?.reason === 'string') { + return error.details.reason.trim() || null; + } + + for (const message of collectErrorMessages(error)) { + const normalized = message.trim(); + const match = normalized.match(/^(?:failed to create tron transaction|trc20 transaction build failed)\s*:\s*(.+)$/i); + if (match?.[1]) { + return match[1].trim(); + } + } + + return null; +} + +function extractBroadcastFailureReason(error: unknown): string | null { + if (error instanceof ChainServiceError && typeof error.details?.reason === 'string') { + return error.details.reason.trim() || null; + } + + for (const message of collectErrorMessages(error)) { + const normalized = message.trim(); + const match = normalized.match(/^broadcast failed:\s*(.+)$/i); + if (match?.[1]) { + return match[1].trim(); + } + if (isBroadcastFailureMessage(normalized) && !isGenericBroadcastFailureMessage(normalized)) { + return normalized; + } + } + + return null; +} + +function hasTimeoutInError(error: unknown): boolean { + return collectErrorMessages(error).some((message) => isTimeoutMessage(message)); +} + +function parseHexPrivateKey(secret: string): Uint8Array | null { + const normalized = secret.trim().replace(/^0x/i, ''); + if (!/^[0-9a-fA-F]{64}$/.test(normalized)) { + return null; + } + try { + return hexToBytes(normalized); + } catch { + return null; + } +} + +function deriveWeb3PrivateKey(secret: string, chainConfig: ChainConfig): Uint8Array { + const fallbackPrivateKey = parseHexPrivateKey(secret); + try { + if (chainConfig.chainKind === 'evm') { + return hexToBytes(deriveKey(secret, 'ethereum', 0, 0).privateKey); + } + if (chainConfig.chainKind === 'tron') { + return hexToBytes(deriveKey(secret, 'tron', 0, 0).privateKey); + } + if (chainConfig.chainKind === 'bitcoin') { + return hexToBytes(deriveKey(secret, 'bitcoin', 0, 0).privateKey); + } + } catch { + if (fallbackPrivateKey) return fallbackPrivateKey; + throw new Error(t('error:crypto.keyDerivationFailed')); + } + + if (fallbackPrivateKey) return fallbackPrivateKey; + throw new Error(t('error:chain.transferNotImplemented')); +} + export interface Web3FeeResult { amount: Amount; symbol: string; } -export async function fetchWeb3Fee(chainConfig: ChainConfig, fromAddress: string): Promise { +export interface FetchWeb3FeeParams { + chainConfig: ChainConfig; + fromAddress: string; + toAddress: string; + amount?: Amount | undefined; +} + +export async function fetchWeb3Fee({ chainConfig, fromAddress, toAddress, amount }: FetchWeb3FeeParams): Promise { const chainProvider = getChainProvider(chainConfig.id); if (!chainProvider.supportsFeeEstimate || !chainProvider.supportsBuildTransaction) { throw new Error(`Chain ${chainConfig.id} does not support fee estimation`); } + const estimateAmount = + amount && amount.isPositive() + ? amount + : Amount.fromRaw('1', chainConfig.decimals, chainConfig.symbol); + // 新流程:先构建交易,再估算手续费 const unsignedTx = await chainProvider.buildTransaction!({ type: 'transfer', from: fromAddress, - to: fromAddress, - amount: Amount.fromRaw('1', chainConfig.decimals, chainConfig.symbol), + to: toAddress, + amount: estimateAmount, }); const feeEstimate = await chainProvider.estimateFee!(unsignedTx); @@ -77,9 +196,9 @@ export async function submitWeb3Transfer({ amount, }: SubmitWeb3Params): Promise { // Get mnemonic from wallet storage - let mnemonic: string; + let secret: string; try { - mnemonic = await walletStorageService.getMnemonic(walletId, password); + secret = await walletStorageService.getMnemonic(walletId, password); } catch (error) { if (error instanceof WalletStorageError && error.code === WalletStorageErrorCode.DECRYPTION_FAILED) { return { status: 'password' }; @@ -101,8 +220,7 @@ export async function submitWeb3Transfer({ return { status: 'error', message: t('error:transaction.chainNotSupported', { chainId: chainConfig.id }) }; } - // Derive private key from mnemonic - const seed = mnemonicToSeedSync(mnemonic); + const privateKey = deriveWeb3PrivateKey(secret, chainConfig); // Build unsigned transaction const unsignedTx = await chainProvider.buildTransaction!({ @@ -113,7 +231,7 @@ export async function submitWeb3Transfer({ }); // Sign transaction - const signedTx = await chainProvider.signTransaction!(unsignedTx, { privateKey: seed }); + const signedTx = await chainProvider.signTransaction!(unsignedTx, { privateKey }); // Broadcast transaction const txHash = await chainProvider.broadcastTransaction!(signedTx); @@ -122,6 +240,28 @@ export async function submitWeb3Transfer({ } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); + if (error instanceof ChainServiceError) { + if (error.code === ChainErrorCodes.TX_BUILD_FAILED) { + const reason = extractBuildFailureReason(error); + if (reason && isSelfTransferMessage(reason)) { + return { status: 'error', message: t('error:validation.cannotTransferToSelf') }; + } + return { status: 'error', message: reason ?? errorMessage }; + } + + if (error.code === ChainErrorCodes.TRANSACTION_TIMEOUT || hasTimeoutInError(error)) { + return { status: 'error', message: t('transaction:broadcast.timeout') }; + } + + if (error.code === ChainErrorCodes.TX_BROADCAST_FAILED) { + const reason = extractBroadcastFailureReason(error); + return { + status: 'error', + message: reason ? `${t('transaction:broadcast.failed')}: ${reason}` : t('transaction:broadcast.failed'), + }; + } + } + // Handle specific error cases if (errorMessage.includes('insufficient') || errorMessage.includes('balance')) { return { status: 'error', message: t('error:insufficientFunds') }; @@ -135,6 +275,10 @@ export async function submitWeb3Transfer({ return { status: 'error', message: t('error:chain.transferNotImplemented') }; } + if (isSelfTransferMessage(errorMessage)) { + return { status: 'error', message: t('error:validation.cannotTransferToSelf') }; + } + return { status: 'error', message: errorMessage || t('error:transaction.failed'), diff --git a/src/i18n/locales/ar/transaction.json b/src/i18n/locales/ar/transaction.json index 6f34bc7d3..37519d6d1 100644 --- a/src/i18n/locales/ar/transaction.json +++ b/src/i18n/locales/ar/transaction.json @@ -244,6 +244,7 @@ "explorerNotImplemented": "ميزة مستكشف البلوك قادمة قريبًا", "fee": "رسوم المعاملة", "feeEstimating": "جارٍ التقدير...", + "feePending": "بانتظار التقدير", "feeUnavailable": "الرسوم غير متاحة", "from": "من", "networkWarning": "يرجى التأكد من أن عنوان المستلم هو عنوان شبكة {{chain}}. لا يمكن استرداد المرسل إلى الشبكة الخاطئة", diff --git a/src/i18n/locales/en/transaction.json b/src/i18n/locales/en/transaction.json index 2ae2e6d35..0f2df8b5d 100644 --- a/src/i18n/locales/en/transaction.json +++ b/src/i18n/locales/en/transaction.json @@ -244,6 +244,7 @@ "explorerNotImplemented": "Block explorer feature coming soon", "fee": "Fee", "feeEstimating": "Estimating...", + "feePending": "Pending estimate", "feeUnavailable": "Fee unavailable", "from": "From", "networkWarning": "Please ensure the recipient address is a {{chain}} network address. Sending to the wrong network cannot be recovered", diff --git a/src/i18n/locales/zh-CN/transaction.json b/src/i18n/locales/zh-CN/transaction.json index 99cb51da4..e5693f9a4 100644 --- a/src/i18n/locales/zh-CN/transaction.json +++ b/src/i18n/locales/zh-CN/transaction.json @@ -69,6 +69,7 @@ "twoStepSecretError": "安全密码错误", "fee": "手续费", "feeEstimating": "预估中...", + "feePending": "待估算", "feeUnavailable": "手续费不可用" }, "destroyPage": { diff --git a/src/i18n/locales/zh-TW/transaction.json b/src/i18n/locales/zh-TW/transaction.json index fb5d69ce7..74bd1e85c 100644 --- a/src/i18n/locales/zh-TW/transaction.json +++ b/src/i18n/locales/zh-TW/transaction.json @@ -244,6 +244,7 @@ "explorerNotImplemented": "區塊瀏覽器功能待實現", "fee": "手續費", "feeEstimating": "預估中...", + "feePending": "待估算", "feeUnavailable": "手續費不可用", "from": "發送地址", "networkWarning": "請確保收款地址為 {{chain}} 網絡地址,發送到錯誤網絡將無法找回", diff --git a/src/pages/send/index.tsx b/src/pages/send/index.tsx index eb3075585..be6aecb9a 100644 --- a/src/pages/send/index.tsx +++ b/src/pages/send/index.tsx @@ -213,6 +213,21 @@ function SendPageContent() { const symbol = state.asset?.assetType ?? 'TOKEN'; const feeSymbol = state.feeSymbol || chainConfig?.symbol; const isFeeSameAsset = !!feeSymbol && feeSymbol === symbol; + const hasFeeEstimatePrerequisites = useMemo(() => { + if (!state.asset) return false; + const toAddress = state.toAddress.trim(); + if (toAddress.length === 0) return false; + if (!state.amount?.isPositive()) return false; + return true; + }, [state.amount, state.asset, state.toAddress]); + + const feeDisplayText = useMemo(() => { + if (state.feeLoading) return t('sendPage.feeEstimating'); + if (state.feeAmount) return `${state.feeAmount.toFormatted()} ${state.feeSymbol || symbol}`; + if (!hasFeeEstimatePrerequisites) return t('sendPage.feePending'); + return t('sendPage.feeUnavailable'); + }, [hasFeeEstimatePrerequisites, state.feeAmount, state.feeLoading, state.feeSymbol, symbol, t]); + const maxAmount = useMemo(() => { if (!state.asset || !balance) return undefined; if (!state.feeAmount) return balance; @@ -412,13 +427,7 @@ function SendPageContent() { {/* Fee estimate */}
{t('sendPage.fee')} - - {state.feeLoading - ? t('sendPage.feeEstimating') - : state.feeAmount - ? `${state.feeAmount.toFormatted()} ${state.feeSymbol || symbol}` - : t('sendPage.feeUnavailable')} - + {feeDisplayText}
{/* Network warning */} diff --git a/src/services/chain-adapter/providers/tronwallet-provider.effect.ts b/src/services/chain-adapter/providers/tronwallet-provider.effect.ts index 4f1a6d8df..37861a233 100644 --- a/src/services/chain-adapter/providers/tronwallet-provider.effect.ts +++ b/src/services/chain-adapter/providers/tronwallet-provider.effect.ts @@ -172,6 +172,15 @@ function isRecord(value: unknown): value is UnknownRecord { return typeof value === "object" && value !== null } +function formatTronSignature(txId: string, privateKey: Uint8Array): string { + const txIdBytes = hexToBytes(txId) + const recovered = secp256k1.sign(txIdBytes, privateKey, { prehash: false, format: "recovered" }) + const recovery = recovered[0] + const compact = recovered.subarray(1) + const v = (recovery + 27).toString(16).padStart(2, "0") + return `${bytesToHex(compact)}${v}` +} + function toRawString(value: string | number | undefined | null): string { if (value === undefined || value === null) return "0" return String(value) @@ -225,6 +234,137 @@ function mergeTransactions(nativeTxs: Transaction[], tokenTxs: Transaction[]): T return Array.from(map.values()).sort((a, b) => b.timestamp - a.timestamp) } +function decodeTronErrorMessage(value: unknown): string | null { + if (typeof value !== "string") return null + const trimmed = value.trim() + if (!trimmed) return null + + const normalized = trimmed.startsWith("0x") ? trimmed.slice(2) : trimmed + if (!/^[0-9a-fA-F]+$/.test(normalized) || normalized.length % 2 !== 0) { + return trimmed + } + + try { + const decoded = new TextDecoder().decode(hexToBytes(normalized)).trim() + return decoded || trimmed + } catch { + return trimmed + } +} + +function resolveBroadcastFailureReason(raw: unknown): string { + if (!isRecord(raw)) { + return "Unknown error" + } + + const candidates: unknown[] = [raw.message, raw.code, raw.error] + if (isRecord(raw.result)) { + candidates.push(raw.result.message, raw.result.code, raw.result.error) + } + + for (const candidate of candidates) { + const reason = decodeTronErrorMessage(candidate) + if (reason) return reason + } + + return "Unknown error" +} + +function resolveBuildFailureReason(raw: unknown): string { + if (!isRecord(raw)) { + return "Unknown error" + } + + const errorRecord = isRecord(raw.error) ? raw.error : null + const resultRecord = isRecord(raw.result) ? raw.result : null + const resultErrorRecord = resultRecord && isRecord(resultRecord.error) ? resultRecord.error : null + const dataRecord = isRecord(raw.data) ? raw.data : null + const dataErrorRecord = dataRecord && isRecord(dataRecord.error) ? dataRecord.error : null + + const candidates: unknown[] = [ + errorRecord?.message, + errorRecord?.info, + errorRecord?.code, + raw.message, + raw.code, + resultRecord?.message, + resultRecord?.code, + resultErrorRecord?.message, + resultErrorRecord?.info, + resultErrorRecord?.code, + dataRecord?.message, + dataRecord?.code, + dataErrorRecord?.message, + dataErrorRecord?.info, + dataErrorRecord?.code, + ] + + for (const candidate of candidates) { + const reason = decodeTronErrorMessage(candidate) + if (reason) return reason + } + + return "Unknown error" +} + +function isBroadcastSuccess(raw: unknown): boolean { + if (!isRecord(raw)) return false + if (typeof raw.result === "boolean") return raw.result + if (typeof raw.success === "boolean") return raw.success + return false +} + +function toSignedPayload(data: unknown): TronWalletSignedPayload { + if (isRecord(data) && isRecord(data.signedTx)) { + return data as TronWalletSignedPayload + } + + const signedTx = data as TronSignedTransaction + return { + signedTx, + detail: { + from: "", + to: "", + amount: "0", + fee: "0", + assetSymbol: "", + }, + isToken: false, + } +} + +function pickTronRawTransaction(raw: unknown): TronRawTransaction | null { + if (!isRecord(raw)) return null + + if (typeof raw.txID === "string") { + return raw as TronRawTransaction + } + + if (isRecord(raw.transaction) && typeof raw.transaction.txID === "string") { + return raw.transaction as TronRawTransaction + } + + if (isRecord(raw.result)) { + if (typeof raw.result.txID === "string") { + return raw.result as TronRawTransaction + } + if (isRecord(raw.result.transaction) && typeof raw.result.transaction.txID === "string") { + return raw.result.transaction as TronRawTransaction + } + } + + if (isRecord(raw.data)) { + if (typeof raw.data.txID === "string") { + return raw.data as TronRawTransaction + } + if (isRecord(raw.data.transaction) && typeof raw.data.transaction.txID === "string") { + return raw.data.transaction as TronRawTransaction + } + } + + return null +} + function isConfirmedReceipt(raw: ReceiptResponse): boolean { if (!isRecord(raw)) return false return Object.keys(raw).length > 0 @@ -526,7 +666,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM const fromHex = tronAddressToHex(transferIntent.from) const toHex = tronAddressToHex(transferIntent.to) const hasCustomFee = Boolean(transferIntent.fee) - const feeRaw = transferIntent.fee?.raw ?? "0" + const feeRaw = transferIntent.fee?.toRawString() ?? "0" const tokenAddress = transferIntent.tokenAddress?.trim() const isToken = Boolean(tokenAddress) let assetSymbol = this.symbol @@ -569,7 +709,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM const detail: TronWalletBroadcastDetail = { from: fromHex, to: toHex, - amount: transferIntent.amount.raw, + amount: transferIntent.amount.toRawString(), fee: estimatedFeeRaw, assetSymbol, } @@ -585,7 +725,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM } } - const rawTx = await Effect.runPromise( + const rawTxResponse = await Effect.runPromise( httpFetch({ url: `${this.baseUrl}/trans/create`, method: "POST", @@ -597,6 +737,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM }, }) ) + const rawTx = this.extractNativeTransaction(rawTxResponse) const estimatedFeeRaw = hasCustomFee ? feeRaw @@ -604,7 +745,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM const detail: TronWalletBroadcastDetail = { from: fromHex, to: toHex, - amount: transferIntent.amount.raw, + amount: transferIntent.amount.toRawString(), fee: estimatedFeeRaw, assetSymbol, } @@ -613,7 +754,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM chainId: this.chainId, intentType: "transfer", data: { - rawTx: rawTx as TronRawTransaction, + rawTx, detail, isToken: false, } satisfies TronWalletUnsignedPayload, @@ -629,9 +770,7 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM } const payload = unsignedTx.data as TronWalletUnsignedPayload - const txIdBytes = hexToBytes(payload.rawTx.txID) - const sigBytes = secp256k1.sign(txIdBytes, options.privateKey, { prehash: false, format: "recovered" }) - const signature = bytesToHex(sigBytes) + const signature = formatTronSignature(payload.rawTx.txID, options.privateKey) const signedTx: TronSignedTransaction = { ...payload.rawTx, signature: [signature], @@ -649,24 +788,36 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM } async broadcastTransaction(signedTx: SignedTransaction): Promise { - const payload = signedTx.data as TronWalletSignedPayload + const payload = toSignedPayload(signedTx.data) const url = payload.isToken ? `${this.baseUrl}/trans/trc20/broadcast` : `${this.baseUrl}/trans/broadcast` + const requestBody = payload.detail.assetSymbol + ? { + ...payload.signedTx, + detail: payload.detail, + } + : payload.signedTx const result = await Effect.runPromise( httpFetch({ url, method: "POST", - body: { - ...payload.signedTx, - detail: payload.detail, - }, + body: requestBody, }) ) - const response = result as { result?: boolean; txid?: string } - if (!response.result) { - throw new ChainServiceError(ChainErrorCodes.TX_BROADCAST_FAILED, "Broadcast failed") + if (!isBroadcastSuccess(result)) { + const reason = resolveBroadcastFailureReason(result) + throw new ChainServiceError( + ChainErrorCodes.TX_BROADCAST_FAILED, + `Broadcast failed: ${reason}`, + isRecord(result) ? { provider: this.type, response: result, reason } : { provider: this.type, reason }, + ) + } + + if (isRecord(result) && typeof result.txid === "string" && result.txid.length > 0) { + return result.txid } + return payload.signedTx.txID } @@ -1365,27 +1516,27 @@ export class TronWalletProviderEffect extends TronIdentityMixin(TronTransactionM } private extractTrc20Transaction(raw: unknown): TronRawTransaction { - if (raw && typeof raw === "object") { - const record = raw as Record - if ("success" in record && record.success === false) { - throw new ChainServiceError(ChainErrorCodes.TX_BUILD_FAILED, "TRC20 transaction build failed") - } - const transaction = record["transaction"] - if (transaction && typeof transaction === "object" && "txID" in transaction) { - return transaction as TronRawTransaction - } - const result = record["result"] - if (result && typeof result === "object") { - const nested = (result as Record)["transaction"] - if (nested && typeof nested === "object" && "txID" in nested) { - return nested as TronRawTransaction - } - } - if ("txID" in record) { - return record as TronRawTransaction - } - } - throw new ChainServiceError(ChainErrorCodes.TX_BUILD_FAILED, "Invalid TRC20 transaction response") + const tx = pickTronRawTransaction(raw) + if (tx) return tx + + const reason = resolveBuildFailureReason(raw) + throw new ChainServiceError( + ChainErrorCodes.TX_BUILD_FAILED, + `TRC20 transaction build failed: ${reason}`, + isRecord(raw) ? { provider: this.type, response: raw, reason } : { provider: this.type, reason }, + ) + } + + private extractNativeTransaction(raw: unknown): TronRawTransaction { + const tx = pickTronRawTransaction(raw) + if (tx) return tx + + const reason = resolveBuildFailureReason(raw) + throw new ChainServiceError( + ChainErrorCodes.TX_BUILD_FAILED, + `Failed to create Tron transaction: ${reason}`, + isRecord(raw) ? { provider: this.type, response: raw, reason } : { provider: this.type, reason }, + ) } private fetchPendingTransactions(address: string): Effect.Effect { diff --git a/src/services/chain-adapter/tron/transaction-mixin.test.ts b/src/services/chain-adapter/tron/transaction-mixin.test.ts new file mode 100644 index 000000000..e3360a9d2 --- /dev/null +++ b/src/services/chain-adapter/tron/transaction-mixin.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest' +import { secp256k1 } from '@noble/curves/secp256k1.js' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js' +import { TronTransactionMixin } from './transaction-mixin' +import type { SignedTransaction, UnsignedTransaction } from '../types' +import type { TronSignedTransaction } from './types' + +class TronTestBase { + constructor(public readonly chainId: string) {} +} + +const TronTransactionService = TronTransactionMixin(TronTestBase) + +function expectedTronSignature(txId: string, privateKey: Uint8Array): string { + const recovered = secp256k1.sign(hexToBytes(txId), privateKey, { prehash: false, format: 'recovered' }) + const recovery = recovered[0] + const compact = recovered.subarray(1) + const v = (recovery + 27).toString(16).padStart(2, '0') + return `${bytesToHex(compact)}${v}` +} + +describe('TronTransactionMixin signature format', () => { + it('signs transaction with tron-compatible r+s+v format', async () => { + const service = new TronTransactionService('tron') + const txId = 'd1f6f7cf0ecfdb4f6f07ac8b5f9c5cf6a3dc731fd3704d98ea5f6f5b8d493f0f' + const privateKey = Uint8Array.from([ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x10, + 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, + 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x12, 0x34, + ]) + + const unsignedTx: UnsignedTransaction = { + chainId: 'tron', + intentType: 'transfer', + data: { txID: txId } as UnsignedTransaction['data'], + } + + const signed = await service.signTransaction(unsignedTx, { privateKey }) + const trxSigned = signed as SignedTransaction + const signedData = trxSigned.data as TronSignedTransaction + const signature = signedData.signature?.[0] + + expect(signature).toBe(expectedTronSignature(txId, privateKey)) + expect(signature?.length).toBe(130) + expect(signature?.slice(-2)).toMatch(/1b|1c/) + }) +}) diff --git a/src/services/chain-adapter/tron/transaction-mixin.ts b/src/services/chain-adapter/tron/transaction-mixin.ts index 3133630d4..068555cf1 100644 --- a/src/services/chain-adapter/tron/transaction-mixin.ts +++ b/src/services/chain-adapter/tron/transaction-mixin.ts @@ -35,6 +35,89 @@ type Constructor = new (...args: any[]) => T /** Default Tron RPC 端点 (fallback) */ const DEFAULT_RPC_URL = 'https://api.trongrid.io' +type UnknownRecord = Record + +function isRecord(value: unknown): value is UnknownRecord { + return typeof value === 'object' && value !== null +} + +function formatTronSignature(txId: string, privateKey: Uint8Array): string { + const txIdBytes = hexToBytes(txId) + const recovered = secp256k1.sign(txIdBytes, privateKey, { prehash: false, format: 'recovered' }) + const recovery = recovered[0] + const compact = recovered.subarray(1) + const v = (recovery + 27).toString(16).padStart(2, '0') + return `${bytesToHex(compact)}${v}` +} + +function decodeTronErrorMessage(value: unknown): string | null { + if (typeof value !== 'string') { + return null + } + + const trimmed = value.trim() + if (!trimmed) { + return null + } + + const normalized = trimmed.startsWith('0x') ? trimmed.slice(2) : trimmed + if (!/^[0-9a-fA-F]+$/.test(normalized) || normalized.length % 2 !== 0) { + return trimmed + } + + try { + const decoded = new TextDecoder().decode(hexToBytes(normalized)).trim() + return decoded || trimmed + } catch { + return trimmed + } +} + +function resolveBroadcastFailureReason(raw: unknown): string { + if (!isRecord(raw)) { + return 'Unknown error' + } + + const candidates: unknown[] = [raw.message, raw.code, raw.error] + + if (isRecord(raw.result)) { + candidates.push(raw.result.message, raw.result.code, raw.result.error) + } + + for (const candidate of candidates) { + const reason = decodeTronErrorMessage(candidate) + if (reason) { + return reason + } + } + + return 'Unknown error' +} + +function isBroadcastSuccess(raw: unknown): boolean { + if (!isRecord(raw)) { + return false + } + + if (typeof raw.result === 'boolean') { + return raw.result + } + + if (typeof raw.success === 'boolean') { + return raw.success + } + + return false +} + +function unwrapSignedTransactionPayload(signedTx: SignedTransaction): TronSignedTransaction { + const payload = signedTx.data + if (isRecord(payload) && isRecord(payload.signedTx)) { + return payload.signedTx as TronSignedTransaction + } + return payload as TronSignedTransaction +} + /** * Tron Transaction Mixin - 为任意类添加 Tron 交易能力 * @@ -164,9 +247,7 @@ export function TronTransactionMixin { - const tx = signedTx.data as TronSignedTransaction + const tx = unwrapSignedTransactionPayload(signedTx) const result = await this.#api<{ result?: boolean; txid?: string; code?: string; message?: string }>( '/wallet/broadcasttransaction', tx, ) - if (!result.result) { - const errorMsg = result.message - ? Buffer.from(result.message, 'hex').toString('utf8') - : result.code ?? 'Unknown error' - throw new ChainServiceError(ChainErrorCodes.TX_BROADCAST_FAILED, `Broadcast failed: ${errorMsg}`) + if (!isBroadcastSuccess(result)) { + const errorMsg = resolveBroadcastFailureReason(result) + throw new ChainServiceError( + ChainErrorCodes.TX_BROADCAST_FAILED, + `Broadcast failed: ${errorMsg}`, + { reason: errorMsg, response: isRecord(result) ? result : undefined }, + ) + } + + if (typeof result.txid === 'string' && result.txid.length > 0) { + return result.txid } return tx.txID diff --git a/src/stackflow/activities/sheets/TransferWalletLockJob.tsx b/src/stackflow/activities/sheets/TransferWalletLockJob.tsx index 8020dac57..6999c0f47 100644 --- a/src/stackflow/activities/sheets/TransferWalletLockJob.tsx +++ b/src/stackflow/activities/sheets/TransferWalletLockJob.tsx @@ -203,8 +203,8 @@ function TransferWalletLockJobContent() { } return; } - } catch { - setError(t("transaction:broadcast.unknown")); + } catch (err) { + setError(err instanceof Error ? err.message : t("transaction:broadcast.unknown")); setTxStatus("failed"); } finally { setIsVerifying(false); @@ -295,7 +295,7 @@ function TransferWalletLockJobContent() { title={{ broadcasted: t("transaction:txStatus.broadcasted"), confirmed: t("transaction:sendResult.success"), - failed: t("transaction:broadcast.failed"), + failed: t("transaction:txStatus.failed"), }} description={{ broadcasted: t("transaction:txStatus.broadcastedDesc"), diff --git a/src/stackflow/activities/sheets/__tests__/miniapp-transfer-error.test.ts b/src/stackflow/activities/sheets/__tests__/miniapp-transfer-error.test.ts index d1c3de476..73a64c2a5 100644 --- a/src/stackflow/activities/sheets/__tests__/miniapp-transfer-error.test.ts +++ b/src/stackflow/activities/sheets/__tests__/miniapp-transfer-error.test.ts @@ -51,8 +51,35 @@ describe('miniapp-transfer-error mapper', () => { expect(mapMiniappTransferErrorToMessage(t, error, chainId)).toBe('transaction:broadcast.timeout'); }); - it('falls back to unknown broadcast error', () => { + it('maps broadcast failure with detailed reason', () => { + const error = new ChainServiceError( + ChainErrorCodes.TX_BROADCAST_FAILED, + 'Broadcast failed: SIGERROR', + { reason: 'SIGERROR' }, + ); + expect(mapMiniappTransferErrorToMessage(t, error, chainId)).toBe('transaction:broadcast.failed: SIGERROR'); + }); + + it('maps tx build self-transfer failure', () => { + const error = new ChainServiceError( + ChainErrorCodes.TX_BUILD_FAILED, + 'Failed to create Tron transaction: Cannot transfer TRX to yourself.', + { reason: 'Cannot transfer TRX to yourself.' }, + ); + expect(mapMiniappTransferErrorToMessage(t, error, chainId)).toBe('error:validation.cannotTransferToSelf'); + }); + + it('maps tx build failure reason directly', () => { + const error = new ChainServiceError( + ChainErrorCodes.TX_BUILD_FAILED, + 'Failed to create Tron transaction: account does not exist', + { reason: 'account does not exist' }, + ); + expect(mapMiniappTransferErrorToMessage(t, error, chainId)).toBe('account does not exist'); + }); + + it('falls back to original error message for unknown error', () => { const error = new Error('some-random-error'); - expect(mapMiniappTransferErrorToMessage(t, error, chainId)).toBe('transaction:broadcast.unknown'); + expect(mapMiniappTransferErrorToMessage(t, error, chainId)).toBe('some-random-error'); }); }); diff --git a/src/stackflow/activities/sheets/miniapp-transfer-error.ts b/src/stackflow/activities/sheets/miniapp-transfer-error.ts index df8ca948e..69ceaa659 100644 --- a/src/stackflow/activities/sheets/miniapp-transfer-error.ts +++ b/src/stackflow/activities/sheets/miniapp-transfer-error.ts @@ -11,6 +11,14 @@ function isBroadcastFailedMessage(message: string): boolean { return /failed to broadcast transaction|broadcast failed|tx_broadcast_failed/i.test(message); } +function isSelfTransferMessage(message: string): boolean { + return /cannot transfer(?:\s+\w+)*\s+to yourself|不能转账给自己|不能给自己转账/i.test(message); +} + +function isGenericBroadcastFailureMessage(message: string): boolean { + return /^broadcast failed:?$/i.test(message.trim()) + || /^failed to broadcast transaction:?$/i.test(message.trim()); +} function collectErrorMessages(error: unknown): string[] { const messages: string[] = []; @@ -36,6 +44,33 @@ function hasBroadcastFailedMessageInError(error: unknown): boolean { return collectErrorMessages(error).some((message) => isBroadcastFailedMessage(message)); } +function extractBroadcastFailureReason(error: unknown): string | null { + if (error instanceof ChainServiceError && typeof error.details?.reason === 'string') { + return error.details.reason.trim() || null; + } + + for (const message of collectErrorMessages(error)) { + const normalized = message.trim(); + const match = normalized.match(/^broadcast failed:\s*(.+)$/i); + if (match?.[1]) { + return match[1].trim(); + } + if (!isGenericBroadcastFailureMessage(normalized) && !isBroadcastFailedMessage(normalized)) { + continue; + } + if (!isGenericBroadcastFailureMessage(normalized)) { + return normalized; + } + } + + return null; +} + +function withBroadcastReason(baseMessage: string, reason: string | null): string { + if (!reason) return baseMessage; + return `${baseMessage}: ${reason}`; +} + export function createMiniappUnsupportedPipelineError(chainId: string): ChainServiceError { return new ChainServiceError(ChainErrorCodes.NOT_SUPPORTED, MINIAPP_TRANSFER_UNSUPPORTED_PIPELINE, { scope: 'miniapp-transfer', @@ -57,7 +92,18 @@ export function mapMiniappTransferErrorToMessage(t: TFunction, error: unknown, c if (hasTimeoutMessageInError(error)) { return t('transaction:broadcast.timeout'); } - return t('transaction:broadcast.failed'); + return withBroadcastReason(t('transaction:broadcast.failed'), extractBroadcastFailureReason(error)); + } + + if (error.code === ChainErrorCodes.TX_BUILD_FAILED) { + const reason = typeof error.details?.reason === 'string' ? error.details.reason.trim() : ''; + const displayMessage = reason || error.message; + if (isSelfTransferMessage(displayMessage)) { + return t('error:validation.cannotTransferToSelf'); + } + if (displayMessage) { + return displayMessage; + } } if (error.code === ChainErrorCodes.NETWORK_ERROR) { @@ -65,7 +111,7 @@ export function mapMiniappTransferErrorToMessage(t: TFunction, error: unknown, c return t('transaction:broadcast.timeout'); } if (hasBroadcastFailedMessageInError(error)) { - return t('transaction:broadcast.failed'); + return withBroadcastReason(t('transaction:broadcast.failed'), extractBroadcastFailureReason(error)); } } } @@ -80,7 +126,16 @@ export function mapMiniappTransferErrorToMessage(t: TFunction, error: unknown, c } if (hasBroadcastFailedMessageInError(error)) { - return t('transaction:broadcast.failed'); + return withBroadcastReason(t('transaction:broadcast.failed'), extractBroadcastFailureReason(error)); + } + + if (isSelfTransferMessage(error.message)) { + return t('error:validation.cannotTransferToSelf'); + } + + const [firstMessage] = collectErrorMessages(error); + if (firstMessage) { + return firstMessage; } } diff --git a/src/test/css-compat-features.test.ts b/src/test/css-compat-features.test.ts new file mode 100644 index 000000000..71c7d0989 --- /dev/null +++ b/src/test/css-compat-features.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest' +import { detectCssCompatFeaturesInCode } from '../../scripts/css-compat-features' + +describe('css compat feature detectors', () => { + it('detects field-sizing as unsupported feature', () => { + const features = detectCssCompatFeaturesInCode('.a{field-sizing:content;}') + expect(features.map((item) => item.feature.id)).toContain('field-sizing') + }) + + it('detects scroll-driven animation related features', () => { + const features = detectCssCompatFeaturesInCode( + '@supports (animation-timeline: scroll()) {.a{scroll-timeline: --x block; animation-timeline: view();}}', + ) + expect(features.map((item) => item.feature.id)).toContain('scroll-driven-animations') + }) + + it('detects scrollbar-width as partial support feature', () => { + const features = detectCssCompatFeaturesInCode('.a{scrollbar-width:thin;}') + const target = features.find((item) => item.feature.id === 'css-scrollbar-standard') + expect(target?.feature.support).toBe('partial') + }) +}) diff --git a/src/test/mobile-compat-features.test.ts b/src/test/mobile-compat-features.test.ts new file mode 100644 index 000000000..906cd05ab --- /dev/null +++ b/src/test/mobile-compat-features.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' +import { + collectModernPolyfills, + detectMobileCompatFeaturesInCode, + MOBILE_COMPAT_FEATURES, +} from '../../scripts/mobile-compat-features' + +describe('mobile compat feature detectors', () => { + it('detects Promise.withResolvers', () => { + const features = detectMobileCompatFeaturesInCode('const d = Promise.withResolvers();') + expect(features.map((item) => item.id)).toContain('promise.withResolvers') + }) + + it('detects Set.prototype.intersection from set variable usage', () => { + const features = detectMobileCompatFeaturesInCode('const pending = new Set(); pending.intersection(new Set());') + expect(features.map((item) => item.id)).toContain('set.intersection') + }) + + it('collects expected core-js modules for selected features', () => { + const selected = MOBILE_COMPAT_FEATURES.filter((item) => item.id === 'promise.withResolvers' || item.id === 'set.union') + expect(collectModernPolyfills(selected)).toEqual(['es.promise.with-resolvers', 'es.set.union.v2']) + }) +}) diff --git a/vite.config.ts b/vite.config.ts index 89562b73d..74e3741c5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,5 @@ import { defineConfig, loadEnv } from 'vite'; +import legacy from '@vitejs/plugin-legacy'; import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; import commonjs from 'vite-plugin-commonjs'; @@ -11,6 +12,9 @@ import { miniappsPlugin } from './scripts/vite-plugin-miniapps'; import { remoteMiniappsPlugin, type RemoteMiniappConfig } from './scripts/vite-plugin-remote-miniapps'; import { buildCheckPlugin } from './scripts/vite-plugin-build-check'; import { pruneGenesisTransactionsPlugin } from './scripts/vite-plugin-prune-genesis-transactions'; +import { collectModernPolyfills, MOBILE_COMPAT_FEATURES, scanMobileCompatFeaturesInFiles } from './scripts/mobile-compat-features'; +import { cssCompatReportPlugin } from './scripts/vite-plugin-css-compat-report'; +import { mobileCompatReportPlugin } from './scripts/vite-plugin-mobile-compat-report'; import { buildPermissionsPolicyHeaderValue } from './src/services/ecosystem/permissions-policy'; const remoteMiniappsConfig: RemoteMiniappConfig[] = [ @@ -53,6 +57,18 @@ function collectEcosystemSources(configs: RemoteMiniappConfig[]): EcosystemSourc } const ecosystemSources = collectEcosystemSources(remoteMiniappsConfig); +const MOBILE_COMPAT_BASELINE = 'chrome>=114'; +const CSS_COMPAT_BASELINE = 'chrome>=114'; + +function resolveModernPolyfills(rootDir: string): string[] { + const sourceDir = resolve(rootDir, 'src'); + const featureHits = scanMobileCompatFeaturesInFiles(sourceDir); + const features = MOBILE_COMPAT_FEATURES.filter((feature) => featureHits.has(feature.id)); + if (featureHits.has('atomics.waitAsync')) { + console.warn('[mobile-compat] Atomics.waitAsync detected in src. Please keep runtime fallback to avoid unsupported runtime crashes.'); + } + return collectModernPolyfills(features); +} function getPreferredLanIPv4(): string | undefined { const ifaces = networkInterfaces(); @@ -131,6 +147,7 @@ export default defineConfig(({ mode }) => { const pad = (value: number) => value.toString().padStart(2, '0'); const buildSuffix = `-${pad(buildTime.getUTCMonth() + 1)}${pad(buildTime.getUTCDate())}${pad(buildTime.getUTCHours())}`; const appVersion = `${getPackageVersion()}${isDevBuild ? buildSuffix : ''}`; + const mobileCompatPolyfills = resolveModernPolyfills(__dirname); const permissionsPolicyHeader = buildPermissionsPolicyHeaderValue(); return { @@ -167,6 +184,12 @@ export default defineConfig(({ mode }) => { }), react(), tailwindcss(), + legacy({ + targets: [MOBILE_COMPAT_BASELINE], + modernTargets: [MOBILE_COMPAT_BASELINE], + renderLegacyChunks: false, + modernPolyfills: mobileCompatPolyfills.length > 0 ? mobileCompatPolyfills : false, + }), mockDevToolsPlugin(), // 远程 miniapps (必须在 miniappsPlugin 之前,以便注册到全局状态) remoteMiniappsPlugin({ @@ -189,6 +212,14 @@ export default defineConfig(({ mode }) => { remoteMiniapps: remoteMiniappsConfig, }), pruneGenesisTransactionsPlugin(), + mobileCompatReportPlugin({ + baseline: MOBILE_COMPAT_BASELINE, + configuredPolyfillsMode: 'manual', + configuredPolyfills: mobileCompatPolyfills, + }), + cssCompatReportPlugin({ + baseline: CSS_COMPAT_BASELINE, + }), buildCheckPlugin(), ], resolve: {