diff --git a/AGENTS.md b/AGENTS.md index 55bb606b0..6211b6c63 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,6 +68,8 @@ the ownership evidence the AI-slop review evaluates (see - For code review and verification of review fixes, follow [REVIEW.md](REVIEW.md). Review is read-only unless changes are authorized; findings do not expand that authorization to unrelated fixes, commits, pushes, or GitHub updates. +- For macOS frontend performance or WebView memory investigations, follow + [scripts/perf/AGENTS.md](scripts/perf/AGENTS.md). - When handing work between agents or review rounds, use the [AI context sharing guidance](REVIEW.md#ai-context-sharing). - Read the relevant sections and linked module rules, expanding only when the diff --git a/knip.json b/knip.json index 6ea552114..d01f3cb74 100644 --- a/knip.json +++ b/knip.json @@ -14,7 +14,7 @@ "duplicates", "cycles" ], - "ignoreBinaries": ["rustc", "cc"], + "ignoreBinaries": ["rustc"], "ignoreDependencies": ["lint-staged"], "ignoreIssues": { "src/types/generated/**": ["exports", "types"], diff --git a/package.json b/package.json index f9a080068..fa53db6e5 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,7 @@ "test": "vitest run", "test:watch": "vitest", "knip:check": "knip", - "knip:report": "knip --no-exit-code", - "perf:build": "node scripts/perf/build.mjs", - "perf:run": "node scripts/perf/run.mjs", - "perf:compare": "node scripts/perf/compare.mjs", - "perf:test": "node --test scripts/perf/report.test.mjs" + "knip:report": "knip --no-exit-code" }, "dependencies": { "@dnd-kit/collision": "^0.5.0", @@ -102,7 +98,6 @@ "@types/validator": "^13.15.10", "@vitejs/plugin-legacy": "^8.2.3", "@vitejs/plugin-react": "^6.1.1", - "@wdio/tauri-service": "1.5.1", "adm-zip": "^0.6.1", "axios": "^1.20.0", "cli-color": "^2.0.4", @@ -131,9 +126,7 @@ "typescript-eslint": "^8.70.0", "vite": "^8.3.0", "vite-plugin-svgr": "^5.2.0", - "vitest": "^5.0.1", - "webdriverio": "10.2.0", - "ws": "8.22.2" + "vitest": "^5.0.1" }, "lint-staged": { "*.{ts,tsx,js,mjs}": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac6c4d6b8..10c182e43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,9 +338,6 @@ importers: '@vitejs/plugin-react': specifier: ^6.1.1 version: 6.1.1(vite@8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1)) - '@wdio/tauri-service': - specifier: 1.5.1 - version: 1.5.1(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)) adm-zip: specifier: ^0.6.1 version: 0.6.1 @@ -428,12 +425,6 @@ importers: vitest: specifier: ^5.0.1 version: 5.0.1(@types/node@26.6.5)(vite@8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1)) - webdriverio: - specifier: 10.2.0 - version: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - ws: - specifier: 8.22.2 - version: 8.22.2 packages: @@ -1381,33 +1372,6 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@isaacs/nonrecursive-glob-match@1.1.0': - resolution: {integrity: sha512-GPm/Dbvk5RLmhRnxXIa7nTpK0xtl4E0wj0MslsuNiJ1FIROU+2+9lgUC/dgF7Ey4QCWhfEsgxilMRMCdHPfDfw==} - - '@jest/diff-sequences@30.5.0': - resolution: {integrity: sha512-OsqBjHXCn8cadasoAZBP6nWYvMsRhpMzGXTpxJ5aO04NlbdhIz+FVe3q49l0AwVhsz/cEmIpBes6gAFl1/dWQg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect-utils@30.5.2': - resolution: {integrity: sha512-d/HOLSrJUlBIl6n3LzOa7cjW3xdePm5H5gQw2lixZ/0h157l9sw3mLblzFqnPcTbhDzhRQdBOX3A3KcG73nm5Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/get-type@30.5.0': - resolution: {integrity: sha512-9/2VUPitAjmBzbvDvqrxmvB7BzWsBW0WmkkojX1ODuxX1NLGxx9gfaZpHB0z8DtJ9uhGNmZG/VXBhf8uO0OV8Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/pattern@30.5.0': - resolution: {integrity: sha512-HdNQYSdRTEBNrginaqzQtTjG0HRMfrra/z6Ok7uL3S87vSlarIVohEsJsSj5edu3MiHoHjAkvPROz5ZjoKai+w==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/schemas@30.5.0': - resolution: {integrity: sha512-/hunigyNpc4RCjC0VaW3f5RCUZVM2+WQ65qP7z083Gmvac7or2LI50XVNOtE4YPgBpV0yxYiAgorAPGniCoJmg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/types@30.5.1': - resolution: {integrity: sha512-LvVYn83nnXPl+Rg98nvcFgjx6nRMTArhSn6RAX/w3ELn54S8A42TYZvsCMdGUqTM8S0wyXbtlQdU6Hi6dykj9g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1911,22 +1875,6 @@ packages: '@preact/signals-core@1.14.4': resolution: {integrity: sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==} - '@promptbook/utils@0.69.5': - resolution: {integrity: sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==} - - '@puppeteer/browsers@3.2.3': - resolution: {integrity: sha512-2Bt3m6dDAJqmZehn0wiXSYyQmuhyiHHZ7FjvrVaS1W2RoFGV6eCK71UvKYkNJjkwC1Q9nobjhvTMhLQztFVINA==} - engines: {node: '>=22.12.0'} - hasBin: true - peerDependencies: - proxy-agent: '>=8.0.1' - yauzl: ^2.10.0 || ^3.4.0 - peerDependenciesMeta: - proxy-agent: - optional: true - yauzl: - optional: true - '@remix-run/route-pattern@0.22.1': resolution: {integrity: sha512-czdGJWh09Lapvcqt7Vb09KlrU2PhRJ8xQaI+AItTuD9aDJ5MAgxnS38lnys6Ll+6RJEqPiaUqTwIQhVLwFvv+w==} @@ -2038,9 +1986,6 @@ packages: rollup: optional: true - '@sinclair/typebox@0.34.52': - resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==} - '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -2285,15 +2230,6 @@ packages: '@types/hast@3.0.5': resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2309,12 +2245,6 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@20.19.43': - resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} - - '@types/node@22.20.5': - resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==} - '@types/node@26.6.5': resolution: {integrity: sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==} @@ -2337,15 +2267,6 @@ packages: '@types/react@19.3.0': resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} - '@types/sinonjs__fake-timers@15.0.1': - resolution: {integrity: sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==} - - '@types/split2@4.2.3': - resolution: {integrity: sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==} - - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -2358,15 +2279,6 @@ packages: '@types/validator@13.15.10': resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.70.0': resolution: {integrity: sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2583,103 +2495,12 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - '@vitest/spy@5.0.1': resolution: {integrity: sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==} - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} - '@wdio/config@10.2.0': - resolution: {integrity: sha512-dCsMVpgmyL8fLRQVcxVyYXBG/pgtJeY897+LUxU/Vw+JvJOV2iTycHn5xUD5EehbNsstfxgJwkLCVunhJcbXdg==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - '@wdio/globals@9.31.3': - resolution: {integrity: sha512-ZzmRKUlwUBahNoNxHbAzst+9dNInEjLQc7Z3c86OhDRxAf3Ivw8k2rc56UCbyYiZnkd1dOpuA91zFiTrUNNORw==} - engines: {node: '>=18.20.0'} - peerDependencies: - expect-webdriverio: ^6.0.9 - webdriverio: ^9.28.0 - - '@wdio/logger@10.2.0': - resolution: {integrity: sha512-C7qpFSA+Zuu/6xbx+ALoEeOiyIWlD02/Llb8ufjypCOpe9rAy77JgAHeQlKYWg7aitRQRt7aG2oeix0mNhTk5A==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - '@wdio/logger@9.29.1': - resolution: {integrity: sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==} - engines: {node: '>=18.20.0'} - - '@wdio/native-core@1.3.1': - resolution: {integrity: sha512-fdGk2lEr2vS+bClJXuZEnsG30cLzQv+B9W+dAt/Yte/fE33s/vDBn7hHwFx8aZLTOS0pPExErUxYiZEMXX+04g==} - engines: {node: '>=18.20.0'} - - '@wdio/native-spy@1.4.1': - resolution: {integrity: sha512-KGOmgaicHqDkqj+s2bxXsRsb27IzW6carlyTu1xcrJI34yUbC0v14F8mbhuZcewW+mt2xhmC17yITwAbNa8PKw==} - engines: {node: '>=18.20.0'} - - '@wdio/native-types@2.6.1': - resolution: {integrity: sha512-Rm5chX+MrRhVgJYcYdcX/yyTZaLjxvi0w/fnSgPaDT16gMIntXlk22K2x3hwEdaUonOKepZyhh4jr9ZgRz7UaQ==} - engines: {node: '>=18.20.0'} - - '@wdio/native-utils@2.8.1': - resolution: {integrity: sha512-QbmZjGkLUC2bmUJSzyqsHM11HND1D7tkxHFssUV1Kmx43+oPAeHHVR0NSZu2hMzSNrDZMBAmNYycR+3X8rqWcA==} - engines: {node: '>=18.20.0'} - peerDependencies: - tsx: ^4.22.4 - peerDependenciesMeta: - tsx: - optional: true - - '@wdio/protocols@10.2.0': - resolution: {integrity: sha512-HuSsvpv7hV8hbhFdlMraz7c+Bpu9O4FiKg2XdztZupj59wx0c7n7YPnZRGEY5yW3/wRt9PfqWkBI0EO81Y+Zqw==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - '@wdio/repl@10.2.0': - resolution: {integrity: sha512-bVx6C9c1LgAzOyWevg796NwghExw28oPLp0NlfYGc0gErxO8wtpvLJGwCYLXQ/V372b2en5H4OZ05QXoGmXOWw==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - '@wdio/reporter@9.31.2': - resolution: {integrity: sha512-3cKE6vCOsro/Ep9dGTIbo3FBn3WNvJ++BIn32lFd9m19pFdjuwlVgLtPXHb9jWNCJs740h5AIUcVkcscqUYhAA==} - engines: {node: '>=18.20.0'} - - '@wdio/spec-reporter@9.31.2': - resolution: {integrity: sha512-lNLLPUk4y1Yp5pWwBFF+tjZIy7bOv0TPTk4HDWV4LnnHnkc24abI0nN+kb2y5/JV8ZgwLCCVKUFRdRUxfKsAaQ==} - engines: {node: '>=18.20.0'} - - '@wdio/tauri-service@1.5.1': - resolution: {integrity: sha512-nfzJU2x7bxvIXtYo+yvrX4Xwuba8h5hXOlJ0IyHanGg0ELavY/2UfyxygJDTcSGO+pCQMdC9JJEyMvaS50AtZQ==} - engines: {node: '>=18.20.0'} - peerDependencies: - webdriverio: ^9.0.0 - - '@wdio/types@10.2.0': - resolution: {integrity: sha512-a9qwqHzf0t2ctrXs+qUEhobH/VK0PmeoErCBWdN6DRxhTP8YiXQco4wjTj1z7H4k5xwZYxMT0KC0QzN/e4B73Q==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - '@wdio/types@9.31.2': - resolution: {integrity: sha512-lQKaiQDCJJ6VC1K/cKzaAxCpwB9pL1U0VbmX2uGksMxO4EDbNuLvIxWryuUDJ7OaSYTjxILTKYFErdbT/xEikg==} - engines: {node: '>=18.20.0'} - - '@wdio/utils@10.2.0': - resolution: {integrity: sha512-C4ok/WatCBV3gHmIYGjj4Aa43tQTD53eTFs3okpKxuc1wmFy/1zDFOYLDjeTq4xSFygoRBI+ZMyc9LBEiok3ng==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - '@zip.js/zip.js@2.24.0': - resolution: {integrity: sha512-WlaVKzdqBK+RF3bdiXZWbdCnvGB6PdO4IHC1mn/2Yfl3bASgqUNegnU3+PNg61iecwoPfUi5BgLUPqBukXpyWw==} - engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=18.0.0'} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2711,58 +2532,19 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.3.0: - resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - archiver@8.0.0: - resolution: {integrity: sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==} - engines: {node: '>=18'} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} axios@1.20.0: resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==} - b4a@1.9.0: - resolution: {integrity: sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -2789,46 +2571,6 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - bare-events@2.9.2: - resolution: {integrity: sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - bare-fs@4.8.1: - resolution: {integrity: sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==} - engines: {bare: '>=1.28.0'} - peerDependencies: - bare-buffer: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - - bare-path@3.1.2: - resolution: {integrity: sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==} - - bare-stream@2.13.4: - resolution: {integrity: sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==} - peerDependencies: - bare-abort-controller: '*' - bare-buffer: '*' - bare-events: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - bare-buffer: - optional: true - bare-events: - optional: true - - bare-url@2.5.4: - resolution: {integrity: sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.11.25: resolution: {integrity: sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==} engines: {node: '>=6.0.0'} @@ -2837,9 +2579,6 @@ packages: before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@5.0.12: resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} @@ -2856,19 +2595,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-crc32@1.0.0: - resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} - engines: {node: '>=8.0.0'} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - cacheable@2.5.0: resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} @@ -2894,18 +2623,6 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - chalk@6.0.1: - resolution: {integrity: sha512-/Ce6KNm3vIbWdMlNna6RVIZ/ICQxnJxCicet5LBKK9ZffBkqzDw0xh9EiKSljdRtiIQ1S1z4YgcscUUGzNCWrA==} - engines: {node: '>=22'} - character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -2918,13 +2635,6 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - cheerio-select@2.1.0: - resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - - cheerio@1.2.0: - resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} - engines: {node: '>=20.18.1'} - chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} @@ -2933,10 +2643,6 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} - ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} - engines: {node: '>=8'} - cidr-block@2.3.0: resolution: {integrity: sha512-U5NE6QJKNOhIsVrI6sicbwGtEmEORbrz9B5Mbl+Mi05sz1Ic4egoLGV4DkJp56VWva61WxOf+vzIxTKEmRazqg==} engines: {node: '>=16'} @@ -2948,25 +2654,10 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - cliui@9.0.1: - resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} - engines: {node: '>=20'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2981,10 +2672,6 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commander@9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} - comment-parser@1.4.9: resolution: {integrity: sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==} engines: {node: '>= 12.0.0'} @@ -2992,10 +2679,6 @@ packages: compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - compress-commons@7.0.1: - resolution: {integrity: sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==} - engines: {node: '>=18'} - content-type@3.1.1: resolution: {integrity: sha512-GW4qUsfFo59d0HbUibDlWv5wPz+vAAcaTWbKIuKCf0JkC7wWkSyf8f13IpXn5JkeMlB2P8iTSSIjwXljorg2vA==} engines: {node: '>=22'} @@ -3016,9 +2699,6 @@ packages: core-js@3.50.0: resolution: {integrity: sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==} - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -3032,15 +2712,6 @@ packages: typescript: optional: true - crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - - crc32-stream@7.0.1: - resolution: {integrity: sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==} - engines: {node: '>=18'} - cross-env@10.1.0: resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} engines: {node: '>=20'} @@ -3050,19 +2721,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css-select@5.2.2: - resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - - css-shorthand-properties@1.1.2: - resolution: {integrity: sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==} - - css-value@0.0.1: - resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==} - - css-what@6.2.2: - resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} - engines: {node: '>= 6'} - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -3082,31 +2740,12 @@ packages: supports-color: optional: true - decamelize@6.0.1: - resolution: {integrity: sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - decamelize@7.0.0: - resolution: {integrity: sha512-uT/0KzZIx9u4b5vW3HLWtSYwjLTuY2QFVY3xNmfXXUmTqnfgzfT8DEA2gY93go5sXEjnbqP66KcURdVxm0rDXA==} - engines: {node: '>=22'} - decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge-ts@8.0.2: - resolution: {integrity: sha512-uqbvqLUMrc6p0MO+WBRtTxY55hmyh94WRwI5a++PZe54X+bfVh59FSN7uWCBCW1CCVjzjnrwzfI8zidE2obMMw==} - engines: {node: '>=16.9.0'} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -3122,29 +2761,12 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff@8.0.4: - resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} - engines: {node: '>=0.3.1'} - dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - dompurify@3.4.15: resolution: {integrity: sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==} - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -3152,25 +2774,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - easy-table@1.2.0: - resolution: {integrity: sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==} - - edgedriver@8.0.0: - resolution: {integrity: sha512-h4twAKTFILvpjrxfeN1gslvnggztA6Cgb4iHQdLzEq3TivaHHlf6qEETpCgU7+iw9JIWjcveeckrDlbcY28a/A==} - engines: {node: '>=22.19.0'} - hasBin: true - peerDependencies: - '@wdio/logger': ^9.29.1 || ^10.0.0 - electron-to-chromium@1.5.444: resolution: {integrity: sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==} - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - encoding-sniffer@0.2.1: - resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -3179,10 +2785,6 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} - error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -3228,10 +2830,6 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -3418,33 +3016,10 @@ packages: event-target-bus@1.1.0: resolution: {integrity: sha512-24JX34nM0qRcsj7KvtLbVpMcX8ZQYmb0+0a6viaV2GJtVFg5UGyJyDLITkBipJIG0fSscNskI2PxdhTBIMeg8g==} - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - expect-type@1.4.0: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} - expect-webdriverio@6.1.0: - resolution: {integrity: sha512-RXgaXoH1U8IiAHPelkMhZSnWUT0HfIhkjKK8nBkIpnXRO+zJ8dRafoyZhVy5mu/LtoyIr9juRWRwN284OSNM6Q==} - engines: {node: '>=20'} - peerDependencies: - '@wdio/globals': ^9.0.0 - '@wdio/logger': ^9.0.0 - webdriverio: ^9.28.0 - - expect@30.5.2: - resolution: {integrity: sha512-0LNuMvs8/5mRYhnCR4k+lGV7fqPMs6Bnksda4S3zsCUmdxBpAn4zU4NNzXdq1pMKe/H4W5t/zVIDSJ/H3e0vDA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - ext@1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} @@ -3454,9 +3029,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-fnv1a@1.0.0: resolution: {integrity: sha512-Y7iaFmz24pNJX+nqXgDuyHYFTfV9XPWJRfqp0hlG0Xuz1vKnelwLwvmKkbvVVvp5WzYWnrovBTLdk2PWkOYzCg==} engines: {node: ^22.15.0 || ^23.8.0 || >=24.0.0} @@ -3470,9 +3042,6 @@ packages: fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -3488,10 +3057,6 @@ packages: find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - find-up-simple@1.0.1: - resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} - engines: {node: '>=18'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3546,33 +3111,14 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - geckodriver@8.0.0: - resolution: {integrity: sha512-2jGCA6deI5MAjODxmHljPqvaUccGbeeqMUHXlW4k3FNDp4YDpxSI+P5QG+w5KIsnCeuGEfTTNi2euKqHI1eD6Q==} - engines: {node: '>=22.19.0'} - hasBin: true - peerDependencies: - '@wdio/logger': ^9.18.0 || ^10.0.0 - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-east-asian-width@1.7.0: - resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==} - engines: {node: '>=18'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-port@7.2.0: - resolution: {integrity: sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==} - engines: {node: '>=16'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -3584,10 +3130,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@14.0.4: - resolution: {integrity: sha512-mMiwNKoJZ4HAIRVJxotO6LsNRReA30gQwP9E2/p94tfLv+jygCNpKDjhhSL6OLY6LE371EZm5EAf7RpYuUqlsw==} - engines: {node: 18 || 20 || >=22} - globals@17.12.0: resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} engines: {node: '>=18'} @@ -3596,12 +3138,6 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3670,12 +3206,6 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - htmlfy@0.8.1: - resolution: {integrity: sha512-xWROBw9+MEGwxpotll0h672KCaLrKKiCYzsyN8ZgL9cQbVumFnyvsk2JqiB9ELAV1GLj1GG/jxZUjV9OZZi/yQ==} - - htmlparser2@10.1.0: - resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} - https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -3698,13 +3228,6 @@ packages: typescript: optional: true - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -3713,9 +3236,6 @@ packages: resolution: {integrity: sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==} engines: {node: '>= 4'} - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@5.1.9: resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} @@ -3723,16 +3243,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -3773,40 +3287,9 @@ packages: is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jest-diff@30.5.2: - resolution: {integrity: sha512-rBtHnI6BWTiWj3lM7fOLVfChx7R9B0u9VV3PRHawKs79x6ZjW1QoSrKenaigNGWlvhtoi2BqvDbABDie+Os3xQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-matcher-utils@30.5.2: - resolution: {integrity: sha512-lFkB365PTIHOhPzwrb9T6gvlDnPpOZ7/c3ewOKKB7bzztqjVrtlyRL2MkyfJXpyY2u2SKw001JKrTx14fBUFcQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-message-util@30.5.1: - resolution: {integrity: sha512-UdQlLdd9wL/Ys7xRErckqwD6wPlSZYueosSWuHc1r2ztGLwlgPvtSJq2+BPEgaEY13WLvfFbmhTj8pba0Sd1jg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-mock@30.5.2: - resolution: {integrity: sha512-KTHb37Fhj8xY8qPFvTFaFJHcdsumQp+ExsUlgoX232Agmc706gagBs53+CYvulV7MVQo9AgXOUxsqzzVBoJlrA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-regex-util@30.5.0: - resolution: {integrity: sha512-Mg0WK7A6xRHLSA1udJ8y9f3lM0uUhFTBnLKzwPmqB9AylvpleJ6BLemR8K9dK27DY+cesDryoA7yLZCAHsPG1A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-util@30.5.1: - resolution: {integrity: sha512-yKuxmNy2rSbTXw+3SIPanJo+nV4/BS1p26v44IYBFMsswSQySfMMcPHErnOncda7i9HEz0q605rIhSTBVgrZTg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -3850,9 +3333,6 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - jszip@3.10.2: - resolution: {integrity: sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==} - keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} @@ -3861,17 +3341,10 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - lightningcss-android-arm64@1.33.0: resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} engines: {node: '>= 12.0.0'} @@ -3954,9 +3427,6 @@ packages: engines: {node: '>=22.22.1'} hasBin: true - locate-app@2.5.0: - resolution: {integrity: sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3964,22 +3434,12 @@ packages: lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} - lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - loglevel-plugin-prefix@0.8.4: - resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} - - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -3990,19 +3450,12 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lru-cache@11.5.3: - resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} - engines: {node: 20 || >=22} - lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magic-string@1.4.1: resolution: {integrity: sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==} @@ -4126,10 +3579,6 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimatch@11.0.5: - resolution: {integrity: sha512-snPlVppFcYWfLLFQGhDxUyBcHNtMlWmn3jtj7pEaO+DP8pqL0IvAzXhMYmn1z9ASNtgmwLBdFxSf4tMUB0OTKA==} - engines: {node: 20 || >=22} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} @@ -4138,13 +3587,6 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - modern-tar@0.8.5: - resolution: {integrity: sha512-snEhs+6G5Tjd4I7tLCDOaoln2RgE0bD19RzEKgvgK2hZ5VKy3MpLhLTZ2fWpXSTg4K2cyPwp+VHATFJhxfnOeA==} - engines: {node: '>=18.0.0'} - monaco-editor@0.57.0: resolution: {integrity: sha512-5BkI9KGoqrNvBGUe15/QlZq3OooZ8WLg1AxTpaqHRCP3HNpzPPZKE2EDz8M7c+VRmCeUw1Brp4cx/PWm3kI/5A==} @@ -4201,25 +3643,10 @@ packages: resolution: {integrity: sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==} engines: {node: '>=18'} - non-error@0.1.0: - resolution: {integrity: sha512-TMB1uHiGsHRGv1uYclfhivcnf0/PdFp2pNqRxXjncaAsjYMoisaQJI+SSZCqRq+VliwRTC8tsMQfmrWjDMhkPQ==} - engines: {node: '>=20'} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - obug@2.2.1: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} @@ -4246,9 +3673,6 @@ packages: package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4260,16 +3684,6 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - - parse5-htmlparser2-tree-adapter@7.1.0: - resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} - - parse5-parser-stream@7.1.2: - resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -4287,20 +3701,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@3.0.0: - resolution: {integrity: sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==} - engines: {node: 18 || 20 || >=22} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -4321,21 +3725,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-format@30.5.1: - resolution: {integrity: sha512-byhRAPguVKMQIj4kjJwJ5lAskVhfuiSdiYl/aLTWpgkGEmic2jYhJh1yE9ih8Ox44Xg9ccCT11/S6QrzSJuNrg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - pretty-ms@9.3.1: - resolution: {integrity: sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==} - engines: {node: '>=18'} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -4371,9 +3760,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - query-selector-shadow-dom@1.0.1: - resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} - react-dom@19.3.1: resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==} peerDependencies: @@ -4414,9 +3800,6 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.3.0: resolution: {integrity: sha512-UpMYezM4v5/18F28aC66AEsjXIgE02kyEMH6yLdgLXu/UTfa1Ntwck/nNLrbqJsEXW7gPb0coNO9FQse9WTovA==} @@ -4446,17 +3829,6 @@ packages: resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} engines: {node: '>=0.10.0'} - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdir-glob@3.0.0: - resolution: {integrity: sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==} - engines: {node: '>=18'} - readdirp@5.1.1: resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} @@ -4509,35 +3881,11 @@ packages: engines: {node: '>= 0.4'} hasBin: true - ret@0.5.0: - resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} - engines: {node: '>=10'} - - rgb2hex@0.2.5: - resolution: {integrity: sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==} - rolldown@1.2.16: resolution: {integrity: sha512-bWvlMpFKiTfDKfnK7FC2MPgPeWCf/Kpwsq49ApdW+FQaBZb6O6dZ4MAPbPUjp7hYQrw/RobqCzn64BRrQecGHw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - safaridriver@3.0.0: - resolution: {integrity: sha512-NJSsR0tgn5j4fhg7vj4kD5mQ+t2ZRH3GX1jdUYraEjej9j8ZtBXEe4WZh2/89i7S9+GO3cZKWhUisWb7gyjRwA==} - engines: {node: '>=22.19.0'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex2@5.1.1: - resolution: {integrity: sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==} - hasBin: true - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.104.1: resolution: {integrity: sha512-yDA+1aIG3EHgN4V/BvuhCvu61FF6hEd4e+9DxikUm9U0CAGuvdIZ/UYy7qbOxjhbbWQraoyLlMuzdRGOSV5Bmw==} engines: {node: '>=20.19.0'} @@ -4559,16 +3907,9 @@ packages: engines: {node: '>=10'} hasBin: true - serialize-error@13.0.2: - resolution: {integrity: sha512-G+YKwp9I6IvWMe5RlwqxdmTPIcq/G8HK/sU51gdV1XAJznYkRSTB0ph+zB9AfSxG0fx6xrqYA6i9HeIcHFTXow==} - engines: {node: '>=20'} - server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -4580,10 +3921,6 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - smol-toml@1.9.1: resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==} engines: {node: '>= 18'} @@ -4609,21 +3946,10 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - spacetrim@0.11.59: - resolution: {integrity: sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -4633,9 +3959,6 @@ packages: std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} - streamx@2.28.1: - resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} - string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -4643,27 +3966,9 @@ packages: string-ts@2.3.1: resolution: {integrity: sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==} - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - string-width@8.3.0: - resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} - engines: {node: '>=20'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} @@ -4697,13 +4002,6 @@ packages: systemjs@6.15.1: resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} - tagged-tag@1.0.0: - resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} - engines: {node: '>=20'} - - tar-stream@3.2.1: - resolution: {integrity: sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==} - tar@7.5.22: resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} engines: {node: '>=18'} @@ -4712,17 +4010,11 @@ packages: resolution: {gitHosted: true, integrity: sha512-fy5wxkEBj+L3c1ZefPpU6F9Wmhsx9iLJI3xBeYlvpGg7Iqzo6+qR2KcWpGilgHbpJWcPb6c22QMxyLR369vkeQ==, tarball: https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/e49bbedcc67d60da2c10e81bc6433b0f8e8fa232} version: 0.7.0 - teex@1.0.1: - resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} - terser@5.51.2: resolution: {integrity: sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==} engines: {node: '>=10'} hasBin: true - text-decoder@1.2.7: - resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - timers-ext@0.1.8: resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} engines: {node: '>=0.12'} @@ -4743,10 +4035,6 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.1: - resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} - engines: {node: '>=14.0.0'} - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -4778,14 +4066,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@4.26.0: - resolution: {integrity: sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==} - engines: {node: '>=16'} - - type-fest@5.10.0: - resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==} - engines: {node: '>=20'} - type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -4808,9 +4088,6 @@ packages: resolution: {integrity: sha512-t3OptUArewg9OpYGDskTSlxhyLDahfmZ4d36hE44Efb1y3HRLLpzsVqwH+AwBVymP4t7pP71xN+nnduudn2awA==} engines: {node: '>=14'} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} @@ -4818,14 +4095,6 @@ packages: resolution: {integrity: sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==} engines: {node: '>=18.17'} - undici@7.30.1: - resolution: {integrity: sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==} - engines: {node: '>=20.18.1'} - - undici@8.12.3: - resolution: {integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==} - engines: {node: '>=22.19.0'} - unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -4875,21 +4144,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - urlpattern-polyfill@10.1.0: - resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} - use-sync-external-store@1.7.0: resolution: {integrity: sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - userhome@1.0.1: - resolution: {integrity: sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==} - engines: {node: '>= 0.8.0'} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - validator@13.15.35: resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} engines: {node: '>= 0.10'} @@ -5008,43 +4267,13 @@ packages: vscode-uri@3.2.0: resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} - wait-port@1.1.0: - resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} - engines: {node: '>=10'} - hasBin: true - walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - webdriver@10.2.0: - resolution: {integrity: sha512-RV2+j+CAgJgJb59QliIqMik2DmP4l9dW38tDSmzWdilonCAxM5fKZAydpBt6CNlToG0jqpZ9nL3Mo6YnQh/xqw==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - - webdriverio@10.2.0: - resolution: {integrity: sha512-efmXasjo/+tWN+C5HCqsaPIJDJLH7smua1aeizL/DoEDg7f4YYVLgD9Zhav9DYrrouag9C04Mmj6bo6a2KA3hA==} - engines: {node: '>=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1'} - peerDependencies: - puppeteer-core: '>=24 <26' - peerDependenciesMeta: - puppeteer-core: - optional: true - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -5059,26 +4288,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} - - ws@8.22.2: - resolution: {integrity: sha512-/rEZ2ejown2N5gxVTHUu9bVVC7ttu0/Tgzf5hbTOixR3Fs7mbbXgn2DbdbcKgJOmpQMtrIUWivtYuVMnp/W/yA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -5095,25 +4304,10 @@ packages: engines: {node: '>= 14.6'} hasBin: true - yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yargs@18.2.0: - resolution: {integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zip-stream@7.0.5: - resolution: {integrity: sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==} - engines: {node: '>=18'} - zod-validation-error@3.5.4: resolution: {integrity: sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==} engines: {node: '>=18.0.0'} @@ -6249,35 +5443,6 @@ snapshots: dependencies: minipass: 7.1.3 - '@isaacs/nonrecursive-glob-match@1.1.0': {} - - '@jest/diff-sequences@30.5.0': {} - - '@jest/expect-utils@30.5.2': - dependencies: - '@jest/get-type': 30.5.0 - - '@jest/get-type@30.5.0': {} - - '@jest/pattern@30.5.0': - dependencies: - '@types/node': 26.6.5 - jest-regex-util: 30.5.0 - - '@jest/schemas@30.5.0': - dependencies: - '@sinclair/typebox': 0.34.52 - - '@jest/types@30.5.1': - dependencies: - '@jest/pattern': 30.5.0 - '@jest/schemas': 30.5.0 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 26.6.5 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -6663,17 +5828,6 @@ snapshots: '@preact/signals-core@1.14.4': {} - '@promptbook/utils@0.69.5': - dependencies: - spacetrim: 0.11.59 - - '@puppeteer/browsers@3.2.3(yauzl@2.10.0)': - dependencies: - modern-tar: 0.8.5 - yargs: 18.2.0 - optionalDependencies: - yauzl: 2.10.0 - '@remix-run/route-pattern@0.22.1': {} '@rolldown/binding-android-arm-eabi@1.2.16': @@ -6729,8 +5883,6 @@ snapshots: estree-walker: 2.0.2 picomatch: 4.0.7 - '@sinclair/typebox@0.34.52': {} - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: '@babel/core': 7.29.7(supports-color@7.2.0) @@ -6931,16 +6083,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - '@types/json-schema@7.0.15': {} '@types/lodash-es@4.17.12': @@ -6955,14 +6097,6 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@20.19.43': - dependencies: - undici-types: 6.21.0 - - '@types/node@22.20.5': - dependencies: - undici-types: 6.21.0 - '@types/node@26.6.5': dependencies: undici-types: 8.9.0 @@ -6983,14 +6117,6 @@ snapshots: dependencies: csstype: 3.2.3 - '@types/sinonjs__fake-timers@15.0.1': {} - - '@types/split2@4.2.3': - dependencies: - '@types/node': 26.6.5 - - '@types/stack-utils@2.0.3': {} - '@types/trusted-types@2.0.7': optional: true @@ -7000,16 +6126,6 @@ snapshots: '@types/validator@13.15.10': {} - '@types/ws@8.18.1': - dependencies: - '@types/node': 26.6.5 - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.35': - dependencies: - '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.11.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -7206,169 +6322,10 @@ snapshots: optionalDependencies: vite: 8.3.3(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1) - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.1.1 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 - '@vitest/spy@5.0.1': {} - '@vitest/utils@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.1 - '@vscode/l10n@0.0.18': {} - '@wdio/config@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)': - dependencies: - '@wdio/logger': 10.2.0 - '@wdio/types': 10.2.0 - '@wdio/utils': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - deepmerge-ts: 8.0.2 - glob: 14.0.4 - import-meta-resolve: 4.2.0 - jiti: 2.7.0 - transitivePeerDependencies: - - proxy-agent - - supports-color - - yauzl - - '@wdio/globals@9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))': - dependencies: - expect-webdriverio: 6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)) - webdriverio: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - - '@wdio/logger@10.2.0': - dependencies: - chalk: 6.0.1 - loglevel: 1.9.2 - loglevel-plugin-prefix: 0.8.4 - safe-regex2: 5.1.1 - strip-ansi: 7.2.0 - - '@wdio/logger@9.29.1': - dependencies: - chalk: 5.6.2 - loglevel: 1.9.2 - loglevel-plugin-prefix: 0.8.4 - safe-regex2: 5.1.1 - strip-ansi: 7.2.0 - - '@wdio/native-core@1.3.1(supports-color@7.2.0)': - dependencies: - '@wdio/logger': 9.29.1 - '@wdio/native-types': 2.6.1 - '@wdio/native-utils': 2.8.1(supports-color@7.2.0) - debug: 4.4.3(supports-color@7.2.0) - get-port: 7.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - tsx - - '@wdio/native-spy@1.4.1': {} - - '@wdio/native-types@2.6.1': {} - - '@wdio/native-utils@2.8.1(supports-color@7.2.0)': - dependencies: - '@wdio/logger': 9.29.1 - debug: 4.4.3(supports-color@7.2.0) - esbuild: 0.28.2 - find-up-simple: 1.0.1 - json5: 2.2.3 - smol-toml: 1.9.1 - yaml: 2.9.1 - transitivePeerDependencies: - - supports-color - - '@wdio/protocols@10.2.0': {} - - '@wdio/repl@10.2.0': - dependencies: - '@types/node': 22.20.5 - - '@wdio/reporter@9.31.2': - dependencies: - '@types/node': 20.19.43 - '@wdio/logger': 9.29.1 - '@wdio/types': 9.31.2 - diff: 8.0.4 - object-inspect: 1.13.4 - - '@wdio/spec-reporter@9.31.2': - dependencies: - '@wdio/reporter': 9.31.2 - '@wdio/types': 9.31.2 - chalk: 5.6.2 - easy-table: 1.2.0 - pretty-ms: 9.3.1 - - '@wdio/tauri-service@1.5.1(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0))': - dependencies: - '@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)) - '@wdio/logger': 9.29.1 - '@wdio/native-core': 1.3.1(supports-color@7.2.0) - '@wdio/native-spy': 1.4.1 - '@wdio/native-types': 2.6.1 - '@wdio/native-utils': 2.8.1(supports-color@7.2.0) - '@wdio/spec-reporter': 9.31.2 - '@wdio/types': 9.31.2 - debug: 4.4.3(supports-color@7.2.0) - get-port: 7.2.0 - tslib: 2.8.1 - webdriverio: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - transitivePeerDependencies: - - expect-webdriverio - - supports-color - - tsx - - '@wdio/types@10.2.0': - dependencies: - '@types/node': 22.20.5 - - '@wdio/types@9.31.2': - dependencies: - '@types/node': 20.19.43 - - '@wdio/utils@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)': - dependencies: - '@puppeteer/browsers': 3.2.3(yauzl@2.10.0) - '@types/split2': 4.2.3 - '@wdio/logger': 10.2.0 - '@wdio/types': 10.2.0 - aria-query: 5.3.2 - decamelize: 7.0.0 - deepmerge-ts: 8.0.2 - edgedriver: 8.0.0(@wdio/logger@10.2.0) - electron-to-chromium: 1.5.444 - geckodriver: 8.0.0(@wdio/logger@10.2.0) - get-port: 7.2.0 - import-meta-resolve: 4.2.0 - locate-app: 2.5.0 - mitt: 3.0.1 - safaridriver: 3.0.0 - split2: 4.2.0 - wait-port: 1.1.0(supports-color@7.2.0) - transitivePeerDependencies: - - proxy-agent - - supports-color - - yauzl - - '@zip.js/zip.js@2.24.0': {} - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - acorn-jsx@5.3.2(acorn@8.18.0): dependencies: acorn: 8.18.0 @@ -7405,42 +6362,10 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-regex@5.0.1: {} - - ansi-regex@6.3.0: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.3: {} - - archiver@8.0.0: - dependencies: - async: 3.2.6 - buffer-crc32: 1.0.0 - is-stream: 4.0.1 - lazystream: 1.0.1 - normalize-path: 3.0.0 - readable-stream: 4.7.0 - readdir-glob: 3.0.0 - tar-stream: 3.2.1 - zip-stream: 7.0.5 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - argparse@2.0.1: {} - aria-query@5.3.2: {} - assertion-error@2.0.1: {} - async@3.2.6: {} - asynckit@0.4.0: {} axios@1.20.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): @@ -7453,8 +6378,6 @@ snapshots: - debug - supports-color - b4a@1.9.0: {} - babel-plugin-macros@3.1.0: dependencies: '@babel/runtime': 7.29.7 @@ -7489,43 +6412,10 @@ snapshots: balanced-match@4.0.4: {} - bare-events@2.9.2: {} - - bare-fs@4.8.1: - dependencies: - bare-events: 2.9.2 - bare-path: 3.1.2 - bare-stream: 2.13.4(bare-events@2.9.2) - bare-url: 2.5.4 - fast-fifo: 1.3.2 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - bare-path@3.1.2: {} - - bare-stream@2.13.4(bare-events@2.9.2): - dependencies: - b4a: 1.9.0 - streamx: 2.28.1 - teex: 1.0.1 - optionalDependencies: - bare-events: 2.9.2 - transitivePeerDependencies: - - react-native-b4a - - bare-url@2.5.4: - dependencies: - bare-path: 3.1.2 - - base64-js@1.5.1: {} - baseline-browser-mapping@2.11.25: {} before-after-hook@4.0.0: {} - boolbase@1.0.0: {} - brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -7543,18 +6433,8 @@ snapshots: node-releases: 2.0.56 update-browserslist-db: 1.3.3(browserslist@4.29.0) - buffer-crc32@0.2.13: - optional: true - - buffer-crc32@1.0.0: {} - buffer-from@1.1.2: {} - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - cacheable@2.5.0: dependencies: '@cacheable/memory': 2.2.0 @@ -7578,15 +6458,6 @@ snapshots: chai@6.2.2: {} - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@5.6.2: {} - - chalk@6.0.1: {} - character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -7595,37 +6466,12 @@ snapshots: character-reference-invalid@2.0.1: {} - cheerio-select@2.1.0: - dependencies: - boolbase: 1.0.0 - css-select: 5.2.2 - css-what: 6.2.2 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - - cheerio@1.2.0: - dependencies: - cheerio-select: 2.1.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - domutils: 3.2.2 - encoding-sniffer: 0.2.1 - htmlparser2: 10.1.0 - parse5: 7.3.0 - parse5-htmlparser2-tree-adapter: 7.1.0 - parse5-parser-stream: 7.1.2 - undici: 7.30.1 - whatwg-mimetype: 4.0.0 - chokidar@5.0.0: dependencies: readdirp: 5.1.1 chownr@3.0.0: {} - ci-info@4.4.0: {} - cidr-block@2.3.0: {} cli-color@2.0.4: @@ -7638,23 +6484,8 @@ snapshots: client-only@0.0.1: {} - cliui@9.0.1: - dependencies: - string-width: 7.2.0 - strip-ansi: 7.2.0 - wrap-ansi: 9.0.2 - - clone@1.0.4: - optional: true - clsx@2.1.1: {} - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -7665,20 +6496,10 @@ snapshots: commander@2.20.3: {} - commander@9.5.0: {} - comment-parser@1.4.9: {} compare-versions@6.1.1: {} - compress-commons@7.0.1: - dependencies: - crc-32: 1.2.2 - crc32-stream: 7.0.1 - is-stream: 4.0.1 - normalize-path: 3.0.0 - readable-stream: 4.7.0 - content-type@3.1.1: {} convert-source-map@1.9.0: {} @@ -7693,8 +6514,6 @@ snapshots: core-js@3.50.0: {} - core-util-is@1.0.3: {} - cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -7712,13 +6531,6 @@ snapshots: optionalDependencies: typescript: 6.0.3 - crc-32@1.2.2: {} - - crc32-stream@7.0.1: - dependencies: - crc-32: 1.2.2 - readable-stream: 4.7.0 - cross-env@10.1.0: dependencies: '@epic-web/invariant': 1.0.0 @@ -7730,20 +6542,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-select@5.2.2: - dependencies: - boolbase: 1.0.0 - css-what: 6.2.2 - domhandler: 5.0.3 - domutils: 3.2.2 - nth-check: 2.1.1 - - css-shorthand-properties@1.1.2: {} - - css-value@0.0.1: {} - - css-what@6.2.2: {} - csstype@3.2.3: {} d@1.0.2: @@ -7759,25 +6557,12 @@ snapshots: optionalDependencies: supports-color: 7.2.0 - decamelize@6.0.1: {} - - decamelize@7.0.0: {} - decode-named-character-reference@1.3.0: dependencies: character-entities: 2.0.2 - deep-eql@5.0.2: {} - deep-is@0.1.4: {} - deepmerge-ts@8.0.2: {} - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - optional: true - delayed-stream@1.0.0: {} dequal@2.0.3: {} @@ -7788,35 +6573,15 @@ snapshots: dependencies: dequal: 2.0.3 - diff@8.0.4: {} - dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.29.7 csstype: 3.2.3 - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - dompurify@3.4.15: optionalDependencies: '@types/trusted-types': 2.0.7 - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dot-case@3.0.4: dependencies: no-case: 3.0.4 @@ -7828,34 +6593,12 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - easy-table@1.2.0: - dependencies: - ansi-regex: 5.0.1 - optionalDependencies: - wcwidth: 1.0.1 - - edgedriver@8.0.0(@wdio/logger@10.2.0): - dependencies: - '@wdio/logger': 10.2.0 - '@zip.js/zip.js': 2.24.0 - decamelize: 6.0.1 - undici: 8.12.3 - electron-to-chromium@1.5.444: {} - emoji-regex@10.6.0: {} - - encoding-sniffer@0.2.1: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding: 3.1.1 - entities@4.5.0: {} entities@6.0.1: {} - entities@7.0.1: {} - error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -7930,11 +6673,10 @@ snapshots: '@esbuild/win32-arm64': 0.28.2 '@esbuild/win32-ia32': 0.28.2 '@esbuild/win32-x64': 0.28.2 + optional: true escalade@3.2.0: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} eslint-import-context@0.1.9(unrs-resolver@1.12.2): @@ -8215,37 +6957,8 @@ snapshots: event-target-bus@1.1.0: {} - event-target-shim@5.0.1: {} - - events-universal@1.0.1: - dependencies: - bare-events: 2.9.2 - transitivePeerDependencies: - - bare-abort-controller - - events@3.3.0: {} - expect-type@1.4.0: {} - expect-webdriverio@6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)): - dependencies: - '@vitest/snapshot': 4.1.11 - '@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0)) - '@wdio/logger': 9.29.1 - deep-eql: 5.0.2 - expect: 30.5.2 - jest-matcher-utils: 30.5.2 - webdriverio: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - - expect@30.5.2: - dependencies: - '@jest/expect-utils': 30.5.2 - '@jest/get-type': 30.5.0 - jest-matcher-utils: 30.5.2 - jest-message-util: 30.5.1 - jest-mock: 30.5.2 - jest-util: 30.5.1 - ext@1.7.0: dependencies: type: 2.7.3 @@ -8254,8 +6967,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-fifo@1.3.2: {} - fast-fnv1a@1.0.0: {} fast-json-stable-stringify@2.1.0: {} @@ -8266,11 +6977,6 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - optional: true - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -8281,8 +6987,6 @@ snapshots: find-root@1.1.0: {} - find-up-simple@1.0.1: {} - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -8334,20 +7038,8 @@ snapshots: function-bind@1.1.2: {} - geckodriver@8.0.0(@wdio/logger@10.2.0): - dependencies: - '@wdio/logger': 10.2.0 - '@zip.js/zip.js': 2.24.0 - decamelize: 6.0.1 - modern-tar: 0.8.5 - undici: 8.12.3 - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} - - get-east-asian-width@1.7.0: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -8361,8 +7053,6 @@ snapshots: hasown: 2.0.4 math-intrinsics: 1.1.0 - get-port@7.2.0: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -8376,21 +7066,12 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@14.0.4: - dependencies: - minimatch: 11.0.5 - minipass: 7.1.3 - path-scurry: 3.0.0 - globals@17.12.0: {} gopd@1.2.0: {} - graceful-fs@4.2.11: {} - - grapheme-splitter@1.0.4: {} - - has-flag@4.0.0: {} + has-flag@4.0.0: + optional: true has-symbols@1.1.0: {} @@ -8505,15 +7186,6 @@ snapshots: html-void-elements@3.0.0: {} - htmlfy@0.8.1: {} - - htmlparser2@10.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 7.0.1 - https-proxy-agent@5.0.1(supports-color@7.2.0): dependencies: agent-base: 6.0.2(supports-color@7.2.0) @@ -8536,18 +7208,10 @@ snapshots: optionalDependencies: typescript: 6.0.3 - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - ignore@5.3.2: {} ignore@7.0.9: {} - immediate@3.0.6: {} - immutable@5.1.9: {} import-fresh@3.3.1: @@ -8555,12 +7219,8 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-meta-resolve@4.2.0: {} - imurmurhash@0.1.4: {} - inherits@2.0.4: {} - inline-style-parser@0.2.7: {} is-alphabetical@2.0.1: {} @@ -8594,57 +7254,8 @@ snapshots: is-promise@2.2.2: {} - is-stream@4.0.1: {} - - isarray@1.0.0: {} - isexe@2.0.0: {} - jest-diff@30.5.2: - dependencies: - '@jest/diff-sequences': 30.5.0 - '@jest/get-type': 30.5.0 - chalk: 4.1.2 - pretty-format: 30.5.1 - - jest-matcher-utils@30.5.2: - dependencies: - '@jest/get-type': 30.5.0 - chalk: 4.1.2 - jest-diff: 30.5.2 - pretty-format: 30.5.1 - - jest-message-util@30.5.1: - dependencies: - '@babel/code-frame': 7.29.7 - '@jest/types': 30.5.1 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-util: 30.5.1 - picomatch: 4.0.7 - pretty-format: 30.5.1 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@30.5.2: - dependencies: - '@jest/expect-utils': 30.5.2 - '@jest/types': 30.5.1 - '@types/node': 26.6.5 - jest-util: 30.5.1 - - jest-regex-util@30.5.0: {} - - jest-util@30.5.1: - dependencies: - '@jest/types': 30.5.1 - '@types/node': 26.6.5 - chalk: 4.1.2 - ci-info: 4.4.0 - graceful-fs: 4.2.11 - picomatch: 4.0.7 - jiti@2.7.0: {} js-cookie@3.0.8: {} @@ -8673,13 +7284,6 @@ snapshots: jsonc-parser@3.3.1: {} - jszip@3.10.2: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -8698,19 +7302,11 @@ snapshots: unbash: 6.0.4 yaml: 2.9.1 - lazystream@1.0.1: - dependencies: - readable-stream: 2.3.8 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - lie@3.3.0: - dependencies: - immediate: 3.0.6 - lightningcss-android-arm64@1.33.0: optional: true @@ -8770,28 +7366,16 @@ snapshots: optionalDependencies: yaml: 2.9.1 - locate-app@2.5.0: - dependencies: - '@promptbook/utils': 0.69.5 - type-fest: 4.26.0 - userhome: 1.0.1 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash-es@4.18.1: {} - lodash.clonedeep@4.5.0: {} - lodash.debounce@4.0.8: {} lodash@4.18.1: {} - loglevel-plugin-prefix@0.8.4: {} - - loglevel@1.9.2: {} - longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -8802,8 +7386,6 @@ snapshots: dependencies: tslib: 2.8.1 - lru-cache@11.5.3: {} - lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -8812,10 +7394,6 @@ snapshots: dependencies: es5-ext: 0.10.64 - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - magic-string@1.4.1: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -9071,21 +7649,12 @@ snapshots: dependencies: brace-expansion: 5.0.12 - minimatch@11.0.5: - dependencies: - '@isaacs/nonrecursive-glob-match': 1.1.0 - brace-expansion: 5.0.12 - minipass@7.1.3: {} minizlib@3.1.0: dependencies: minipass: 7.1.3 - mitt@3.0.1: {} - - modern-tar@0.8.5: {} - monaco-editor@0.57.0: dependencies: dompurify: 3.4.15 @@ -9146,18 +7715,8 @@ snapshots: node-releases@2.0.56: {} - non-error@0.1.0: {} - - normalize-path@3.0.0: {} - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - object-assign@4.1.1: {} - object-inspect@1.13.4: {} - obug@2.2.1: {} optionator@0.9.4: @@ -9225,8 +7784,6 @@ snapshots: package-manager-detector@1.8.0: {} - pako@1.0.11: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -9248,17 +7805,6 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse-ms@4.0.0: {} - - parse5-htmlparser2-tree-adapter@7.1.0: - dependencies: - domhandler: 5.0.3 - parse5: 7.3.0 - - parse5-parser-stream@7.1.2: - dependencies: - parse5: 7.3.0 - parse5@7.3.0: dependencies: entities: 6.0.1 @@ -9271,18 +7817,8 @@ snapshots: path-parse@1.0.7: {} - path-scurry@3.0.0: - dependencies: - lru-cache: 11.5.3 - minipass: 7.1.3 - path-type@4.0.0: {} - pathe@2.0.3: {} - - pend@1.2.0: - optional: true - picocolors@1.1.1: {} picomatch@4.0.7: {} @@ -9297,21 +7833,6 @@ snapshots: prettier@3.9.8: {} - pretty-format@30.5.1: - dependencies: - '@jest/react-is-18': react-is@18.3.1 - '@jest/react-is-19': react-is@19.3.0 - '@jest/schemas': 30.5.0 - ansi-styles: 5.2.0 - - pretty-ms@9.3.1: - dependencies: - parse-ms: 4.0.0 - - process-nextick-args@2.0.1: {} - - process@0.11.10: {} - prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -9336,8 +7857,6 @@ snapshots: dependencies: react: 19.3.1 - query-selector-shadow-dom@1.0.1: {} - react-dom@19.3.1(react@19.3.1): dependencies: react: 19.3.1 @@ -9366,8 +7885,6 @@ snapshots: react-is@16.13.1: {} - react-is@18.3.1: {} - react-is@19.3.0: {} react-markdown@10.1.0(@types/react@19.3.0)(react@19.3.1)(supports-color@7.2.0): @@ -9407,28 +7924,6 @@ snapshots: react@19.3.1: {} - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readdir-glob@3.0.0: - dependencies: - minimatch: 10.2.6 - readdirp@5.1.1: {} regenerate-unicode-properties@10.2.2: @@ -9495,10 +7990,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - ret@0.5.0: {} - - rgb2hex@0.2.5: {} - rolldown@1.2.16: dependencies: '@oxc-project/types': 0.156.0 @@ -9520,18 +8011,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.16 '@rolldown/binding-win32-x64-msvc': 1.2.16 - safaridriver@3.0.0: {} - - safe-buffer@5.1.2: {} - - safe-buffer@5.2.1: {} - - safe-regex2@5.1.1: - dependencies: - ret: 0.5.0 - - safer-buffer@2.1.2: {} - sass@1.104.1: dependencies: chokidar: 5.0.0 @@ -9548,15 +8027,8 @@ snapshots: semver@7.8.5: {} - serialize-error@13.0.2: - dependencies: - non-error: 0.1.0 - type-fest: 5.10.0 - server-only@0.0.1: {} - setimmediate@1.0.5: {} - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -9565,8 +8037,6 @@ snapshots: siginfo@2.0.0: {} - slash@3.0.0: {} - smol-toml@1.9.1: {} snake-case@3.0.4: @@ -9587,63 +8057,23 @@ snapshots: space-separated-tokens@2.0.2: {} - spacetrim@0.11.59: {} - - split2@4.2.0: {} - stable-hash-x@0.2.0: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - stackback@0.0.2: {} state-local@1.0.7: {} std-env@4.2.0: {} - streamx@2.28.1: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - string-argv@0.3.2: {} string-ts@2.3.1: {} - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.7.0 - strip-ansi: 7.2.0 - - string-width@8.3.0: - dependencies: - get-east-asian-width: 1.7.0 - strip-ansi: 7.2.0 - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.3.0 - strip-json-comments@5.0.3: {} style-to-js@1.1.21: @@ -9659,6 +8089,7 @@ snapshots: supports-color@7.2.0: dependencies: has-flag: 4.0.0 + optional: true supports-preserve-symlinks-flag@1.0.0: {} @@ -9672,19 +8103,6 @@ snapshots: systemjs@6.15.1: {} - tagged-tag@1.0.0: {} - - tar-stream@3.2.1: - dependencies: - b4a: 1.9.0 - bare-fs: 4.8.1 - fast-fifo: 1.3.2 - streamx: 2.28.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - tar@7.5.22: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -9697,13 +8115,6 @@ snapshots: dependencies: '@tauri-apps/api': 2.12.4 - teex@1.0.1: - dependencies: - streamx: 2.28.1 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - terser@5.51.2: dependencies: '@jridgewell/source-map': 0.3.11 @@ -9711,12 +8122,6 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - text-decoder@1.2.7: - dependencies: - b4a: 1.9.0 - transitivePeerDependencies: - - react-native-b4a - timers-ext@0.1.8: dependencies: es5-ext: 0.10.64 @@ -9733,8 +8138,6 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 - tinyrainbow@3.1.1: {} - trim-lines@3.0.1: {} trough@2.2.0: {} @@ -9763,12 +8166,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@4.26.0: {} - - type-fest@5.10.0: - dependencies: - tagged-tag: 1.0.0 - type@2.7.3: {} types-pac@1.0.3: {} @@ -9788,16 +8185,10 @@ snapshots: unbash@6.0.4: {} - undici-types@6.21.0: {} - undici-types@8.9.0: {} undici@6.29.1: {} - undici@7.30.1: {} - - undici@8.12.3: {} - unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -9881,16 +8272,10 @@ snapshots: dependencies: punycode: 2.3.1 - urlpattern-polyfill@10.1.0: {} - use-sync-external-store@1.7.0(react@19.3.1): dependencies: react: 19.3.1 - userhome@1.0.1: {} - - util-deprecate@1.0.2: {} - validator@13.15.35: {} vfile-location@5.0.3: @@ -9969,86 +8354,10 @@ snapshots: vscode-uri@3.2.0: {} - wait-port@1.1.0(supports-color@7.2.0): - dependencies: - chalk: 4.1.2 - commander: 9.5.0 - debug: 4.4.3(supports-color@7.2.0) - transitivePeerDependencies: - - supports-color - walk-up-path@4.0.0: {} - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - optional: true - web-namespaces@2.0.1: {} - webdriver@10.2.0(supports-color@7.2.0)(yauzl@2.10.0): - dependencies: - '@types/node': 22.20.5 - '@types/ws': 8.18.1 - '@wdio/config': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - '@wdio/logger': 10.2.0 - '@wdio/protocols': 10.2.0 - '@wdio/types': 10.2.0 - '@wdio/utils': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - deepmerge-ts: 8.0.2 - https-proxy-agent: 9.1.0(supports-color@7.2.0) - undici: 8.12.3 - ws: 8.22.2 - transitivePeerDependencies: - - bufferutil - - kerberos - - proxy-agent - - supports-color - - utf-8-validate - - yauzl - - webdriverio@10.2.0(supports-color@7.2.0)(yauzl@2.10.0): - dependencies: - '@types/node': 22.20.5 - '@types/sinonjs__fake-timers': 15.0.1 - '@wdio/config': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - '@wdio/logger': 10.2.0 - '@wdio/protocols': 10.2.0 - '@wdio/repl': 10.2.0 - '@wdio/types': 10.2.0 - '@wdio/utils': 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - archiver: 8.0.0 - aria-query: 5.3.2 - cheerio: 1.2.0 - css-shorthand-properties: 1.1.2 - css-value: 0.0.1 - grapheme-splitter: 1.0.4 - htmlfy: 0.8.1 - is-plain-obj: 4.1.0 - jszip: 3.10.2 - lodash.clonedeep: 4.5.0 - query-selector-shadow-dom: 1.0.1 - rgb2hex: 0.2.5 - serialize-error: 13.0.2 - urlpattern-polyfill: 10.1.0 - webdriver: 10.2.0(supports-color@7.2.0)(yauzl@2.10.0) - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - kerberos - - proxy-agent - - react-native-b4a - - supports-color - - utf-8-validate - - yauzl - - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@4.0.0: {} - which@2.0.2: dependencies: isexe: 2.0.0 @@ -10060,16 +8369,6 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@9.0.2: - dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.2.0 - - ws@8.22.2: {} - - y18n@5.0.8: {} - yallist@3.1.1: {} yallist@5.0.0: {} @@ -10078,31 +8377,8 @@ snapshots: yaml@2.9.1: {} - yargs-parser@22.0.0: {} - - yargs@18.2.0: - dependencies: - cliui: 9.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - string-width: 8.3.0 - y18n: 5.0.8 - yargs-parser: 22.0.0 - - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - optional: true - yocto-queue@0.1.0: {} - zip-stream@7.0.5: - dependencies: - compress-commons: 7.0.1 - normalize-path: 3.0.0 - readable-stream: 4.7.0 - zod-validation-error@3.5.4(zod@3.25.76): dependencies: zod: 3.25.76 diff --git a/renovate.json b/renovate.json index 1a3515fa0..a2c6134d1 100644 --- a/renovate.json +++ b/renovate.json @@ -50,12 +50,7 @@ }, { "description": "Update Tauri npm packages and Cargo crates together so frontend and backend stay in sync", - "matchPackageNames": [ - "@tauri-apps/**", - "@wdio/tauri-service", - "tauri", - "tauri-*" - ], + "matchPackageNames": ["@tauri-apps/**", "tauri", "tauri-*"], "groupName": "tauri", "addLabels": ["tauri"] }, diff --git a/scripts/perf/AGENTS.md b/scripts/perf/AGENTS.md new file mode 100644 index 000000000..749d307ae --- /dev/null +++ b/scripts/perf/AGENTS.md @@ -0,0 +1,28 @@ +# Product Measurement Adapter Guidelines + +[GUIDE.md](GUIDE.md) defines the product adapter boundary and explicit tool invocation. +Keep real component imports, initialization and the isolated native entry here, +using product frontend/native build dependencies. Do not copy components into the +tool. Preserve perf build gates; ordinary builds must not require the external tool. + +The adapter must not load or modify daily configuration or control the daily app, +Core, Service, TUN, DNS or system proxy. Preserve loopback isolation, owned-process +PID/start attribution, nonactivation, visibility/occlusion and source continuity. +Do not weaken gates or substitute Chrome/Vite for native WebView evidence. Label +JavaScript adapters separately from native plugin/IPC paths. Expected startup errors +must use the owned app's normal nonzero exit, never panic through Tauri setup. + +Review adapter source changes, including initialization and native/build configuration, +for changed measurement meaning. Matching interfaces alone do not prove equivalence; +an adapter review cannot waive other acceptance conditions. + +For implementation changes, run scoped ESLint, both perf TypeScript projects +(`tsconfig.json` and `tsconfig.node.json`), formatting and relevant production checks, +including Rust checks for native changes. Run scoped Rust Clippy before freezing +the optimized artifact. Check ordinary-build exclusion and application-entry prerequisites. + +Follow the external AGENTS.md's +[measurement-validation requirements](https://github.com/clash-verge-rev/clash-verge-rev-harness/blob/main/AGENTS.md) +and README's +[validation order](https://github.com/clash-verge-rev/clash-verge-rev-harness/blob/main/README.md#validate-before-freezing-a-build). +These govern affected native coverage, comparison, failure cleanup and evidence reuse. diff --git a/scripts/perf/GUIDE.md b/scripts/perf/GUIDE.md index 9623b4863..fdf7d5b7c 100644 --- a/scripts/perf/GUIDE.md +++ b/scripts/perf/GUIDE.md @@ -1,140 +1,35 @@ -# macOS Traffic Performance Harness +# Product measurement adapter -This guide is the operating and maintenance contract for humans and coding agents. -The only scenario is the production home traffic graph in a dedicated, optimized -Tauri WKWebView. It measures foreground updates, native hide, then restoration. -It does not initialize the complete homepage or run Mihomo, Service or networking. +This directory owns the isolated native entry, real Traffic/Logs/Connections +imports, initialization, JavaScript socket adapter, replay receiver and minimal DOM +layer. These evolve with product APIs and use the product's React/Tauri/Wry +versions. Keep the explicit perf build gates; ordinary builds need no external tool. -## Build And Run - -Use this repository's pnpm, Rust and Xcode Command Line Tools on macOS. No global -WebDriver installation is needed. Keep each binary beside its generated manifest. +The external [clash-verge-rev-harness operating guide](https://github.com/clash-verge-rev/clash-verge-rev-harness/blob/main/README.md) +owns running, sampling, reports, comparison and owned cleanup. Follow its +[first setup and installation instructions](https://github.com/clash-verge-rev/clash-verge-rev-harness/blob/main/README.md#build-and-run), +reusing an existing checkout. For subsequent calls, pass both absolute roots: ```sh -pnpm install --frozen-lockfile -pnpm perf:build target/perf/baseline-build -caffeinate -di pnpm perf:run -- --binary target/perf/baseline-build/clash-verge --output target/perf/baseline --repeat 3 --seconds 20 --warmup 10 -pnpm perf:compare -- target/perf/baseline target/perf/baseline -pnpm perf:test +project=/absolute/path/to/clash-verge-rev +harness=/absolute/path/to/clash-verge-rev-harness +node "$harness/build.mjs" --project "$project" --output target/perf/build +caffeinate -di node "$harness/run.mjs" --project "$project" --binary target/perf/build/clash-verge --output target/perf/smoke --repeat 1 --seconds 5 --warmup 2 +node "$harness/compare.mjs" "$project/target/perf/smoke" "$project/target/perf/smoke" ``` -`perf:build [directory]` uses release optimization, Vite `--mode perf` and the -explicit Cargo `perf-harness` feature. It preserves the binary and records HEAD, -dirty state, compiled source, lockfile, frontend, build-settings and binary hashes. -Source changes during compilation reject the manifest. Each run also hashes the -external collector and runner. Ordinary builds exclude the driver and measurement -entry; debug assertions are not the gate. Do not edit compiled inputs during builds. +JavaScript socket replacement does not measure native plugin IPC. DOM mutation/text +hashes show view-input delivery, not painting or cache ownership. Adapter changes +require reviewing measurement meaning under the external validation requirements. -Output directories must be new and Git ignored; use `target/perf/`. JSON/CSV keep -raw process samples, observations, phase markers, attribution and cleanup evidence. -`summary.json` and `report.md` provide distributions by independent run, phase and -process role. Failed runs exit nonzero and retain their evidence. The explicit -`traffic-webview-v3` schema rejects older reports instead of silently migrating them. +For virtual display mode, this adapter places only the hidden measurement window +on the exact owned NSScreen, checks complete visibleFrame containment, mode/Space +flags and nonactivation, and exits on drift. The tool owns helper lifecycle and topology +restoration; see its +[virtual display guide](https://github.com/clash-verge-rev/clash-verge-rev-harness/blob/main/README.md#optional-virtual-display). -## Isolation And Visibility - -The dedicated measurement main bypasses normal initialization and shutdown. It -does not read or write daily configuration, subscriptions, proxy, TUN or Service. -It uses a distinct app identity and incognito WebView. Both automatic services -bind loopback and are owned by the current run; never kill processes by name. - -The measurement window stays above ordinary windows and covers part of the screen, -without activation or mouse capture: clicks pass through to the application behind it. -Native topmost and click-through states are verified, while native focus and application -activation must remain false. WebDriver uses the embedded WebView directly; DOM focus -is recorded but not required. Daily windows are unchanged; the topmost window exits -with its owned measurement instance. You may keep using another application. -The console must be unlocked. Lock state is checked before startup and recorded -at phase boundaries; unknown state fails and observed drift rejects comparison. -Boundary checks do not provide continuous lock monitoring or unlock the computer. - -No focus does not mean no visibility: shown stages require `document.hidden=false`, -fresh Worker data, advancing Canvas draw timestamps and changed Canvas pixels. -Topmost placement avoids ordinary-window occlusion; system overlays or another Space -can still pause WebKit. Such a run fails; do not weaken Canvas assertions to make it -pass. `caffeinate -di` lasts only for the command and cannot prevent deliberate lock. -Hidden stages still require continuous input and Worker data. - -## Fixed Workload - -The native window is 1000 x 700 logical pixels. Verify driver dimensions against -DPR and record the actual CSS viewport; titlebar space is excluded. The graph is -920 x 360 with a ten-minute range, default bezier style and real data retention. -The measurement preload sets blur pause false without changing production defaults. - -Deterministic loopback WebSocket replay enters the public traffic append boundary, -then the production Worker and Canvas. It does not cover Mihomo or network traffic. -There is no history prefill. Absolute 200 ms deadlines sustain 5 Hz without cumulative -timer drift or catch-up bursts; missing a complete period fails the source. -Warmup is at least the requested duration with a five-second readiness allowance. -Fixed input anchors reserve two seconds for each native transition; measured stages -retain their full duration. Readiness uses observable state and deadlines, not sleep. -Before the first show, wait for actual replay input and Worker data so the graph's -visibility listeners are installed; then require fresh Canvas output after showing. -Total replay, including allowances, must remain below ten minutes for raw history. -Two-input endpoint variation is 400 ms; larger differences reject comparison. - -Worker snapshots are asynchronous. Count/latest-value checks verify deterministic -upload/download values at the retained point count, with a joint period of 60 and -received-minus-retained count below 60. This detects net missing/duplicate points, -not every historical element or exactly cancelling losses and duplicates. -Source age must be below 2500 ms, Worker age below 3500 ms and visible draw age below -2500 ms. Future timestamps, lost sequence, process changes and failed restore fail. - -## Metrics And Comparison - -The native command obtains the actual WKWebView process ID using the private -`_webProcessIdentifier` selector, with availability checking. External collection -verifies the canonical system WebContent executable and process start identity. -It handles PID reuse and exits; unsupported ownership fails closed. Main and WebView -remain separate. Mihomo is not started; shared GPU/network attribution is unavailable. - -CPU is cumulative process time converted from Mach ticks; interval percentages use -adjacent valid samples, with one occupied core equal to 100%. The first difference -is unavailable. Memory is RSS bytes, not physical footprint or exclusive memory. -Reports include CPU mean/P50/P95/peak/seconds, RSS median/P95/peak/delta, sample count -and time coverage. Memory slope requires at least 60 seconds of continuous samples. -RSS growth is not proof of a leak; process statistics are not function profiles. -The embedded driver adds a 20 Hz macOS runloop timer and observation overhead. - -For A/B, build the candidate separately and alternate independent instances: - -```sh -pnpm perf:build target/perf/candidate-build -for i in 1 2 3; do - caffeinate -di pnpm perf:run -- --binary target/perf/baseline-build/clash-verge --repeat 1 --output target/perf/ab-baseline/$i - caffeinate -di pnpm perf:run -- --binary target/perf/candidate-build/clash-verge --repeat 1 --output target/perf/ab-candidate/$i -done -pnpm perf:compare -- target/perf/ab-baseline target/perf/ab-candidate -pnpm perf:run -- --binary target/perf/candidate-build/clash-verge --repeat 1 --seconds 5 --warmup 2 --fail-phase hidden --output target/perf/failed -``` - -Comparison accepts summary files, run directories or groups of run directories. -Build mode, toolchain, platform, host, power, WebKit, viewport and workload must match. -Invalid and incomparable reports exit 2. A/A remains inconclusive. A/B compares -independent-run summaries, not adjacent time samples. An optional third numeric -argument is a percentage floor justified by baseline noise; there is no universal -pass threshold. Fewer than three runs per group, overlapping ranges or differences -within either group's range remain inconclusive. Background load limits inference. - -## Automation Delivery Boundary - -When the operator is a coding agent, the final change keeps only the -performance-optimization code. Harness additions introduced during the -work — measurement entries, configs, scripts, reports, manifests — must -stay untracked by Git and out of every commit; keep them on ignored -paths or remove them before delivery. Commits remain minimal and -necessary for the optimization itself. Track or commit harness files -only when a human explicitly requests it at the end. - -## Maintenance And Acceptance - -Keep measurement code behind the explicit build gates. Reuse the existing driver, -Worker, graph, attribution and cleanup; do not add mock IPC or replace real WebView -acceptance with Chrome/Vite. Preserve source/binary/report hashes and every failed run. -After changes, run scoped ESLint, both perf TypeScript projects, `pnpm perf:test`, -format checks and relevant production checks. Verify a real three-phase smoke, -three independent runs, A/A, invalid/incomparable reports and injected-failure cleanup. -Verify ordinary builds exclude measurement code and daily instance state is intact. -Changes to contracts require a schema bump and new reports. Use bounded WebKit CPU -or Allocation/Heap captures for deeper diagnosis; do not infer hotspots from counters. +`isolation.json` owns the product configuration and process watch scope; keep it +aligned with product data directories and executable names. The tool owns +[command isolation snapshots](https://github.com/clash-verge-rev/clash-verge-rev-harness/blob/main/README.md#command-isolation-snapshots). +The adapter must not load daily configuration or control Core, Service, TUN, DNS +or system proxy. diff --git a/scripts/perf/adapter.ts b/scripts/perf/adapter.ts new file mode 100644 index 000000000..2a4a5ead1 --- /dev/null +++ b/scripts/perf/adapter.ts @@ -0,0 +1,57 @@ +import { MihomoWebSocket } from 'tauri-plugin-mihomo-api' + +import { subscribe, scenario } from './replay' + +// Only this perf entry replaces socket transport; the normal application never imports it. +export function installAdapter() { + const connect = async (kind: string) => { + const data = document.documentElement.dataset + data.perfSubscriptions = String(Number(data.perfSubscriptions || 0) + 1) + const callbacks = new Set< + (message: { type: 'Text'; data: string }) => void + >() + const unsubscribe = subscribe((frame) => { + const messages = + kind === 'logs' + ? (frame.payload as ILogItem[]) + : [ + kind === 'traffic' + ? { up: frame.sequence * 1000, down: frame.sequence * 5000 } + : frame.payload, + ] + for (const payload of messages) { + const message = { type: 'Text' as const, data: JSON.stringify(payload) } + callbacks.forEach((callback) => { + callback(message) + data.perfDelivered = String(Number(data.perfDelivered || 0) + 1) + }) + } + }) + return { + addListener: ( + callback: (message: { type: 'Text'; data: string }) => void, + ) => { + callbacks.add(callback) + return () => callbacks.delete(callback) + }, + close: async () => { + unsubscribe() + callbacks.clear() + data.perfSubscriptions = String(Number(data.perfSubscriptions) - 1) + }, + } as unknown as MihomoWebSocket + } + MihomoWebSocket.connect_logs = async () => connect('logs') + MihomoWebSocket.connect_connections = async () => connect('connections') + MihomoWebSocket.connect_traffic = async () => connect('traffic') + if (scenario === 'logs') + localStorage.setItem( + 'clash-log', + JSON.stringify({ + enable: true, + logLevel: 'INFO', + logFilter: 'all', + logOrder: 'desc', + }), + ) +} diff --git a/scripts/perf/app.rs b/scripts/perf/app.rs index cdd2245b5..546743d58 100644 --- a/scripts/perf/app.rs +++ b/scripts/perf/app.rs @@ -1,5 +1,136 @@ use objc2::{class, msg_send, runtime::AnyObject, sel}; -use tauri::WebviewWindow; +use objc2_foundation::{NSPoint, NSRect, ns_string}; +use tauri::{Manager as _, WebviewWindow}; + +#[link(name = "CoreGraphics", kind = "framework")] +unsafe extern "C" { + fn CGDisplayCopyDisplayMode(display: u32) -> *mut std::ffi::c_void; + fn CGDisplayModeGetRefreshRate(mode: *mut std::ffi::c_void) -> f64; + fn CGDisplayModeRelease(mode: *mut std::ffi::c_void); + fn CGMainDisplayID() -> u32; + fn CGDisplayIsInMirrorSet(display: u32) -> u32; + fn CGDisplayModeGetWidth(mode: *mut std::ffi::c_void) -> usize; + fn CGDisplayModeGetHeight(mode: *mut std::ffi::c_void) -> usize; + fn CGDisplayModeGetPixelWidth(mode: *mut std::ffi::c_void) -> usize; + fn CGDisplayModeGetPixelHeight(mode: *mut std::ffi::c_void) -> usize; +} + +fn expected_display() -> Result, String> { + match std::env::var("PERF_DISPLAY_MODE").as_deref() { + Ok("physical") | Err(std::env::VarError::NotPresent) => Ok(None), + Ok("virtual") => { + let id = std::env::var("PERF_DISPLAY_ID") + .map_err(|e| e.to_string())? + .parse::() + .map_err(|e| e.to_string())?; + if id == 0 { + return Err("invalid owned display ID".into()); + } + Ok(Some(id)) + } + _ => Err("unknown measurement display mode".into()), + } +} +const fn bounds(rect: NSRect) -> [f64; 4] { + [rect.origin.x, rect.origin.y, rect.size.width, rect.size.height] +} +fn contained(frame: NSRect, screen: NSRect) -> bool { + frame.origin.x >= screen.origin.x + && frame.origin.y >= screen.origin.y + && frame.origin.x + frame.size.width <= screen.origin.x + screen.size.width + && frame.origin.y + frame.size.height <= screen.origin.y + screen.size.height +} +unsafe fn screen_id(screen: *mut AnyObject) -> u32 { + unsafe { + if screen.is_null() { + return 0; + } + let dictionary: *mut AnyObject = msg_send![screen, deviceDescription]; + let value: *mut AnyObject = msg_send![dictionary, objectForKey: ns_string!("NSScreenNumber")]; + msg_send![value, unsignedIntValue] + } +} +unsafe fn display_state(native: &AnyObject) -> Result, String> { + let Some(wanted) = expected_display()? else { + return Ok(None); + }; + unsafe { + let screen: *mut AnyObject = msg_send![native, screen]; + let id = screen_id(screen); + if id == 0 || id != wanted || id == CGMainDisplayID() || CGDisplayIsInMirrorSet(id) != 0 { + return Err("owned virtual screen unavailable".into()); + } + let frame: NSRect = msg_send![screen, frame]; + let visible: NSRect = msg_send![screen, visibleFrame]; + let window_frame: NSRect = msg_send![native, frame]; + let scale: f64 = msg_send![screen, backingScaleFactor]; + let behavior: usize = msg_send![native, collectionBehavior]; + let mode = CGDisplayCopyDisplayMode(id); + if mode.is_null() { + return Err("owned display mode unavailable".into()); + } + let rate = CGDisplayModeGetRefreshRate(mode); + let logical = [CGDisplayModeGetWidth(mode), CGDisplayModeGetHeight(mode)]; + let pixels = [CGDisplayModeGetPixelWidth(mode), CGDisplayModeGetPixelHeight(mode)]; + CGDisplayModeRelease(mode); + if logical != [1280, 900] + || pixels != [2560, 1800] + || scale != 2.0 + || (rate - 60.0).abs() > 0.01 + || frame.size.width != 1280.0 + || frame.size.height != 900.0 + || !contained(window_frame, visible) + || behavior & 1 == 0 + || behavior & 2 != 0 + { + return Err("owned virtual screen geometry/mode changed".into()); + } + Ok(Some( + serde_json::json!({"id": id, "frame": bounds(frame), "visibleFrame": bounds(visible), "windowFrame": bounds(window_frame), "scale": scale, "refreshHz": rate, "collectionBehavior": behavior, "contained": true, "main": false, "mirrored": false, "logical": logical, "pixels": pixels}), + )) + } +} +unsafe fn target_screen(wanted: u32) -> Result<*mut AnyObject, String> { + unsafe { + let screens: *mut AnyObject = msg_send![class!(NSScreen), screens]; + let count: usize = msg_send![screens, count]; + let mut target = std::ptr::null_mut::(); + for index in 0..count { + let screen: *mut AnyObject = msg_send![screens, objectAtIndex: index]; + if screen_id(screen) == wanted { + target = screen; + break; + } + } + if target.is_null() || wanted == CGMainDisplayID() { + return Err("exact owned virtual display missing".into()); + } + Ok(target) + } +} +unsafe fn place_virtual(window: &WebviewWindow) -> Result<(), String> { + unsafe { + let Some(wanted) = expected_display()? else { + return Ok(()); + }; + let target = target_screen(wanted)?; + let native = &*(window.ns_window().map_err(|e| e.to_string())? as *const AnyObject); + let visible: NSRect = msg_send![target, visibleFrame]; + let frame: NSRect = msg_send![native, frame]; + if frame.size.width > visible.size.width || frame.size.height > visible.size.height { + return Err("owned window does not fit virtual visibleFrame".into()); + } + let behavior: usize = msg_send![native, collectionBehavior]; + let _: () = msg_send![native, setCollectionBehavior: (behavior & !2usize) | 1usize]; + let origin = NSPoint::new( + visible.origin.x + (visible.size.width - frame.size.width) / 2.0, + visible.origin.y + (visible.size.height - frame.size.height) / 2.0, + ); + let _: () = msg_send![native, setFrameOrigin: origin]; + display_state(native)?; + Ok(()) + } +} #[tauri::command] async fn perf_state(window: WebviewWindow, action: String) -> Result { @@ -7,16 +138,28 @@ async fn perf_state(window: WebviewWindow, action: String) -> Result(); + let display = match display_state(native) { + Ok(state) => state, + Err(error) => { + let _: () = msg_send![native, orderOut: sender]; + let _ = tx.send(Err(error)); + fail_app.exit(2); + return; + } + }; if action == "show" { let _: () = msg_send![native, orderBack: sender]; } else if action == "hide" { let _: () = msg_send![native, orderOut: sender]; } let visible: bool = msg_send![native, isVisible]; + let occlusion: usize = msg_send![native, occlusionState]; + let active_space: bool = msg_send![native, isOnActiveSpace]; let focused: bool = msg_send![native, isKeyWindow]; let click_through: bool = msg_send![native, ignoresMouseEvents]; let app: *const AnyObject = msg_send![class!(NSApplication), sharedApplication]; @@ -28,21 +171,41 @@ async fn perf_state(window: WebviewWindow, action: String) -> Result Vec { + Vec::new() +} + +#[tauri::command] +fn get_verge_config() -> serde_json::Value { + serde_json::json!({"language": "en", "theme_mode": "light"}) +} + pub fn run() -> std::process::ExitCode { #[cfg(feature = "clippy")] let context = tauri::test::mock_context(tauri::test::noop_assets()); @@ -50,23 +213,66 @@ pub fn run() -> std::process::ExitCode { let context = tauri::generate_context!(); let result = tauri::Builder::default() .plugin(tauri_plugin_wdio_webdriver::init()) - .invoke_handler(tauri::generate_handler![perf_state]) + .invoke_handler(tauri::generate_handler![perf_state, get_clash_logs, get_verge_config]) .setup(|app| { - app.handle().set_activation_policy(tauri::ActivationPolicy::Accessory)?; - let port: u16 = std::env::var("PERF_REPLAY_PORT")?.parse()?; - tauri::WebviewWindowBuilder::new( - app, - "perf", - tauri::WebviewUrl::App(format!("index.html?port={port}").into()), - ) - .title("Clash Verge - performance replay") - .inner_size(1000., 700.) - .visible(false) - .focused(false) - .always_on_top(true) - .incognito(true) - .build()? - .set_ignore_cursor_events(true)?; + let startup = (|| -> Result<(), Box> { + app.handle() + .set_activation_policy(tauri::ActivationPolicy::Prohibited)?; + let scenario = std::env::var("PERF_SCENARIO").unwrap_or_else(|_| "traffic".into()); + let layer = std::env::var("PERF_LAYER").unwrap_or_else(|_| "component".into()); + if !matches!(scenario.as_str(), "traffic" | "logs" | "connections") + || !matches!(layer.as_str(), "page" | "component" | "dom") + { + return Err("invalid perf scene".into()); + } + if let Some(wanted) = expected_display().map_err(std::io::Error::other)? { + unsafe { + target_screen(wanted).map_err(std::io::Error::other)?; + } + } + let port: u16 = std::env::var("PERF_REPLAY_PORT")?.parse()?; + let window = tauri::WebviewWindowBuilder::new( + app, + "perf", + tauri::WebviewUrl::App(format!("index.html?port={port}&scenario={scenario}&layer={layer}").into()), + ) + .title("Clash Verge - performance replay") + .inner_size(1000., 700.) + .visible(false) + .focused(false) + .focusable(false) + .always_on_top(true) + .incognito(true) + .build()?; + window.set_ignore_cursor_events(true)?; + unsafe { + place_virtual(&window).map_err(std::io::Error::other)?; + } + if expected_display().map_err(std::io::Error::other)?.is_some() { + let handle = app.handle().clone(); + window.on_window_event(move |event| { + if matches!( + event, + tauri::WindowEvent::Moved(_) | tauri::WindowEvent::ScaleFactorChanged { .. } + ) && let Some(window) = handle.get_webview_window("perf") + { + let valid = window + .ns_window() + .ok() + .is_some_and(|native| unsafe { display_state(&*(native as *const AnyObject)).is_ok() }); + if !valid { + let _ = window.hide(); + handle.exit(2); + } + } + }); + } + Ok(()) + })(); + if let Err(error) = startup { + eprintln!("Measurement startup failed: {error}"); + app.handle().exit(1); + } Ok(()) }) .build(context); diff --git a/scripts/perf/build.mjs b/scripts/perf/build.mjs deleted file mode 100644 index a8e39dff2..000000000 --- a/scripts/perf/build.mjs +++ /dev/null @@ -1,107 +0,0 @@ -import { execFileSync } from 'node:child_process' -import { createHash } from 'node:crypto' -import { - copyFileSync, - existsSync, - mkdirSync, - readFileSync, - writeFileSync, -} from 'node:fs' -import { resolve } from 'node:path' -const output = resolve(process.argv[2] ?? 'target/perf/build') -const hash = (bytes) => createHash('sha256').update(bytes).digest('hex') -const command = (cmd, args) => - execFileSync(cmd, args, { encoding: 'utf8' }).trim() -const source = () => - command('git', ['ls-files', '-co', '--exclude-standard']) - .split('\n') - .filter(existsSync) - .filter( - (path) => - !path.startsWith('scripts/perf/') || - /\.(rs|tsx|html|mts|json)$/.test(path), - ) - .sort() - .map((path) => `${path}\0${hash(readFileSync(path))}`) - .join('\n') -mkdirSync(output, { recursive: true }) -const before = hash(source()) -const buildSettingsSha256 = hash( - ['Cargo.toml', '.cargo/config.toml'] - .filter(existsSync) - .map((path) => `${path}\0${hash(readFileSync(path))}`) - .join('\n'), -) -execFileSync( - 'pnpm', - [ - 'exec', - 'vite', - 'build', - '--mode', - 'perf', - '--config', - 'scripts/perf/vite.mts', - ], - { stdio: 'inherit' }, -) -const mode = 'release + perf-harness (build settings hash recorded)' -const buildCommand = - 'TAURI_CONFIG= cargo build -p clash-verge --release --features perf-harness --locked --target-dir target/perf/cargo' -execFileSync( - 'cargo', - [ - 'build', - '-p', - 'clash-verge', - '--release', - '--features', - 'perf-harness', - '--locked', - '--target-dir', - 'target/perf/cargo', - ], - { - stdio: 'inherit', - env: { - ...process.env, - TAURI_CONFIG: readFileSync('scripts/perf/tauri.json', 'utf8'), - }, - }, -) -if (before !== hash(source())) - throw new Error('Source changed during build; refuse manifest') -const binary = `${output}/clash-verge` -copyFileSync('target/perf/cargo/release/clash-verge', binary) -const files = command('find', ['target/perf/dist', '-type', 'f']) - .split('\n') - .sort() -writeFileSync( - `${binary}.json`, - JSON.stringify( - { - head: command('git', ['rev-parse', 'HEAD']), - dirty: command('git', ['status', '--porcelain']), - sourceSha256: before, - binarySha256: hash(readFileSync(binary)), - frontendSha256: hash( - files.map((path) => `${path}\0${hash(readFileSync(path))}`).join('\n'), - ), - mode, - buildSettingsSha256, - buildEnvironment: Object.fromEntries( - Object.entries(process.env).filter(([key]) => - /^(RUSTFLAGS|CARGO_ENCODED_RUSTFLAGS|CARGO_PROFILE_|RUSTC_WRAPPER)/.test( - key, - ), - ), - ), - buildCommand, - binary, - toolchain: `${command('rustc', ['--version'])}; node ${process.version}; pnpm ${command('pnpm', ['--version'])}`, - }, - null, - 2, - ), -) -console.log(`Measurement binary and manifest: ${binary}`) diff --git a/scripts/perf/compare.mjs b/scripts/perf/compare.mjs deleted file mode 100644 index a061f1831..000000000 --- a/scripts/perf/compare.mjs +++ /dev/null @@ -1,41 +0,0 @@ -import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs' -import { join } from 'node:path' - -import { compare } from './report.mjs' -const args = process.argv.slice(2).filter((v) => v !== '--') -if (args.length < 2) - throw new Error( - 'Usage: pnpm perf:compare -- baseline/summary.json candidate/summary.json [noise-threshold-percent]', - ) -const threshold = args[2] === undefined ? null : Number(args[2]) -if (threshold !== null && (!Number.isFinite(threshold) || threshold < 0)) - throw new Error( - 'threshold must be a nonnegative percentage justified by baseline noise', - ) -function load(path) { - if (statSync(path).isFile()) return JSON.parse(readFileSync(path)) - if (existsSync(join(path, 'summary.json'))) - return load(join(path, 'summary.json')) - const reports = readdirSync(path) - .sort() - .map((name) => load(join(path, name))) - const first = reports[0] - if ( - !first || - reports.some( - (r) => - r.build?.binarySha256 !== first.build?.binarySha256 || - ['invalid', 'incomparable'].includes(compare(first, r).verdict), - ) - ) - throw new Error('group contains mixed builds, contracts, or invalid runs') - return { ...first, runs: reports.flatMap((r) => r.runs) } -} -let result -try { - result = compare(...args.slice(0, 2).map(load), threshold) -} catch (error) { - result = { verdict: 'invalid', reason: String(error) } -} -console.log(JSON.stringify(result, null, 2)) -if (['invalid', 'incomparable'].includes(result.verdict)) process.exitCode = 2 diff --git a/scripts/perf/entry.tsx b/scripts/perf/entry.tsx index 4fb70ac09..0f20c2e56 100644 --- a/scripts/perf/entry.tsx +++ b/scripts/perf/entry.tsx @@ -1,52 +1,29 @@ -import { ThemeProvider, createTheme } from '@mui/material' -import { useEffect } from 'react' -import { createRoot } from 'react-dom/client' -import { SWRConfig } from 'swr' +import { layer, scenario, startReplay } from './replay' -import { EnhancedCanvasTrafficGraph } from '../../src/components/home/enhanced-canvas-traffic-graph' -import { useTrafficMonitorEnhanced } from '../../src/hooks/use-traffic-monitor' -import { initializeLanguage } from '../../src/services/i18n' -import { setPreloadConfig } from '../../src/services/preload' - -setPreloadConfig({ - pause_render_traffic_stats_on_blur: false, -} as IVergeConfig) -await initializeLanguage('en') -export function Replay() { - const { - graphData: { appendData }, - } = useTrafficMonitorEnhanced({ subscribe: false }) - useEffect(() => { - let received = 0, - previous = 0, - gaps = 0 - const socket = new WebSocket( - `ws://127.0.0.1:${new URLSearchParams(location.search).get('port')}`, - ) - socket.onmessage = ({ data }) => { - const point = JSON.parse(data) - appendData(point) - if (previous && point.sequence !== previous + 1) gaps++ - previous = point.sequence - document.documentElement.dataset.perfReceived = String(++received) - document.documentElement.dataset.perfGaps = String(gaps) - document.documentElement.dataset.perfSourceTime = String(point.timestamp) +if (scenario === 'traffic') { + await import('./traffic') +} else { + const root = document.getElementById('root')! + document.body.style.cssText = 'margin:0;font:14px -apple-system,sans-serif' + root.style.cssText = 'width:1000px;height:700px;overflow:hidden' + new MutationObserver(() => { + document.documentElement.dataset.perfDraw = String(Date.now()) + }).observe(root, { childList: true, characterData: true, subtree: true }) + window.addEventListener('error', (event) => { + document.documentElement.dataset.perfError = event.message + }) + window.addEventListener('unhandledrejection', (event) => { + document.documentElement.dataset.perfError = String(event.reason) + }) + if (layer === 'dom') { + const { mount } = await import('./minimal') + mount(root) + } else { + if (layer === 'page') { + const { installAdapter } = await import('./adapter') + installAdapter() } - return () => socket.close() - }, [appendData]) - return ( -
- -
- ) + await import('./scenes') + } + startReplay() } -createRoot(document.getElementById('root')!).render( - - - - - , -) diff --git a/scripts/perf/isolation.json b/scripts/perf/isolation.json new file mode 100644 index 000000000..d441ea6e8 --- /dev/null +++ b/scripts/perf/isolation.json @@ -0,0 +1,13 @@ +{ + "configRoots": [ + "Library/Application Support/io.github.clash-verge-rev.clash-verge-rev", + "Library/Application Support/io.github.clash-verge-rev.clash-verge-rev.dev" + ], + "configDirectories": ["profiles"], + "configExtensions": [".yaml", ".yml", ".json", ".js"], + "processNames": { + "app": "clash-verge", + "core": "verge-mihomo", + "service": "clash-verge-service" + } +} diff --git a/scripts/perf/minimal.ts b/scripts/perf/minimal.ts new file mode 100644 index 000000000..ab08cf738 --- /dev/null +++ b/scripts/perf/minimal.ts @@ -0,0 +1,21 @@ +import { scenario, subscribe } from './replay' + +export function mount(root: HTMLElement) { + const rows = Array.from({ length: 20 }, () => { + const row = document.createElement('div') + row.style.cssText = 'height:32px;white-space:nowrap;overflow:hidden' + root.appendChild(row) + return row + }) + subscribe(({ payload }) => { + const texts = + scenario === 'logs' + ? (payload as ILogItem[]).map((log) => log.payload) + : (payload as IConnections).connections.map( + (connection) => connection.metadata.host, + ) + rows.forEach((row, index) => { + row.textContent = texts[index % texts.length] ?? '' + }) + }) +} diff --git a/scripts/perf/replay.ts b/scripts/perf/replay.ts new file mode 100644 index 000000000..4efc08339 --- /dev/null +++ b/scripts/perf/replay.ts @@ -0,0 +1,37 @@ +export interface Frame { + sequence: number + timestamp: number + payload: ILogItem[] | IConnections +} +export const params = new URLSearchParams(location.search) +export const scenario = params.get('scenario') ?? 'traffic' +export const layer = params.get('layer') ?? 'component' +const listeners = new Set<(frame: Frame) => void>() +export const subscribe = (listener: (frame: Frame) => void) => { + listeners.add(listener) + return () => { + listeners.delete(listener) + } +} +export function startReplay() { + const data = document.documentElement.dataset + let previous = 0 + data.perfReceived = '0' + data.perfGaps = '0' + const socket = new WebSocket(`ws://127.0.0.1:${params.get('port')}`) + socket.onmessage = ({ data: raw }) => { + const frame = JSON.parse(raw) as Frame + if (frame.sequence !== previous + 1) + data.perfGaps = String(Number(data.perfGaps) + 1) + previous = frame.sequence + data.perfReceived = String(previous) + data.perfSourceTime = String(frame.timestamp) + for (const listener of listeners) listener(frame) + } + socket.onerror = () => { + data.perfError = 'replay socket failed' + } + socket.onclose = () => { + data.perfError = 'replay socket closed' + } +} diff --git a/scripts/perf/report.mjs b/scripts/perf/report.mjs deleted file mode 100644 index 9ca534d6e..000000000 --- a/scripts/perf/report.mjs +++ /dev/null @@ -1,443 +0,0 @@ -import { isDeepStrictEqual } from 'node:util' - -export const SCHEMA = 'traffic-webview-v3' -export const phases = ['foreground', 'hidden', 'restored'] -export const roles = ['webview', 'main'] -export const quantile = (values, p) => { - const v = values.filter(Number.isFinite).sort((a, b) => a - b) - if (!v.length) return null - const index = (v.length - 1) * p - return ( - v[Math.floor(index)] + - (v[Math.ceil(index)] - v[Math.floor(index)]) * (index % 1) - ) -} -export function delta(previous, next) { - if ( - !previous || - previous.pid !== next.pid || - previous.start !== next.start || - ![previous.time, next.time, previous.cpu, next.cpu].every( - Number.isFinite, - ) || - next.time <= previous.time || - next.cpu < previous.cpu - ) - return null - return ((next.cpu - previous.cpu) / (next.time - previous.time)) * 100000 -} -export function summarize(rows, seconds) { - const output = {} - for (const phase of phases) - for (const role of roles) { - const selected = rows.filter((r) => r.phase === phase && r.role === role) - const samples = selected.map((r, i) => ({ - ...r, - percent: delta(selected[i - 1], r), - cpuDelta: i ? r.cpu - selected[i - 1].cpu : null, - })) - const valid = samples.filter((r) => Number.isFinite(r.percent)) - const memory = samples.map((r) => r.rss) - const span = - samples.length > 1 ? samples.at(-1).time - samples[0].time : 0 - const continuous = - samples.length > 1 && - samples.every( - (r) => - [r.time, r.cpu, r.rss].every(Number.isFinite) && - Number.isInteger(r.pid) && - r.pid > 0 && - /^\d+$/.test(r.start) && - r.time >= 0 && - r.cpu >= 0 && - r.rss > 0, - ) && - samples.every( - (r, i) => - !i || - (r.time > samples[i - 1].time && - r.time - samples[i - 1].time < 2500 && - r.cpu >= samples[i - 1].cpu && - r.pid === samples[i - 1].pid && - r.start === samples[i - 1].start), - ) - output[`${phase}/${role}`] = { - count: samples.length, - cpuCount: valid.length, - missing: Math.max(0, Math.floor(seconds) - samples.length), - coverage: Math.min(1, samples.length / seconds), - continuous, - observedSeconds: span / 1000, - intervalCoverage: Math.min(1, span / ((seconds - 1) * 1000)), - cpuMean: null, - cpuP50: quantile( - valid.map((r) => r.percent), - 0.5, - ), - cpuP95: quantile( - valid.map((r) => r.percent), - 0.95, - ), - cpuPeak: quantile( - valid.map((r) => r.percent), - 1, - ), - cpuSeconds: valid.length - ? valid.reduce((sum, r) => sum + r.cpuDelta, 0) - : null, - rssP50: quantile(memory, 0.5), - rssP95: quantile(memory, 0.95), - rssPeak: quantile(memory, 1), - rssDelta: continuous ? samples.at(-1).rss - samples[0].rss : null, - rssSlopeBytesPerSecond: - span >= 60000 && continuous - ? (samples.at(-1).rss - samples[0].rss) / (span / 1000) - : null, - } - output[`${phase}/${role}`].cpuMean = - valid.length && span > 0 - ? (output[`${phase}/${role}`].cpuSeconds / span) * 100000 - : null - } - return output -} -export function replayValues(sequence) { - return { - up: 100000 + (sequence % 20) * 10000, - down: 500000 + (sequence % 30) * 20000, - } -} -export const validVisibility = (s) => - typeof s.hidden === 'boolean' && - (s.phase === 'hidden' - ? s.visible === false && s.hidden - : phases.includes(s.phase) && s.visible === true && !s.hidden) -export const validObservation = (s) => - validWorkload(s) && - validVisibility(s) && - s.nativeFocused === false && - s.appActive === false && - s.top === true && - s.clickThrough === true && - [s.now, s.source, s.worker, s.draw].every( - (v) => Number.isFinite(v) && v > 0, - ) && - s.now >= s.source && - s.now - s.source < 2500 && - s.now >= s.worker && - s.now - s.worker < 3500 && - s.now >= s.draw && - (s.phase === 'hidden' || s.now - s.draw < 2500) -export function validWorkload(s) { - if (!s) return false - const expected = replayValues(s.points) - return ( - Number.isInteger(s.received) && - Number.isInteger(s.points) && - s.points > 0 && - s.received >= s.points && - s.received - s.points < 60 && - s.lastUp === expected.up && - s.lastDown === expected.down && - s.gaps === 0 - ) -} -export function compare(a, b, threshold = null) { - const stats = new Map() - const text = (value) => typeof value === 'string' && value.length > 0 - const object = (value) => - value && typeof value === 'object' && !Array.isArray(value) - const expectedMarks = [ - 'ready', - ...phases.flatMap((phase) => [phase, `${phase}:end`]), - ] - const valid = (x) => - x?.schema === SCHEMA && - text(x?.harnessSha256) && - [ - 'head', - 'sourceSha256', - 'binarySha256', - 'frontendSha256', - 'buildSettingsSha256', - 'mode', - 'buildCommand', - 'binary', - 'toolchain', - ].every((key) => text(x.build?.[key])) && - typeof x.build.dirty === 'string' && - object(x.build.buildEnvironment) && - [ - 'method', - 'mode', - 'toolchain', - 'buildSettingsSha256', - 'platform', - 'machine', - 'arch', - 'host', - 'hardware', - 'power', - 'powerSource', - 'webkit', - 'replay', - 'automation', - ].every((key) => text(x.contract?.[key])) && - object(x.contract.buildEnvironment) && - x.build.buildSettingsSha256 === x.contract.buildSettingsSha256 && - [ - 'warmup', - 'intervalMs', - 'transitionSeconds', - 'readinessAllowanceSeconds', - ].every((key) => Number.isFinite(x.contract[key]) && x.contract[key] > 0) && - x.contract.scenario === 'traffic' && - typeof x.contract.consoleLocked === 'boolean' && - x.contract.windowPolicy === 'nonactivating-top-click-through-v1' && - x.contract.replayMs === 200 && - x.contract.rangeMinutes === 10 && - ['window', 'viewport'].every( - (key) => - Array.isArray(x.contract[key]) && - x.contract[key].length === (key === 'window' ? 2 : 3) && - x.contract[key].every((v) => Number.isFinite(v) && v > 0), - ) && - Number.isFinite(x.contract?.seconds) && - x.contract.seconds >= 5 && - Array.isArray(x.contract.phaseTargets) && - x.contract.phaseTargets.length === phases.length && - x.contract.phaseTargets.every((n) => Number.isInteger(n) && n > 0) && - Array.isArray(x.runs) && - x.runs.length && - x.runs.every((r) => { - if ( - !r?.ok || - !Array.isArray(r.samples) || - !r.samples.every( - (s) => s && roles.includes(s.role) && phases.includes(s.phase), - ) || - !Array.isArray(r.marks) || - !r.marks.every((m) => m && typeof m.name === 'string') - ) - return false - stats.set(r, summarize(r.samples, x.contract.seconds)) - return ( - r.cleanup?.mainExited && - r.cleanup?.driverClosed && - r.cleanup?.webviewExited && - Array.isArray(r.consoleStates) && - r.consoleStates.length === expectedMarks.length && - r.consoleStates.every( - (s, i) => - s && - s.name === (i ? expectedMarks[i] : 'initial') && - typeof s.locked === 'boolean' && - Number.isFinite(s.time) && - s.time > 0 && - (!i || s.time >= r.consoleStates[i - 1].time), - ) && - Number.isInteger(r.replay?.sent) && - r.replay.sent > 0 && - Number.isFinite(r.replay.maxLateMs) && - r.replay.maxLateMs >= 0 && - r.replay.maxLateMs < x.contract.replayMs && - !r.replay.error && - r.actualWarmupMs >= x.contract.warmup * 1000 && - Array.isArray(r.observations) && - r.observations.length > 0 && - r.observations.every( - (s, i) => - validObservation(s) && (!i || s.now > r.observations[i - 1].now), - ) && - r.marks.length === expectedMarks.length && - r.marks.every( - (m, i) => - m.name === expectedMarks[i] && - Number.isFinite(m.time) && - m.time > 0 && - (!i || - (validObservation({ ...m, phase: m.name.split(':')[0] }) && - /^[a-f0-9]{64}$/.test(m.pixelsSha256))) && - (!phases.includes(m.name) || - (m.target === x.contract.phaseTargets[phases.indexOf(m.name)] && - m.received === m.target)) && - (!i || m.time > r.marks[i - 1].time), - ) && - roles.every((role) => - r.samples - .filter((s) => s.role === role) - .every( - (s, i, rows) => - !i || - (s.pid === rows[i - 1].pid && - s.start === rows[i - 1].start && - s.time > rows[i - 1].time && - s.cpu >= rows[i - 1].cpu && - phases.indexOf(s.phase) >= phases.indexOf(rows[i - 1].phase)), - ), - ) && - validStats(stats.get(r)) && - phases.every((phase) => { - const start = r.marks.find((m) => m.name === phase) - const end = r.marks.find((m) => m.name === `${phase}:end`) - const observed = r.observations.filter((s) => s.phase === phase) - return ( - start && - end && - observed.length > 0 && - [ - start.deadlineIndex, - end.deadlineIndex, - start.senderSent, - end.senderSent, - ].every((n) => Number.isInteger(n) && n >= 0) && - end.deadlineIndex > start.deadlineIndex && - end.senderSent >= start.senderSent && - end.senderSent <= r.replay.sent && - start.senderSent >= start.received && - end.senderSent >= end.received && - Math.abs( - end.received - - start.received - - (end.deadlineIndex - start.deadlineIndex), - ) <= 2 && - observed.every( - (s) => - s.now >= start.now && - s.now <= end.now && - s.received >= start.received && - s.received <= end.received, - ) && - end.worker > start.worker && - (phase === 'hidden' || - (end.draw > start.draw && - end.pixelsSha256 !== start.pixelsSha256)) - ) - }) - ) - }) - if (![a, b].every(valid)) - return { verdict: 'invalid', reason: 'failed, malformed or missing runs' } - if ( - [a, b].some((x) => - x.runs.some((r) => - r.consoleStates.some((s) => s.locked !== x.contract.consoleLocked), - ), - ) - ) - return { - verdict: 'incomparable', - reason: 'console lock state changed at observed boundaries', - } - if ( - !isDeepStrictEqual(a.contract, b.contract) || - ['mode', 'buildCommand', 'toolchain', 'buildEnvironment'].some( - (key) => !isDeepStrictEqual(a.build[key], b.build[key]), - ) || - a.harnessSha256 !== b.harnessSha256 - ) - return { - verdict: 'incomparable', - reason: 'harness, platform, build mode, workload or environment differ', - } - for (const phase of phases) { - for (const name of [phase, `${phase}:end`]) { - const sizes = [a, b].map((x) => - x.runs.map((r) => r.marks.find((m) => m.name === name)?.received), - ) - if (!sizes.flat().every(Number.isFinite)) - return { verdict: 'invalid', reason: 'missing observed workload size' } - if ( - Math.abs(quantile(sizes[0], 0.5) - quantile(sizes[1], 0.5)) > 2 || - sizes.some((s) => Math.max(...s) - Math.min(...s) > 2) - ) - return { - verdict: 'incomparable', - reason: `${name}: input size varies by more than two replay points`, - } - } - } - const same = a.build.binarySha256 === b.build.binarySha256 - const enough = a.runs.length >= 3 && b.runs.length >= 3 - const metrics = {} - for (const phase of phases) - for (const role of roles) - for (const metric of ['cpuMean', 'rssP50']) { - const key = `${phase}/${role}` - const av = a.runs.map((r) => stats.get(r)[key][metric]) - const bv = b.runs.map((r) => stats.get(r)[key][metric]) - const before = quantile(av, 0.5), - after = quantile(bv, 0.5) - const noise = Math.max(...av) - Math.min(...av) - const candidateRange = Math.max(...bv) - Math.min(...bv) - const overlap = - Math.max(...av) >= Math.min(...bv) && - Math.max(...bv) >= Math.min(...av) - metrics[`${key}/${metric}`] = { - before, - after, - delta: after - before, - baselineRange: noise, - candidateRange, - verdict: - same || - !enough || - threshold === null || - !av.every(Number.isFinite) || - !bv.every(Number.isFinite) || - overlap || - Math.abs(after - before) <= - Math.max( - noise, - candidateRange, - (Math.abs(before) * threshold) / 100, - ) - ? 'inconclusive' - : after < before - ? 'lower' - : 'higher', - } - } - return { - verdict: 'inconclusive', - kind: same ? 'A/A' : 'A/B', - reason: same - ? 'same binary; natural variation only' - : 'inspect independent-run metrics; no universal pass threshold', - metrics, - } -} -export function markdown(report) { - const lines = [ - '# Home traffic graph - replay measurement', - '', - `Success: ${report.runs.filter((r) => r.ok).length}/${report.runs.length}`, - '', - 'CPU: one fully occupied core = 100%. Memory: process resident size bytes, not exclusive memory. Null = unavailable; first CPU observation has no delta. Slope requires 60 seconds of continuous observations.', - '', - '| Run / stage / role | CPU mean / P50 / P95 / peak (%) | CPU s | RSS P50 / P95 / peak / delta (bytes) | Samples / coverage |', - '|---|---|---|---|---|', - ] - for (const [i, run] of report.runs.entries()) { - if (run.error) lines.push(`\nRun ${i + 1} failed: ${run.error}\n`) - for (const [key, s] of Object.entries(run.stats)) - lines.push( - `| ${i + 1}/${key} | ${[s.cpuMean, s.cpuP50, s.cpuP95, s.cpuPeak].map((v) => v?.toFixed(2) ?? 'unavailable').join(' / ')} | ${s.cpuSeconds?.toFixed(3) ?? 'unavailable'} | ${[s.rssP50, s.rssP95, s.rssPeak, s.rssDelta].map((v) => v ?? 'unavailable').join(' / ')} | ${s.count} / ${(s.coverage * 100).toFixed(1)}% |`, - ) - } - lines.push( - '', - 'Scope: loopback replay → public traffic append boundary → production Worker → homepage Canvas component. Mihomo and its network/subscription chain are not running. Shared GPU/network processes are unavailable and excluded. Process statistics do not identify function hotspots, component CPU or retained objects; memory growth is not proof of a leak.', - '', - 'Next measurement: use WebKit CPU sampling for the highest-CPU foreground/restored phase; use Allocation/Heap captures for repeatable RSS growth. React Compiler is an untested local A/B candidate only.', - ) - return lines.join('\n') + '\n' -} -export const validStats = (stats) => - Object.values(stats).every( - (s) => - s.continuous && - s.coverage >= 0.8 && - s.intervalCoverage >= 0.8 && - s.cpuCount >= 3, - ) diff --git a/scripts/perf/report.test.mjs b/scripts/perf/report.test.mjs deleted file mode 100644 index c0b5af052..000000000 --- a/scripts/perf/report.test.mjs +++ /dev/null @@ -1,340 +0,0 @@ -import assert from 'node:assert/strict' -import { execFileSync } from 'node:child_process' -import { mkdirSync } from 'node:fs' -import { test } from 'node:test' - -import { - compare, - delta, - phases, - roles, - quantile, - summarize, - validWorkload, - replayValues, - SCHEMA, - validVisibility, -} from './report.mjs' -const latest = (sequence) => { - const { up, down } = replayValues(sequence) - return { lastUp: up, lastDown: down } -} -const observation = (phase, now) => ({ - phase, - now, - source: now - 100, - worker: now - 100, - draw: now - 100, - visible: phase !== 'hidden', - hidden: phase === 'hidden', - focused: false, - nativeFocused: false, - appActive: false, - top: true, - clickThrough: true, -}) -const rows = phases.flatMap((phase, phaseIndex) => - roles.flatMap((role) => - Array.from({ length: 5 }, (_, i) => ({ - phase, - role, - pid: role === 'main' ? 1 : 2, - start: '1', - time: (phaseIndex * 5 + i) * 1000, - cpu: (phaseIndex * 5 + i) / 10, - rss: 100 + i, - })), - ), -) -const run = { - ok: true, - samples: rows, - cleanup: { mainExited: true, driverClosed: true, webviewExited: true }, - replay: { sent: 130, maxLateMs: 10 }, - actualWarmupMs: 3000, - observations: phases.map((phase, i) => ({ - ...observation(phase, 2000 + i * 10000), - received: 35 + i * 35, - points: 34 + i * 35, - gaps: 0, - ...latest(34 + i * 35), - })), - marks: [ - { name: 'ready', time: 1 }, - ...phases.flatMap((name, i) => [ - { - name, - target: 35 + i * 35, - deadlineIndex: 35 + i * 35, - senderSent: 35 + i * 35, - received: 35 + i * 35, - points: 34 + i * 35, - ...latest(34 + i * 35), - gaps: 0, - time: 1000 + i * 10000, - ...observation(name, 1000 + i * 10000), - pixelsSha256: 'a'.repeat(64), - }, - { - name: `${name}:end`, - deadlineIndex: 60 + i * 35, - senderSent: 60 + i * 35, - received: 60 + i * 35, - points: 59 + i * 35, - ...latest(59 + i * 35), - gaps: 0, - time: 6000 + i * 10000, - ...observation(name, 6000 + i * 10000), - pixelsSha256: 'b'.repeat(64), - }, - ]), - ], -} -run.consoleStates = run.marks.map((m, i) => ({ - name: i ? m.name : 'initial', - time: m.time, - locked: false, -})) -const baseline = { - schema: SCHEMA, - harnessSha256: 'fixture-harness', - build: { - head: 'fixture-head', - dirty: '', - sourceSha256: 'fixture-source', - binarySha256: 'same', - frontendSha256: 'fixture-assets', - buildSettingsSha256: 'fixture-settings', - mode: 'release', - buildCommand: 'fixture-build', - binary: '/fixture/app', - toolchain: 'fixture-tools', - buildEnvironment: {}, - }, - contract: { - scenario: 'traffic', - replayMs: 200, - consoleLocked: false, - windowPolicy: 'nonactivating-top-click-through-v1', - seconds: 5, - warmup: 2, - phaseTargets: [35, 70, 105], - transitionSeconds: 2, - readinessAllowanceSeconds: 5, - intervalMs: 1000, - rangeMinutes: 10, - window: [1000, 700], - viewport: [1000, 668, 2], - method: 'fixture-method', - mode: 'release', - toolchain: 'fixture-tools', - buildSettingsSha256: 'fixture-settings', - platform: 'macOS', - machine: 'fixture-model', - arch: 'arm64', - host: 'fixture-host', - hardware: 'fixture-hardware', - power: 'fixture-power', - powerSource: 'AC', - webkit: 'fixture-webkit', - replay: 'fixture-5Hz', - automation: 'fixture-wdio', - buildEnvironment: {}, - }, - runs: [run, run, run], -} -test('quantiles, CPU units, first observation and PID reuse', () => { - assert.equal(quantile([0, 10, 20], 0.95), 19) - assert.equal(delta(undefined, rows[0]), null) - assert.equal(delta(rows[0], rows[1]), 10) - assert.equal(delta(rows[0], { ...rows[1], start: '2' }), null) - assert.equal(delta(rows[0], { ...rows[1], time: NaN }), null) - assert.equal(summarize(rows, 5)['hidden/main'].cpuMean, 10) - assert.equal(summarize([], 5)['hidden/main'].cpuSeconds, null) -}) -test('report schema: A/A, failed, incomparable and corrupt samples', () => { - assert.equal(compare(baseline, baseline).kind, 'A/A') - assert.equal( - compare(baseline, { ...baseline, schema: 'old' }).verdict, - 'invalid', - ) - for (const change of [ - { appActive: true }, - { nativeFocused: true }, - { top: false }, - { top: undefined }, - { clickThrough: false }, - { clickThrough: undefined }, - { hidden: true }, - { worker: 3000 }, - { now: 6000 }, - { draw: 3000 }, - { worker: null }, - ]) { - const invalid = structuredClone(baseline) - Object.assign(invalid.runs[0].observations[0], change) - assert.equal(compare(baseline, invalid).verdict, 'invalid') - } - for (const key of ['draw', 'pixelsSha256']) { - const stalled = structuredClone(baseline) - stalled.runs[0].marks[2][key] = stalled.runs[0].marks[1][key] - assert.equal(compare(baseline, stalled).verdict, 'invalid') - } - const missingPhase = structuredClone(baseline) - missingPhase.runs[0].observations.pop() - assert.equal(compare(baseline, missingPhase).verdict, 'invalid') - const consoleDrift = structuredClone(baseline) - consoleDrift.runs[0].consoleStates[3].locked = true - assert.equal(compare(baseline, consoleDrift).verdict, 'incomparable') - consoleDrift.runs[0].consoleStates[3].locked = null - assert.equal(compare(baseline, consoleDrift).verdict, 'invalid') - assert.equal( - validVisibility({ phase: 'restored', visible: true, hidden: true }), - false, - ) - const missedPeriod = structuredClone(baseline) - missedPeriod.runs[0].replay.maxLateMs = 200 - assert.equal(compare(baseline, missedPeriod).verdict, 'invalid') - const delayed = { - received: 100, - points: 97, - gaps: 0, - lastUp: 270000, - lastDown: 640000, - } - assert.equal(validWorkload(delayed), true) - assert.equal(validWorkload(null), false) - assert.equal(validWorkload({ ...delayed, points: 96 }), false) - assert.equal(validWorkload({ ...delayed, points: 98 }), false) - assert.equal(validWorkload({ ...delayed, received: 157 }), false) - assert.equal(validWorkload({ ...delayed, lastDown: 660000 }), false) - const missedAnchor = structuredClone(baseline) - missedAnchor.runs[0].marks[1].received++ - assert.equal(compare(baseline, missedAnchor).verdict, 'invalid') - const droppedInputs = structuredClone(baseline) - const end = droppedInputs.runs[0].marks[2] - end.received -= 10 - end.points -= 10 - Object.assign(end, latest(end.points)) - assert.equal(compare(droppedInputs, droppedInputs).verdict, 'invalid') - const workerLag = structuredClone(baseline) - workerLag.runs[0].observations[0].points -= 3 - assert.equal(compare(baseline, workerLag).verdict, 'invalid') - const inputDrift = structuredClone(baseline) - inputDrift.runs = baseline.runs.map((r) => structuredClone(r)) - inputDrift.runs[0].marks[2].received += 3 - inputDrift.runs[0].marks[2].points += 3 - inputDrift.runs[0].marks[2].deadlineIndex += 3 - inputDrift.runs[0].marks[2].senderSent += 3 - Object.assign( - inputDrift.runs[0].marks[2], - latest(inputDrift.runs[0].marks[2].points), - ) - assert.equal(compare(baseline, inputDrift).verdict, 'incomparable') - const snapshotPhase = structuredClone(baseline) - snapshotPhase.runs[0].marks[2].points++ - Object.assign( - snapshotPhase.runs[0].marks[2], - latest(snapshotPhase.runs[0].marks[2].points), - ) - assert.equal(compare(baseline, snapshotPhase).kind, 'A/A') - const changedSettings = structuredClone(baseline) - changedSettings.build.buildSettingsSha256 = - changedSettings.contract.buildSettingsSha256 = 'changed-settings' - assert.equal(compare(baseline, changedSettings).verdict, 'incomparable') - assert.equal( - compare(baseline, { ...baseline, harnessSha256: 'changed' }).verdict, - 'incomparable', - ) - assert.equal( - compare(baseline, { - ...baseline, - contract: { ...baseline.contract, seconds: 6 }, - }).verdict, - 'incomparable', - ) - assert.equal( - compare(baseline, { ...baseline, runs: [{ ...run, ok: false }] }).verdict, - 'invalid', - ) - const corrupt = structuredClone(baseline) - corrupt.runs[0].samples[1].time = 0 - assert.equal(compare(baseline, corrupt).verdict, 'invalid') - const shifted = structuredClone(baseline) - shifted.runs[0].samples - .filter((s) => s.phase === 'hidden') - .forEach((s) => { - s.time -= 5000 - }) - assert.equal(compare(baseline, shifted).verdict, 'invalid') - const badMarks = structuredClone(baseline) - badMarks.runs[0].marks.forEach((m) => { - m.time = -1 - }) - assert.equal(compare(baseline, badMarks).verdict, 'invalid') - const clustered = structuredClone(baseline) - clustered.runs[0].samples.forEach((s) => { - s.time /= 1000 - }) - assert.equal(compare(baseline, clustered).verdict, 'invalid') - for (const [section, key] of [ - ['contract', 'platform'], - ['build', 'sourceSha256'], - ]) { - const missing = structuredClone(baseline) - delete missing[section][key] - assert.equal(compare(missing, missing).verdict, 'invalid') - } - assert.equal( - compare(baseline, { - ...baseline, - contract: { ...baseline.contract, platform: 'other-macOS' }, - }).verdict, - 'incomparable', - ) - for (const malformed of [ - null, - {}, - { runs: [null] }, - { ...baseline, runs: [{ ...run, samples: null }] }, - ]) - assert.equal(compare(baseline, malformed).verdict, 'invalid') - const noisy = structuredClone(baseline) - noisy.runs = baseline.runs.map((r) => structuredClone(r)) - noisy.build.binarySha256 = 'different' - noisy.runs.forEach((r, i) => - r.samples.forEach((s) => { - s.cpu *= [0.1, 0.2, 10][i] - }), - ) - assert.equal( - compare(baseline, noisy, 5).metrics['foreground/webview/cpuMean'].verdict, - 'inconclusive', - ) -}) -test('macOS collector agrees with process.cpuUsage on a busy owned process', { - skip: process.platform !== 'darwin', -}, () => { - mkdirSync('target/perf', { recursive: true }) - execFileSync('cc', [ - '-Wall', - '-Wextra', - '-Werror', - 'scripts/perf/sample.c', - '-o', - 'target/perf/test-sample', - ]) - const sample = () => - Number( - execFileSync('target/perf/test-sample', [String(process.pid)], { - encoding: 'utf8', - }).split(' ')[2], - ) - const first = sample(), - cpu = process.cpuUsage(), - start = performance.now() - while (performance.now() - start < 500) Math.sqrt(Math.random()) - const used = process.cpuUsage(cpu), - measured = sample() - first - assert.ok(Math.abs(measured - (used.user + used.system) / 1e6) < 0.05) -}) diff --git a/scripts/perf/run.mjs b/scripts/perf/run.mjs deleted file mode 100644 index 9094faa77..000000000 --- a/scripts/perf/run.mjs +++ /dev/null @@ -1,674 +0,0 @@ -import { spawn, execFileSync } from 'node:child_process' -import { createHash, randomUUID } from 'node:crypto' -import { - mkdirSync, - readFileSync, - writeFileSync, - openSync, - closeSync, - realpathSync, -} from 'node:fs' -import { createServer } from 'node:net' -import { resolve, dirname } from 'node:path' -import { setTimeout as sleep } from 'node:timers/promises' -import { parseArgs } from 'node:util' - -import { createTauriCapabilities } from '@wdio/tauri-service' -import { remote } from 'webdriverio' -import { WebSocketServer } from 'ws' - -import { - delta, - summarize, - markdown, - phases, - roles, - validStats, - validWorkload, - replayValues, - SCHEMA, - validObservation, - validVisibility, -} from './report.mjs' - -const { values } = parseArgs({ - args: process.argv.slice(2).filter((v) => v !== '--'), - options: { - output: { type: 'string', default: `target/perf/runs/${Date.now()}` }, - binary: { type: 'string', default: 'target/perf/build/clash-verge' }, - repeat: { type: 'string', default: '3' }, - seconds: { type: 'string', default: '20' }, - warmup: { type: 'string', default: '10' }, - 'fail-phase': { type: 'string' }, - }, -}) -const seconds = Number(values.seconds), - warmup = Number(values.warmup), - repeat = Number(values.repeat) -const replayMs = 200 -if (process.platform !== 'darwin') throw new Error('macOS only') -function readConsole() { - const consoleState = JSON.parse( - execFileSync('/usr/bin/plutil', ['-convert', 'json', '-o', '-', '-'], { - input: execFileSync('/usr/sbin/ioreg', ['-n', 'Root', '-d1', '-a']), - encoding: 'utf8', - }), - ) - const locked = consoleState.IOConsoleLocked - if (typeof locked !== 'boolean') - throw new Error('Environment unavailable: unknown console lock state') - return locked -} -function checkConsole() { - const locked = readConsole() - if (locked) - throw new Error( - 'Environment unavailable: an unlocked graphical session is required; window focus is not required', - ) - return locked -} -const consoleLocked = checkConsole() -if ( - ![seconds, warmup, repeat].every(Number.isFinite) || - seconds < 5 || - warmup < 2 || - repeat < 1 || - !Number.isInteger(repeat) -) - throw new Error('Require seconds >= 5, warmup >= 2, integer repeat >= 1') -const phaseTargets = phases.map( - (_, i) => - Math.ceil(((warmup + 5) * 1000) / replayMs) + - i * Math.ceil(((seconds + 2) * 1000) / replayMs), -) -if ((phaseTargets[2] * replayMs) / 1000 + seconds + 5 >= 600) - throw new Error( - 'Total replay including transition allowance must be < 10 minutes', - ) -const binary = realpathSync(values.binary), - output = resolve(values.output) -const webcontentBinary = realpathSync( - '/System/Library/Frameworks/WebKit.framework/Versions/A/XPCServices/com.apple.WebKit.WebContent.xpc/Contents/MacOS/com.apple.WebKit.WebContent', -) -const build = JSON.parse(readFileSync(`${binary}.json`)) -const hash = (data) => createHash('sha256').update(data).digest('hex') -if (hash(readFileSync(binary)) !== build.binarySha256) - throw new Error('Binary does not match build manifest') -try { - execFileSync('git', ['check-ignore', '-q', '--no-index', output]) -} catch { - throw new Error('Output directory must be Git ignored') -} -mkdirSync(dirname(output), { recursive: true }) -mkdirSync(output) -execFileSync('cc', ['-O2', 'scripts/perf/sample.c', '-o', `${output}/sample`]) -const command = (name, args) => - execFileSync(name, args, { encoding: 'utf8' }).trim() -const report = { - schema: SCHEMA, - build, - contract: { - scenario: 'traffic', - replayMs, - consoleLocked, - method: 'mach-rusage-v0-seconds/rss-bytes-v1', - mode: build.mode, - buildSettingsSha256: build.buildSettingsSha256, - buildEnvironment: build.buildEnvironment, - toolchain: build.toolchain, - platform: command('sw_vers', []), - machine: command('sysctl', ['-n', 'hw.model']), - arch: process.arch, - host: hash(command('scutil', ['--get', 'LocalHostName'])), - hardware: command('sysctl', ['hw.ncpu', 'hw.memsize']), - power: command('pmset', ['-g', 'custom']), - powerSource: command('pmset', ['-g', 'batt']).split('\n')[0], - webkit: command('defaults', [ - 'read', - '/System/Library/Frameworks/WebKit.framework/Resources/Info', - 'CFBundleVersion', - ]), - seconds, - warmup, - phaseTargets, - transitionSeconds: 2, - readinessAllowanceSeconds: 5, - intervalMs: 1000, - window: [1000, 700], - windowPolicy: 'nonactivating-top-click-through-v1', - rangeMinutes: 10, - replay: - 'deterministic-v1, 5Hz, no prefill, append boundary, default bezier, blur pause false', - automation: - 'wdio plugin 1.3.0; 20Hz macOS runloop pump plus one WebDriver observation/second', - }, - runs: [], -} -report.units = { - time: 'monotonic milliseconds', - cpu: 'cumulative CPU seconds', - percent: 'one CPU core = 100%', - rss: 'resident size bytes', - start: 'Mach process start ticks', -} -report.monotonicOriginEpochMs = performance.timeOrigin -report.harnessSha256 = hash( - ['run.mjs', 'report.mjs', 'sample.c'] - .map((p) => readFileSync(`scripts/perf/${p}`)) - .join('\n'), -) -let interrupted = false -const abort = new AbortController() -const pause = (ms) => sleep(ms, undefined, { signal: abort.signal }) -for (const signal of ['SIGINT', 'SIGTERM']) - process.on(signal, () => { - interrupted = true - abort.abort() - }) -const assert = (value, reason) => { - if (!value) throw new Error(reason) -} -const drawn = (s) => s.now >= s.draw && s.now - s.draw < 2500 -const marker = (s) => ({ - ...s, - pixelsSha256: hash(s.pixels), - pixels: undefined, - ua: undefined, - token: undefined, -}) -async function freePort() { - const server = createServer() - await new Promise((done) => server.listen(0, '127.0.0.1', done)) - const port = server.address().port - await new Promise((done) => server.close(done)) - return port -} -for (let iteration = 0; iteration < repeat && !interrupted; iteration++) { - const initialLocked = checkConsole() - const token = randomUUID(), - port = await freePort() - const run = { - token, - ok: false, - samples: [], - observations: [], - consoleStates: [ - { name: 'initial', locked: initialLocked, time: Date.now() }, - ], - marks: [], - stats: {}, - cleanup: {}, - replay: { sent: 0, maxLateMs: 0 }, - } - report.runs.push(run) - const replay = new WebSocketServer({ host: '127.0.0.1', port: 0 }) - await new Promise((done) => replay.on('listening', done)) - let sequence = 0, - replayStarted, - timer - replay.on('connection', () => { - if (replayStarted !== undefined) { - run.replay.error = 'unexpected replay reconnection' - clearTimeout(timer) - return - } - replayStarted = performance.now() - timer = setTimeout(send, replayMs) - }) - const deadlineIndex = () => - Math.floor((performance.now() - replayStarted) / replayMs) - function send() { - const deadline = replayStarted + (sequence + 1) * replayMs - const late = performance.now() - deadline - if (late < 0) { - timer = setTimeout(send, Math.ceil(-late)) - return - } - run.replay.maxLateMs = Math.max(run.replay.maxLateMs, late) - if (late >= replayMs) { - run.replay.error = `replay source missed a complete ${replayMs} ms period` - return - } - sequence++ - run.replay.sent = sequence - const timestamp = Date.now() - const point = JSON.stringify({ - sequence, - timestamp, - ...replayValues(sequence), - }) - for (const socket of replay.clients) - if (socket.readyState === 1) socket.send(point) - timer = setTimeout( - send, - Math.max( - 0, - replayStarted + (sequence + 1) * replayMs - performance.now(), - ), - ) - } - const log = openSync(`${output}/${iteration + 1}.log`, 'w') - const child = spawn(binary, [], { - env: { - ...process.env, - PERF_TOKEN: token, - PERF_REPLAY_PORT: String(replay.address().port), - TAURI_WEBDRIVER_PORT: String(port), - }, - stdio: ['ignore', log, log], - }) - closeSync(log) - let browser, childError, previous, mainStart - const identities = {} - child.on('error', (error) => { - childError = error - }) - const exited = new Promise((done) => child.once('exit', done)) - const alive = () => - !childError && child.exitCode === null && child.signalCode === null - const collect = (pids) => - command(`${output}/sample`, pids.map(String)) - .split('\n') - .map((line) => { - const [pid, start, cpu, rss, ...path] = line.split(' ') - return { - pid: Number(pid), - start, - cpu: Number(cpu), - rss: Number(rss), - path: path.join(' '), - time: performance.now(), - } - }) - const identify = (role, row) => { - assert( - row.path === (role === 'main' ? binary : webcontentBinary), - `unexpected ${role} process executable: ${row.path}`, - ) - assert( - BigInt(row.start) >= BigInt(mainStart), - 'process predates owned application', - ) - const identity = `${row.pid}/${row.start}` - assert( - !identities[role] || identities[role] === identity, - 'process identity changed during scenario', - ) - identities[role] = identity - } - const state = async (action = 'state', pixels = false) => - browser.execute( - async (action, pixels) => { - const native = await window.__TAURI_INTERNALS__.invoke('perf_state', { - action, - }) - const canvas = document.querySelector('canvas') - return { - ...native, - now: Date.now(), - hidden: document.hidden, - focused: document.hasFocus(), - received: Number(document.documentElement.dataset.perfReceived), - source: Number(document.documentElement.dataset.perfSourceTime), - gaps: Number(document.documentElement.dataset.perfGaps), - worker: Number(document.documentElement.dataset.perfWorker), - points: Number(document.documentElement.dataset.perfPoints), - lastUp: Number(document.documentElement.dataset.perfLastUp), - lastDown: Number(document.documentElement.dataset.perfLastDown), - draw: Number(canvas?.dataset.perfDraw), - pixels: pixels ? canvas?.toDataURL() : null, - ua: navigator.userAgent, - } - }, - action, - pixels, - ) - const checked = async (action, pixels) => { - assert(!interrupted, 'interrupted') - assert(!run.replay.error, run.replay.error) - assert( - alive(), - `owned app exited: ${childError ?? child.exitCode ?? child.signalCode}`, - ) - const value = await state(action, pixels) - assert(!run.replay.error, run.replay.error) - run.lastObservation = { ...value, pixels: undefined, ua: undefined } - assert( - value.token === token && value.main === child.pid, - 'instance identity mismatch', - ) - if (!identities.webview) { - run.initialAttribution = collect([value.webview])[0] - identify('webview', run.initialAttribution) - } - assert( - value.nativeFocused === false && value.appActive === false, - 'measurement window unexpectedly activated', - ) - assert( - value.top === true && value.clickThrough === true, - 'measurement window must be natively topmost and click-through', - ) - return value - } - try { - mainStart = collect([child.pid])[0].start - const deadline = Date.now() + 30000 - while (true) { - assert( - Date.now() < deadline && !interrupted && alive(), - 'embedded WebDriver readiness deadline or app exit', - ) - try { - if ( - ( - await fetch(`http://127.0.0.1:${port}/status`, { - signal: AbortSignal.timeout(500), - }) - ).ok - ) - break - } catch {} - await pause(100) - } - const listener = command('lsof', [ - '-nP', - '-a', - '-p', - String(child.pid), - `-iTCP:${port}`, - '-sTCP:LISTEN', - ]) - assert( - listener.includes(`127.0.0.1:${port}`) && !listener.includes(`*:${port}`), - 'driver must belong to this app and bind loopback', - ) - const capabilities = createTauriCapabilities(binary) - delete capabilities['wdio:tauriServiceOptions'] - browser = await remote({ - hostname: '127.0.0.1', - port, - capabilities, - logLevel: 'error', - connectionRetryCount: 0, - connectionRetryTimeout: 5000, - }) - const handles = await browser.getWindowHandles() - assert(handles.length === 1, 'expected exactly one measurement WebView') - await browser.switchToWindow(handles[0]) - // Replay's parent effect starts the socket after the graph installs its listeners. - await browser.waitUntil( - async () => { - const s = await checked() - return ( - validWorkload(s) && - s.received >= 3 && - s.now >= s.worker && - s.now - s.worker < 2500 - ) - }, - { - timeout: 15000, - interval: 100, - timeoutMsg: 'traffic effects/Worker not ready before show', - }, - ) - run.initialData = run.lastObservation - await checked('show') - await browser.waitUntil( - async () => { - const s = await checked() - return validObservation({ phase: 'foreground', ...s }) - }, - { - timeout: 15000, - interval: 250, - timeoutMsg: 'traffic data path/page not ready', - }, - ) - run.userAgent = (await checked()).ua - run.backgroundLoad = command('sysctl', ['-n', 'vm.loadavg']) - const viewport = await browser.execute(() => [ - innerWidth, - innerHeight, - devicePixelRatio, - ]) - run.viewport = { dom: viewport, driver: await browser.getWindowRect() } - const driverScale = run.viewport.driver.width / 1000 - assert( - viewport[0] === 1000 && - viewport[1] > 0 && - viewport[1] <= 700 && - (driverScale === 1 || driverScale === viewport[2]) && - run.viewport.driver.height / driverScale === 700, - `unexpected window dimensions: ${JSON.stringify(run.viewport)}`, - ) - assert( - !report.contract.viewport || - JSON.stringify(report.contract.viewport) === JSON.stringify(viewport), - 'viewport changed between runs', - ) - report.contract.viewport = viewport - run.marks.push({ name: 'ready', time: Date.now() }) - const readyAt = performance.now() - assert( - phaseTargets[0] - (await checked()).received >= - (warmup * 1000) / replayMs, - 'readiness exceeded fixed replay allowance', - ) - for (const phase of phases) { - run.consoleStates.push({ - name: phase, - locked: readConsole(), - time: Date.now(), - }) - const operation = performance.now() - let s = await checked(phase === 'hidden' ? 'hide' : 'show', true) - assert( - s.visible === (phase !== 'hidden'), - `${phase}: native visibility mismatch`, - ) - const beforeDraw = s.draw - await browser.waitUntil( - async () => { - s = await checked('state', true) - return ( - validVisibility({ phase, ...s }) && - (phase === 'hidden' || (s.draw > beforeDraw && drawn(s))) - ) - }, - { - timeout: 5000, - interval: 100, - timeoutMsg: `${phase}: window/Canvas readiness deadline`, - }, - ) - const operationMs = performance.now() - operation - const target = phaseTargets[phases.indexOf(phase)] - await browser.waitUntil( - async () => { - s = await checked('state', true) - assert( - s.received <= target, - `${phase}: missed replay anchor ${target}`, - ) - return s.received === target - }, - { - timeout: Math.max(1000, (target - s.received) * replayMs + 1000), - interval: 50, - timeoutMsg: `${phase}: replay anchor deadline`, - }, - ) - assert( - validObservation({ phase, ...s }), - `${phase}: invalid traffic observation`, - ) - const first = s, - start = performance.now(), - firstIndex = deadlineIndex(), - phaseEpoch = Date.now() - if (phase === 'foreground') { - run.actualWarmupMs = start - readyAt - assert( - run.actualWarmupMs >= warmup * 1000, - 'insufficient actual warmup', - ) - } - run.marks.push({ - name: phase, - time: phaseEpoch, - operationMs, - transitionMs: start - operation, - target, - ...marker(s), - deadlineIndex: firstIndex, - senderSent: run.replay.sent, - }) - previous = {} - if (values['fail-phase'] === phase) - throw new Error(`injected failure at ${phase}`) - while (performance.now() - start < seconds * 1000) { - const tick = performance.now() - s = await checked() - run.observations.push({ phase, ...run.lastObservation }) - assert( - validObservation({ phase, ...s }), - `${phase}: invalid visibility, workload or freshness`, - ) - assert( - performance.now() - replayStarted < 600000, - 'replay exceeded raw history window', - ) - const samples = collect(roles.map((role) => s[role])) - run.attribution = { - main: s.main, - webview: s.webview, - observed: samples, - } - for (let i = 0; i < roles.length; i++) { - const row = { ...samples[i], phase, role: roles[i] } - identify(row.role, row) - row.percent = delta(previous[row.role], row) - row.cpuDelta = - row.percent === null ? null : row.cpu - previous[row.role].cpu - run.samples.push(row) - previous[row.role] = row - } - await pause(Math.max(0, 1000 - (performance.now() - tick))) - } - s = await checked('state', true) - const endIndex = deadlineIndex() - const expected = endIndex - firstIndex - assert( - validObservation({ phase, ...s }) && - s.worker > first.worker && - Math.abs(s.received - first.received - expected) <= 2, - `${phase}: unstable or lost workload`, - ) - if (phase !== 'hidden') - assert( - s.draw > first.draw && s.pixels !== first.pixels, - `${phase}: Canvas did not render fresh data`, - ) - run.marks.push({ - name: `${phase}:end`, - time: s.now, - ...marker(s), - deadlineIndex: endIndex, - senderSent: run.replay.sent, - }) - run.consoleStates.push({ - name: `${phase}:end`, - locked: readConsole(), - time: Date.now(), - }) - } - run.stats = summarize(run.samples, seconds) - assert( - validStats(run.stats), - 'insufficient coverage or process discontinuity', - ) - run.ok = true - } catch (error) { - run.error = String(error) - process.exitCode = 1 - } finally { - await browser?.deleteSession().catch(() => {}) - try { - if (alive() && collect([child.pid])[0].start === mainStart) { - child.kill('SIGTERM') - await Promise.race([exited, sleep(5000)]) - if (alive() && collect([child.pid])[0].start === mainStart) { - child.kill('SIGKILL') - await Promise.race([exited, sleep(2000)]) - } - } - } catch (error) { - run.cleanup.error = String(error) - } - clearTimeout(timer) - for (const socket of replay.clients) socket.terminate() - await new Promise((done) => replay.close(done)) - run.cleanup.mainExited = - child.exitCode !== null || child.signalCode !== null - if (identities.webview) { - const [pid, start] = identities.webview.split('/') - const remains = () => { - try { - return collect([pid])[0].start === start - } catch { - return false - } - } - const deadline = Date.now() + 5000 - while (remains() && Date.now() < deadline) await sleep(100) - run.cleanup.webviewExited = !remains() - } - try { - await fetch(`http://127.0.0.1:${port}/status`, { - signal: AbortSignal.timeout(500), - }) - run.cleanup.driverClosed = false - } catch { - run.cleanup.driverClosed = true - } - if ( - !run.cleanup.mainExited || - !run.cleanup.driverClosed || - run.cleanup.webviewExited === false - ) { - run.ok = false - run.error = `${run.error ?? ''} cleanup failed` - process.exitCode = 1 - } - run.stats = summarize(run.samples, seconds) - writeFileSync( - `${output}/${iteration + 1}.json`, - JSON.stringify(run, null, 2), - ) - const columns = [ - 'phase', - 'role', - 'time', - 'pid', - 'start', - 'cpu', - 'percent', - 'rss', - ] - writeFileSync( - `${output}/${iteration + 1}.csv`, - [ - columns.join(','), - ...run.samples.map((r) => - columns.map((c) => r[c] ?? 'unavailable').join(','), - ), - ].join('\n'), - ) - writeFileSync(`${output}/summary.json`, JSON.stringify(report, null, 2)) - writeFileSync(`${output}/report.md`, markdown(report)) - } - console.log(`Run ${iteration + 1}: ${run.ok ? 'passed' : run.error}`) -} -console.log(`Reports: ${output}`) diff --git a/scripts/perf/sample.c b/scripts/perf/sample.c deleted file mode 100644 index f0128c954..000000000 --- a/scripts/perf/sample.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include -#include - -int main(int argc, char **argv) { - mach_timebase_info_data_t timebase; - if (mach_timebase_info(&timebase) != KERN_SUCCESS) return 1; - for (int i = 1; i < argc; i++) { - int pid = atoi(argv[i]); - struct rusage_info_v0 r; - char path[PROC_PIDPATHINFO_MAXSIZE]; - if (pid <= 0 || proc_pid_rusage(pid, RUSAGE_INFO_V0, (rusage_info_t *)&r)) return 1; - if (proc_pidpath(pid, path, sizeof(path)) <= 0) return 1; - printf("%d %llu %.9f %llu %s\n", pid, r.ri_proc_start_abstime, - ((double)r.ri_user_time + r.ri_system_time) * timebase.numer / timebase.denom / 1e9, r.ri_resident_size, path); - } - return 0; -} diff --git a/scripts/perf/scenes.tsx b/scripts/perf/scenes.tsx new file mode 100644 index 000000000..02992fec5 --- /dev/null +++ b/scripts/perf/scenes.tsx @@ -0,0 +1,81 @@ +import { ThemeProvider, createTheme } from '@mui/material' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' +import { useEffect, useState } from 'react' +import { createRoot } from 'react-dom/client' +import { SWRConfig } from 'swr' + +import { VirtualList } from '../../src/components/base/virtual-list' +import { ConnectionRowItem } from '../../src/components/connection/connection-row-item' +import { useConnectionRowViews } from '../../src/components/connection/connection-row-view' +import LogItem from '../../src/components/log/log-item' +import { initializeLanguage } from '../../src/services/i18n' +import { setPreloadConfig } from '../../src/services/preload' + +import { layer, scenario, subscribe } from './replay' +import '../../src/assets/styles/page.scss' + +const searchState = { + text: '', + matchCase: false, + matchWholeWord: false, + useRegularExpression: false, +} +export function Components() { + const [logs, setLogs] = useState([]) + const [connections, setConnections] = useState([]) + useEffect( + () => + subscribe(({ payload }) => { + if (scenario === 'logs') + setLogs((previous) => + previous.concat(payload as ILogItem[]).slice(-1000), + ) + else setConnections((payload as IConnections).connections) + }), + [], + ) + const rows = useConnectionRowViews(connections) + return ( + + scenario === 'logs' ? ( + + ) : ( + {}} + /> + ) + } + /> + ) +} +dayjs.extend(relativeTime) +const root = document.getElementById('root')! +setPreloadConfig({ + language: 'en', + theme_mode: 'light', + pause_render_traffic_stats_on_blur: false, +} as IVergeConfig) +await initializeLanguage('en') +const Scene = + layer === 'component' + ? Components + : scenario === 'logs' + ? (await import('../../src/pages/logs')).default + : (await import('../../src/pages/connections')).default +createRoot(root).render( + + + + + , +) diff --git a/scripts/perf/traffic.tsx b/scripts/perf/traffic.tsx new file mode 100644 index 000000000..4fb70ac09 --- /dev/null +++ b/scripts/perf/traffic.tsx @@ -0,0 +1,52 @@ +import { ThemeProvider, createTheme } from '@mui/material' +import { useEffect } from 'react' +import { createRoot } from 'react-dom/client' +import { SWRConfig } from 'swr' + +import { EnhancedCanvasTrafficGraph } from '../../src/components/home/enhanced-canvas-traffic-graph' +import { useTrafficMonitorEnhanced } from '../../src/hooks/use-traffic-monitor' +import { initializeLanguage } from '../../src/services/i18n' +import { setPreloadConfig } from '../../src/services/preload' + +setPreloadConfig({ + pause_render_traffic_stats_on_blur: false, +} as IVergeConfig) +await initializeLanguage('en') +export function Replay() { + const { + graphData: { appendData }, + } = useTrafficMonitorEnhanced({ subscribe: false }) + useEffect(() => { + let received = 0, + previous = 0, + gaps = 0 + const socket = new WebSocket( + `ws://127.0.0.1:${new URLSearchParams(location.search).get('port')}`, + ) + socket.onmessage = ({ data }) => { + const point = JSON.parse(data) + appendData(point) + if (previous && point.sequence !== previous + 1) gaps++ + previous = point.sequence + document.documentElement.dataset.perfReceived = String(++received) + document.documentElement.dataset.perfGaps = String(gaps) + document.documentElement.dataset.perfSourceTime = String(point.timestamp) + } + return () => socket.close() + }, [appendData]) + return ( +
+ +
+ ) +} +createRoot(document.getElementById('root')!).render( + + + + + , +) diff --git a/scripts/perf/tsconfig.json b/scripts/perf/tsconfig.json index b26f93df8..0a50a8fa6 100644 --- a/scripts/perf/tsconfig.json +++ b/scripts/perf/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["../../src", "./*.tsx"] + "include": ["../../src", "./*.tsx", "./*.ts"] }