diff --git a/.gitignore b/.gitignore index b88c8135..218facab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Logs logs *.log +!tests/*.log npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/bun.lock b/bun.lock new file mode 100644 index 00000000..6f37f91f --- /dev/null +++ b/bun.lock @@ -0,0 +1,1242 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "pvm-debugger", + "dependencies": { + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-hover-card": "^1.1.1", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-radio-group": "^1.2.0", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-tooltip": "^1.1.7", + "@reduxjs/toolkit": "^2.9.0", + "@tanstack/react-virtual": "^3.10.9", + "@typeberry/lib": "^0.5.0-6a64d40", + "@typeberry/spectool-wasm": "0.23.0", + "@uiw/react-codemirror": "^4.25.1", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001769", + "class-variance-authority": "^0.7.1", + "classnames": "^2.5.1", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "framer-motion": "^12.33.0", + "lodash": "^4.17.21", + "lucide-react": "^0.544.0", + "path-browserify": "^1.0.1", + "react": "^19.0.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^19.2.3", + "react-dropzone": "^14.3.8", + "react-intersection-observer": "^10.0.0", + "react-katex": "^3.1.0", + "react-redux": "^9.1.2", + "react-router": "^7.12.0", + "react-toastify": "^11.0.5", + "redux-persist": "^6.0.0", + "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.18", + "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2", + "zod": "^4.2.1", + }, + "devDependencies": { + "@eslint/js": "^9.39.0", + "@playwright/test": "^1.57.0", + "@tailwindcss/postcss": "^4.1.18", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.8.0", + "@testing-library/react": "^16.3.0", + "@types/lodash": "^4.17.7", + "@types/node": "^24.9.1", + "@types/react": "^19.0.1", + "@types/react-dom": "^19.0.1", + "@types/react-katex": "^3.0.4", + "@typescript-eslint/eslint-plugin": "^8.44.1", + "@typescript-eslint/parser": "^8.45.0", + "@vitejs/plugin-react": "^5.1.0", + "autoprefixer": "^10.4.19", + "esbuild": "^0.25.10", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.4.0", + "husky": "^9.1.1", + "jsdom": "^27.0.0", + "lint-staged": "^16.2.3", + "postcss": "^8.4.39", + "prettier": "3.3.3", + "typescript": "^5.9.2", + "vite": "^7.1.12", + "vite-plugin-top-level-await": "^1.4.4", + "vite-plugin-wasm": "^3.5.0", + "vitest": "^4.0.14", + }, + }, + }, + "trustedDependencies": [ + "@swc/core", + ], + "packages": { + "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], + + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@4.0.4", "", { "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "lru-cache": "^11.1.0" } }, "sha512-cKjSKvWGmAziQWbCouOsFwb14mp1betm8Y7Fn+yglDMUUu3r9DCbJ9iJbeFDenLMqFbIMC0pQP8K+B8LAxX3OQ=="], + + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.5.5", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-kI2MX9pmImjxWT8nxDZY+MuN6r1jJGe7WxizEbsAEPB/zxfW5wYLIiPG1v3UKgEOOP8EsDkp0ZL99oRFAdPM8g=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], + + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.0", "", {}, "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw=="], + + "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], + + "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + + "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], + + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], + + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], + + "@babel/runtime": ["@babel/runtime@7.28.3", "", {}, "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], + + "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + + "@codemirror/autocomplete": ["@codemirror/autocomplete@6.18.6", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg=="], + + "@codemirror/commands": ["@codemirror/commands@6.8.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw=="], + + "@codemirror/language": ["@codemirror/language@6.11.3", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", "@lezer/common": "^1.1.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA=="], + + "@codemirror/lint": ["@codemirror/lint@6.8.5", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA=="], + + "@codemirror/search": ["@codemirror/search@6.5.11", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "crelt": "^1.0.5" } }, "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA=="], + + "@codemirror/state": ["@codemirror/state@6.5.2", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA=="], + + "@codemirror/theme-one-dark": ["@codemirror/theme-one-dark@6.1.3", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@lezer/highlight": "^1.0.0" } }, "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA=="], + + "@codemirror/view": ["@codemirror/view@6.38.1", "", { "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], + + "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], + + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.0.14", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], + + "@lezer/common": ["@lezer/common@1.2.3", "", {}, "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="], + + "@lezer/highlight": ["@lezer/highlight@1.2.1", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA=="], + + "@lezer/lr": ["@lezer/lr@1.4.2", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA=="], + + "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@playwright/test": ["@playwright/test@1.57.0", "", { "dependencies": { "playwright": "1.57.0" }, "bin": { "playwright": "cli.js" } }, "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@react-dnd/asap": ["@react-dnd/asap@5.0.2", "", {}, "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A=="], + + "@react-dnd/invariant": ["@react-dnd/invariant@4.0.2", "", {}, "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw=="], + + "@react-dnd/shallowequal": ["@react-dnd/shallowequal@4.0.2", "", {}, "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="], + + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.9.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^10.0.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" } }, "sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.43", "", {}, "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ=="], + + "@rollup/plugin-virtual": ["@rollup/plugin-virtual@3.0.2", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.48.1", "", { "os": "android", "cpu": "arm" }, "sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.48.1", "", { "os": "android", "cpu": "arm64" }, "sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.48.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.48.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.48.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.48.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.48.1", "", { "os": "linux", "cpu": "arm" }, "sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.48.1", "", { "os": "linux", "cpu": "arm" }, "sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.48.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.48.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.48.1", "", { "os": "linux", "cpu": "none" }, "sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.48.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.48.1", "", { "os": "linux", "cpu": "none" }, "sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.48.1", "", { "os": "linux", "cpu": "none" }, "sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.48.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.48.1", "", { "os": "linux", "cpu": "x64" }, "sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.48.1", "", { "os": "linux", "cpu": "x64" }, "sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.48.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.48.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.48.1", "", { "os": "win32", "cpu": "x64" }, "sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@swc/core": ["@swc/core@1.13.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.5", "@swc/core-darwin-x64": "1.13.5", "@swc/core-linux-arm-gnueabihf": "1.13.5", "@swc/core-linux-arm64-gnu": "1.13.5", "@swc/core-linux-arm64-musl": "1.13.5", "@swc/core-linux-x64-gnu": "1.13.5", "@swc/core-linux-x64-musl": "1.13.5", "@swc/core-win32-arm64-msvc": "1.13.5", "@swc/core-win32-ia32-msvc": "1.13.5", "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.13.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.13.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.13.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.13.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.13.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.13.5", "", { "os": "linux", "cpu": "x64" }, "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.13.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.13.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.13.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.13.5", "", { "os": "win32", "cpu": "x64" }, "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q=="], + + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/types": ["@swc/types@0.1.24", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng=="], + + "@swc/wasm": ["@swc/wasm@1.13.5", "", {}, "sha512-ZBZcxieydxNwgEU9eFAXGMaDb1Xoh+ZkZcUQ27LNJzc2lPSByoL6CSVqnYiaVo+n9JgqbYyHlMq+i7z0wRNTfA=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="], + + "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="], + + "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.12", "", {}, "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.8.0", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ=="], + + "@testing-library/react": ["@testing-library/react@16.3.0", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw=="], + + "@typeberry/lib": ["@typeberry/lib@0.5.0-6a64d40", "", {}, "sha512-I58CgZE7bhLwMtWGqWjMHeXML7RU0PsySCldbIoL8POvTE9UPbGWk7rTHoTftXluak3G9PoCamLpPUHsmUdPVw=="], + + "@typeberry/spectool-wasm": ["@typeberry/spectool-wasm@0.23.0", "", {}, "sha512-7FVzKKEgt2l/4mVk7Q9UgSW5T8gFc5Tp8sUKMx7kGGM0cqIpMxarnGnebZpKDPgVINPlwNWsnqXGT9+ReHOtSw=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="], + + "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + + "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/react-katex": ["@types/react-katex@3.0.4", "", { "dependencies": { "@types/react": "*" } }, "sha512-aLkykKzSKLpXI6REJ3uClao6P47HAFfR1gcHOZwDeTuALsyjgMhz+oynLV4gX0kiJVnvHrBKF/TLXqyNTpHDUg=="], + + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.44.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.44.1", "@typescript-eslint/type-utils": "8.44.1", "@typescript-eslint/utils": "8.44.1", "@typescript-eslint/visitor-keys": "8.44.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.44.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.45.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", "@typescript-eslint/typescript-estree": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.45.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.45.0", "@typescript-eslint/types": "^8.45.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.44.1", "", { "dependencies": { "@typescript-eslint/types": "8.44.1", "@typescript-eslint/visitor-keys": "8.44.1" } }, "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.45.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.44.1", "", { "dependencies": { "@typescript-eslint/types": "8.44.1", "@typescript-eslint/typescript-estree": "8.44.1", "@typescript-eslint/utils": "8.44.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.45.0", "", {}, "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.45.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.45.0", "@typescript-eslint/tsconfig-utils": "8.45.0", "@typescript-eslint/types": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.44.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.44.1", "@typescript-eslint/types": "8.44.1", "@typescript-eslint/typescript-estree": "8.44.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.44.1", "", { "dependencies": { "@typescript-eslint/types": "8.44.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw=="], + + "@uiw/codemirror-extensions-basic-setup": ["@uiw/codemirror-extensions-basic-setup@4.25.1", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" }, "peerDependencies": { "@codemirror/autocomplete": ">=6.0.0", "@codemirror/commands": ">=6.0.0", "@codemirror/language": ">=6.0.0", "@codemirror/lint": ">=6.0.0", "@codemirror/search": ">=6.0.0", "@codemirror/state": ">=6.0.0", "@codemirror/view": ">=6.0.0" } }, "sha512-zxgA2QkvP3ZDKxTBc9UltNFTrSeFezGXcZtZj6qcsBxiMzowoEMP5mVwXcKjpzldpZVRuY+JCC+RsekEgid4vg=="], + + "@uiw/react-codemirror": ["@uiw/react-codemirror@4.25.1", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", "@uiw/codemirror-extensions-basic-setup": "4.25.1", "codemirror": "^6.0.0" }, "peerDependencies": { "@babel/runtime": ">=7.11.0", "@codemirror/state": ">=6.0.0", "@codemirror/theme-one-dark": ">=6.0.0", "@codemirror/view": ">=6.0.0", "codemirror": ">=6.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0" } }, "sha512-eESBKHndoYkaEGlKCwRO4KrnTw1HkWBxVpEeqntoWTpoFEUYxdLWUYmkPBVk4/u8YzVy9g91nFfIRpqe5LjApg=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="], + + "@vitest/expect": ["@vitest/expect@4.0.14", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.14", "@vitest/utils": "4.0.14", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw=="], + + "@vitest/mocker": ["@vitest/mocker@4.0.14", "", { "dependencies": { "@vitest/spy": "4.0.14", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw"] }, "sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.0.14", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ=="], + + "@vitest/runner": ["@vitest/runner@4.0.14", "", { "dependencies": { "@vitest/utils": "4.0.14", "pathe": "^2.0.3" } }, "sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.0.14", "", { "dependencies": { "@vitest/pretty-format": "4.0.14", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag=="], + + "@vitest/spy": ["@vitest/spy@4.0.14", "", {}, "sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg=="], + + "@vitest/utils": ["@vitest/utils@4.0.14", "", { "dependencies": { "@vitest/pretty-format": "4.0.14", "tinyrainbow": "^3.0.3" } }, "sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw=="], + + "acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-escapes": ["ansi-escapes@7.1.1", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "attr-accept": ["attr-accept@2.2.5", "", {}, "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ=="], + + "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + + "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.25.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": "cli.js" }, "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], + + "chai": ["chai@6.2.1", "", {}, "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-truncate": ["cli-truncate@5.1.0", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-7JDGG+4Zp0CsknDCedl0DYdaeOhc46QNpXi3NLQblkZpXXgA6LncLDUUyvrjSvZeF3VRQa+KiMGomazQrC1V8g=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + + "codemirror": ["codemirror@6.0.2", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + + "commander": ["commander@14.0.1", "", {}, "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + + "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssstyle": ["cssstyle@5.3.0", "", { "dependencies": { "@asamuzakjp/css-color": "^4.0.3", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0" } }, "sha512-RveJPnk3m7aarYQ2bJ6iw+Urh55S6FzUiqtBq+TihnTDP4cI8y/TYDqGOyqgnG1J1a6BxJXZsV9JFSTulm9Z7g=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-urls": ["data-urls@6.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.0.0" } }, "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "dnd-core": ["dnd-core@16.0.1", "", { "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", "redux": "^4.2.0" } }, "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.208", "", {}, "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg=="], + + "emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": "bin/esbuild" }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "bin": "bin/eslint.js" }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], + + "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.24", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "file-selector": ["file-selector@2.1.2", "", { "dependencies": { "tslib": "^2.7.0" } }, "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + + "framer-motion": ["framer-motion@12.34.0", "", { "dependencies": { "motion-dom": "^12.34.0", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "husky": ["husky@9.1.7", "", { "bin": "bin.js" }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "immer": ["immer@10.1.1", "", {}, "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@2.6.1", "", { "bin": "lib/jiti-cli.mjs" }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsdom": ["jsdom@27.0.0", "", { "dependencies": { "@asamuzakjp/dom-selector": "^6.5.4", "cssstyle": "^5.3.0", "data-urls": "^6.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^7.3.0", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.0.0", "ws": "^8.18.2", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": "bin/jsesc" }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "katex": ["katex@0.16.22", "", { "dependencies": { "commander": "^8.3.0" }, "bin": "cli.js" }, "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "lint-staged": ["lint-staged@16.2.3", "", { "dependencies": { "commander": "^14.0.1", "listr2": "^9.0.4", "micromatch": "^4.0.8", "nano-spawn": "^1.0.3", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": "bin/lint-staged.js" }, "sha512-1OnJEESB9zZqsp61XHH2fvpS1es3hRCxMplF/AJUDa8Ho8VrscYDIuxGrj3m8KPXbcWZ8fT9XTMUhEQmOVKpKw=="], + + "listr2": ["listr2@9.0.4", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-1wd/kpAdKRLwv7/3OKC8zZ5U8e/fajCfWMxacUvB79S5nLrYGPtUI/8chMQhn3LQjsRVErTb9i1ECAwW0ZIHnQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lucide-react": ["lucide-react@0.544.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": "bin/bin.js" }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "motion-dom": ["motion-dom@12.34.0", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q=="], + + "motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nano-spawn": ["nano-spawn@1.0.3", "", {}, "sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + + "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": "bin/pidtree.js" }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + + "playwright": ["playwright@1.57.0", "", { "dependencies": { "playwright-core": "1.57.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": "cli.js" }, "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw=="], + + "playwright-core": ["playwright-core@1.57.0", "", { "bin": "cli.js" }, "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.3.3", "", { "bin": "bin/prettier.cjs" }, "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + + "react-dnd": ["react-dnd@16.0.1", "", { "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", "dnd-core": "^16.0.1", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { "@types/hoist-non-react-statics": ">= 3.3.1", "@types/node": ">= 12", "@types/react": ">= 16", "react": ">= 16.14" }, "optionalPeers": ["@types/hoist-non-react-statics"] }, "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q=="], + + "react-dnd-html5-backend": ["react-dnd-html5-backend@16.0.1", "", { "dependencies": { "dnd-core": "^16.0.1" } }, "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw=="], + + "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], + + "react-dropzone": ["react-dropzone@14.3.8", "", { "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">= 16.8 || 18.0.0" } }, "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug=="], + + "react-intersection-observer": ["react-intersection-observer@10.0.0", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "react-katex": ["react-katex@3.1.0", "", { "dependencies": { "katex": "^0.16.0" }, "peerDependencies": { "prop-types": "^15.8.1", "react": ">=15.3.2 <20" } }, "sha512-At9uLOkC75gwn2N+ZXc5HD8TlATsB+3Hkp9OGs6uA8tM3dwZ3Wljn74Bk3JyHFPgSnesY/EMrIAB1WJwqZqejA=="], + + "react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" } }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="], + + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-router": ["react-router@7.13.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "react-toastify": ["react-toastify@11.0.5", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="], + + "redux-persist": ["redux-persist@6.0.0", "", { "peerDependencies": { "redux": ">4.0.0" } }, "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ=="], + + "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rollup": ["rollup@4.48.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.48.1", "@rollup/rollup-android-arm64": "4.48.1", "@rollup/rollup-darwin-arm64": "4.48.1", "@rollup/rollup-darwin-x64": "4.48.1", "@rollup/rollup-freebsd-arm64": "4.48.1", "@rollup/rollup-freebsd-x64": "4.48.1", "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", "@rollup/rollup-linux-arm-musleabihf": "4.48.1", "@rollup/rollup-linux-arm64-gnu": "4.48.1", "@rollup/rollup-linux-arm64-musl": "4.48.1", "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", "@rollup/rollup-linux-ppc64-gnu": "4.48.1", "@rollup/rollup-linux-riscv64-gnu": "4.48.1", "@rollup/rollup-linux-riscv64-musl": "4.48.1", "@rollup/rollup-linux-s390x-gnu": "4.48.1", "@rollup/rollup-linux-x64-gnu": "4.48.1", "@rollup/rollup-linux-x64-musl": "4.48.1", "@rollup/rollup-win32-arm64-msvc": "4.48.1", "@rollup/rollup-win32-ia32-msvc": "4.48.1", "@rollup/rollup-win32-x64-msvc": "4.48.1", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg=="], + + "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], + + "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], + + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], + + "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], + + "tldts": ["tldts@7.0.14", "", { "dependencies": { "tldts-core": "^7.0.14" }, "bin": "bin/cli.js" }, "sha512-lMNHE4aSI3LlkMUMicTmAG3tkkitjOQGDTFboPJwAg2kJXKP1ryWEyqujktg5qhrFZOkk5YFzgkxg3jErE+i5w=="], + + "tldts-core": ["tldts-core@7.0.14", "", {}, "sha512-viZGNK6+NdluOJWwTO9olaugx0bkKhscIdriQQ+lNNhwitIKvb+SvhbYgnCz6j9p7dX3cJntt4agQAKMXLjJ5g=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], + + "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], + + "uuid": ["uuid@10.0.0", "", { "bin": "dist/bin/uuid" }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], + + "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="], + + "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx"], "bin": "bin/vite.js" }, "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], + + "vite-plugin-top-level-await": ["vite-plugin-top-level-await@1.6.0", "", { "dependencies": { "@rollup/plugin-virtual": "^3.0.2", "@swc/core": "^1.12.14", "@swc/wasm": "^1.12.14", "uuid": "10.0.0" }, "peerDependencies": { "vite": ">=2.8" } }, "sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww=="], + + "vite-plugin-wasm": ["vite-plugin-wasm@3.5.0", "", { "peerDependencies": { "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7" } }, "sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ=="], + + "vitest": ["vitest@4.0.14", "", { "dependencies": { "@vitest/expect": "4.0.14", "@vitest/mocker": "4.0.14", "@vitest/pretty-format": "4.0.14", "@vitest/runner": "4.0.14", "@vitest/snapshot": "4.0.14", "@vitest/spy": "4.0.14", "@vitest/utils": "4.0.14", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.14", "@vitest/browser-preview": "4.0.14", "@vitest/browser-webdriverio": "4.0.14", "@vitest/ui": "4.0.14", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom"], "bin": "vitest.mjs" }, "sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw=="], + + "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "webidl-conversions": ["webidl-conversions@8.0.0", "", {}, "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA=="], + + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@15.1.0", "", { "dependencies": { "tr46": "^6.0.0", "webidl-conversions": "^8.0.0" } }, "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": "cli.js" }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@2.8.1", "", { "bin": "bin.mjs" }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + + "@asamuzakjp/css-color/lru-cache": ["lru-cache@11.2.1", "", {}, "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + + "@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.45.0", "", { "dependencies": { "@typescript-eslint/types": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0" } }, "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA=="], + + "@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.45.0", "", { "dependencies": { "@typescript-eslint/types": "8.45.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag=="], + + "@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.44.1", "", {}, "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ=="], + + "@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.44.1", "", {}, "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ=="], + + "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.44.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.44.1", "@typescript-eslint/tsconfig-utils": "8.44.1", "@typescript-eslint/types": "8.44.1", "@typescript-eslint/visitor-keys": "8.44.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A=="], + + "@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.45.0", "", { "dependencies": { "@typescript-eslint/types": "8.45.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.44.1", "", {}, "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.44.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.44.1", "@typescript-eslint/tsconfig-utils": "8.44.1", "@typescript-eslint/types": "8.44.1", "@typescript-eslint/visitor-keys": "8.44.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A=="], + + "@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.44.1", "", {}, "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ=="], + + "autoprefixer/caniuse-lite": ["caniuse-lite@1.0.30001737", "", {}, "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw=="], + + "browserslist/caniuse-lite": ["caniuse-lite@1.0.30001737", "", {}, "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "dnd-core/redux": ["redux@4.2.1", "", { "dependencies": { "@babel/runtime": "^7.9.2" } }, "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w=="], + + "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "rollup/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.44.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.44.1", "@typescript-eslint/types": "^8.44.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA=="], + + "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.44.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ=="], + + "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.44.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.44.1", "@typescript-eslint/types": "^8.44.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.44.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + } +} diff --git a/package-lock.json b/package-lock.json index bf801e02..b1885582 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,8 @@ "@typeberry/lib": "^0.5.0-6a64d40", "@typeberry/spectool-wasm": "0.23.0", "@uiw/react-codemirror": "^4.25.1", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001769", "class-variance-authority": "^0.7.1", "classnames": "^2.5.1", "clsx": "^2.1.1", @@ -4593,6 +4595,18 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -4670,10 +4684,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001737", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", - "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", - "dev": true, + "version": "1.0.30001770", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 1ded72c0..3ccfee4a 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "@typeberry/lib": "^0.5.0-6a64d40", "@typeberry/spectool-wasm": "0.23.0", "@uiw/react-codemirror": "^4.25.1", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001769", "class-variance-authority": "^0.7.1", "classnames": "^2.5.1", "clsx": "^2.1.1", @@ -93,12 +95,16 @@ "vite": "^7.1.12", "vite-plugin-top-level-await": "^1.4.4", "vite-plugin-wasm": "^3.5.0", - "vitest": "^4.0.14" + "vitest": "^4.0.14", + "@swc/core": "^1.13.5" }, "lint-staged": { "**/*.{ts,tsx}": [ "npm run format", "npm run lint" ] - } + }, + "trustedDependencies": [ + "@swc/core" + ] } diff --git a/src/components/DebuggerControlls/index.tsx b/src/components/DebuggerControlls/index.tsx index 952fa73d..cb617d1d 100644 --- a/src/components/DebuggerControlls/index.tsx +++ b/src/components/DebuggerControlls/index.tsx @@ -28,6 +28,7 @@ export const DebuggerControlls = () => { const workers = useAppSelector((state) => state.workers); const isLoading = useAppSelector(selectIsAnyWorkerLoading); const dispatch = useAppDispatch(); + const noWorkers = workers.length === 0; const currentInstruction = programPreviewResult.find((x) => x.address === workers[0]?.currentPc); @@ -156,6 +157,7 @@ export const DebuggerControlls = () => { onOpen={() => { setError(undefined); }} + disabled={!pvmInitialized || isProgramEditMode || isLoading || noWorkers} /> {!isDebugFinished && isRunMode ? ( @@ -181,7 +183,7 @@ export const DebuggerControlls = () => { className="md:mr-3" variant="secondary" onClick={handleRunProgram} - disabled={isDebugFinished || !pvmInitialized || isProgramEditMode || isLoading || !!error} + disabled={isDebugFinished || !pvmInitialized || isProgramEditMode || isLoading || !!error || noWorkers} > {isLoading ? : } Run @@ -197,7 +199,8 @@ export const DebuggerControlls = () => { !pvmInitialized || isProgramEditMode || isLoading || - !!error + !!error || + noWorkers } > {isLoading ? ( @@ -217,7 +220,8 @@ export const DebuggerControlls = () => { !pvmInitialized || isProgramEditMode || isLoading || - !!error + !!error || + noWorkers } > {isLoading ? ( diff --git a/src/components/DebuggerSettings/Content.tsx b/src/components/DebuggerSettings/Content.tsx index 42f25971..bad9b10a 100644 --- a/src/components/DebuggerSettings/Content.tsx +++ b/src/components/DebuggerSettings/Content.tsx @@ -1,11 +1,16 @@ import { DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { useAppDispatch, useAppSelector } from "@/store/hooks"; -import { setServiceId, setSpiArgs, setUiRefreshRate, UiRefreshMode } from "@/store/debugger/debuggerSlice"; +import { + setServiceId, + setSpiArgs, + setUiRefreshRate, + UiRefreshMode, + selectHostCallsTrace, +} from "@/store/debugger/debuggerSlice"; import { NumeralSystemContext } from "@/context/NumeralSystemContext"; import { valueToNumeralSystem } from "../Instructions/utils"; import { useContext, useState } from "react"; -import { setAllWorkersServiceId } from "@/store/workers/workersSlice"; import { isSerializedError } from "@/store/utils"; import { logger } from "@/utils/loggerService"; import { ToggleDarkMode } from "@/packages/ui-kit/DarkMode/ToggleDarkMode"; @@ -16,6 +21,8 @@ import { cn } from "@/lib/utils"; import { bytes } from "@typeberry/lib"; import { Button } from "@/components/ui/button"; import { SpiConfigDialog } from "./SpiConfigDialog"; +import { TraceConfigDialog } from "./TraceConfigDialog"; +import { CheckCircle2, FileText } from "lucide-react"; function stringToNumber(value: string, cb: (x: string) => T): T { try { @@ -27,6 +34,7 @@ function stringToNumber(value: string, cb: (x: string) => T): T { export const DebuggerSettingsContent = () => { const debuggerState = useAppSelector((state) => state.debugger); + const hostCallsTrace = useAppSelector(selectHostCallsTrace); const dispatch = useAppDispatch(); const spiArgs = bytes.BytesBlob.blobFrom(debuggerState.spiArgs?.slice(0) ?? new Uint8Array()); const { numeralSystem } = useContext(NumeralSystemContext); @@ -34,6 +42,10 @@ export const DebuggerSettingsContent = () => { const [textSpi, setTextSpi] = useState(spiArgs.toString()); const isSpiError = textSpi !== spiArgs.toString(); const [isSpiConfigDialogOpen, setIsSpiConfigDialogOpen] = useState(false); + const [isTraceConfigDialogOpen, setIsTraceConfigDialogOpen] = useState(false); + + const hasTrace = hostCallsTrace !== null; + const traceHostCallCount = hostCallsTrace?.parsed?.hostCalls.length ?? 0; const handleTextSpi = (newVal: string) => { setTextSpi(newVal); try { @@ -48,7 +60,6 @@ export const DebuggerSettingsContent = () => { setError(""); try { dispatch(setServiceId(newServiceId)); - await dispatch(setAllWorkersServiceId()).unwrap(); } catch (e) { if (e instanceof Error || isSerializedError(e)) { setError(e.message); @@ -161,13 +172,29 @@ export const DebuggerSettingsContent = () => {
Host Calls Trace +
+ {hasTrace && ( + + + {traceHostCallCount} host call{traceHostCallCount !== 1 ? "s" : ""} + + )} + +
{error &&

{error}

} @@ -184,7 +211,6 @@ export const DebuggerSettingsContent = () => { // Update service ID if it changed try { dispatch(setServiceId(serviceId)); - await dispatch(setAllWorkersServiceId()).unwrap(); } catch (e) { if (e instanceof Error || isSerializedError(e)) { setError(e.message); @@ -193,6 +219,8 @@ export const DebuggerSettingsContent = () => { } }} /> + + ); }; diff --git a/src/components/DebuggerSettings/TraceConfigDialog.tsx b/src/components/DebuggerSettings/TraceConfigDialog.tsx new file mode 100644 index 00000000..5f753e12 --- /dev/null +++ b/src/components/DebuggerSettings/TraceConfigDialog.tsx @@ -0,0 +1,266 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { useState, useEffect, useCallback } from "react"; +import { Separator } from "../ui/separator"; +import { useAppSelector, useAppDispatch } from "@/store/hooks"; +import { + setHostCallsTrace, + setAutoContinueOnHostCalls, + selectHostCallsTrace, + selectAutoContinueOnHostCalls, +} from "@/store/debugger/debuggerSlice"; +import { validateTraceContent, getTraceSummary, parseTrace } from "@/lib/host-call-trace"; +import CodeMirror from "@uiw/react-codemirror"; +import { useIsDarkMode } from "@/packages/ui-kit/DarkMode/utils"; +import classNames from "classnames"; +import { Switch } from "@/components/ui/switch"; +import { Label } from "@/components/ui/label"; +import { AlertCircle, CheckCircle2, Upload } from "lucide-react"; +import { useDropzone } from "react-dropzone"; + +type TraceConfigDialogProps = { + open: boolean; + onOpenChange: (open: boolean) => void; +}; + +const EXAMPLE_TRACE = `# Example Ecalli Trace +# (program line is hidden - loaded separately) + +ecalli=10 pc=0 gas=10000 r01=0x1 r03=0x1000 +memread 0x00001000 len=4 -> 0x01020304 +setreg r00 <- 0x100 +setgas <- 9950 + +HALT pc=42 gas=9920 r00=0x100 +`; + +function stripProgramLine(content: string): { stripped: string; programLine: string | null } { + const lines = content.split("\n"); + let programLine: string | null = null; + + const filtered = lines.filter((line) => { + if (line.trim().startsWith("program ")) { + programLine = line; + return false; + } + return true; + }); + + return { stripped: filtered.join("\n"), programLine }; +} + +function restoreProgramLine(content: string, programLine: string | null): string { + if (!programLine) return content; + const lines = content.split("\n"); + const hasContent = lines.some((l) => l.trim() && !l.trim().startsWith("#")); + if (hasContent) { + return programLine + "\n" + content; + } + return content; +} + +export const TraceConfigDialog = ({ open, onOpenChange }: TraceConfigDialogProps) => { + const dispatch = useAppDispatch(); + const isDark = useIsDarkMode(); + const existingTrace = useAppSelector(selectHostCallsTrace); + const autoContinue = useAppSelector(selectAutoContinueOnHostCalls); + + const [traceContent, setTraceContent] = useState(""); + const [programLine, setProgramLine] = useState(null); + const [validationErrors, setValidationErrors] = useState([]); + const [summary, setSummary] = useState | null>(null); + + const validateCurrentContent = useCallback((content: string) => { + if (!content.trim()) { + setValidationErrors([]); + setSummary(null); + return; + } + + const validation = validateTraceContent(content); + setValidationErrors(validation.errors.map((e) => `Line ${e.lineNumber}: ${e.message}`)); + + if (validation.errors.length === 0) { + const fullParsed = parseTrace(content); + setSummary(getTraceSummary(fullParsed)); + } else { + setSummary(null); + } + }, []); + + useEffect(() => { + if (open) { + const raw = existingTrace?.rawContent ?? ""; + const { stripped, programLine: pl } = stripProgramLine(raw); + setTraceContent(stripped); + setProgramLine(pl); + validateCurrentContent(raw); + } + }, [open, existingTrace, validateCurrentContent]); + + const handleContentChange = useCallback( + (value: string) => { + setTraceContent(value); + const fullContent = restoreProgramLine(value, programLine); + validateCurrentContent(fullContent); + }, + [validateCurrentContent, programLine], + ); + + const handleApply = () => { + const fullContent = restoreProgramLine(traceContent.trim(), programLine); + dispatch(setHostCallsTrace(fullContent || null)); + onOpenChange(false); + }; + + const handleClear = () => { + setTraceContent(""); + setProgramLine(null); + setValidationErrors([]); + setSummary(null); + }; + + const handleFileDrop = useCallback( + (acceptedFiles: File[]) => { + if (acceptedFiles.length > 0) { + const file = acceptedFiles[0]; + const reader = new FileReader(); + reader.onload = (e) => { + const content = e.target?.result as string; + const { stripped, programLine: pl } = stripProgramLine(content); + setTraceContent(stripped); + setProgramLine(pl); + validateCurrentContent(content); + }; + reader.readAsText(file); + } + }, + [validateCurrentContent], + ); + + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + onDrop: handleFileDrop, + accept: { "text/plain": [".txt", ".trace", ".log"] }, + noClick: true, + multiple: false, + }); + + const hasContent = traceContent.trim().length > 0; + const hasErrors = validationErrors.length > 0; + const isValid = hasContent && !hasErrors; + + return ( + + + + Configure Ecalli Trace + + +
+ + +
+
+
+ dispatch(setAutoContinueOnHostCalls(checked))} + /> + +
+
+
+ + +
+
+ +
+ +
+ +
+ {hasErrors ? ( +
+ +
+

Validation errors:

+
    + {validationErrors.slice(0, 3).map((err, i) => ( +
  • {err}
  • + ))} + {validationErrors.length > 3 &&
  • ...and {validationErrors.length - 3} more
  • } +
+
+
+ ) : summary ? ( +
+ +
+

Valid trace

+

+ {summary.hostCallCount} host call{summary.hostCallCount !== 1 ? "s" : ""} + {programLine && " • program line preserved (hidden)"} + {summary.hasTermination && " • has termination"} +

+
+
+ ) : ( +

+ Paste or upload an Ecalli trace file. The trace will be used to auto-fill host call responses during + execution. +

+ )} +
+
+ +
+ +
+ +
+ + +
+
+
+ ); +}; diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx new file mode 100644 index 00000000..eb0fa2fa --- /dev/null +++ b/src/components/ErrorBoundary.tsx @@ -0,0 +1,77 @@ +import { Component, ErrorInfo, ReactNode } from "react"; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; + errorInfo: ErrorInfo | null; +} + +export class ErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { hasError: false, error: null, errorInfo: null }; + } + + static getDerivedStateFromError(error: Error): Partial { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error("ErrorBoundary caught an error:", error); + console.error("Component stack:", errorInfo.componentStack); + this.setState({ errorInfo }); + } + + render() { + if (this.state.hasError) { + if (this.props.fallback) { + return this.props.fallback; + } + + return ( +
+
+

Something went wrong

+
+

Error:

+
+                {this.state.error?.message || "Unknown error"}
+              
+
+ {this.state.error?.stack && ( +
+

Stack trace:

+
{this.state.error.stack}
+
+ )} + {this.state.errorInfo?.componentStack && ( +
+

Component stack:

+
+                  {this.state.errorInfo.componentStack}
+                
+
+ )} + +
+
+ ); + } + + return this.props.children; + } +} diff --git a/src/components/HostCallDialog/DefaultHostCallContent.tsx b/src/components/HostCallDialog/DefaultHostCallContent.tsx index f799d2e3..60290903 100644 --- a/src/components/HostCallDialog/DefaultHostCallContent.tsx +++ b/src/components/HostCallDialog/DefaultHostCallContent.tsx @@ -1,10 +1,11 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { Input } from "@/components/ui/input"; import { valueToNumeralSystem } from "@/components/Instructions/utils"; import { DEFAULT_GAS, DEFAULT_REGS, ExpectedState, RegistersArray } from "@/types/pvm"; import { NumeralSystem } from "@/context/NumeralSystem"; import { MemoryEditor } from "./MemoryEditor"; import { HostCallActionButtons } from "./handlers/HostCallActionButtons"; +import { HostCallEntry } from "@/lib/host-call-trace"; // Re-export for convenience export type { MemoryEdit } from "@/store/workers/workersSlice"; @@ -27,8 +28,30 @@ interface DefaultHostCallContentProps { onGasChange: (gas: bigint) => void; onMemoryChange: (memory: MemoryEdit | null) => void; onResume: (mode: HostCallResumeMode, regs?: bigint[], gas?: bigint) => void; + onRestart?: () => void; + traceEntry?: HostCallEntry | null; + hostCallId?: number; } +// Log level styles (matches LogHostCall.tsx) +const LOG_LEVEL_STYLES: Record = { + 0: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300", // ERROR + 1: "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300", // WARNING + 2: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300", // INFO + 3: "bg-gray-100 text-gray-800 dark:bg-gray-800/50 dark:text-gray-300", // DEBUG + 4: "bg-gray-100 text-gray-600 dark:bg-gray-800/30 dark:text-gray-400", // NIT +}; + +const LOG_LEVEL_NAMES: Record = { + 0: "ERROR", + 1: "WARNING", + 2: "INFO", + 3: "DEBUG", + 4: "NIT", +}; + +const decoder = new TextDecoder("utf8"); + export const DefaultHostCallContent: React.FC = ({ currentState, isLoading, @@ -36,28 +59,104 @@ export const DefaultHostCallContent: React.FC = ({ readMemory, onRegsChange, onResume, + onRestart, onGasChange, onMemoryChange, + traceEntry, + hostCallId, }) => { // Local state for editable values const [regs, setRegs] = useState([...DEFAULT_REGS]); const [gas, setGas] = useState(0n); + // Original values to track modifications + const [originalRegs, setOriginalRegs] = useState([...DEFAULT_REGS]); + const [originalGas, setOriginalGas] = useState(0n); + + // Log message state (for log host call) + const [logTarget, setLogTarget] = useState(""); + const [logMessage, setLogMessage] = useState(""); + const [logLevel, setLogLevel] = useState(2); + const [isLoadingLog, setIsLoadingLog] = useState(false); + // Initialize local state when currentState changes useEffect(() => { if (currentState) { - // Copy current registers - const newRegs = currentState.regs ? [...currentState.regs] : [...DEFAULT_REGS]; + // Copy current registers as original values + const originalRegsFromState = currentState.regs ? [...currentState.regs] : [...DEFAULT_REGS]; + setOriginalRegs([...originalRegsFromState]); + + // Apply trace entry modifications if available + const newRegs = [...originalRegsFromState]; + if (traceEntry) { + for (const rw of traceEntry.registerWrites) { + newRegs[rw.index] = rw.value; + } + } setRegs(newRegs); onRegsChange(newRegs); - // Set gas to current gas - 10 (as per requirement) + // Set gas - original is current gas - 10 const currentGas = currentState.gas ?? DEFAULT_GAS; - const newGas = currentGas > 10n ? currentGas - 10n : 0n; + const originalGasValue = currentGas > 10n ? currentGas - 10n : 0n; + setOriginalGas(originalGasValue); + + // Apply trace entry gas if available + const newGas = traceEntry?.gasAfter ?? originalGasValue; setGas(newGas); onGasChange(newGas); } - }, [currentState, onRegsChange, onGasChange]); + }, [currentState, traceEntry, onRegsChange, onGasChange]); + + // Load log message when this is a log host call (index 100) + useEffect(() => { + if (hostCallId !== 100 || !currentState?.regs) { + setLogTarget(""); + setLogMessage(""); + return; + } + + const loadLogMessage = async () => { + setIsLoadingLog(true); + try { + const currentRegs = currentState.regs ?? DEFAULT_REGS; + const level = Number(currentRegs[7] ?? 0n); + const targetStart = Number(currentRegs[8] ?? 0n); + const targetLength = Number(currentRegs[9] ?? 0n); + const msgStart = Number(currentRegs[10] ?? 0n); + const msgLength = Number(currentRegs[11] ?? 0n); + + setLogLevel(level); + + // Read target string from memory + if (targetStart !== 0 && targetLength > 0) { + const targetBytes = await readMemory(targetStart, targetLength); + setLogTarget(decoder.decode(targetBytes)); + } else { + setLogTarget(""); + } + + // Read message string from memory + if (msgLength > 0) { + const msgBytes = await readMemory(msgStart, msgLength); + setLogMessage(decoder.decode(msgBytes)); + } else { + setLogMessage(""); + } + } catch (e) { + console.error("Failed to read log message:", e); + } finally { + setIsLoadingLog(false); + } + }; + + loadLogMessage(); + }, [hostCallId, currentState?.regs, readMemory]); + + // Check if a value has been modified from original + // Gas is considered modified if trace provides gasAfter OR user changed it + const isGasModified = traceEntry?.gasAfter != null || gas !== originalGas; + const isRegisterModified = (index: number) => regs[index] !== originalRegs[index]; const handleRegisterChange = (index: number, value: string) => { const newValue = stringToNumber(value, BigInt); @@ -82,54 +181,110 @@ export const DefaultHostCallContent: React.FC = ({ [onMemoryChange], ); + // Extract memory writes from trace entry (memoized to prevent unnecessary re-renders) + const pendingMemoryWrites = useMemo( + () => + traceEntry?.memoryWrites.map((mw) => ({ + address: mw.address, + data: mw.data, + })), + [traceEntry?.memoryWrites], + ); + + // Get initial address and length from first memory write in trace + const firstMemWrite = traceEntry?.memoryWrites[0]; + const initialMemoryAddress = firstMemWrite?.address; + const initialMemoryLength = firstMemWrite ? Math.max(256, firstMemWrite.data.length) : undefined; + + // Reusable components for gas and registers + const gasAndRegisters = ( +
+ {/* Gas */} +
+ + handleGasChange(e.target.value)} + onKeyUp={(e) => e.key === "Enter" && e.currentTarget.blur()} + disabled={isLoading} + /> +
+ + {/* Registers */} +
+ +
+ {(regs as RegistersArray).map((regValue, index) => { + const isModified = isRegisterModified(index); + return ( +
+ + ω{index} + + handleRegisterChange(index, e.target.value)} + onKeyUp={(e) => e.key === "Enter" && e.currentTarget.blur()} + disabled={isLoading} + /> +
+ ); + })} +
+
+
+ ); + + const memoryEditor = ( +
+ +
+ ); + + // Log message display component + const logMessageDisplay = hostCallId === 100 && ( +
+
+ Log: + + {LOG_LEVEL_NAMES[logLevel] ?? `UNKNOWN(${logLevel})`} + + {logTarget && [{logTarget}]} +
+ {isLoadingLog ? ( +
Loading message...
+ ) : ( +
+ {logMessage || (empty)} +
+ )} +
+ ); + return ( <>
- {/* Two-column layout: 1fr for gas/registers, 2fr for memory */} -
- {/* Column 1: Gas and Registers */} -
- {/* Gas */} -
- - handleGasChange(e.target.value)} - onKeyUp={(e) => e.key === "Enter" && e.currentTarget.blur()} - disabled={isLoading} - /> -
- - {/* Registers */} -
- -
- {(regs as RegistersArray).map((regValue, index) => ( -
- - ω{index} - - handleRegisterChange(index, e.target.value)} - onKeyUp={(e) => e.key === "Enter" && e.currentTarget.blur()} - disabled={isLoading} - /> -
- ))} -
-
-
+ {/* Log message for log host call */} + {logMessageDisplay} - {/* Column 2: Memory Editor (2x width) */} -
- -
+ {/* Two-column layout: registers left, memory right (always) */} +
+
{gasAndRegisters}
+
{memoryEditor}
- + ); }; diff --git a/src/components/HostCallDialog/MemoryEditor.tsx b/src/components/HostCallDialog/MemoryEditor.tsx index ce5aed63..2ed5ffe2 100644 --- a/src/components/HostCallDialog/MemoryEditor.tsx +++ b/src/components/HostCallDialog/MemoryEditor.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Input } from "@/components/ui/input"; +import type { MemoryEdit } from "@/store/workers/workersSlice"; const MAX_LENGTH = 4096; const BYTES_PER_ROW = 16; @@ -8,6 +9,9 @@ interface MemoryEditorProps { readMemory: (startAddress: number, length: number) => Promise; disabled?: boolean; onMemoryChange?: (address: number, data: Uint8Array) => void; + pendingWrites?: MemoryEdit[]; + initialAddress?: number; + initialLength?: number; } function parseNumber(value: string): number | null { @@ -34,12 +38,35 @@ function toAscii(byte: number): string { return "."; } -export const MemoryEditor: React.FC = ({ readMemory, disabled, onMemoryChange }) => { - const [addressInput, setAddressInput] = useState("0x1000"); - const [lengthInput, setLengthInput] = useState("256"); - const [address, setAddress] = useState(0x1000); - const [length, setLength] = useState(256); +export const MemoryEditor: React.FC = ({ + readMemory, + disabled, + onMemoryChange, + pendingWrites, + initialAddress, + initialLength, +}) => { + const defaultAddress = initialAddress ?? 0x1000; + const defaultLength = initialLength ?? 256; + + const [addressInput, setAddressInput] = useState(`0x${defaultAddress.toString(16)}`); + const [lengthInput, setLengthInput] = useState(defaultLength.toString()); + const [address, setAddress] = useState(defaultAddress); + const [length, setLength] = useState(defaultLength); + + // Update state when initial values change (e.g., when trace is loaded) + useEffect(() => { + if (initialAddress !== undefined) { + setAddressInput(`0x${initialAddress.toString(16)}`); + setAddress(initialAddress); + } + if (initialLength !== undefined) { + setLengthInput(initialLength.toString()); + setLength(initialLength); + } + }, [initialAddress, initialLength]); const [data, setData] = useState(null); + const [originalData, setOriginalData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [focusedByte, setFocusedByte] = useState(null); @@ -51,6 +78,7 @@ export const MemoryEditor: React.FC = ({ readMemory, disabled const loadMemory = async () => { if (address === null || length === null || length <= 0) { setData(null); + setOriginalData(null); return; } @@ -63,17 +91,52 @@ export const MemoryEditor: React.FC = ({ readMemory, disabled setError(null); try { const memoryData = await readMemory(address, length); - setData(memoryData); + setOriginalData(new Uint8Array(memoryData)); + + // Apply pending writes from trace if any + const modifiedData = new Uint8Array(memoryData); + if (pendingWrites && pendingWrites.length > 0) { + for (const write of pendingWrites) { + const writeStart = write.address; + const writeEnd = write.address + write.data.length; + const viewStart = address; + const viewEnd = address + length; + + // Check if this write overlaps with current view + if (writeStart < viewEnd && writeEnd > viewStart) { + const overlapStart = Math.max(writeStart, viewStart); + const overlapEnd = Math.min(writeEnd, viewEnd); + + for (let addr = overlapStart; addr < overlapEnd; addr++) { + const dataIndex = addr - viewStart; + const writeIndex = addr - writeStart; + modifiedData[dataIndex] = write.data[writeIndex]; + } + } + } + } + + setData(modifiedData); } catch (e) { setError(e instanceof Error ? e.message : "Failed to read memory"); setData(null); + setOriginalData(null); } finally { setIsLoading(false); } }; loadMemory(); - }, [address, length, readMemory]); + }, [address, length, readMemory, pendingWrites]); + + // Check if a byte at given index has been modified + const isByteModified = useCallback( + (byteIndex: number): boolean => { + if (!data || !originalData) return false; + return data[byteIndex] !== originalData[byteIndex]; + }, + [data, originalData], + ); const updateAddress = useCallback((addressInput: string) => { setAddressInput(addressInput); @@ -411,6 +474,7 @@ export const MemoryEditor: React.FC = ({ readMemory, disabled {row.bytes.map((byte, i) => { const byteIndex = row.offset + i; const isActive = focusedByte === byteIndex; + const isModified = isByteModified(byteIndex); return ( = ({ readMemory, disabled disabled ? "cursor-not-allowed opacity-50 bg-muted" : "cursor-text hover:border-brand focus-visible:ring-1 focus-visible:ring-brand bg-background" - } ${isActive ? "border-brand" : "border-transparent"}`} + } ${isActive ? "border-brand" : "border-transparent"} ${isModified ? "bg-yellow-100 dark:bg-yellow-900/40 text-yellow-800 dark:text-yellow-200" : ""}`} value={toHex(byte)} onFocus={() => !disabled && setFocusedByte(byteIndex)} onKeyDown={(e) => handleByteKeyDown(byteIndex, e)} @@ -456,6 +520,7 @@ export const MemoryEditor: React.FC = ({ readMemory, disabled {row.bytes.map((byte, i) => { const byteIndex = row.offset + i; const asciiChar = toAscii(byte); + const isModified = isByteModified(byteIndex); return ( = ({ readMemory, disabled disabled ? "cursor-not-allowed opacity-50 bg-muted" : "cursor-text hover:border-brand focus-visible:ring-1 focus-visible:ring-brand bg-background" - } ${focusedByte === byteIndex ? "border-brand" : "border-transparent"}`} + } ${focusedByte === byteIndex ? "border-brand" : "border-transparent"} ${isModified ? "bg-yellow-100 dark:bg-yellow-900/40 text-yellow-800 dark:text-yellow-200" : ""}`} value={asciiChar} onFocus={() => { if (!disabled) { diff --git a/src/components/HostCallDialog/handlers/FetchHostCall.tsx b/src/components/HostCallDialog/handlers/FetchHostCall.tsx index 863bd0f9..8f1040b3 100644 --- a/src/components/HostCallDialog/handlers/FetchHostCall.tsx +++ b/src/components/HostCallDialog/handlers/FetchHostCall.tsx @@ -76,7 +76,13 @@ const FETCH_KIND_INFO: Record = ({ currentState, isLoading, onResume, serviceId }) => { +const FetchHostCallComponent: React.FC = ({ + currentState, + isLoading, + onResume, + onRestart, + serviceId, +}) => { const regs = currentState.regs ?? DEFAULT_REGS; const fetchKind = Number(regs[10] ?? 0n); const fetchInfo = FETCH_KIND_INFO[fetchKind] || { @@ -202,7 +208,7 @@ const FetchHostCallComponent: React.FC = ({ currentState,
ω₁₁: {regs[11]?.toString()}
- + ); }; diff --git a/src/components/HostCallDialog/handlers/GasHostCall.tsx b/src/components/HostCallDialog/handlers/GasHostCall.tsx index 8884865b..5099d0ca 100644 --- a/src/components/HostCallDialog/handlers/GasHostCall.tsx +++ b/src/components/HostCallDialog/handlers/GasHostCall.tsx @@ -14,7 +14,13 @@ interface GasResult { } // eslint-disable-next-line react-refresh/only-export-components -const GasHostCallComponent: React.FC = ({ currentState, isLoading, onResume, serviceId }) => { +const GasHostCallComponent: React.FC = ({ + currentState, + isLoading, + onResume, + onRestart, + serviceId, +}) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); const [result, setResult] = useState(null); @@ -91,7 +97,7 @@ const GasHostCallComponent: React.FC = ({ currentState, is )} - + ); }; diff --git a/src/components/HostCallDialog/handlers/HostCallActionButtons.tsx b/src/components/HostCallDialog/handlers/HostCallActionButtons.tsx index 5cccc07f..c0afcf40 100644 --- a/src/components/HostCallDialog/handlers/HostCallActionButtons.tsx +++ b/src/components/HostCallDialog/handlers/HostCallActionButtons.tsx @@ -1,27 +1,66 @@ import { Button } from "@/components/ui/button"; +import { Switch } from "@/components/ui/switch"; +import { Label } from "@/components/ui/label"; import { Play, StepForward, SkipForward } from "lucide-react"; import { HostCallResumeMode } from "@/store/workers/workersSlice"; +import { useAppDispatch, useAppSelector } from "@/store/hooks"; +import { + selectAutoContinueOnHostCalls, + selectHostCallsTrace, + setAutoContinueOnHostCalls, +} from "@/store/debugger/debuggerSlice"; interface HostCallActionButtonsProps { onResume: (mode: HostCallResumeMode) => void; + onRestart?: () => void; disabled?: boolean; } -export const HostCallActionButtons: React.FC = ({ onResume, disabled }) => { +export const HostCallActionButtons: React.FC = ({ onResume, onRestart, disabled }) => { + const dispatch = useAppDispatch(); + const hasTrace = useAppSelector(selectHostCallsTrace) !== null; + const autoContinue = useAppSelector(selectAutoContinueOnHostCalls); + return ( -
- - - +
+ {onRestart && ( + + )} + {hasTrace && ( +
+ dispatch(setAutoContinueOnHostCalls(checked))} + disabled={disabled} + /> + +
+ )} +
+ + + +
); }; diff --git a/src/components/HostCallDialog/handlers/InfoHostCall.tsx b/src/components/HostCallDialog/handlers/InfoHostCall.tsx index 807d7889..bc65e48d 100644 --- a/src/components/HostCallDialog/handlers/InfoHostCall.tsx +++ b/src/components/HostCallDialog/handlers/InfoHostCall.tsx @@ -26,7 +26,13 @@ const DEFAULT_LAST_ACCUMULATION = "0"; const DEFAULT_PARENT_SERVICE = "0"; // eslint-disable-next-line react-refresh/only-export-components -const InfoHostCallComponent: React.FC = ({ currentState, isLoading, onResume, serviceId }) => { +const InfoHostCallComponent: React.FC = ({ + currentState, + isLoading, + onResume, + onRestart, + serviceId, +}) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); const requestedServiceId = regs[7]; @@ -234,7 +240,7 @@ const InfoHostCallComponent: React.FC = ({ currentState, i {/* Error */} {error &&
{error}
}
- + ); }; diff --git a/src/components/HostCallDialog/handlers/LogHostCall.tsx b/src/components/HostCallDialog/handlers/LogHostCall.tsx index 2e373633..c7a4feaa 100644 --- a/src/components/HostCallDialog/handlers/LogHostCall.tsx +++ b/src/components/HostCallDialog/handlers/LogHostCall.tsx @@ -37,7 +37,13 @@ function getLogLevelStyle(level: number): string { const decoder = new TextDecoder("utf8"); // eslint-disable-next-line react-refresh/only-export-components -const LogHostCallComponent: React.FC = ({ currentState, isLoading, readMemory, onResume }) => { +const LogHostCallComponent: React.FC = ({ + currentState, + isLoading, + readMemory, + onResume, + onRestart, +}) => { const [target, setTarget] = useState(""); const [message, setMessage] = useState(""); const [isLoadingMemory, setIsLoadingMemory] = useState(true); @@ -129,7 +135,7 @@ const LogHostCallComponent: React.FC = ({ currentState, is - + ); }; diff --git a/src/components/HostCallDialog/handlers/ProvideHostCall.tsx b/src/components/HostCallDialog/handlers/ProvideHostCall.tsx index c09b855f..6f925f91 100644 --- a/src/components/HostCallDialog/handlers/ProvideHostCall.tsx +++ b/src/components/HostCallDialog/handlers/ProvideHostCall.tsx @@ -21,6 +21,7 @@ const ProvideHostCallComponent: React.FC = ({ isLoading, readMemory, onResume, + onRestart, serviceId, }) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); @@ -168,7 +169,7 @@ const ProvideHostCallComponent: React.FC = ({ {error &&
{error}
} - + ); }; diff --git a/src/components/HostCallDialog/handlers/ReadHostCall.tsx b/src/components/HostCallDialog/handlers/ReadHostCall.tsx index 3ceeae57..e979bf0f 100644 --- a/src/components/HostCallDialog/handlers/ReadHostCall.tsx +++ b/src/components/HostCallDialog/handlers/ReadHostCall.tsx @@ -18,6 +18,7 @@ const ReadHostCallComponent: React.FC = ({ isLoading, readMemory, onResume, + onRestart, serviceId, }) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); @@ -210,7 +211,7 @@ const ReadHostCallComponent: React.FC = ({ {/* Error */} {error &&
{error}
} - + ); }; diff --git a/src/components/HostCallDialog/handlers/TransferHostCall.tsx b/src/components/HostCallDialog/handlers/TransferHostCall.tsx index f9d73397..d1990a1a 100644 --- a/src/components/HostCallDialog/handlers/TransferHostCall.tsx +++ b/src/components/HostCallDialog/handlers/TransferHostCall.tsx @@ -22,6 +22,7 @@ const TransferHostCallComponent: React.FC = ({ isLoading, readMemory, onResume, + onRestart, serviceId, }) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); @@ -174,7 +175,7 @@ const TransferHostCallComponent: React.FC = ({ {error &&
{error}
} - + ); }; diff --git a/src/components/HostCallDialog/handlers/WriteHostCall.tsx b/src/components/HostCallDialog/handlers/WriteHostCall.tsx index ac630488..f96c5ae6 100644 --- a/src/components/HostCallDialog/handlers/WriteHostCall.tsx +++ b/src/components/HostCallDialog/handlers/WriteHostCall.tsx @@ -66,6 +66,7 @@ const WriteHostCallComponent: React.FC = ({ isLoading, readMemory, onResume, + onRestart, serviceId, }) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); @@ -286,7 +287,7 @@ const WriteHostCallComponent: React.FC = ({ )} - + ); }; diff --git a/src/components/HostCallDialog/handlers/YieldHostCall.tsx b/src/components/HostCallDialog/handlers/YieldHostCall.tsx index 9f163b57..4d0681a4 100644 --- a/src/components/HostCallDialog/handlers/YieldHostCall.tsx +++ b/src/components/HostCallDialog/handlers/YieldHostCall.tsx @@ -20,6 +20,7 @@ const YieldHostCallComponent: React.FC = ({ isLoading, readMemory, onResume, + onRestart, serviceId, }) => { const regs = useMemo(() => currentState.regs ?? DEFAULT_REGS, [currentState.regs]); @@ -150,7 +151,7 @@ const YieldHostCallComponent: React.FC = ({ {error &&
{error}
} - + ); }; diff --git a/src/components/HostCallDialog/handlers/types.ts b/src/components/HostCallDialog/handlers/types.ts index 4aafd8da..547548f2 100644 --- a/src/components/HostCallDialog/handlers/types.ts +++ b/src/components/HostCallDialog/handlers/types.ts @@ -6,12 +6,14 @@ export interface HostCallHandlerProps { currentState: ExpectedState; /** Whether the handler is currently loading/processing */ isLoading: boolean; - /** Configured service id used as the “current” service */ + /** Configured service id used as the "current" service */ serviceId: number | null; /** Function to read memory from PVM */ readMemory: (startAddress: number, length: number) => Promise; /** Function to resume execution */ onResume: (mode: HostCallResumeMode, regs?: bigint[], gas?: bigint, memoryEdits?: MemoryEdit[]) => void; + /** Function to restart execution from the beginning */ + onRestart?: () => void; } export interface HostCallHandler { diff --git a/src/components/HostCallDialog/index.tsx b/src/components/HostCallDialog/index.tsx index 9e011f2e..b202aea4 100644 --- a/src/components/HostCallDialog/index.tsx +++ b/src/components/HostCallDialog/index.tsx @@ -1,11 +1,14 @@ -import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { useCallback, useContext, useEffect, useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { useAppDispatch, useAppSelector } from "@/store/hooks"; import { resumeAfterHostCall, readMemoryRange, HostCallResumeMode } from "@/store/workers/workersSlice"; import { NumeralSystemContext } from "@/context/NumeralSystemContext"; import { getHostCallHandler } from "./handlers"; import { DefaultHostCallContent, MemoryEdit } from "./DefaultHostCallContent"; -import { DEFAULT_GAS, DEFAULT_REGS } from "@/types/pvm"; +import { DEFAULT_GAS, DEFAULT_REGS, ExpectedState } from "@/types/pvm"; +import { AlertTriangle } from "lucide-react"; +import { serializeHostCallEntry } from "@/lib/host-call-trace"; +import { useDebuggerActions } from "@/hooks/useDebuggerActions"; const HOST_CALL_NAMES: Record = { 0: "gas", @@ -46,63 +49,75 @@ function getHostCallName(index: number | null): string { export const HostCallDialog = () => { const dispatch = useAppDispatch(); const { numeralSystem } = useContext(NumeralSystemContext); + const { restartProgram } = useDebuggerActions(); - const hasHostCallOpen = useAppSelector((state) => state.debugger.hasHostCallOpen); const configuredServiceId = useAppSelector((state) => state.debugger.serviceId); - const pendingHostCallIndex = useAppSelector((state) => state.debugger.pendingHostCallIndex); + const pendingHostCall = useAppSelector((state) => state.debugger.pendingHostCall); + const initialState = useAppSelector((state) => state.debugger.initialState); const workers = useAppSelector((state) => state.workers); const firstWorker = workers[0]; - const currentState = useMemo(() => firstWorker?.currentState ?? {}, [firstWorker?.currentState]); + const currentState: ExpectedState | undefined = firstWorker?.currentState; + const regs = (currentState?.regs ?? DEFAULT_REGS) as bigint[]; + const traceEntry = pendingHostCall?.entry ?? null; + const mismatches = pendingHostCall?.mismatches ?? []; const [isLoading, setIsLoading] = useState(false); const [useGenericUI, setUseGenericUI] = useState(false); + const [showTraceEntry, setShowTraceEntry] = useState(true); // State for default UI - lifted up so footer can access it - const [pendingRegs, setPendingRegs] = useState(null); - const [pendingGas, setPendingGas] = useState(null); + const [pendingRegs, setPendingRegs] = useState(regs); + const [pendingGas, setPendingGas] = useState(currentState?.gas ?? 0n); const [pendingMemoryEdits, setPendingMemoryEdits] = useState([]); - // Reset state when dialog opens useEffect(() => { - if (hasHostCallOpen) { - setIsLoading(false); - setUseGenericUI(false); - setPendingRegs(null); - setPendingGas(null); - setPendingMemoryEdits([]); + if (traceEntry === null) { + return; } - }, [hasHostCallOpen]); + + // set pending registers + setPendingRegs((regs) => { + const newRegs = [...regs]; + for (const rw of traceEntry.registerWrites) { + newRegs[rw.index] = rw.value; + } + return newRegs; + }); + + // set pending gas + if (traceEntry.gasAfter !== null) { + setPendingGas(traceEntry.gasAfter); + } + + // set pending memory writes + const memEdits: MemoryEdit[] = traceEntry.memoryWrites.map((mw) => ({ + address: mw.address, + data: mw.data, + })); + setPendingMemoryEdits(memEdits); + }, [traceEntry]); const handleResume = useCallback( - async (mode: HostCallResumeMode, regs?: bigint[], gas?: bigint, memoryEdits?: MemoryEdit[]) => { + async (mode: HostCallResumeMode) => { setIsLoading(true); try { // Use provided values or fall back to pending/current state - const finalRegs = regs ?? pendingRegs ?? currentState.regs ?? DEFAULT_REGS; - const currentGas = currentState.gas ?? DEFAULT_GAS; - const finalGas = gas ?? pendingGas ?? (currentGas > 10n ? currentGas - 10n : 0n); - - // Combine memory edits from host calls with pending memory from default UI - const finalMemoryEdits: MemoryEdit[] = []; - if (memoryEdits && memoryEdits.length > 0) { - finalMemoryEdits.push(...memoryEdits); - } - if (pendingMemoryEdits.length > 0) { - finalMemoryEdits.push(...pendingMemoryEdits); - } + const finalRegs = pendingRegs ?? currentState?.regs ?? DEFAULT_REGS; + const currentGas = currentState?.gas ?? DEFAULT_GAS; + const finalGas = pendingGas ?? (currentGas > 10n ? currentGas - 10n : 0n); await dispatch( resumeAfterHostCall({ - regs: finalRegs as bigint[], + regs: finalRegs, gas: finalGas, mode, - memoryEdits: finalMemoryEdits.length > 0 ? finalMemoryEdits : undefined, + memoryEdits: pendingMemoryEdits.length > 0 ? [...pendingMemoryEdits] : undefined, }), ).unwrap(); - setPendingMemoryEdits([]); } catch (e) { console.error("Failed to resume after host call:", e); + } finally { setIsLoading(false); } }, @@ -133,12 +148,12 @@ export const HostCallDialog = () => { ); // Check for special handler - const specialHandler = pendingHostCallIndex !== null ? getHostCallHandler(pendingHostCallIndex) : undefined; - - const hostCallName = getHostCallName(pendingHostCallIndex); + const hostCallId = pendingHostCall?.hostCallId ?? 10_000; + const specialHandler = getHostCallHandler(hostCallId); + const hostCallName = getHostCallName(hostCallId); return ( - + { - {/* Host Call Index */}
Host Call: {hostCallName} - (index: {pendingHostCallIndex ?? "?"}) - {specialHandler?.hasCustomUI && ( + (index: {hostCallId}) + {traceEntry && ( + + )} + {specialHandler?.hasCustomUI && !traceEntry && (
- {/* Scrollable content area */} - {specialHandler?.hasCustomUI && !useGenericUI ? ( + {mismatches.length > 0 && ( +
+ +
+

State mismatch with trace file

+
    + {mismatches.map((m, i) => ( +
  • + {m.details ?? m.field}: expected {m.expected}, got {m.actual} +
  • + ))} +
+
+
+ )} + + {traceEntry && showTraceEntry && ( +
+
+              {serializeHostCallEntry(traceEntry)}
+            
+
+ )} + + {/* When trace entry is available, always use generic UI to show trace data */} + {specialHandler?.hasCustomUI && !useGenericUI && !traceEntry ? ( restartProgram(initialState)} /> ) : ( { numeralSystem={numeralSystem} readMemory={readMemory} onResume={handleResume} + onRestart={() => restartProgram(initialState)} onRegsChange={setPendingRegs} onGasChange={setPendingGas} onMemoryChange={handleManualMemoryChange} + traceEntry={traceEntry} + hostCallId={hostCallId} /> )}
diff --git a/src/components/ProgramLoader/Examples.tsx b/src/components/ProgramLoader/Examples.tsx index b5bf036d..09cf549a 100644 --- a/src/components/ProgramLoader/Examples.tsx +++ b/src/components/ProgramLoader/Examples.tsx @@ -5,10 +5,16 @@ import { programs } from "./examplePrograms"; import { useState } from "react"; import doomUrl from "./doom.bin?url"; import golUrl from "./gol.jam?url"; -import { loadFileFromUint8Array } from "./loadingUtils"; +import { loadFileFromUint8Array } from "./loading-utils"; import { useAppSelector } from "@/store/hooks"; -export const Examples = ({ onProgramLoad }: { onProgramLoad: (val: ProgramUploadFileOutput) => void }) => { +export const Examples = ({ + onProgramLoad, + disabled, +}: { + onProgramLoad: (val: ProgramUploadFileOutput) => void; + disabled?: boolean; +}) => { const [isDoomLoading, setIsDoomLoading] = useState(false); const [isGolLoading, setIsGolLoading] = useState(false); const spiArgs = useAppSelector((state) => state.debugger.spiArgs); @@ -21,9 +27,10 @@ export const Examples = ({ onProgramLoad }: { onProgramLoad: (val: ProgramUpload { + if (disabled) return; onProgramLoad({ initial: { regs: program.regs.map((x) => BigInt(x)) as RegistersArray, @@ -47,9 +54,9 @@ export const Examples = ({ onProgramLoad }: { onProgramLoad: (val: ProgramUpload { - if (isDoomLoading) { + if (isDoomLoading || disabled) { return; } setIsDoomLoading(true); @@ -67,9 +74,9 @@ export const Examples = ({ onProgramLoad }: { onProgramLoad: (val: ProgramUpload { - if (isGolLoading) { + if (isGolLoading || disabled) { return; } setIsGolLoading(true); diff --git a/src/components/ProgramLoader/Links.tsx b/src/components/ProgramLoader/Links.tsx index 679e513d..7b187e8c 100644 --- a/src/components/ProgramLoader/Links.tsx +++ b/src/components/ProgramLoader/Links.tsx @@ -5,7 +5,12 @@ export const Links = () => {
  • - +
    @@ -25,7 +30,7 @@ export const Links = () => {
  • - +
    @@ -46,7 +51,7 @@ export const Links = () => {
  • - +
    @@ -65,6 +70,32 @@ export const Links = () => {
  • +
  • +
    + + + +
    +

    Ecalli Trace file (.log, .trace)

    +

    + + Load a trace file with embedded program and host call data. See{" "} + + specification + + +

    +
    +
    +
); }; diff --git a/src/components/ProgramLoader/Loader.tsx b/src/components/ProgramLoader/Loader.tsx index 804addd5..5d995deb 100644 --- a/src/components/ProgramLoader/Loader.tsx +++ b/src/components/ProgramLoader/Loader.tsx @@ -4,14 +4,14 @@ import { useState, useCallback, useEffect } from "react"; import { ProgramUploadFileOutput } from "./types"; import { useDebuggerActions } from "@/hooks/useDebuggerActions"; import { useAppDispatch, useAppSelector } from "@/store/hooks.ts"; -import { setIsProgramEditMode, setSpiArgs, setServiceId } from "@/store/debugger/debuggerSlice.ts"; -import { selectIsAnyWorkerLoading, setAllWorkersServiceId } from "@/store/workers/workersSlice"; +import { setIsProgramEditMode, setSpiArgs, setServiceId, setHostCallsTrace } from "@/store/debugger/debuggerSlice.ts"; +import { selectIsAnyWorkerLoading } from "@/store/workers/workersSlice"; import { isSerializedError } from "@/store/utils"; import { ProgramFileUpload } from "@/components/ProgramLoader/ProgramFileUpload.tsx"; import { useNavigate } from "react-router"; import { Links } from "./Links"; import { Separator } from "../ui/separator"; -import { TriangleAlert } from "lucide-react"; +import { TriangleAlert, FileText } from "lucide-react"; import { bytes } from "@typeberry/lib"; import { EntrypointSelector, @@ -22,6 +22,7 @@ import { } from "./EntrypointSelector"; import { loadSpiConfig, saveSpiConfig } from "./spiConfig"; import { encodeRefineParams, encodeAccumulateParams, encodeIsAuthorizedParams } from "./spiEncoding"; +import { getTraceSummary, parseTrace } from "@/lib/host-call-trace"; type LoaderStep = "upload" | "entrypoint"; @@ -30,6 +31,8 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = const [programLoad, setProgramLoad] = useState(); const [error, setError] = useState(); const [currentStep, setCurrentStep] = useState("upload"); + const [traceContent, setTraceContent] = useState(null); + const [traceSummary, setTraceSummary] = useState | null>(null); // Load saved config once on mount const savedConfig = loadSpiConfig(); @@ -58,6 +61,31 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = setCurrentStep("upload"); }, [programLoad]); + // Auto-switch to RAW mode when a trace is loaded + useEffect(() => { + if (traceContent !== null) { + setIsManualMode(true); + } + }, [traceContent]); + + const handleFileUpload = useCallback((output: ProgramUploadFileOutput, trace?: string) => { + setProgramLoad(output); + if (trace) { + try { + setTraceContent(trace); + const parsed = parseTrace(trace); + setTraceSummary(getTraceSummary(parsed)); + } catch (e) { + console.error("Failed to parse trace:", e); + setTraceContent(null); + setTraceSummary(null); + } + } else { + setTraceContent(null); + setTraceSummary(null); + } + }, []); + // Auto-encode parameters when they change and update PC useEffect(() => { if (!isSpiProgram) return; @@ -99,11 +127,13 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = // For SPI programs, update PC and encode parameters if (loadedProgram?.spiProgram) { - // Parse the encoded SPI arguments (either auto-generated or manually entered) - const parsedArgs = bytes.BytesBlob.parseBlob(encodedSpiArgs); + // Use SPI args from trace if available, otherwise use auto-generated/manual ones + const spiArgsToUse = loadedProgram.spiArgs + ? loadedProgram.spiArgs + : bytes.BytesBlob.parseBlob(encodedSpiArgs).raw; - // Set the encoded parameters as SPI arguments - dispatch(setSpiArgs(parsedArgs.raw)); + // Set the SPI arguments + dispatch(setSpiArgs(spiArgsToUse)); // Extract and set service ID from entrypoint parameters let serviceId: number; @@ -119,7 +149,6 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = break; } dispatch(setServiceId(serviceId)); - await dispatch(setAllWorkersServiceId()).unwrap(); // Update the initial state with the correct PC for the entrypoint const pc = parseInt(manualPc, 10) || 0; @@ -134,6 +163,10 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = await debuggerActions.handleProgramLoad(modifiedProgram); + if (traceContent) { + dispatch(setHostCallsTrace(traceContent)); + } + // Save SPI configuration to localStorage after successful load if (loadedProgram?.spiProgram) { saveSpiConfig({ @@ -170,11 +203,14 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = accumulateParams, isAuthorizedParams, isManualMode, + traceContent, ], ); + const hasTraceWithEntrypoint = traceContent !== null; + const handleNextStep = () => { - if (currentStep === "upload" && isSpiProgram) { + if (currentStep === "upload" && isSpiProgram && !hasTraceWithEntrypoint) { setCurrentStep("entrypoint"); } else { handleLoad(); @@ -196,9 +232,9 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = {currentStep === "upload" && ( <> { - setProgramLoad(val); - // For non-SPI programs, load directly. For SPI programs, go to entrypoint selection + handleFileUpload(val); if (val.spiProgram === null) { handleLoad(val); } @@ -206,7 +242,12 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = />
- +
{error && (

@@ -230,6 +271,18 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) =

{JSON.stringify(programLoad.initial, null, 2)}
+ + {traceSummary && ( +
+
+ Host Call Trace: + + + {traceSummary.hostCallCount} host call{traceSummary.hostCallCount !== 1 ? "s" : ""} included + +
+
+ )} )} {!error && !programLoad && } @@ -273,10 +326,10 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) = id="load-button" data-testid="load-button" type="button" - disabled={!isProgramLoaded} + disabled={!isProgramLoaded || isLoading} onClick={currentStep === "entrypoint" ? () => handleLoad() : handleNextStep} > - {currentStep === "entrypoint" || !isSpiProgram ? "Load" : "Next"} + {currentStep === "entrypoint" || !isSpiProgram || hasTraceWithEntrypoint ? "Load" : "Next"} diff --git a/src/components/ProgramLoader/ProgramFileUpload.tsx b/src/components/ProgramLoader/ProgramFileUpload.tsx index ed3c97dd..cc885855 100644 --- a/src/components/ProgramLoader/ProgramFileUpload.tsx +++ b/src/components/ProgramLoader/ProgramFileUpload.tsx @@ -7,16 +7,23 @@ import { UploadCloud } from "lucide-react"; import { Button } from "../ui/button"; import { useCallback, useEffect, useState } from "react"; import { Input } from "../ui/input"; -import { loadFileFromUint8Array } from "./loadingUtils"; +import { loadFileFromUint8Array } from "./loading-utils"; type ProgramFileUploadProps = { - onFileUpload: (val: ProgramUploadFileOutput) => void; + onFileUpload: (val: ProgramUploadFileOutput, traceContent?: string) => void; setError: (e?: string) => void; isError: boolean; close?: () => void; + disabled?: boolean; }; -export const ProgramFileUpload: React.FC = ({ isError, onFileUpload, close, setError }) => { +export const ProgramFileUpload: React.FC = ({ + isError, + onFileUpload, + close, + setError, + disabled, +}) => { const initialState = useAppSelector(selectInitialState); const spiArgs = useAppSelector((state) => state.debugger.spiArgs); @@ -71,8 +78,13 @@ export const ProgramFileUpload: React.FC = ({ isError, o const { getRootProps, getInputProps, open } = useDropzone({ onDrop, - accept: { "application/octet-stream": [".bin", ".pvm", ".jam"], "application/json": [".json"] }, + accept: { + "application/octet-stream": [".bin", ".pvm", ".jam"], + "application/json": [".json"], + "text/plain": [".log", ".trace"], + }, noClick: true, + disabled, }); const handleOpen = useCallback(() => { @@ -92,7 +104,10 @@ export const ProgramFileUpload: React.FC = ({ isError, o
{isUpload ? ( @@ -110,16 +125,17 @@ export const ProgramFileUpload: React.FC = ({ isError, o className={cn("flex-auto text-xs", { "focus-visible:ring-red-500 ring-red-500 ring-2": isError, })} + disabled={disabled} /> )}
{isUpload && ( - )} -
diff --git a/src/components/ProgramLoader/index.tsx b/src/components/ProgramLoader/index.tsx index 90d93f33..9ae4e3ed 100644 --- a/src/components/ProgramLoader/index.tsx +++ b/src/components/ProgramLoader/index.tsx @@ -5,21 +5,27 @@ import { InitialState } from "@/types/pvm"; import { Loader } from "./Loader.tsx"; import { Upload } from "lucide-react"; -export const ProgramLoader = (props: { initialState: InitialState; program: number[]; onOpen: () => void }) => { +export const ProgramLoader = (props: { + initialState: InitialState; + program: number[]; + onOpen: () => void; + disabled?: boolean; +}) => { const [isDialogOpen, setIsDialogOpen] = useState(false); return ( { + if (props.disabled) return; setIsDialogOpen(val); if (val) { props.onOpen(); } }} > - e.stopPropagation()}> - diff --git a/src/components/ProgramLoader/loading-utils.test.ts b/src/components/ProgramLoader/loading-utils.test.ts new file mode 100644 index 00000000..3375804c --- /dev/null +++ b/src/components/ProgramLoader/loading-utils.test.ts @@ -0,0 +1,63 @@ +import { describe, it, expect, vi } from "vitest"; +import * as fs from "fs"; +import * as path from "path"; + +describe("loadingUtils", () => { + describe("tryParseTraceFile via loadFileFromUint8Array", () => { + it("should load io-trace-output.log without throwing", async () => { + const tracePath = path.join(__dirname, "../../../tests/io-trace-output.log"); + + expect(fs.existsSync(tracePath), `Fixture file not found: ${tracePath}`).toBe(true); + + const content = fs.readFileSync(tracePath); + const uint8Array = new Uint8Array(content); + + const { loadFileFromUint8Array } = await import("./loading-utils"); + + const setError = vi.fn(); + const onFileUpload = vi.fn(); + const initialState = { + pc: 0, + gas: 1000000n, + regs: [0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n] as [ + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + ], + }; + + expect(() => { + loadFileFromUint8Array("io-trace-output.log", uint8Array, null, setError, onFileUpload, initialState); + }).not.toThrow(); + + if (setError.mock.calls.length > 0) { + console.error("Error set:", setError.mock.calls[0][0]); + } + + expect(setError).not.toHaveBeenCalledWith(expect.stringContaining("error")); + expect(onFileUpload).toHaveBeenCalled(); + + const uploadedData = onFileUpload.mock.calls[0][0]; + expect(uploadedData).toBeDefined(); + expect(uploadedData.kind).toContain("Trace"); + expect(uploadedData.program).toBeDefined(); + expect(uploadedData.initial).toBeDefined(); + expect(uploadedData.initial.pc).toBe(0); + + expect(uploadedData.kind).toBe("Ecalli Trace (SPI)"); + expect(typeof uploadedData.initial.gas).toBe("bigint"); + expect(uploadedData.initial.pageMap?.length).toBeGreaterThan(0); + expect(uploadedData.initial.memory?.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/src/components/ProgramLoader/loading-utils.ts b/src/components/ProgramLoader/loading-utils.ts new file mode 100644 index 00000000..e2efde3e --- /dev/null +++ b/src/components/ProgramLoader/loading-utils.ts @@ -0,0 +1,317 @@ +import { mapUploadFileInputToOutput } from "./utils"; +import { bytes, pvm_interpreter as pvm } from "@typeberry/lib"; +import { DEFAULT_GAS, DEFAULT_REGS, ExpectedState, MemoryChunkItem, PageMapItem } from "@/types/pvm.ts"; +import { type ZodSafeParseResult, z } from "zod"; +import { bigUint64ArrayToRegistersArray, getAsChunks, getAsPageMap } from "@/lib/utils.ts"; +import { decodeSpiWithMetadata } from "@/utils/spi"; +import { ProgramUploadFileInput, ProgramUploadFileOutput } from "./types"; +import { parseTrace } from "@/lib/host-call-trace"; + +export interface LoadFileResult { + output: ProgramUploadFileOutput; + traceContent?: string; +} + +type RawProgramUploadFileInput = unknown; +type ValidationResult = ZodSafeParseResult; + +const validateJsonTestCaseSchema = (json: RawProgramUploadFileInput): ValidationResult => { + const pageMapSchema = z.object({ + address: z.number(), + length: z.number(), + "is-writable": z.boolean(), + }); + + const memorySchema = z.object({ + address: z.number(), + contents: z.array(z.number()), + }); + + const schema: z.ZodType = z.object({ + name: z.string(), + "initial-regs": z.array(z.number()).length(13), + "initial-pc": z.number(), + "initial-page-map": z.array(pageMapSchema), + "initial-memory": z.array(memorySchema), + "initial-gas": z.number(), + program: z.array(z.number()), + "expected-status": z.string(), + "expected-regs": z.array(z.number()), + "expected-pc": z.number(), + "expected-memory": z.array(memorySchema), + "expected-gas": z.number(), + }); + + return schema.safeParse(json); +}; + +const MAX_SPI_ARGS_SIZE = 16 * 1024 * 1024; // 16 MiB + +function buildSpiArgsFromMemoryWrites(memoryWrites: { address: number; data: Uint8Array }[]): Uint8Array { + if (memoryWrites.length === 0) { + return new Uint8Array(); + } + + const sorted = [...memoryWrites].sort((a, b) => a.address - b.address); + const firstAddr = sorted[0].address; + const lastWrite = sorted[sorted.length - 1]; + const lastAddr = lastWrite.address + lastWrite.data.length; + const totalSize = lastAddr - firstAddr; + + if (totalSize > MAX_SPI_ARGS_SIZE) { + throw new Error(`Trace memory span too large: ${totalSize} bytes (max: ${MAX_SPI_ARGS_SIZE})`); + } + + const result = new Uint8Array(totalSize); + for (const mw of sorted) { + const offset = mw.address - firstAddr; + result.set(mw.data, offset); + } + + return result; +} + +function tryParseTraceFile(content: string, fileName: string, initialState: ExpectedState): LoadFileResult | null { + const trace = parseTrace(content); + + // Reject traces with parse errors + if (trace.errors.length > 0) { + console.warn("Trace parse errors:", trace.errors); + return null; + } + + if (!trace.prelude.program) { + return null; + } + + const programHex = trace.prelude.program; + let programBytes: Uint8Array; + try { + programBytes = bytes.BytesBlob.parseBlob(programHex).raw; + } catch { + return null; + } + + let spiArgs: Uint8Array; + try { + spiArgs = buildSpiArgsFromMemoryWrites(trace.prelude.initialMemoryWrites); + } catch (e) { + console.warn("Trace memory span too large for SPI args:", e); + return null; + } + + let spi = null; + try { + spi = decodeSpiWithMetadata(programBytes, spiArgs); + } catch (e) { + console.warn("Trace program is not SPI:", e); + } + + if (spi !== null) { + const { code, memory, registers, metadata } = spi; + const pageMap: PageMapItem[] = getAsPageMap(memory); + const chunks: MemoryChunkItem[] = getAsChunks(memory); + + const pc = trace.prelude.start?.pc ?? initialState.pc ?? 0; + const gas = trace.prelude.start?.gas ?? initialState.gas ?? DEFAULT_GAS; + + return { + output: { + program: Array.from(code), + name: `${fileName} [trace+spi]`, + spiProgram: { + program: programBytes, + hasMetadata: metadata !== undefined, + }, + kind: "Ecalli Trace (SPI)", + initial: { + regs: bigUint64ArrayToRegistersArray(registers), + pc, + pageMap, + memory: chunks, + gas, + }, + spiArgs, + }, + traceContent: content, + }; + } + + let program = null; + try { + program = pvm.Program.fromGeneric(programBytes, false); + } catch (e) { + console.warn("Trace program is not generic PVM:", e); + } + + if (program !== null) { + const memory: MemoryChunkItem[] = trace.prelude.initialMemoryWrites.map((mw) => ({ + address: mw.address, + contents: Array.from(mw.data), + })); + + // Generate pageMap entries for all pages spanned by memory chunks + const pageMap: PageMapItem[] = memory.flatMap((chunk) => { + const startPage = Math.floor(chunk.address / 4096); + const endAddr = chunk.address + chunk.contents.length; + const endPage = Math.floor((endAddr - 1) / 4096); + + const pages: PageMapItem[] = []; + for (let pageIdx = startPage; pageIdx <= endPage; pageIdx++) { + pages.push({ + address: pageIdx * 4096, + length: 4096, + "is-writable": true, + }); + } + return pages; + }); + + // Remove duplicate pages (same address) + const uniquePageMap = pageMap.filter( + (item, index, self) => self.findIndex((t) => t.address === item.address) === index, + ); + + const startRegs = trace.prelude.start?.registers; + const regs: typeof DEFAULT_REGS = startRegs + ? (DEFAULT_REGS.map((_, i) => startRegs.get(i) ?? 0n) as typeof DEFAULT_REGS) + : DEFAULT_REGS; + + return { + output: { + program: Array.from(programBytes), + name: `${fileName} [trace+generic]`, + spiProgram: null, + kind: "Ecalli Trace (Generic)", + initial: { + regs, + pc: trace.prelude.start?.pc ?? initialState.pc ?? 0, + pageMap: uniquePageMap.length > 0 ? uniquePageMap : initialState.pageMap, + memory: memory.length > 0 ? memory : initialState.memory, + gas: trace.prelude.start?.gas ?? initialState.gas ?? DEFAULT_GAS, + }, + }, + traceContent: content, + }; + } + + return null; +} + +export function loadFileFromUint8Array( + loadedFileName: string, + uint8Array: Uint8Array, + spiArgsAsBytes: Uint8Array | null, + setError: (e?: string) => void, + onFileUpload: (d: ProgramUploadFileOutput, traceContent?: string) => void, + initialState: ExpectedState, +) { + setError(undefined); + + let jsonFile = null; + let stringContent = ""; + let rawBytes = uint8Array; + + try { + stringContent = new TextDecoder("utf-8").decode(uint8Array); + } catch (e) { + console.warn("not a string", e); + } + + const isLikelyTrace = stringContent.includes("program 0x"); + + if (isLikelyTrace) { + const traceResult = tryParseTraceFile(stringContent, loadedFileName, initialState); + if (traceResult) { + onFileUpload(traceResult.output, traceResult.traceContent); + return; + } + } + + if (stringContent.startsWith("0x")) { + try { + rawBytes = bytes.BytesBlob.parseBlob(stringContent).raw; + } catch (e) { + console.warn("not a bytes blob", e); + } + } else { + try { + jsonFile = JSON.parse(stringContent); + } catch (e) { + console.warn("not a JSON", e); + } + } + + if (jsonFile !== null) { + const result = validateJsonTestCaseSchema(jsonFile); + if (!result.success) { + console.warn("not a valid JSON", result.error); + const errorMessage = generateErrorMessageFromZodValidation(result); + setError(errorMessage || ""); + return; + } + + onFileUpload(mapUploadFileInputToOutput(jsonFile, "JSON test")); + return; + } + + let spi = null; + try { + spi = decodeSpiWithMetadata(rawBytes, spiArgsAsBytes ?? new Uint8Array()); + } catch (e) { + console.warn("not an SPI blob", e); + } + if (spi !== null) { + const { code, memory, registers, metadata } = spi; + const pageMap: PageMapItem[] = getAsPageMap(memory); + const chunks: MemoryChunkItem[] = getAsChunks(memory); + + onFileUpload({ + program: Array.from(code), + name: `${loadedFileName} [spi]`, + spiProgram: { + program: rawBytes, + hasMetadata: metadata !== undefined, + }, + kind: "JAM SPI", + initial: { + regs: bigUint64ArrayToRegistersArray(registers), + pc: 5, + pageMap, + memory: chunks, + gas: DEFAULT_GAS, + }, + }); + return; + } + + let program = null; + try { + program = pvm.Program.fromGeneric(rawBytes, false); + } catch (e) { + console.warn("not a generic PVM", e); + } + + if (program !== null) { + onFileUpload({ + program: Array.from(rawBytes), + name: `${loadedFileName} [generic]`, + spiProgram: null, + kind: "Generic PVM", + initial: initialState, + }); + } else { + setError("Unrecognized program format (see console)."); + } +} + +const generateErrorMessageFromZodValidation = (result: ValidationResult): string => { + if (!result.error) return "Unknown error occurred"; + + const formattedErrors = result.error.issues.map((err) => { + const path = err.path.join(" > ") || "root"; + return `Field: "${path}" - ${err.message}`; + }); + + return `File validation failed with the following errors:\n\n${formattedErrors.join("\n")}`; +}; diff --git a/src/components/ProgramLoader/loadingUtils.ts b/src/components/ProgramLoader/loadingUtils.ts deleted file mode 100644 index d7a03135..00000000 --- a/src/components/ProgramLoader/loadingUtils.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { mapUploadFileInputToOutput } from "./utils"; -import { bytes, pvm_interpreter as pvm } from "@typeberry/lib"; -import { DEFAULT_GAS, ExpectedState, MemoryChunkItem, PageMapItem } from "@/types/pvm.ts"; -import { type ZodSafeParseResult, z } from "zod"; -import { bigUint64ArrayToRegistersArray, getAsChunks, getAsPageMap } from "@/lib/utils.ts"; -import { decodeSpiWithMetadata } from "@/utils/spi"; -import { ProgramUploadFileInput, ProgramUploadFileOutput } from "./types"; - -type RawProgramUploadFileInput = unknown; -type ValidationResult = ZodSafeParseResult; - -const validateJsonTestCaseSchema = (json: RawProgramUploadFileInput): ValidationResult => { - const pageMapSchema = z.object({ - address: z.number(), - length: z.number(), - "is-writable": z.boolean(), - }); - - const memorySchema = z.object({ - address: z.number(), - contents: z.array(z.number()), - }); - - const schema: z.ZodType = z.object({ - name: z.string(), - "initial-regs": z.array(z.number()).length(13), - "initial-pc": z.number(), - "initial-page-map": z.array(pageMapSchema), - "initial-memory": z.array(memorySchema), - "initial-gas": z.number(), - program: z.array(z.number()), - "expected-status": z.string(), - "expected-regs": z.array(z.number()), - "expected-pc": z.number(), - "expected-memory": z.array(memorySchema), - "expected-gas": z.number(), - }); - - return schema.safeParse(json); -}; - -export function loadFileFromUint8Array( - loadedFileName: string, - uint8Array: Uint8Array, - spiArgsAsBytes: Uint8Array | null, - setError: (e?: string) => void, - onFileUpload: (d: ProgramUploadFileOutput) => void, - initialState: ExpectedState, -) { - // reset error state - setError(undefined); - - // Try to parse file as a JSON first - let jsonFile = null; - let stringContent = ""; - let rawBytes = uint8Array; - - try { - stringContent = new TextDecoder("utf-8").decode(uint8Array); - } catch (e) { - console.warn("not a string", e); - } - - if (stringContent.startsWith("0x")) { - try { - rawBytes = bytes.BytesBlob.parseBlob(stringContent).raw; - } catch (e) { - console.warn("not a bytes blob", e); - } - } else { - try { - jsonFile = JSON.parse(stringContent); - } catch (e) { - console.warn("not a JSON", e); - } - } - - if (jsonFile !== null) { - const result = validateJsonTestCaseSchema(jsonFile); - if (!result.success) { - console.warn("not a valid JSON", result.error); - const errorMessage = generateErrorMessageFromZodValidation(result); - setError(errorMessage || ""); - return; - } - - onFileUpload(mapUploadFileInputToOutput(jsonFile, "JSON test")); - return; - } - - // Try to decode the program as an SPI - let spi = null; - try { - spi = decodeSpiWithMetadata(rawBytes, spiArgsAsBytes ?? new Uint8Array()); - } catch (e) { - console.warn("not an SPI blob", e); - } - if (spi !== null) { - const { code, memory, registers, metadata } = spi; - const pageMap: PageMapItem[] = getAsPageMap(memory); - const chunks: MemoryChunkItem[] = getAsChunks(memory); - - onFileUpload({ - program: Array.from(code), - name: `${loadedFileName} [spi]`, - spiProgram: { - program: rawBytes, - hasMetadata: metadata !== undefined, - }, - kind: "JAM SPI", - initial: { - regs: bigUint64ArrayToRegistersArray(registers), - pc: 5, - pageMap, - memory: chunks, - gas: DEFAULT_GAS, - }, - }); - return; - } - - // Finally try to load program as a Generic - let program = null; - try { - program = pvm.Program.fromGeneric(rawBytes, false); - } catch (e) { - console.warn("not a generic PVM", e); - } - - if (program !== null) { - onFileUpload({ - program: Array.from(rawBytes), - name: `${loadedFileName} [generic]`, - spiProgram: null, - kind: "Generic PVM", - initial: initialState, - }); - } else { - setError("Unrecognized program format (see console)."); - } -} - -const generateErrorMessageFromZodValidation = (result: ValidationResult): string => { - if (!result.error) return "Unknown error occurred"; - - const formattedErrors = result.error.issues.map((err) => { - const path = err.path.join(" > ") || "root"; - return `Field: "${path}" - ${err.message}`; - }); - - return `File validation failed with the following errors:\n\n${formattedErrors.join("\n")}`; -}; diff --git a/src/components/ProgramLoader/types.ts b/src/components/ProgramLoader/types.ts index 5ea002fa..74fad0c9 100644 --- a/src/components/ProgramLoader/types.ts +++ b/src/components/ProgramLoader/types.ts @@ -7,6 +7,7 @@ export type ProgramUploadFileOutput = { exampleName?: string; kind: string; spiProgram: SpiProgram | null; + spiArgs?: Uint8Array; }; export type ProgramUploadFileInput = { diff --git a/src/hooks/useDebuggerActions.ts b/src/hooks/useDebuggerActions.ts index 4293a10d..8cb1776c 100644 --- a/src/hooks/useDebuggerActions.ts +++ b/src/hooks/useDebuggerActions.ts @@ -14,6 +14,7 @@ import { setPvmInitialized, setIsStepMode, setPvmLoaded, + resetHostCallIndex, } from "@/store/debugger/debuggerSlice"; import { useAppDispatch, useAppSelector } from "@/store/hooks"; import { @@ -35,10 +36,39 @@ import { useCallback } from "react"; import { storageManager } from "@/components/HostCallDialog/handlers/storageManager"; export const useDebuggerActions = () => { - const { breakpointAddresses, initialState } = useAppSelector((state) => state.debugger); + const { breakpointAddresses, initialState, pvmOptions } = useAppSelector((state) => state.debugger); const workers = useAppSelector((state) => state.workers); const dispatch = useAppDispatch(); + const recreateWorker = useCallback( + async ({ id, type, params }: SelectedPvmWithPayload) => { + await dispatch(createWorker(id)).unwrap(); + + let pvmType: PvmTypes; + if (id === AvailablePvms.TYPEBERRY) { + pvmType = PvmTypes.BUILT_IN; + } else if (type === AvailablePvms.WASM_FILE) { + pvmType = PvmTypes.WASM_FILE; + } else if (type === AvailablePvms.WASM_WS) { + pvmType = PvmTypes.WASM_WS; + } else { + // Default to WASM_URL for POLKAVM and others + pvmType = PvmTypes.WASM_URL; + } + + await dispatch( + loadWorker({ + id, + payload: { + type: pvmType, + params, + }, + }), + ).unwrap(); + }, + [dispatch], + ); + const restartProgram = useCallback( async (state: ExpectedState) => { storageManager.clear(); @@ -46,6 +76,23 @@ export const useDebuggerActions = () => { dispatch(setIsDebugFinished(false)); dispatch(setIsRunMode(false)); dispatch(setIsStepMode(false)); + dispatch(resetHostCallIndex()); + + // Save memory ranges before destroying workers + // Guard: ensure memoryRanges is always an array + const memoryRanges = Array.isArray(workers[0]?.memoryRanges) ? workers[0].memoryRanges : []; + + // Destroy all existing workers to clear message listeners + await Promise.all(workers.map((worker: WorkerState) => dispatch(destroyWorker(worker.id)).unwrap())); + + // Recreate workers with the same PVM configurations + const selectedPvmConfigs = pvmOptions.allAvailablePvms.filter((pvm) => pvmOptions.selectedPvm.includes(pvm.id)); + await Promise.all(selectedPvmConfigs.map(recreateWorker)); + + // Restore memory ranges and initialize (only if there are memory ranges) + if (memoryRanges.length > 0) { + await dispatch(syncMemoryRangeAllWorkers({ memoryRanges })); + } dispatch(setAllWorkersCurrentState(state)); dispatch(setAllWorkersPreviousState(state)); await dispatch(initAllWorkers()).unwrap(); @@ -54,7 +101,7 @@ export const useDebuggerActions = () => { dispatch(setAllWorkersCurrentPc(state.pc)); dispatch(setClickedInstruction(null)); }, - [dispatch], + [dispatch, workers, pvmOptions, recreateWorker], ); const startProgram = useCallback( @@ -130,83 +177,22 @@ export const useDebuggerActions = () => { async (selectedPvms: SelectedPvmWithPayload[]) => { logger.debug("selectedPvms vs workers ", selectedPvms, workers); - await Promise.all( - workers.map((worker: WorkerState) => { - return dispatch(destroyWorker(worker.id)).unwrap(); - }), - ); - - const memoryRanges = workers[0]?.memoryRanges; - - await Promise.all( - selectedPvms.map(async ({ id, type, params }) => { - logger.info("Selected PVM type", id, type, params); - - if (workers.find((worker: WorkerState) => worker.id === id)) { - logger.info("Worker already initialized"); - // TODO: for now just initialize the worker one more time - } - logger.info("Worker not initialized"); - - if (id === AvailablePvms.POLKAVM) { - await dispatch(createWorker(AvailablePvms.POLKAVM)).unwrap(); - await dispatch( - loadWorker({ - id, - payload: { - type: PvmTypes.WASM_URL as PvmTypes, - params, - }, - }), - ).unwrap(); - } else if (id === AvailablePvms.TYPEBERRY) { - await dispatch(createWorker(AvailablePvms.TYPEBERRY)).unwrap(); - await dispatch( - loadWorker({ - id, - payload: { - type: PvmTypes.BUILT_IN, - }, - }), - ).unwrap(); - } else if (type === AvailablePvms.WASM_FILE) { - await dispatch(createWorker(id)).unwrap(); - await dispatch( - loadWorker({ - id, - payload: { - type: PvmTypes.WASM_FILE, - params, - }, - }), - ).unwrap(); - } else if (type === AvailablePvms.WASM_URL) { - await dispatch(createWorker(id)).unwrap(); - await dispatch( - loadWorker({ - id, - payload: { - type: PvmTypes.WASM_URL, - params, - }, - }), - ).unwrap(); - } else if (type === AvailablePvms.WASM_WS) { - await dispatch(createWorker(id)).unwrap(); - await dispatch( - loadWorker({ - id, - payload: { - type: PvmTypes.WASM_WS, - params, - }, - }), - ).unwrap(); - } - }), - ); + // Guard: ensure memoryRanges is always an array + const memoryRanges = Array.isArray(workers[0]?.memoryRanges) ? workers[0].memoryRanges : []; + + // Destroy all existing workers + await Promise.all(workers.map((worker: WorkerState) => dispatch(destroyWorker(worker.id)).unwrap())); - await dispatch(syncMemoryRangeAllWorkers({ memoryRanges })); + // TODO [ToDr] For some reason this must not be here, although it seems sensible. + // There is some other weird way how these workers become alive. + // Recreate workers with the same PVM configurations + //const selectedPvmConfigs = pvmOptions.allAvailablePvms.filter((pvm) => pvmOptions.selectedPvm.includes(pvm.id)); + //await Promise.all(selectedPvmConfigs.map(recreateWorker)); + + // Restore memory ranges only if there are any + if (memoryRanges.length > 0) { + await dispatch(syncMemoryRangeAllWorkers({ memoryRanges })); + } await dispatch(refreshMemoryRangeAllWorkers()).unwrap(); await restartProgram(initialState); dispatch(setPvmLoaded(true)); diff --git a/src/lib/host-call-trace.test.ts b/src/lib/host-call-trace.test.ts new file mode 100644 index 00000000..85bf282b --- /dev/null +++ b/src/lib/host-call-trace.test.ts @@ -0,0 +1,88 @@ +import { describe, it, expect } from "vitest"; +import { parseTrace, getTraceSummary } from "./host-call-trace"; +import * as fs from "fs"; +import * as path from "path"; + +describe("hostCallTrace", () => { + describe("parseTrace", () => { + it("should parse a simple trace", () => { + const trace = `program 0x0102aabbccdd +memwrite 0x00001000 len=8 <- 0x0000000000000001 +start pc=0 gas=10000 r07=0x10 + +ecalli=10 pc=42 gas=9980 r01=0x1 r03=0x1000 +memread 0x00001000 len=4 -> 0x01020304 +setreg r00 <- 0x100 +setgas <- 9950 + +HALT pc=50 gas=9920 r00=0x100`; + + const result = parseTrace(trace); + + expect(result.prelude.program).toBe("0x0102aabbccdd"); + expect(result.prelude.start).toEqual({ + pc: 0, + gas: 10000n, + registers: new Map([[7, 0x10n]]), + }); + expect(result.prelude.initialMemoryWrites).toHaveLength(1); + expect(result.hostCalls).toHaveLength(1); + expect(result.hostCalls[0].index).toBe(10); + expect(result.hostCalls[0].pc).toBe(42); + expect(result.termination?.reason.type).toBe("HALT"); + }); + + it("should parse trace with logging prefix", () => { + const trace = `INFO [test-runner] Some context +TRACE [ ecalli] program 0xaabbccdd +TRACE [ ecalli] memwrite 0x00001000 len=4 <- 0x01020304 +TRACE [ ecalli] start pc=5 gas=100000 r00=0xffff0000 +TRACE [ ecalli] ecalli=1 pc=100 gas=99900 r00=0x1 +TRACE [ ecalli] setreg r07 <- 0x10 +TRACE [ ecalli] setgas <- 99890 +TRACE [ ecalli] HALT pc=200 gas=99800`; + + const result = parseTrace(trace); + + expect(result.prelude.program).toBe("0xaabbccdd"); + expect(result.prelude.start?.pc).toBe(5); + expect(result.prelude.start?.gas).toBe(100000n); + expect(result.hostCalls).toHaveLength(1); + expect(result.termination?.reason.type).toBe("HALT"); + }); + + it("should handle malformed hex gracefully", () => { + const trace = `program 0xaabb +start pc=0 gas=1000`; + + const result = parseTrace(trace); + + expect(result.prelude.program).toBe("0xaabb"); + expect(result.prelude.start?.pc).toBe(0); + }); + }); + + describe("io-trace-output.log", () => { + it("should parse the real trace file", () => { + const tracePath = path.join(__dirname, "../../tests/io-trace-output.log"); + + expect(fs.existsSync(tracePath), `Fixture file not found: ${tracePath}`).toBe(true); + + const content = fs.readFileSync(tracePath, "utf-8"); + const result = parseTrace(content); + + expect(result.errors).toHaveLength(0); + expect(result.prelude.program).toBeTruthy(); + expect(result.prelude.start).toBeTruthy(); + expect(result.prelude.start?.pc).toBe(0); + expect(result.prelude.start?.gas).toBe(100000n); + expect(result.hostCalls.length).toBeGreaterThan(0); + expect(result.termination?.reason.type).toBe("HALT"); + + const summary = getTraceSummary(result); + expect(summary.hasProgram).toBe(true); + expect(summary.hasTermination).toBe(true); + expect(summary.hostCallCount).toBeGreaterThan(0); + }); + }); +}); diff --git a/src/lib/host-call-trace.ts b/src/lib/host-call-trace.ts new file mode 100644 index 00000000..48e21bed --- /dev/null +++ b/src/lib/host-call-trace.ts @@ -0,0 +1,914 @@ +/** + * ecalli / PVM IO Trace Parser and Types + * + * This module provides types and parsing utilities for the JIP-6 host call trace format. + * The trace format is a human-readable, newline-delimited text format for recording + * PVM execution including host calls (ecalli) and their effects. + * + * @see https://github.com/tomusdrw/JIPs/blob/td-jip6-ecalliloggin/JIP-6.md + */ + +import { z } from "zod"; + +/** Register dump: mapping from register index to value */ +export type RegisterDump = Map; + +/** Memory read operation during a host call */ +export interface MemoryRead { + address: number; + length: number; + data: Uint8Array; +} + +/** Memory write operation during a host call */ +export interface MemoryWrite { + address: number; + length: number; + data: Uint8Array; +} + +/** Register write operation during a host call */ +export interface RegisterWrite { + index: number; + value: bigint; +} + +/** Host call entry from trace */ +export interface HostCallEntry { + /** Host call index (ecalli operand) */ + index: number; + /** Program counter at the ecalli instruction */ + pc: number; + /** Gas before executing the host call */ + gas: bigint; + /** Register state before the host call (non-zero registers only) */ + registers: RegisterDump; + /** Memory reads performed during the host call */ + memoryReads: MemoryRead[]; + /** Memory writes performed during the host call */ + memoryWrites: MemoryWrite[]; + /** Register writes performed during the host call */ + registerWrites: RegisterWrite[]; + /** Gas value after the host call (from setgas) */ + gasAfter: bigint | null; + /** Raw line number in the trace file for error reporting */ + lineNumber: number; +} + +/** Initial execution state from 'start' entry */ +export interface StartEntry { + pc: number; + gas: bigint; + registers: RegisterDump; +} + +/** Program prelude from trace */ +export interface TracePrelude { + /** Hex-encoded program blob */ + program: string | null; + /** Initial execution state (pc, gas, registers) from 'start' entry */ + start: StartEntry | null; + /** Initial memory writes before execution */ + initialMemoryWrites: MemoryWrite[]; +} + +/** Termination reason */ +export type TerminationReason = { type: "HALT" } | { type: "PANIC"; argument: number } | { type: "OOG" }; + +/** Termination entry from trace */ +export interface TerminationEntry { + reason: TerminationReason; + pc: number; + gas: bigint; + registers: RegisterDump; + lineNumber: number; +} + +/** Complete parsed trace */ +export interface ParsedTrace { + /** Context lines (implementation metadata, etc.) */ + contextLines: string[]; + /** Program and initial memory state */ + prelude: TracePrelude; + /** Host call entries in execution order */ + hostCalls: HostCallEntry[]; + /** Termination entry (if present) */ + termination: TerminationEntry | null; + /** Parse errors encountered */ + errors: TraceParseError[]; +} + +/** Parse error with location information */ +export interface TraceParseError { + lineNumber: number; + line: string; + message: string; +} + +/** + * Result of looking up a host call entry for the current PVM state + */ +export interface HostCallLookupResult { + /** The matching entry, if found */ + entry: HostCallEntry | null; + /** Mismatches between PVM state and trace entry */ + mismatches: StateMismatch[]; +} + +/** A mismatch between PVM state and trace entry */ +export interface StateMismatch { + field: "index" | "gas" | "register" | "memread"; + expected: string; + actual: string; + details?: string; +} + +// ============================================================================ +// Zod Validation Schemas +// ============================================================================ + +/** Zod schema for MemoryRead */ +const MemoryReadSchema = z.object({ + address: z.number(), + length: z.number(), + data: z.instanceof(Uint8Array), +}); + +/** Zod schema for MemoryWrite */ +const MemoryWriteSchema = z.object({ + address: z.number(), + length: z.number(), + data: z.instanceof(Uint8Array), +}); + +/** Zod schema for RegisterWrite */ +const RegisterWriteSchema = z.object({ + index: z.number(), + value: z.bigint(), +}); + +/** Zod schema for HostCallEntry */ +const HostCallEntrySchema = z.object({ + index: z.number(), + pc: z.number(), + gas: z.bigint(), + registers: z.instanceof(Map), + memoryReads: z.array(MemoryReadSchema), + memoryWrites: z.array(MemoryWriteSchema), + registerWrites: z.array(RegisterWriteSchema), + gasAfter: z.bigint().nullable(), + lineNumber: z.number(), +}); + +/** Zod schema for StartEntry */ +const StartEntrySchema = z.object({ + pc: z.number(), + gas: z.bigint(), + registers: z.instanceof(Map), +}); + +/** Zod schema for TracePrelude */ +const TracePreludeSchema = z.object({ + program: z.string().nullable(), + start: StartEntrySchema.nullable(), + initialMemoryWrites: z.array(MemoryWriteSchema), +}); + +/** Zod schema for TerminationReason */ +const TerminationReasonSchema = z.union([ + z.object({ type: z.literal("HALT") }), + z.object({ type: z.literal("PANIC"), argument: z.number() }), + z.object({ type: z.literal("OOG") }), +]); + +/** Zod schema for TerminationEntry */ +const TerminationEntrySchema = z.object({ + reason: TerminationReasonSchema, + pc: z.number(), + gas: z.bigint(), + registers: z.instanceof(Map), + lineNumber: z.number(), +}); + +/** Zod schema for TraceParseError */ +const TraceParseErrorSchema = z.object({ + lineNumber: z.number(), + line: z.string(), + message: z.string(), +}); + +/** Zod schema for ParsedTrace */ +const ParsedTraceSchema = z.object({ + contextLines: z.array(z.string()), + prelude: TracePreludeSchema, + hostCalls: z.array(HostCallEntrySchema), + termination: TerminationEntrySchema.nullable(), + errors: z.array(TraceParseErrorSchema), +}); + +/** + * Validate a parsed trace structure using Zod + * @param trace The parsed trace to validate + * @returns Zod safe parse result + */ +export function validateParsedTrace(trace: unknown) { + return ParsedTraceSchema.safeParse(trace); +} + +/** + * Validate trace content string and return detailed validation result + * @param content The trace content to validate + * @returns Object containing validation result and any errors + */ +export function validateTraceContent(content: string): { + success: boolean; + errors: TraceParseError[]; + parseErrors?: z.ZodError; +} { + const parsed = parseTrace(content); + + // Check for parse errors first + if (parsed.errors.length > 0) { + return { success: false, errors: parsed.errors }; + } + + // Validate structure with Zod + const validation = validateParsedTrace(parsed); + if (!validation.success) { + return { + success: false, + errors: parsed.errors, + parseErrors: validation.error, + }; + } + + return { success: true, errors: [] }; +} + +// ============================================================================ +// Parsing utilities +// ============================================================================ + +/** + * Parse a hex-encoded string (0x-prefixed) to Uint8Array + */ +function parseHexBytes(hex: string): Uint8Array { + if (!hex.startsWith("0x")) { + throw new Error(`Expected 0x prefix: ${hex}`); + } + const clean = hex.slice(2); + if (clean.length % 2 !== 0) { + throw new Error(`Invalid hex length: ${hex}`); + } + const bytes = new Uint8Array(clean.length / 2); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16); + } + return bytes; +} + +/** + * Parse a hex-encoded 32-bit address + */ +function parseAddress(hex: string): number { + if (!hex.startsWith("0x")) { + throw new Error(`Expected 0x prefix for address: ${hex}`); + } + return parseInt(hex, 16); +} + +/** + * Parse a hex-encoded 64-bit value (for registers) + */ +function parseHexValue(hex: string): bigint { + if (!hex.startsWith("0x")) { + throw new Error(`Expected 0x prefix for value: ${hex}`); + } + return BigInt(hex); +} + +/** + * Parse register dump from space-separated r{idx}={value} pairs + */ +function parseRegisterDump(parts: string[]): RegisterDump { + const registers = new Map(); + for (const part of parts) { + const match = part.match(/^r(\d+)=(0x[0-9a-fA-F]+)$/); + if (match) { + const idx = parseInt(match[1], 10); + const value = parseHexValue(match[2]); + registers.set(idx, value); + } + } + return registers; +} + +/** + * Parse an ecalli line + * Format: ecalli={index} pc={pc} gas={gas} {register-dump} + */ +function parseEcalliLine( + line: string, + lineNumber: number, +): Omit | null { + const match = line.match(/ecalli=(\d+)\s+pc=(\d+)\s+gas=(\d+)\s*(.*)/); + if (!match) return null; + + const [, indexStr, pcStr, gasStr, regDump] = match; + const registers = parseRegisterDump(regDump.trim().split(/\s+/).filter(Boolean)); + + return { + index: parseInt(indexStr, 10), + pc: parseInt(pcStr, 10), + gas: BigInt(gasStr), + registers, + lineNumber, + }; +} + +/** + * Parse a memread line + * Format: memread {hex-address} len={length} -> {hex-data} + */ +function parseMemreadLine(line: string): MemoryRead | null { + const match = line.match(/memread\s+(0x[0-9a-fA-F]+)\s+len=(\d+)\s+->\s+(0x[0-9a-fA-F]*)/); + if (!match) return null; + + const [, addrHex, lenStr, dataHex] = match; + const address = parseAddress(addrHex); + const length = parseInt(lenStr, 10); + const data = parseHexBytes(dataHex); + + // Validate that data length matches declared length + if (data.length !== length) { + throw new Error( + `memread length mismatch at 0x${address.toString(16)}: declared len=${length}, actual data length=${data.length}`, + ); + } + + return { + address, + length, + data, + }; +} + +/** + * Parse a memwrite line + * Format: memwrite {hex-address} len={length} <- {hex-data} + */ +function parseMemwriteLine(line: string): MemoryWrite | null { + const match = line.match(/memwrite\s+(0x[0-9a-fA-F]+)\s+len=(\d+)\s+<-\s+(0x[0-9a-fA-F]*)/); + if (!match) return null; + + const [, addrHex, lenStr, dataHex] = match; + const address = parseAddress(addrHex); + const length = parseInt(lenStr, 10); + const data = parseHexBytes(dataHex); + + // Validate that data length matches declared length + if (data.length !== length) { + throw new Error( + `memwrite length mismatch at 0x${address.toString(16)}: declared len=${length}, actual data length=${data.length}`, + ); + } + + return { + address, + length, + data, + }; +} + +/** + * Parse a setreg line + * Format: setreg r{idx} <- {hex-value} + */ +function parseSetregLine(line: string): RegisterWrite | null { + const match = line.match(/setreg\s+r(\d+)\s+<-\s+(0x[0-9a-fA-F]+)/); + if (!match) return null; + + const [, idxStr, valueHex] = match; + return { + index: parseInt(idxStr, 10), + value: parseHexValue(valueHex), + }; +} + +/** + * Parse a setgas line + * Format: setgas <- {gas} + */ +function parseSetgasLine(line: string): bigint | null { + const match = line.match(/setgas\s+<-\s+(\d+)/); + if (!match) return null; + return BigInt(match[1]); +} + +/** + * Parse a program line + * Format: program {hex-encoded-program} + */ +function parseProgramLine(line: string): string | null { + const match = line.match(/program\s+(0x[0-9a-fA-F]+)/); + if (!match) return null; + return match[1]; +} + +/** + * Parse a start entry line + * Format: start pc={pc} gas={gas} {register-dump} + */ +function parseStartLine(line: string): StartEntry | null { + const match = line.match(/start\s+pc=(\d+)\s+gas=(\d+)\s*(.*)/); + if (!match) return null; + + const [, pcStr, gasStr, regDump] = match; + return { + pc: parseInt(pcStr, 10), + gas: BigInt(gasStr), + registers: parseRegisterDump(regDump.trim().split(/\s+/).filter(Boolean)), + }; +} + +/** + * Parse a termination line + * Format: HALT pc={pc} gas={gas} {register-dump} + * PANIC={arg} pc={pc} gas={gas} {register-dump} + * OOG pc={pc} gas={gas} {register-dump} + */ +function parseTerminationLine(line: string, lineNumber: number): TerminationEntry | null { + // HALT + let match = line.match(/HALT\s+pc=(\d+)\s+gas=(\d+)\s*(.*)/); + if (match) { + const [, pcStr, gasStr, regDump] = match; + return { + reason: { type: "HALT" }, + pc: parseInt(pcStr, 10), + gas: BigInt(gasStr), + registers: parseRegisterDump(regDump.trim().split(/\s+/).filter(Boolean)), + lineNumber, + }; + } + + // PANIC + match = line.match(/PANIC=(\d+)\s+pc=(\d+)\s+gas=(\d+)\s*(.*)/); + if (match) { + const [, argStr, pcStr, gasStr, regDump] = match; + return { + reason: { type: "PANIC", argument: parseInt(argStr, 10) }, + pc: parseInt(pcStr, 10), + gas: BigInt(gasStr), + registers: parseRegisterDump(regDump.trim().split(/\s+/).filter(Boolean)), + lineNumber, + }; + } + + // OOG + match = line.match(/OOG\s+pc=(\d+)\s+gas=(\d+)\s*(.*)/); + if (match) { + const [, pcStr, gasStr, regDump] = match; + return { + reason: { type: "OOG" }, + pc: parseInt(pcStr, 10), + gas: BigInt(gasStr), + registers: parseRegisterDump(regDump.trim().split(/\s+/).filter(Boolean)), + lineNumber, + }; + } + + return null; +} + +/** + * Parse a complete JIP-6 trace file + */ +export function parseTrace(content: string): ParsedTrace { + const lines = content.split("\n"); + const result: ParsedTrace = { + contextLines: [], + prelude: { + program: null, + start: null, + initialMemoryWrites: [], + }, + hostCalls: [], + termination: null, + errors: [], + }; + + let currentHostCall: HostCallEntry | null = null; + let inPrelude = true; + + for (let i = 0; i < lines.length; i++) { + const lineNumber = i + 1; + const line = lines[i].trim(); + + // Skip empty lines + if (!line) continue; + + try { + // Check for program line (prelude) + const program = parseProgramLine(line); + if (program !== null) { + result.prelude.program = program; + continue; + } + + // Check for start entry in prelude: start pc={pc} gas={gas} {register-dump} + if (inPrelude) { + const startEntry = parseStartLine(line); + if (startEntry) { + result.prelude.start = startEntry; + continue; + } + } + + // Check for termination line + const termination = parseTerminationLine(line, lineNumber); + if (termination) { + // Finalize any pending host call + if (currentHostCall) { + result.hostCalls.push(currentHostCall); + currentHostCall = null; + } + result.termination = termination; + continue; + } + + // Check for ecalli line (starts a new host call entry) + const ecalli = parseEcalliLine(line, lineNumber); + if (ecalli) { + // Finalize any pending host call + if (currentHostCall) { + result.hostCalls.push(currentHostCall); + } + inPrelude = false; + currentHostCall = { + ...ecalli, + memoryReads: [], + memoryWrites: [], + registerWrites: [], + gasAfter: null, + }; + continue; + } + + // Check for memwrite line + const memwrite = parseMemwriteLine(line); + if (memwrite) { + if (inPrelude) { + result.prelude.initialMemoryWrites.push(memwrite); + } else if (currentHostCall) { + currentHostCall.memoryWrites.push(memwrite); + } + continue; + } + + // Check for memread line (only valid within host call) + const memread = parseMemreadLine(line); + if (memread) { + if (currentHostCall) { + currentHostCall.memoryReads.push(memread); + } + continue; + } + + // Check for setreg line (only valid within host call) + const setreg = parseSetregLine(line); + if (setreg) { + if (currentHostCall) { + currentHostCall.registerWrites.push(setreg); + } + continue; + } + + // Check for setgas line (only valid within host call) + const setgas = parseSetgasLine(line); + if (setgas !== null) { + if (currentHostCall) { + currentHostCall.gasAfter = setgas; + } + continue; + } + + // If we're in prelude and the line doesn't match any known format, + // treat it as a context line + if (inPrelude && !line.startsWith("memwrite") && !line.startsWith("program")) { + result.contextLines.push(line); + } + } catch (e) { + result.errors.push({ + lineNumber, + line, + message: e instanceof Error ? e.message : String(e), + }); + } + } + + // Finalize any pending host call + if (currentHostCall) { + result.hostCalls.push(currentHostCall); + } + + return result; +} + +/** + * Serialize a trace back to string format + */ +export function serializeTrace(trace: ParsedTrace): string { + const lines: string[] = []; + + // Context lines + for (const line of trace.contextLines) { + lines.push(line); + } + + // Program + if (trace.prelude.program) { + lines.push(`program ${trace.prelude.program}`); + } + + // Initial memory writes + for (const mw of trace.prelude.initialMemoryWrites) { + lines.push(formatMemwrite(mw)); + } + + // Start entry (initial execution state) + if (trace.prelude.start) { + lines.push(formatStart(trace.prelude.start)); + } + + // Add blank line before host calls if we have prelude content + if (lines.length > 0 && trace.hostCalls.length > 0) { + lines.push(""); + } + + // Host calls + for (const hc of trace.hostCalls) { + lines.push(formatEcalli(hc)); + + for (const mr of hc.memoryReads) { + lines.push(formatMemread(mr)); + } + + for (const mw of hc.memoryWrites) { + lines.push(formatMemwrite(mw)); + } + + for (const rw of hc.registerWrites) { + lines.push(formatSetreg(rw)); + } + + if (hc.gasAfter !== null) { + lines.push(`setgas <- ${hc.gasAfter}`); + } + + lines.push(""); // Blank line between host calls + } + + // Termination + if (trace.termination) { + lines.push(formatTermination(trace.termination)); + } + + return lines.join("\n"); +} + +function formatMemwrite(mw: MemoryWrite): string { + const addrHex = "0x" + mw.address.toString(16).padStart(8, "0"); + const dataHex = + "0x" + + Array.from(mw.data) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + return `memwrite ${addrHex} len=${mw.length} <- ${dataHex}`; +} + +function formatMemread(mr: MemoryRead): string { + const addrHex = "0x" + mr.address.toString(16).padStart(8, "0"); + const dataHex = + "0x" + + Array.from(mr.data) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + return `memread ${addrHex} len=${mr.length} -> ${dataHex}`; +} + +function formatSetreg(rw: RegisterWrite): string { + const idxStr = rw.index.toString().padStart(2, "0"); + const valueHex = "0x" + rw.value.toString(16); + return `setreg r${idxStr} <- ${valueHex}`; +} + +function formatStart(start: StartEntry): string { + const regParts: string[] = []; + const sortedRegs = Array.from(start.registers.entries()).sort((a, b) => a[0] - b[0]); + for (const [idx, value] of sortedRegs) { + if (value !== 0n) { + const idxStr = idx.toString().padStart(2, "0"); + regParts.push(`r${idxStr}=0x${value.toString(16)}`); + } + } + const regDump = regParts.join(" "); + return `start pc=${start.pc} gas=${start.gas}${regDump ? " " + regDump : ""}`; +} + +function formatEcalli(hc: HostCallEntry): string { + const regParts: string[] = []; + const sortedRegs = Array.from(hc.registers.entries()).sort((a, b) => a[0] - b[0]); + for (const [idx, value] of sortedRegs) { + if (value !== 0n) { + const idxStr = idx.toString().padStart(2, "0"); + regParts.push(`r${idxStr}=0x${value.toString(16)}`); + } + } + const regDump = regParts.join(" "); + return `ecalli=${hc.index} pc=${hc.pc} gas=${hc.gas}${regDump ? " " + regDump : ""}`; +} + +function formatTermination(term: TerminationEntry): string { + const regParts: string[] = []; + const sortedRegs = Array.from(term.registers.entries()).sort((a, b) => a[0] - b[0]); + for (const [idx, value] of sortedRegs) { + if (value !== 0n) { + const idxStr = idx.toString().padStart(2, "0"); + regParts.push(`r${idxStr}=0x${value.toString(16)}`); + } + } + const regDump = regParts.join(" "); + + switch (term.reason.type) { + case "HALT": + return `HALT pc=${term.pc} gas=${term.gas}${regDump ? " " + regDump : ""}`; + case "PANIC": + return `PANIC=${term.reason.argument} pc=${term.pc} gas=${term.gas}${regDump ? " " + regDump : ""}`; + case "OOG": + return `OOG pc=${term.pc} gas=${term.gas}${regDump ? " " + regDump : ""}`; + } +} + +/** + * Find a host call entry matching the current PVM state. + * Returns the entry and any mismatches found. + * + * @param trace The parsed trace + * @param indexInTrace Current host call index in the trace + * @param pc Current program counter + * @param gas Current gas + * @param registers Current register values + * @param hostCallIndex The ecalli index being executed + * @param readMemory Function to read memory for comparison + */ +function collectStateMismatches( + entry: HostCallEntry, + registers: bigint[], + gas: bigint, + readMemory?: (address: number, length: number) => Uint8Array | null, +): StateMismatch[] { + const mismatches: StateMismatch[] = []; + + if (entry.gas !== gas) { + mismatches.push({ + field: "gas", + expected: entry.gas.toString(), + actual: gas.toString(), + }); + } + + for (const [idx, expectedValue] of entry.registers) { + const actualValue = registers[idx] ?? 0n; + if (expectedValue !== actualValue) { + mismatches.push({ + field: "register", + expected: `r${idx}=0x${expectedValue.toString(16)}`, + actual: `r${idx}=0x${actualValue.toString(16)}`, + details: `Register ${idx}`, + }); + } + } + + if (readMemory) { + for (const mr of entry.memoryReads) { + const actualData = readMemory(mr.address, mr.length); + if (actualData) { + const expectedHex = Array.from(mr.data) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + const actualHex = Array.from(actualData) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + if (expectedHex !== actualHex) { + mismatches.push({ + field: "memread", + expected: `0x${expectedHex}`, + actual: `0x${actualHex}`, + details: `Memory at 0x${mr.address.toString(16)} (${mr.length} bytes)`, + }); + } + } + } + } + + return mismatches; +} + +export function findHostCallEntry( + trace: ParsedTrace, + indexInTrace: number, + pc: number, + gas: bigint, + registers: bigint[], + hostCallIndex: number, + readMemory?: (address: number, length: number) => Uint8Array | null, +): HostCallLookupResult { + const remainingEntries = trace.hostCalls.slice(indexInTrace); + + const exactIndexMatch = remainingEntries.find((hc) => hc.index === hostCallIndex && hc.pc === pc); + + if (exactIndexMatch) { + const mismatches = collectStateMismatches(exactIndexMatch, registers, gas, readMemory); + return { entry: exactIndexMatch, mismatches }; + } + + const matchingEntries = remainingEntries.filter((hc) => hc.pc === pc && hc.gas <= gas); + + if (matchingEntries.length === 0) { + return { entry: null, mismatches: [] }; + } + + const entry = matchingEntries[0]; + const mismatches: StateMismatch[] = []; + + if (entry.index !== hostCallIndex) { + mismatches.push({ + field: "index", + expected: entry.index.toString(), + actual: hostCallIndex.toString(), + }); + } + + mismatches.push(...collectStateMismatches(entry, registers, gas, readMemory)); + + return { entry, mismatches }; +} + +/** + * Serialize a single host call entry to its textual format + */ +export function serializeHostCallEntry(hc: HostCallEntry): string { + const lines: string[] = []; + + lines.push(formatEcalli(hc)); + + for (const mr of hc.memoryReads) { + lines.push(formatMemread(mr)); + } + + for (const mw of hc.memoryWrites) { + lines.push(formatMemwrite(mw)); + } + + for (const rw of hc.registerWrites) { + lines.push(formatSetreg(rw)); + } + + if (hc.gasAfter !== null) { + lines.push(`setgas <- ${hc.gasAfter}`); + } + + return lines.join("\n"); +} + +/** + * Validate trace content and return errors + */ +export function validateTrace(content: string): TraceParseError[] { + const parsed = parseTrace(content); + return parsed.errors; +} + +/** + * Check if trace content is valid + */ +export function isValidTrace(content: string): boolean { + return validateTrace(content).length === 0; +} + +/** + * Get summary statistics for a trace + */ +export function getTraceSummary(trace: ParsedTrace): { + hostCallCount: number; + hasProgram: boolean; + hasTermination: boolean; + errorCount: number; +} { + return { + hostCallCount: trace.hostCalls.length, + hasProgram: trace.prelude.program !== null, + hasTermination: trace.termination !== null, + errorCount: trace.errors.length, + }; +} diff --git a/src/main.tsx b/src/main.tsx index 17326bfa..6e68ef0c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -9,22 +9,25 @@ import { HashRouter } from "react-router"; import { PersistGate } from "redux-persist/integration/react"; import { isDarkMode, setColorMode } from "./packages/ui-kit/DarkMode/utils.ts"; import { TooltipProvider } from "@radix-ui/react-tooltip"; +import { ErrorBoundary } from "./components/ErrorBoundary"; setColorMode(isDarkMode()); ReactDOM.createRoot(document.getElementById("root")!).render( // TODO: strict mode is disabled because of the App useEffect for init being called twice // - - - - - - - - - - - , + + + + + + + + + + + + + , // , ); diff --git a/src/packages/web-worker/command-handlers/step.ts b/src/packages/web-worker/command-handlers/step.ts index 1eeb8b97..b6027b7f 100644 --- a/src/packages/web-worker/command-handlers/step.ts +++ b/src/packages/web-worker/command-handlers/step.ts @@ -5,7 +5,6 @@ import { CommandStatus, PvmApiInterface } from "../types"; export type StepParams = { pvm: PvmApiInterface | null; stepsToPerform: number; - serviceId: number | null; }; export type StepResponse = { status: CommandStatus; @@ -38,9 +37,9 @@ const step = async ({ pvm, stepsToPerform }: StepParams) => { return { state, isFinished, currentPc: state.pc ?? 0, exitArg: pvm.getExitArg() }; }; -export const runStep = async ({ pvm, stepsToPerform, serviceId }: StepParams): Promise => { +export const runStep = async ({ pvm, stepsToPerform }: StepParams): Promise => { try { - const data = await step({ pvm, stepsToPerform, serviceId }); + const data = await step({ pvm, stepsToPerform }); return { status: CommandStatus.SUCCESS, ...data }; } catch (error) { return { status: CommandStatus.ERROR, error, isFinished: true, currentPc: 0, state: {}, exitArg: 0 }; diff --git a/src/packages/web-worker/types.ts b/src/packages/web-worker/types.ts index 362032c5..3e24d9d8 100644 --- a/src/packages/web-worker/types.ts +++ b/src/packages/web-worker/types.ts @@ -39,7 +39,6 @@ export type WorkerResponseParams = CommonWorkerResponseParams & storage?: Storage; }; } - | { command: Commands.SET_SERVICE_ID } | { command: Commands.SET_STATE; payload: { state: ExpectedState } } | { command: Commands.SET_MEMORY } | { command: Commands.UNLOAD } @@ -65,7 +64,6 @@ export type CommandWorkerRequestParams = | { command: Commands.STOP } | { command: Commands.MEMORY; payload: { startAddress: number; stopAddress: number } } | { command: Commands.HOST_CALL; payload: { hostCallIdentifier: number } } - | { command: Commands.SET_SERVICE_ID; payload: { serviceId: number } } | { command: Commands.SET_STATE; payload: { regs: bigint[]; gas: bigint } } | { command: Commands.SET_MEMORY; payload: { address: number; data: Uint8Array } } | { command: Commands.UNLOAD }; @@ -80,7 +78,6 @@ export enum Commands { STOP = "stop", MEMORY = "memory", HOST_CALL = "host_call", - SET_SERVICE_ID = "set_service_id", SET_STATE = "set_state", SET_MEMORY = "set_memory", UNLOAD = "unload", diff --git a/src/packages/web-worker/worker.ts b/src/packages/web-worker/worker.ts index 5f149f02..67882f49 100644 --- a/src/packages/web-worker/worker.ts +++ b/src/packages/web-worker/worker.ts @@ -5,8 +5,6 @@ import { Commands, CommandStatus, PvmApiInterface, WorkerRequestParams, WorkerRe let pvm: PvmApiInterface | null = null; let memorySize: number | null = null; let isRunMode = false; -// Set default serviceId to 0x30303030. This is the ASCII code for '0000'. -let serviceId: number | null = parseInt("0x30303030", 16); let socket: WebSocket | undefined | null = null; export function postTypedMessage(msg: WorkerResponseParams) { @@ -56,7 +54,6 @@ async function rawOnMessage(e: MessageEvent) { const { currentPc, state, isFinished, status, exitArg, error } = await commandHandlers.runStep({ pvm, stepsToPerform: e.data.payload.stepsToPerform, - serviceId, }); isRunMode = !isFinished; @@ -118,15 +115,6 @@ async function rawOnMessage(e: MessageEvent) { messageId: e.data.messageId, payload: data, }); - } else if (e.data.command === Commands.SET_SERVICE_ID) { - serviceId = e.data.payload.serviceId; - - postTypedMessage({ - command: Commands.SET_SERVICE_ID, - status: CommandStatus.SUCCESS, - error: null, - messageId: e.data.messageId, - }); } else if (e.data.command === Commands.SET_STATE) { const data = await commandHandlers.runSetState({ pvm, diff --git a/src/store/debugger/debuggerSlice.ts b/src/store/debugger/debuggerSlice.ts index 168e1c70..ca646f97 100644 --- a/src/store/debugger/debuggerSlice.ts +++ b/src/store/debugger/debuggerSlice.ts @@ -13,6 +13,7 @@ import { RootState } from "@/store"; import { SelectedPvmWithPayload } from "@/components/PvmSelect"; import { PvmTypes } from "@/packages/web-worker/types.ts"; import { logger } from "@/utils/loggerService.tsx"; +import { HostCallEntry, ParsedTrace, parseTrace, StateMismatch, validateTraceContent } from "@/lib/host-call-trace"; export type UiRefreshMode = "instructions" | "block"; @@ -21,6 +22,17 @@ export interface UiRefreshRate { instructionCount: number; } +export interface HostCallTraceState { + rawContent: string; + parsed: ParsedTrace | null; +} + +export interface HostCallPaused { + hostCallId: number; + entry: HostCallEntry | null; + mismatches: StateMismatch[]; +} + export interface DebuggerState { pvmOptions: { allAvailablePvms: SelectedPvmWithPayload[]; @@ -29,8 +41,6 @@ export interface DebuggerState { isProgramInvalid: boolean; breakpointAddresses: number[]; clickedInstruction: CurrentInstruction | null; - hasHostCallOpen: boolean; - pendingHostCallIndex: number | null; initialState: ExpectedState; instructionMode: InstructionMode; isDebugFinished: boolean; @@ -47,7 +57,16 @@ export interface DebuggerState { useBlockStepping: boolean; uiRefreshRate: UiRefreshRate; serviceId: number | null; - hostCallsTrace: null; + /** Loaded host call trace information. */ + hostCallsTrace: HostCallTraceState | null; + /** + * Index (count) of the next host call (NOT: id of the host call) + * In case the trace is not loaded, this will simply count the host calls. + */ + nextHostCallIndex: number; + /** Currently pending host call. May include data from trace log. */ + pendingHostCall: HostCallPaused | null; + autoContinueOnHostCalls: boolean; spiProgram: SpiProgram | null; spiArgs: Uint8Array | null; activeMobileTab: "program" | "status" | "memory"; @@ -96,8 +115,6 @@ const initialState: DebuggerState = { isProgramInvalid: false, isRunMode: false, isStepMode: false, - hasHostCallOpen: false, - pendingHostCallIndex: null, programPreviewResult: [], breakpointAddresses: [], clickedInstruction: null, @@ -112,6 +129,9 @@ const initialState: DebuggerState = { instructionCount: 10_000, }, hostCallsTrace: null, + pendingHostCall: null, + nextHostCallIndex: 0, + autoContinueOnHostCalls: false, serviceId: parseInt("0x00000000", 16), activeMobileTab: "program", }; @@ -184,12 +204,6 @@ const debuggerSlice = createSlice({ state.stepsToPerform = action.payload.instructionCount; state.useBlockStepping = action.payload.mode === "block"; }, - setHasHostCallOpen(state, action) { - state.hasHostCallOpen = action.payload; - }, - setPendingHostCallIndex(state, action: { payload: number | null }) { - state.pendingHostCallIndex = action.payload; - }, setServiceId(state, action) { state.serviceId = action.payload; }, @@ -208,6 +222,56 @@ const debuggerSlice = createSlice({ setActiveMobileTab(state, action) { state.activeMobileTab = action.payload; }, + setHostCallsTrace(state, action: { payload: string | null }) { + if (action.payload === null || action.payload.trim() === "") { + state.hostCallsTrace = null; + // reset host call index and pending state when clearing trace + state.nextHostCallIndex = 0; + state.pendingHostCall = null; + } else { + // Validate trace content (includes both parse errors and Zod structure validation) + const validation = validateTraceContent(action.payload); + if (!validation.success) { + console.error("Trace validation errors:", validation.errors); + if (validation.parseErrors) { + console.error("Trace structure validation errors:", validation.parseErrors); + } + // Don't set invalid trace - keep existing state or set to null + state.hostCallsTrace = null; + state.nextHostCallIndex = 0; + state.pendingHostCall = null; + return; + } + + const parsed = parseTrace(action.payload); + state.hostCallsTrace = { + rawContent: action.payload, + parsed, + }; + // reset host call index + state.nextHostCallIndex = 0; + state.pendingHostCall = null; + } + }, + setPendingHostCall( + state, + action: { + payload: { + pendingHostCall: HostCallPaused | null; + nextHostCallIndex: number; + }; + }, + ) { + state.pendingHostCall = action.payload.pendingHostCall; + state.nextHostCallIndex = action.payload.nextHostCallIndex; + }, + setAutoContinueOnHostCalls(state, action: { payload: boolean }) { + state.autoContinueOnHostCalls = action.payload; + }, + resetHostCallIndex(state) { + state.nextHostCallIndex = 0; + state.pendingHostCall = null; + }, }, }); @@ -215,8 +279,6 @@ export const { setIsProgramInvalid, setBreakpointAddresses, setClickedInstruction, - setHasHostCallOpen, - setPendingHostCallIndex, setInitialState, setInstructionMode, setIsDebugFinished, @@ -235,6 +297,10 @@ export const { setPvmOptions, setSelectedPvms, setActiveMobileTab, + setHostCallsTrace, + setPendingHostCall, + setAutoContinueOnHostCalls, + resetHostCallIndex, } = debuggerSlice.actions; export const debuggerSliceListenerMiddleware = createListenerMiddleware(); @@ -280,5 +346,7 @@ export const selectIsDebugFinished = (state: RootState) => state.debugger.isDebu export const selectPvmInitialized = (state: RootState) => state.debugger.pvmInitialized; export const selectAllAvailablePvms = (state: RootState) => state.debugger.pvmOptions.allAvailablePvms; export const selectSelectedPvms = (state: RootState) => state.debugger.pvmOptions.selectedPvm; +export const selectHostCallsTrace = (state: RootState) => state.debugger.hostCallsTrace; +export const selectAutoContinueOnHostCalls = (state: RootState) => state.debugger.autoContinueOnHostCalls; export default debuggerSlice.reducer; diff --git a/src/store/utils.ts b/src/store/utils.ts index 62e460c0..a926f539 100644 --- a/src/store/utils.ts +++ b/src/store/utils.ts @@ -20,11 +20,12 @@ export const asyncWorkerPostMessage = ( const messageId = getMessageId(); const timeoutId = setTimeout(() => { reject(`PVM ${id} reached max timeout ${RESPONSE_WAIT_TIMEOUT}ms for ${messageId}`); + worker.removeEventListener("message", messageHandler); }, RESPONSE_WAIT_TIMEOUT); const messageHandler = (event: MessageEvent) => { - logger.info("📥 Debugger received message", event.data); if (event.data.messageId === messageId) { + logger.info(`📥 (${id}) Debugger received message`, event.data); clearTimeout(timeoutId); worker.removeEventListener("message", messageHandler); resolve(event.data as Extract); diff --git a/src/store/workers/workersSlice.ts b/src/store/workers/workersSlice.ts index 5385efb2..ba669793 100644 --- a/src/store/workers/workersSlice.ts +++ b/src/store/workers/workersSlice.ts @@ -6,15 +6,14 @@ import { setIsDebugFinished, setIsRunMode, setIsStepMode, - setHasHostCallOpen, - setPendingHostCallIndex, + setPendingHostCall, } from "@/store/debugger/debuggerSlice.ts"; +import { findHostCallEntry, HostCallEntry, StateMismatch } from "@/lib/host-call-trace"; import PvmWorker from "@/packages/web-worker/worker?worker&inline"; import { logger } from "@/utils/loggerService"; import { Commands, PvmTypes } from "@/packages/web-worker/types"; import { asyncWorkerPostMessage, hasCommandStatusError, LOAD_MEMORY_CHUNK_SIZE, MEMORY_SPLIT_STEP } from "../utils"; import { chunk, inRange, isNumber } from "lodash"; -import { isInstructionError } from "@/types/type-guards"; import { SerializedFile } from "@/lib/utils.ts"; // TODO: remove this when found a workaround for BigInt support in JSON.stringify @@ -114,40 +113,40 @@ export const loadWorker = createAsyncThunk( }, ); -export const setAllWorkersServiceId = createAsyncThunk("workers/setAllStorage", async (_, { getState }) => { +export const initAllWorkers = createAsyncThunk("workers/initAllWorkers", async (_, { getState, dispatch }) => { const state = getState() as RootState; - const debuggerState = state.debugger; - const serviceId = debuggerState.serviceId; - - if (serviceId === null) { - throw new Error("Service id is not set"); + const { program, initialState, spiProgram, spiArgs } = state.debugger; + + if (state.workers.length === 0) { + logger.warn("initAllWorkers: No workers to initialize. Waiting..."); + // Retry a few times in case workers are being created + for (let i = 0; i < 10; i++) { + await new Promise((r) => setTimeout(r, 500)); + const newState = getState() as RootState; + if (newState.workers.length > 0) { + break; + } + } + const finalState = getState() as RootState; + if (finalState.workers.length === 0) { + logger.warn("initAllWorkers: Still no workers after wait. Aborting."); + return; + } } - return Promise.all( - state.workers.map(async (worker) => { - await asyncWorkerPostMessage(worker.id, worker.worker, { - command: Commands.SET_SERVICE_ID, - payload: { - serviceId, - }, - }); - }), - ); -}); - -export const initAllWorkers = createAsyncThunk("workers/initAllWorkers", async (_, { getState, dispatch }) => { - const state = getState() as RootState; - const debuggerState = state.debugger; + // Refresh state after wait + const currentState = getState() as RootState; + const currentWorkers = currentState.workers; - return Promise.all( - state.workers.map(async (worker) => { + await Promise.all( + currentWorkers.map(async (worker) => { const initData = await asyncWorkerPostMessage(worker.id, worker.worker, { command: Commands.INIT, payload: { - spiProgram: state.debugger.spiProgram, - spiArgs: state.debugger.spiArgs, - initialState: debuggerState.initialState, - program: new Uint8Array(debuggerState.program), + spiProgram: spiProgram, + spiArgs: spiArgs || new Uint8Array(), + initialState: initialState, + program: new Uint8Array(program), }, }); @@ -180,6 +179,12 @@ export const loadMemoryChunkAllWorkers = createAsyncThunk( ) => { const state = getState() as RootState; + // Guard: skip if no workers or PVM not initialized + if (state.workers.length === 0 || !state.debugger.pvmInitialized) { + logger.info("loadMemoryChunkAllWorkers: skipping - no workers or PVM not initialized"); + return; + } + return Promise.all( state.workers.map(async (worker) => { const resp = await asyncWorkerPostMessage(worker.id, worker.worker, { @@ -211,6 +216,12 @@ export const refreshPageAllWorkers = createAsyncThunk( async (_, { getState, dispatch }) => { const state = getState() as RootState; + // Guard: skip if no workers or PVM not initialized + if (state.workers.length === 0 || !state.debugger.pvmInitialized) { + logger.info("refreshPageAllWorkers: skipping - no workers or PVM not initialized"); + return; + } + return Promise.all( state.workers.map(async (worker) => { // No memory, nothing to refresh @@ -250,56 +261,113 @@ export const handleHostCall = createAsyncThunk( "workers/handleHostCall", async ({ workerId }: { workerId?: string }, { getState, dispatch }) => { const state = getState() as RootState; + const workerState = state.workers.filter(({ id }) => { + return workerId ? workerId === id : true; + })[0]; + + const exitArg = workerState?.exitArg ?? -1; + const trace = state.debugger.hostCallsTrace; + const nextHostCallIndex = state.debugger.nextHostCallIndex; + const autoContinue = state.debugger.autoContinueOnHostCalls; + + const openDialog = (hcId: number, entry: HostCallEntry | null, mismatches: StateMismatch[] = []) => { + dispatch( + setPendingHostCall({ + pendingHostCall: { + hostCallId: hcId, + entry, + mismatches, + }, + nextHostCallIndex: nextHostCallIndex, + }), + ); + }; - const instructionEnriched = state.debugger.programPreviewResult.find( - (instruction) => instruction.address === state.workers[0].previousState.pc, - ); + // just open the dialog + if (trace === null || !trace.parsed) { + logger.info(" [handleHostCall] No trace -> opening dialog"); + openDialog(exitArg, null); + return; + } + const currentState = workerState?.currentState; + const pc = currentState?.pc ?? 0; + const gas = currentState?.gas ?? 0n; + const regs = (currentState?.regs ?? []) as bigint[]; - if ( - !instructionEnriched || - isInstructionError(instructionEnriched) - // !isOneImmediateArgs(instructionEnriched.args) - ) { - throw new Error("Invalid host call instruction"); + const result = findHostCallEntry(trace.parsed, nextHostCallIndex, pc, gas, regs, exitArg); + + if (result.entry === null) { + logger.info(" [handleHostCall] No entry found -> opening dialog"); + openDialog(exitArg, null); + return; + } + + if (result.mismatches.length > 0) { + logger.info(" [handleHostCall] Mismatches found -> stopping, opening dialog"); + openDialog(exitArg, result.entry, result.mismatches); + return; } - // Open dialog if no traces are available, allowing user to manually set register values - if (state.debugger.hostCallsTrace === null) { - const exitArg = state.workers[0]?.exitArg ?? -1; - dispatch(setPendingHostCallIndex(exitArg)); - dispatch(setHasHostCallOpen(true)); + if (!autoContinue) { + logger.debug(" [handleHostCall] autoContinue=false -> opening dialog"); + openDialog(exitArg, result.entry, result.mismatches); return; } + logger.debug(" [handleHostCall] AUTO-CONTINUING with trace data"); + // TODO [ToDr] This should be a separate method? + const newRegs = [...regs]; + for (const rw of result.entry.registerWrites) { + newRegs[rw.index] = rw.value; + } + const newGas = result.entry.gasAfter ?? gas; + const memoryEdits = result.entry.memoryWrites.map((mw) => ({ + address: mw.address, + data: mw.data, + })); + + logger.debug(` [handleHostCall] Setting state: gas=${newGas}, memEdits=${memoryEdits.length}`); await Promise.all( state.workers .filter(({ id }) => { - // Allow to call it for a single worker return workerId ? workerId === id : true; }) .map(async (worker) => { const resp = await asyncWorkerPostMessage(worker.id, worker.worker, { - command: Commands.HOST_CALL, - payload: { hostCallIdentifier: worker.exitArg ?? -1 }, + command: Commands.SET_STATE, + payload: { regs: newRegs, gas: newGas }, }); - // TODO [ToDr] Handle host call response? - - if ((getState() as RootState).debugger.isRunMode) { - dispatch(continueAllWorkers()); - } - if (hasCommandStatusError(resp)) { throw new Error(resp.error.message); } + + logger.info(` [handleHostCall] dispatching setWorkerCurrentState for ${worker.id}`); + dispatch(setWorkerCurrentState({ id: worker.id, currentState: resp.payload.state })); + + for (const mem of memoryEdits) { + const memResp = await asyncWorkerPostMessage(worker.id, worker.worker, { + command: Commands.SET_MEMORY, + payload: { address: mem.address, data: mem.data }, + }); + + if (hasCommandStatusError(memResp)) { + throw new Error(memResp.error.message); + } + } }), ); - if (selectShouldContinueRunning(state)) { - dispatch(continueAllWorkers()); - } - - return; + logger.info(" [handleHostCall] Done - auto-continuing without opening dialog"); + // Advance host call index before resuming execution + dispatch( + setPendingHostCall({ + pendingHostCall: null, + nextHostCallIndex: nextHostCallIndex + 1, + }), + ); + // Resume execution + await dispatch(runAllWorkers()).unwrap(); }, ); @@ -374,6 +442,7 @@ export const stepAllWorkers = createAsyncThunk( return; } + let shouldOpenHostCalls; const responses = await Promise.all( state.workers.map(async (worker) => { const data = await asyncWorkerPostMessage(worker.id, worker.worker, { @@ -412,13 +481,10 @@ export const stepAllWorkers = createAsyncThunk( } if (state.status === Status.HOST) { - // break execution if host call trace is not provided - // and we the PVM stops. - if (debuggerState.hostCallsTrace === null) { - dispatch(setIsRunMode(false)); - } - - await dispatch(handleHostCall({ workerId: worker.id })).unwrap(); + logger.group(`[HOST CALL] PVM stopped at pc=${state.pc}, exitArg=${exitArg}`); + dispatch(setIsRunMode(false)); + shouldOpenHostCalls = true; + logger.groupEnd(); } return { @@ -430,6 +496,11 @@ export const stepAllWorkers = createAsyncThunk( }), ); + // we wait for all of the workers to reach the host call before handling it + if (shouldOpenHostCalls) { + await dispatch(handleHostCall({})).unwrap(); + } + const allFinished = responses.every((response) => response.isFinished); await dispatch(refreshPageAllWorkers()); @@ -460,6 +531,12 @@ export const refreshMemoryRangeAllWorkers = createAsyncThunk( async (_, { getState, dispatch }) => { const state = getState() as RootState; + // Guard: skip if no workers or PVM not initialized + if (state.workers.length === 0 || !state.debugger.pvmInitialized) { + logger.info("refreshMemoryRangeAllWorkers: skipping - no workers or PVM not initialized"); + return; + } + await Promise.all( state.workers.map(async (worker) => { await Promise.all( @@ -499,6 +576,13 @@ export const loadMemoryRangeAllWorkers = createAsyncThunk( { getState, dispatch }, ) => { const state = getState() as RootState; + + // Guard: skip if no workers or PVM not initialized + if (state.workers.length === 0 || !state.debugger.pvmInitialized) { + logger.info("loadMemoryRangeAllWorkers: skipping - no workers or PVM not initialized"); + return; + } + const stopAddress = startAddress + length; return Promise.all( @@ -606,6 +690,13 @@ export const readMemoryRange = createAsyncThunk( "workers/readMemoryRange", async ({ startAddress, length }: { startAddress: number; length: number }, { getState }) => { const state = getState() as RootState; + + // Guard: return empty array if no workers or PVM not initialized + if (state.workers.length === 0 || !state.debugger.pvmInitialized) { + logger.info("readMemoryRange: skipping - no workers or PVM not initialized"); + return new Uint8Array(length); + } + const worker = state.workers[0]; if (!worker) { @@ -642,9 +733,10 @@ export const resumeAfterHostCall = createAsyncThunk( }: { regs: bigint[]; gas: bigint; mode: HostCallResumeMode; memoryEdits?: MemoryEdit[] }, { getState, dispatch }, ) => { + logger.group(`[resumeAfterHostCall] mode=${mode}, gas=${gas}, memEdits=${memoryEdits?.length ?? 0}`); const state = getState() as RootState; - // Set state for all workers + logger.info("Setting state for all workers..."); await Promise.all( state.workers.map(async (worker) => { const resp = await asyncWorkerPostMessage(worker.id, worker.worker, { @@ -656,16 +748,14 @@ export const resumeAfterHostCall = createAsyncThunk( throw resp.error; } - // Update Redux state with new values dispatch(setWorkerCurrentState({ id: worker.id, currentState: resp.payload.state })); }), ); - // Write all memory edits to all workers if provided + logger.info(`Writing ${memoryEdits?.length} memory edits...`); if (memoryEdits && memoryEdits.length > 0) { await Promise.all( state.workers.map(async (worker) => { - // Write each memory edit sequentially for this worker for (const memory of memoryEdits) { const resp = await asyncWorkerPostMessage(worker.id, worker.worker, { command: Commands.SET_MEMORY, @@ -680,11 +770,16 @@ export const resumeAfterHostCall = createAsyncThunk( ); } - // Close the dialog - dispatch(setHasHostCallOpen(false)); - dispatch(setPendingHostCallIndex(null)); + logger.info("Closing dialog..."); + dispatch( + setPendingHostCall({ + pendingHostCall: null, + nextHostCallIndex: state.debugger.nextHostCallIndex + 1, + }), + ); // Resume execution based on mode + logger.info(`Resuming execution in mode: ${mode}`); if (mode === "run") { dispatch(setIsRunMode(true)); await dispatch(runAllWorkers()).unwrap(); @@ -692,12 +787,13 @@ export const resumeAfterHostCall = createAsyncThunk( const stepsToPerform = calculateStepsToExitBlockForAllWorkers(getState() as RootState); await dispatch(stepAllWorkers({ stepsToPerform })).unwrap(); } else { - // step await dispatch(stepAllWorkers({ stepsToPerform: 1 })).unwrap(); } await dispatch(refreshPageAllWorkers()); await dispatch(refreshMemoryRangeAllWorkers()).unwrap(); + logger.info("resumeAfterHostCall complete"); + logger.groupEnd(); }, ); @@ -708,17 +804,18 @@ export const destroyWorker = createAsyncThunk("workers/destroyWorker", async (id return; } - await Promise.all( - state.workers.map(async (worker) => { - const data = await asyncWorkerPostMessage(worker.id, worker.worker, { - command: Commands.UNLOAD, - }); + // Send UNLOAD only to the specific worker being destroyed + try { + const data = await asyncWorkerPostMessage(worker.id, worker.worker, { + command: Commands.UNLOAD, + }); - if (hasCommandStatusError(data)) { - throw data.error; - } - }), - ); + if (hasCommandStatusError(data)) { + console.warn(`Worker ${id} UNLOAD error:`, data.error); + } + } catch (e) { + console.warn(`Failed to send UNLOAD to worker ${id}:`, e); + } worker.worker.terminate(); diff --git a/src/types/type-guards.ts b/src/types/type-guards.ts deleted file mode 100644 index 4a583735..00000000 --- a/src/types/type-guards.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { pvm_interpreter as pvm } from "@typeberry/lib"; -import { CurrentInstruction } from "./pvm"; - -export function isInstructionError( - instruction: CurrentInstruction, -): instruction is Extract { - return "error" in instruction; -} - -export function isOneImmediateArgs( - args: pvm.args.Args, -): args is Extract { - return args.type === pvm.args.ArgumentType.ONE_IMMEDIATE; -} diff --git a/src/utils/loggerService.tsx b/src/utils/loggerService.tsx index d9123f61..2112e0d0 100644 --- a/src/utils/loggerService.tsx +++ b/src/utils/loggerService.tsx @@ -11,31 +11,25 @@ const errorToast = throttle((msg: string) => { { autoClose: 3000 }, ); }, 6000); + class Logger { constructor() { if (typeof window !== "undefined") { // eslint-disable-next-line no-console - console.info(` - .!YGBBPJ^ - :B@&57~!?P&@5 - ...:YBB@&: !@@. .~~!77! ~YY ^JY~ 7YJ. -^B&&&&@#!!7 ~@@PPY^ 7@@@J~^.@@@ !@@5 &@@: -J@B: . . J57?G@&: 7@@@P5P:&@@.G&B 5&#:#@@B^7@@@J.G&G 5&B -@@. @&. ^@& 7@@@. @@@.@@@.@@@ Y@@? .@@@ @@& &@@ -5@G. GB5@@. Y@G ^&#P B&P ?&#YB#P !&&^ #&P J&#Y@@& -!&@#BBGJ .@@Y! .YBBB#&&J . ~5@&^ - .:^!&@&^ .@@ &@?^^^. ~&&&. .@@B . - :@&JP@# .@@ .Y@@P. ?@@@. ^!!~~^^@@&!!^ .^!~^ - ~@&^!@@ @@ .@@~^&@^ 7@@@ ~@@P^@@#:@@#^@@&.#@@&7 - ^P#BY. :G@@P. &@5Y@@. 7@@@?777@@B!@@B:@@B^@@B.~#@@@^ - :@&:^@@. ~YY! ~~^~~: .^~:~~. :~~~^. :^~^. - .&@5P@& - ~JJ^ -Logger initialized watch console for logs - `); + console.info("Logger initialized — watch console for logs."); } } + group(msg: string) { + // eslint-disable-next-line no-console + console.group(msg); + } + + groupEnd() { + // eslint-disable-next-line no-console + console.groupEnd(); + } + error(msg: string, { error, hideToast }: { error: unknown; hideToast?: boolean }) { if (!hideToast) { errorToast(msg); diff --git a/tests/io-trace-output.log b/tests/io-trace-output.log new file mode 100644 index 00000000..af62f6fb --- /dev/null +++ b/tests/io-trace-output.log @@ -0,0 +1,57 @@ + +> @typeberry/test-runner@0.5.1 w3f-davxy:0.7.2 +> GP_VERSION=0.7.2 tsx ./w3f-davxy-072.ts --pvm ananas ../test-vectors/w3f-davxy_072/stf/accumulate/tiny/process_one_immediate_report-1.json + +INFO [test-runner] Preparing tests for 1 files. +INFO [test-runner] Running 1 tests. +INFO [test-runner] Running accumulate tests [1/1] +TAP version 13 +TRACE [ ecalli] program 0x47000c746573742d7365727669636506302e312e32370a4170616368652d322e30012550617269747920546563686e6f6c6f67696573203c61646d696e407061726974792e696f3ec01c0014000002000020004b598638d6c56d3420286279746573208080808080808080210b59c84216b2005400ffff4600ffff4c00ffff4e00ffff5200ffff4800ffff5000ffff4a00ffff4000ffff4000ffff3e00ffff3600ffff3800ffff3400ffff3c00ffff3200ffff3a00ffff0c00ffff7000ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7c00ffff7600ffff7200ffff7200ffff7a00ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7200ffff7e00ffff7200ffff7200ffff7200ffff7200ffff7800ffff63616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c75652f6d6e742f7373642f646576656c6f702f6a616d2f706f6c6b616a616d2f6372617465732f6a616d2d70766d2d636f6d6d6f6e2f7372632f6d656d2e72732f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f736c6963652e7273486f737443616c6c496e76616c69644f75744f66426f756e6473496e646578556e6b6e6f776e53746f7261676546756c6c426164436f72654e6f436173684761734c696d6974546f6f4c6f77416374696f6e496e76616c69645468697320697320526566696e6520696e207468652054657374205365727669636520682077697468207061796c6f6164206c656e2048656c6c6f205468697320697320416363756d756c61746520696e20746865205465737420536572766963652068207769746820206974656d73203c3d206c61737462616c616e6365206c6f7773657276696365732f746573742d736572766963652f7372632f6c69622e72735472616e73666572206174202066726f6d206820746f2068206f6620206d656d6f206c6173747478000f000000000000000b000000000000000c000000000000000b00000000000000070000000000000006000000000000000e000000000000000d000000000000009bfeffffaafeffffb5feffffc1feffffccfeffffd3feffffd9feffffe7feffff6361706163697479206f766572666c6f772f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7261775f7665632f6d6f642e72732f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f616c6c6f632e72736d656d6f727920616c6c6f636174696f6e206f6620206279746573206661696c65642f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f737472696e672e72736120666f726d617474696e6720747261697420696d706c656d656e746174696f6e2072657475726e656420616e206572726f72207768656e2074686520756e6465726c79696e672073747265616d20646964206e6f742f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f666d742e727300700007002d0101010201020101480b30151001650702060202010423011e1b5b0b3a09090118040109010301052b033b092a180120370101010408040103070a021d013a0101010204080109010a021a010202390104020402020303011e0203010b0239010405010204011402160601013a0101020104080107030a021e013b0101010c01090128010301370101030503010407020b021d013a01020201010303010407020b021c02390201010204080109010a021d0148010401020301010801510102070c08620102090b0749021b0101010101370e01050102050b0124090166040106010202021902040310040d01020206010f01000300041c031d021e02400201070801020b09012d030101750222017603040209010603db0202013a010107010101010208060a0201301f3104300a040326090c02200402063801010203010105380802029803010d0107040106010302c6400001c32100038d016020000669020004010a200250020001030104011902050197021a120d012608190b01012c03300102040202020124014306020202020c0108012f01330101030202050201012a020801ee010201040100010010101000020001e201950500030102050428030401a5020004410500024f04460b31047b01360f290102020a033104020207013d03240501083e010c0234090101080402015f030204060102019d01030815023902010101010c0109010e07030543010206010102010103040301010e025508020301011701510102060101020101020102eb010204060201021b025508020101026a0101010208650101010204010500090102f5010a040401900402020401200a280602040801090602032e0d010200070106010152160207010201027a06030101020107010148020301010100020b023405050317010001060f000c030300053b0700013f0451010b020002002e021700050306080802071e0494030037043208010e011605010f000701110207010201056401a00700013d040004fe0200076d07006080f0002e2e0029207768656e20736c6963696e67206072616e676520656e6420696e6465782001000000000000000000000000000000303132333435363738396162636465663a63616c6c656420604f7074696f6e3a3a756e77726170282960206f6e206120604e6f6e65602076616c756570616e69636b6564206174203a0a3a2030303031303230333034303530363037303830393130313131323133313431353136313731383139323032313232323332343235323632373238323933303331333233333334333533363337333833393430343134323433343434353436343734383439353035313532353335343535353635373538353936303631363236333634363536363637363836393730373137323733373437353736373737383739383038313832383338343835383638373838383939303931393239333934393539363937393839392f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6d6f642e72732f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f6d6f642e7273010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020202020202020202020202020202020202020202020202020202020203030303030303030303030303030303040404040400000000000000000000005b2e2e2e5d626567696e203c3d20656e642028606279746520696e64657820206973206e6f742061206368617220626f756e646172793b20697420697320696e736964652029206f662060206973206f7574206f6620626f756e6473206f6620602f686f6d652f64617678792f2e7275737475702f746f6f6c636861696e732f6e696768746c792d323032352d30352d31302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f756e69636f64652f7072696e7461626c652e7273000601010301040205070702080809020a050b020e04100111021205131c140115021702190d1c051d081f0124016a046b02af03b102bc02cf02d102d40cd509d602d702da01e005e102e704e802ee20f004f802fa04fb010c273b3e4e4f8f9e9e9f7b8b9396a2b2ba86b1060709363d3e56f3d0d1041418363756577faaaeafbd35e01287898e9e040d0e11122931343a4546494a4e4f64658a8c8d8fb6c1c3c4c6cbd65cb6b71b1c07080a0b141736393aa8a9d8d909379091a8070a3b3e66698f92116f5fbfeeef5a62f4fcff53549a9b2e2f2728559da0a1a3a4a7a8adbabcc4060b0c151d3a3f4551a6a7cccda007191a22253e3fe7ecefffc5c604202325262833383a484a4c50535556585a5c5e606365666b73787d7f8aa4aaafb0c0d0aeaf6e6fddde935e227b0503042d036603012f2e80821d03310f1c0424091e052b0544040e2a80aa06240424042808340b4e03340c813709160a08183b45390363080930160521031b05014038044b052f040a070907402027040c0936033a051a07040c07504937330d33072e080a0626031d080280d0521003372c082a161a261c1417094e042409440d19070a0648082709750b423e2a063b050a06510601051003050b5908021d621e48080a80a65e22450b0a060d133a060a06141c2c041780b93c64530c48090a46451b4808530d49070a80b6220e0a06460a1d03474937030e080a0639070a813619073b031d55010f320d839b66750b80c48a4c630d843010160a8f9b0582479ab93a86c68239072a045c06260a460a28051381b03a80c65b654b0439071140050b020e97f80884d6290aa2e781330f011d060e0408818c89046b050d030907108f6080fa0681b44c4709743c80f60a73087015467a140c140c570919808781470385420f1584501f060680d52b053e2101702d031a040281401f113a050181d02a80d62b040181e080f7294c040a04028311444c3d80c23c06010455051b3402810e2c04640c560a80ae381d0d2c040907020e06809a83d80411030d0377045f060c04010f0c0438080a0628082c04023e81540c1d030a0538071c06090780fa840600010305050606020706080709110a1c0b190c1a0d100e0c0f0410031212130916011704180119031a071b011c021f1620032b032d0b2e01300431023201a704a902aa04ab08fa02fb05fd02fe03ff09ad78798b8da23057588b8c901cdd0e0f4b4cfbfc2e2f3f5c5d5fe2848d8e9192a9b1babbc5c6c9cadee4e5ff00041112293134373a3b3d494a5d848e92a9b1b4babbc6cacecfe4e500040d0e11122931343a3b4546494a5e646584919b9dc9cecf0d11293a3b4549575b5c5e5f64658d91a9b4babbc5c9dfe4e5f00d11454964658084b2bcbebfd5d7f0f183858ba4a6bebfc5c7cfdadb4898bdcdc6cecf494e4f57595e5f898e8fb1b6b7bfc1c6c7d71116175b5cf6f7feff806d71dedf0e1f6e6f1c1d5f7d7eaeaf4dbbbc16171e1f46474e4f585a5c5e7e7fb5c5d4d5dcf0f1f572738f747596262e2fa7afb7bfc7cfd7df9a00409798308f1fcecfd2d4ceff4e4f5a5b07080f10272feeef6e6f373d3f42459091536775c8c9d0d1d8d9e7feff00205f2282df048244081b04061181ac0e80ab051f08811c03190801042f043404070301070607110a500f1207550703041c0a090308030703020303030c0405030b06010e15054e071b0757070206170c500443032d03010411060f0c3a041d255f206d046a2580c80582b0031a0682fd03590716091809140c140c6a060a061a0659072b05460a2c040c040103310b2c041a060b0380ac060a062f3180f4083c030f033e0538082b0582ff1118082f112d03210f210f808c04829a160b158894052f053b07020e180980be22740c80d61a81100580e109f29e033709815c1480b80880dd153b030a06380846080c06740b1e035a0459098083181c0a16094c04808a06aba40c170431a10481da26070c050580a61081f50701202a064c04808d0480be031b030f0d206f7574206f662072616e676520666f7220736c696365206f66206c656e67746820736c69636520696e64657820737461727473206174202062757420656e64732061742000000003000083042000910560005d13a0001217201f0c20601fef2c202b2a30a02b6fa6602c02a8e02c1efbe02d00fe20369eff6036fd01e136010a2137240de137ab0e61392f18e139301ce14af31ee14e4034a1521e61e153f06a61544f6fe1549dbc615500cf615665d1a15600da215700e0a158aee2215aece4e15bd0e8615c2000ee5cf0017f5d2f686f6d652f64617678792f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d313934396366386336623562353537662f6a616d2d636f6465632d302e312e312f7372632f636f6465632e72734572726f722f686f6d652f64617678792f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d313934396366386336623562353537662f6a616d2d636f6465632d302e312e312f7372632f636f6d706163742e72736372617465732f6a616d2d70766d2d636f6d6d6f6e2f7372632f686f73745f63616c6c732e7273686f73742063616c6c2072657475726e7320636f727265637420747970653b207165646974656d206d75737420626520617661696c61626c653b2071656450616e69632068616e646c65722063616c6c65642150616e6963206d6573736167653a2050726f746f636f6c20706172616d65746572732061726520696e76616c69646372617465732f6a616d2d70766d2d636f6d6d6f6e2f7372632f737461727475702e72736372617465732f6a616d2d70766d2d636f6d6d6f6e2f7372632f726573756c742e72734f7572206f776e2041504920696d706c2068617320726573756c74656420696e20737563636573732076616c7565207768696368206973206f7574206f662072616e67652e6062617369635f70696563655f6c656e60206973206e6f74206576656e6062617369635f70696563655f6c656e6020646f6573206e6f742064697669646520696e746f20605345474d454e545f4c454e602028342c3130342960726f746174696f6e5f706572696f646020646f6573206e6f742064697669646520696e746f206065706f63685f706572696f64606076616c5f636f756e746020646f6573206e6f7420646976696465206279206056414c535f5045525f434f5245602028332960726563656e745f626c6f636b5f636f756e7460206d6179206265206e6f206c6172676572207468616e2060426c6f636b496e6465783a3a4d415860202832353529607472616e736665725f6d656d6f5f73697a6560206d75737420626520657175616c20746f7220604d454d4f5f4c454e602028313238296076616c5f636f756e7420213d20636f72655f636f756e74202a2056414c535f5045525f434f524560202833296065706f63685f7461696c5f737461727460206d757374206265206c657373207468616e206065706f63685f706572696f64602e2e2e0000000000000002000000000000000000000000000000000002000000000000000000000000000000000000000000200000e9000000002230780000000000000000000000000000000000000000000100000000000000980000000000000033010100000000003e00000000000000200000000900000071010100000000007a00000000000000da0200001a000000440201000000000023000000000000006702010000000000130000000000000080020100000000002700000000000000a7020100000000000700000000000000ae0201000000000006000000000000000000000000000000010000000000000001000000000000000a00000000000000c70201000000000020000000000000002000000040000000e7020100000000000c00000000000000f3020100000000000600000000000000f9020100000000000500000000000000fe02010000000000050000000000000003030100000000000600000000000000c702010000000000200000000000000028000000480000009a00000000000000180000000000000008000000000000009e00000000000000a400000000000000a80000000000000070030100000000001100000000000000810301000000000080000000000000002e020000110000007b04010000000000150000000000000090040100000000000d0000000000000001040100000000007a00000000000000b50100000d0000009d040100000000007b00000000000000e80100001700000000000000000000000000000000000000010000000000000098000000000000006e0501000000000078000000000000008a0200000e00000001000000000000000000000000000000180901000000000001000000000000001809010000000000010000000000000001000000000000000000000000000000520901000000000002000000000000001c0a0100000000007b00000000000000990a0000260000001c0a0100000000007b00000000000000a20a00001a000000170c0100000000000e00000000000000b4020100000000000400000000000000d8080100000000001000000000000000250c0100000000000100000000000000260c0100000000000b00000000000000310c010000000000260000000000000008000100000000000800000000000000570c0100000000000600000000000000250c0100000000000100000000000000260c0100000000000b000000000000005d0c0100000000001600000000000000250c0100000000000100000000000000970a0100000000007b000000000000009e0100002c000000730c01000000000085000000000000000a0000002b000000730c01000000000085000000000000001a00000036000000e8080100000000001000000000000000a1120100000000002200000000000000c3120100000000001600000000000000d9120100000000000d000000000000000000000000000000080100000000000008000000000000002a010000000000003a010000000000003c01000000000000d2130100000000005f000000000000002c0100003d00000070130100000000005d000000000000006f0400000f00000070130100000000005d00000000000000a20400001000000031140100000000002700000000000000c20000001c000000000000000000000000000000000000000100000000000000980000000000000031140100000000002700000000000000ca0000002e00000031140100000000002700000000000000d40000002e000000311401000000000027000000000000000e0100002b0000000000000000000000100000000000000008000000000000004201000000000000d9140100000000002400000000000000040000001400000020150100000000004500000000000000fd140100000000002300000000000000590000000d000000381701000000000001000000000000003817010000000000010000000000000039170100000000000200000000000000fe1601000000000003000000000000000000000000000000000000000000000008000000656e000080b602c0d8602d00bf002d0160016d01af020c0304043b044a045b04b8040705710506061e065606cd0615073907860720083d0851086508d108ec08270945097809ad1a1f1b981bc61d141e0d1f5b1fbf1f70208920e2206121d7212a224b229222b422cd22162371239a239f23a423a823ad23b223b723bc23dd23e223e723ec23f123f523fa23ff236824c2246a25b5253926cc26d127c828f128362948298c29a029cb29f129f829562af22afc2a142b1d2c882c472d742daa2d022e2e2e472e812ece2e78319131bf3187320b338333f633063459347d348535b535c935e2358f369536a336a936af366b37503b513b043e293e4f3e663e933eb33ec13eef3e4f3fae3fdb3f0f40ad40bf40c641d44124428f42e9424643b2432e44b844f64428457945f3456846c14621477847dc471c482d486248b248de4816492749e549114a514bdb4c074dd74d724e8c4eac4e734f864fd44f5750d450f75052519651e851f251fe512b547957f25a085b105b28f30200002802089511e07b10187b15107b16086485829808647651081582971051074e82976459501002d559289d003307011408e0ffffff1f000000ac584233091800037b19959848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e528433307011408e0ffffff1f000000ae58073307284933091800037b19959848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e595571f9877058377330850100412548219959800204628480701887801330901db77097b687b69087b651082101882151082160895112032009511b87b10407b15387b1630c88909ac895064758277330808977a01e48908e4a80657063d5107108258087b18187b1728330701017b172064179519186468501006e0fe82175207238217087b567b5708821040821538821630951148320033073309c0180128a7258217088218103309c01801289925827a08827910648764a828a72d7c793302100301330b5003018287828808979c03979a02c82c0c82c9c8ba0a81aa828c18c8ab08320c9511c87b10307b15287b1620827a08330901510a73827b7cb895a2ff95b5017b757b720851081e01330cff00abc86a330c09acca537cb8027cb9037cbc047cb60197880897991097cc18d468027cb6057cb506d4c9097cbc077cb808975508d4650597cc10978818d4c80895aaf795bb09d42909d45808978820d498087b7b7b7a0828c00033070182103082152882162095113832006c8957090a3306491118286584984052080833060128438498205208083306022838849810520808330603282d84980852080833060428228498045208083306052817849802520808330606280c84980152086e33060701491118ae621433070182103082152882162095113832007b170895171864587b191064697b1250100cfb5b8217c96707c865058568078219087b957b97088219189b87ff821810e07807976603cf6708c89808019887208e7782103082152882162095113832003307581b01286e2f951130ff7b10c8007b15c0007b16b8006486647550100ee846976720987720510768021408ffffffff00000000d258087c89957bff9585017b15487b1b5051090201330aff00aba9595317093e027c89027c8a037c8b047c8c0197990897aa1097bb18d4c9097c8c057c8606d4ba0a7c8b077c8508976608d46c0c97bb10975518d45b0b9576f7958809d4a909d4cb0b97b720d497077b18487b1650289d006c987b18387b1b3057080a3306491160285684874052070833060128448487205207083306022839848710520708330603282e84870852070833060428238487045207083306052818848702520708330606280d8487015207160433060701491160ac6b860195176064586469501010a65a821730c96707c865058568077b15487b17509b87ff821860821938e07907976603cf6707c887070198771052074b0195174850101260fd84770152073c0195174850101451fd84770152072d01648595174850101640fd84770152071c01821950978620986620ac690f018388510836017b15330a330b82154849111801646733080040017b1728e6870cc9ba077b1930acc7407b1a20acc98b00c8cb077b1738821718c8b707645664c5646864c9501018f259821930c95909821728c95707c86505821838648b821a205207ba28c000c8cb065706d0027b1c107b1b08520a073307280f8217187b17607b1a70330701017b176895179000951960646850101a06fb821790005207a702821798007b1718821930821b08821c107b1620aec97dff7b15488217307b17508217205107488217185107423305180003955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e582171850101c18529557002046274807013307080101330a401701330b60170133082b951960283e357b15487b1950531920320288a7017b172882152811531920d333084911180149112801017a15447b18387b18b00095174433080c019519b000330aaa00330b901701330c02492180007b1790007b1898007b19a0007b1aa800951790007b1b607b1c687b17707b1c7895174895186050101e0322821550821b5882164833070233083309645a3300200a64015106413306180003956748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e56457501022335195670020462748070133077a0201821a3895a6067b1760491168068217187b17707b1a78570648013307011408daffffff1f000000aea80864672890203305180003955848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e595a72598770583773308501024044e7b152095580020462848075107b633087b1690007b17387b1798004921a000951568018217900082568259f8c98707ac672f821738017b18307b1738c88707649864695010269557821830c868089555107b18a00095178800ab75ca281d951790007b19386469501028b5f9821938821798008218a00028bf821598008217285207476486821820958748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e582171850102a0350821820958700204627480764680164578210c8008215c0008216b8009511d000320033073309701b0128471f821798008218a0003309701b0128371f3307781701283a1f510ab0fd3305180003955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e5286dfd3307581b01285a2a951108fb7b10f0047b15e8047b16e0046485647650102cd4419766209755209866209855207b1658027b1560029517580250102e5ef984770151070628971d6485951758025010304af984770151070628831d64869517580250103236f9847701510706286f1d9788207a1548027a164c0295174c0233090c0195165002330aaa00330bb01701330c03492178027b1720037b19207b1928037b1630037b1a3803951720039888207b1850027b1b58027b1c60027b17680249217002029517f00295185802501034381f8215f802821b00038216f00233070233083309645a3300360a640133081800037b182851063f958748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e56457501038624e821828958700204627480701492170030e951788039518700350103a9c3a82188803140700000000000000807b1818ab780628aa1c82189003821798037b18087b18a0037b17a8039517a00350103c23f8847701821528510706284a1b83874921b0034921b803084921c003330452070628e215978820330759988c201408210b59c84216b2007b1810140a00000000000000807b1a4049214002083308082813821c30821738c97c0c6447520c06285e148219b003e67c05c9490a7b1c307b1538ae5a0628ed135107d99647b8009577b000645628c700821a4002c85a077b7050ff821888047b7858ff82189004821930027b7968ff7b7860ff821818027b7870ff8218e8017b7878ff821808037b7898821800037b78908218f8027b78888218f0027b7880821878047b78b0821870047b78a8821868047b78a082188004821950047b78b87b79c0821858047b78c882186004821908027a79ec7b78d0821800027b78d88218f8017b78e0821810027a78e8821848047b78f8821840047b78f09544019566ff8218e0017b787b14c0039557b80064a8510609ff7b1620027b1728028215a8037b14380252050628a5198217a0037c78955aff9579017b19a0037b1aa803330a017b153002aaa8ba05510806288019551521072874197c98017c9a7c9b027c9c03978808d4a8027c93047c96057c9a067c980797bb1097cc1897660897a410978818d4cb0bd4360ad44808d42b0bd4a808978820d4b8087b1820037c98097c9a087c9b0a7c9c0b978808d4a8027c930c7c960d7c9a0e7c980f97bb1097cc1897660897a410978818d4cb0bd4360ad44808d42b0bd4a808978820d4b8087b1828037c98117c9a107c9b127c9c13978808d4a8027c93147c96157c9a167c981797bb1097cc18d4cb0b97660897ac10978818d4360ad4c808d42b0bd4a808978820d4b8087b1830037c9a187c9b197c9c1a7c961b9558df97bb0897cc10976618d4ab027c9b1c7c951dd46c0c7c961e7c9a1f975508d45b0b9579217b19a0037b18a80397661097aa18d46a0ad42c0cd4ba0a97aa20d4ca0a7b1a3803330a20aea8062846187c9c7c96017c90027c9b037c94047c95057c98067c9a07976608d46c0c7b1c18027c9c087c96097c920a7c930b97001097bb1897550897881097aa18976608d40b0b7b1b1002d4450b7b1b0802d4a8087b180002d46c0c7b1cf8017c980c7c9a0d7c9b0e7c9c0f97221097331897aa0897bb1097cc18d423067b16f001d4a8087b18e8017c98107c9a11d4cb0b7b1be0017c95127c961397aa08d4a8087b18d801975510976618d465057b15d0017c9b157c9a147c96167c981797bb08d4ba0a7b1ac801976610978818d468087b18c0017c9b197c96187c9a1a7c981b97bb08d46b0497aa10978818d4a8037c9a1d7c961c7c981e7c991f97aa08d46a02978810979918d4890982181802821b1002d48b0b8218080282160002d486068218f801821af001d48a0a8218e8018210e001d480088215d801821cd001d45c00821cc8018215c001d4c505d44303d4290c976620d46b0b978820d4a80882193002959abf975520d405069579417b19a0037b1aa80397cc20d43c0c7b1b58027b1860027b1668027b1c7002551a200728b6167c9c7c96017c93027c90037c98047b1808027c95057c9a067c9807976608d46c0c7b1c18027c96087c9c097c920a7c940b97331097001897550897aa1097881897cc08d4300b7b1b1002821b0802d45b0b7b1b0802d4a800d4c6067c930c7c9a0d7c9c0e7c950f972210974b1897aa0897cc10975818d42b05d43a047c9b107c9a11d4c8037c9c127c981397aa08d4ba0297cc10978818d4c80a7c9b157c9c147c98167c991797bb08d4cb0b978810979918d4980882191802821c1002d4c909821c0802d4c00cd46505d44306d42a0ad4b8087c7b597b1b08027c7b5a7b1bf0017c7b5b7b1b00027c7b5c7b1bf80197cc20976620978820d4c909d46505d4a8087b1998047c795d7b19d8017c795e7b19c0017c795f7b19d0017c79607b19c8017b15a0048219300295999f7b18a8049578617b18a0037b19a803551920072874157c7b717c74727c72737c76747c70757c78767c79777c7a787c7c797b1c18027c757a7c737b7c7c7c97440897221097661897880897991097aa18d4b40b7b1b1002d42604d40800d49a067c727d7c7a7e7c797f7c788000975b0897331097cc1897aa0897991097881882151802d4b505d43c0c82133002d42a0ad498087c79617b19b8017c79627b19a0017c79637b19b0017c79647b19a80182191002d49409d4060bd45c0cd4a8087c7a657b1a10027c7a667b1a70017c7a677b1a90017c7a687b1a880197bb20978820d4b909d4c8087b19b0047b18b8047c78697b1868017c786a7b1848017c786b7b1858017c786c7b1850017c786d7b1898017c786e7b1860017c786f7b1880017c78707b18780195387fff957981007b19a0037b18a803520806284d147c7a810095367eff957c82007b1ca0037b16a803510a9a0a522aff00f209551608072828147c7883007c7984007c7a85007c7b820097880897991097aa18d4b8087c7b86007c7c8700d4a9097c7a88007c76890097cc08d4cb0b97aa10976618d46a0a953676ff957c8a00d49808d4ba0a97aa20d48a0a7b1ca0037b16a803282f0a5515050728c9137c78017b1818027c78027b1800027c78037b1810027c78047b1808029558fb9579057b19a0037b18a803551804072898137c78057b18f8017c78067b18d8017c78077b18f0017c78087b18e8019558f79579097b19a0037b18a803551808072867137c78097b18b8017c7a0a7c700b7c7b0c7c780d7b18d0017c780e7b18b0017c780f7b18c8017c78107b18c0019558ef9579117b19a0037b18a803330c8000aec8062823137c78217b1810017c76227c72237c78247c7c257b1c28017c7c267b1c08017c7c277b1c20017c7c287b1c18017c7c297b1c50017c7c2a7b1c30017c7c2b7b1c40017c7c2c7b1c38017c7c2d7b1c68017c7c2e7b1c48017c7c2f7b1c60017c7c307b1c58017c7c317b1c90017c7c327b1c70017c7c337b1c80017c7c347b1c78017c7c357b1ca8017c7c367b1c88017c7c377b1ca0017c7c387b1c9801955c6fff957391007b13a003821330027b1ca803330508ac5c6d12821c000297cc0864b4821b1802d4cb0b7b1b1802821c100297cc10821b080297bb18d4cb0b7b1b1002821bd80197bb08821cf801d4cb0b7b1b0802821bf00197bb10821ce80197cc18d4cb0b7b1b000297aa08821bb801d4ba0a7b1af801970510974418d4540a7b1af001821ab00197aa08821bd001d4ba0a7b1ae801821ac80197aa10821bc00197bb18d4ba0a7b1ad801976608821a1001d46a0a7b1ad001972210978818d428087b18c80182180801978808821a2801d4a8087b18c00182182001978810821a180197aa18d4a8087b18b80182183001978808821a5001d4a8087b18b00182184001978810821a380197aa18d4a8087b18500182184801978808821a6801d4a8087b18680182186001978810821c580197cc18d4c8087b1860018218700197880882159001d458087b1890018218800197881082167801976618d468087b18800182188801978808643b821aa801d4a803821aa00197aa1082189801978818d4a80295ba67ff957499007b14a0037b1aa80382181802821a1002d4a8087b18100282180802821a0002d4a8087b1808028218f801821af001d48a048218e801821ad801d48a0a8218d001821cc801d48c0c8218c001821bb801d4b808821bb00182105001d4b00b8215680182166001d456008215900182168001d46505d4320697aa20978820970020d44a0a7b1a0002d4c8087b183002d4b0087b1818027c7892007c7a93007c7b9400976620d465057b15e8017c7c910097880897aa1097bb18d4c802d4ab037c7896007c7a95007c7b97007c7c9800978808d4a80497bb1097cc18d4bc007c98017c9a7c9b027c9c03978808d4a80697bb1097cc18d4bc057c98057c9a047c9b067c9c07978808d4a8087b18f80197bb1097cc18d4cb0b7b1bf0017c98097c9a087c9b0a7c9c0b978808d4a8087b18d80197bb1097cc18d4bc0c7c980d7c9a0c7c9b0e7c990f978808d4a8087b18d00197bb10979918d4b9097b19c8017c783a7c79397c7a3b7c7b3c978808d498087b18c00197aa1097bb18d4ba0a7b1ab8017c783e7c793d7c7a3f7c7b40978808d498087b18b00197aa1097bb18d4ba0a7b1aa8017c78427c79417c7a437c7b44978808d498087b18a00197aa1097bb18d4ba0a7b1a98017c78467c79457c7a477c7b48978808d498087b18900197aa1097bb18d4ba0a7b1a88017c784a7c79497c7a4b7c7b4c978808d498087b18800197aa1097bb18d4ba0a7b1a78017c784e7c794d7c7a4f7c7b50978808d498087b18700197aa1097bb18d4ba0a7b1a68017c78527c79517c7a537c7b54978808d498087b18600197aa1097bb18d4ba0a7b1a58017c78567c79557c7a577c7b58978808d498087b18500197aa1097bb18d4ba0a7b1a48017c785a7c79597c7a5b7c7b5c978808d498087b18400197aa1097bb18d4ba0a7b1a38017c785e7c795d7c7a5f7c7b60978808d498087b18300197aa1097bb18d4ba0a7b1a28017c78627c79617c7a637c7b64978808d498087b18200197aa1097bb18d4ba0a7b1a18017c78667c79657c7a677c7b68978808d498087b18100197aa1097bb18d4ba0a7b1a08017c786a7c79697c7a6b7c7b6c978808d498087b18000197aa1097bb18d4ba0a7b1af8007c786e7c796d7c7a6f7c7b70978808d498087b18f00097aa1097bb18d4ba0a7b1ae8007c78727c79717c7a737c7b74978808d498087b18e00097aa1097bb18d4ba0a7b1ad8007c78767c79757c7a777c7b78978808d498087b18d00097aa1097bb18d4ba0a7b1ac8007c788a007c7989007c7a8b007c7b8c00978808d498087b18c00097aa1097bb18d4ba0a7b1ab8007c788e007c798d007c7a8f007c7b9000978808d498087b18b00097aa1097bb18d4ba0a7b1aa8007c7882007c7981007c7a83007c7b8400978808d498087b18a00097aa1097bb18d4ba0a7b1a98007c7886007c7985007c7a87007c7b8800978808d498087b18900097aa1097bb18d4ba0a7b1a88007c787a7c79797c7a7b7c7b7c978808d498087b18800097aa1097bb18d4ba0a7b1a787c787e7c797d7c7a7f7c778000978808d498087b187097aa10977718d4a7077b1758d423077b1768d440077b1750d465057b15608218f8018219f001d498087b18488219d801d4c9097b19f0018219d001821ac801d4a909821ac001821bb801d4ba0a7b1ad801821ab001821ba801d4ba0a821ba001821c9801d4cb0b7b1bd001821b9001821c8801d4cb0b821c800182167801d46c0c7b1cc801821c700182166801d46c0c8216600182155801d465057b15c0018216500182154801d465028216400182153801d465057b15b8018216300182152801d465038216200182151801d465057b15b0018216100182150801d46504821500018216f800d465057b15a8018215f0008216e800d465058216e0008217d800d467077b17a0018217d0008216c800d476068217c0008218b800d487077b1798018217b0008218a800d478008217a00082189800d487077b1790018217900082188800d487077b17800182178000821878d487077b178801821770821858d487077b177801821750977720821868d487077b17f801821848978820821760d487077b1770019799208217f001d497077b17f00197aa208219d801d4a9097b19d80197bb20821ad001d4ba0a97cc20821bc801d4cb0b9722208217c001d472029733208217b801d473039744208217b001d474049755208217a801d475059766208217a001d4760697002082179801d4700c8217800197772082189001d487078218780197882082198801d49808821970017b1930048219f0017b1938048219d8017b1910047b1a18047b1b20047b1228047b13f0037b14f8037b1500047b1608047b18d8037b17e0037b1ce80382104028f8026ca87b18300257080b33054921c004285784874052070833050128448487205207083305022839848710520708330503282e84870852070833050428238487045207083305052818848702520708330506280d84870152077f0b330507014921c004ac56de099517c00464c864597b1c180250103efd43821c1802c95606c85c0c8557079b77ff82183002e078078218c0047b1ca0037b16a803975903cf9707c8780a0151069c097cc79566ff95c5017b15a0037b16a8037b1ae0015107209578f95318fa7e0933083305ff140900000000000000807b19300228789517c0049518a003501040bf2a8218c0043305ff14070000000000000080aa7846097b1830027c18ca047c19c9047c17c8047c1acb04978808d498087c19cc047c1bcd047c1cce047c16cf0497aa10979918d4a90997bb2097cc28d4cb0bd498088219d0047b19e801976630d46b0bd4b80897880801d478069517c0049518a003501042462a8214c00497573faa7481087b1618028217f00197770882180802d487077b170802821800029788108219f801979918d498087b1800028219c0019799088217d801d497077b17f801821ad00197aa10821bc80197bb187b14f001d4ab03821ba00197bb088217b801d47b00821cb00197cc108215a801975518d4c5048215700197550882171002d475058217900197771082188801978818d487078218480197880882196801d498088216580197661082195001979918d4690982166001976608821a9801d4a606821a800197aa1082127801972218d4a20a821b0802821c0002d4bc02821bf801d4b303821cf001d4040b64c0d45707d498087b181002d46a0a7b1a08029519c8048298829908973320977720d4230a7b1a0002d4b7077b17f8017b1830047b193804821720038218280382193003821a38037b1710047b1818047b1920047b1a2804821758028218600282196802821a70027b17f0037b18f8037b190004821798048218a0048219a8047b1a08047b17d8038217b004821ab8047b18e0037b19e8037b17c8037b1ad0030182193004821a38048212100482141804821c2004821628048213f0038215f80382170004821808047b1988047b1a90048219d803821ae003821be8037b12f0027b14f8027b1c00037b160803821cc8038216d0037b1368047b1570047b1778047b1880048217b0037b1950047b1a58047b1b60047b1c40047b164804821438028215280282162002aa74062849ec9517b0037b10f0015010446d248210f001821438028217b8037b1740022829ecc84506821710ac67f1076445520907330728129697b8007b1858027b176802330708019669b8007b17600295172003330808951a5802501046b124821720035207db07821728037b16b0037b1740027b17b803645482153828c1eb8218b00314070000000000000080821528aa7886067b18d8018219b8038217187b19e001520734019647b800c87902649a52047301aaa2cc04330cc9a2073303b8007b1a4002330200201408a737bde94d6f7ad3330601987703140b0000000000000080ca8704821828958748278475fcc828087b182002821940027b1b30027b142802280995cc01aa4c7804ca3c07c87908828aaabaf1828718d4b7077b1c3802abb708520a5b28de7b1a10027b181802828720013e040000039764209944208158d448047a543a040000038488ff005208e55010487c38821720024627480782194002821c38023303b800821b30028214280282181802821a1002510a89828708013e040000039764209944208158d448047a543a040000038488ff005208e550104a2e38821720024627480782194002821c38023303b800821b3002821428022843ff4921d8013309088217187b19e0015107d4fe6446955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e582170850104cca3795570020462748078219e0019667b800c87902649a510695fe3308d4007b181802330808017b181002330862017b18080233081818017b1800023308087b18f8013308f808017b18f00133080903017b18e801140b00000000000000803305482764977b1238027b1b300201647c827682770882c31882c92082ca2895c8b8007b184002aab6f60095b801aa864603aab3ad007b13c8017b1620027b1728023307b802013308047b19d00133004e0a04015317f7199577099777023308440001c887078177c887073207821b300282172802821620028219d001821238028218c80151085a82162833080101c856078477fc3e030000033e040800039783209934208173d443047a7464373a030000033a040800038477ff005207d36497501052a736956700204627480782123802821b30028217280282162002015106320282162833080101c856098499fc3e030000033e040800039783209934208193d443047a9464393a030000033a040800038499ff005209d328db01642682cb1095c8307b1758027b1b60027b1368027b1970027b1a780233097095178002501054493d951748027b172003821718027b1728039517e8027b1730038217207b17380395184c027b1840037b1748039517d8027b175003821710027b175803951758027b176003821708027b17680349211003821700027b17f0024921f80205951820037b1800034921080305951768049518f0025010563206821768048218700482197804951a10047b1a6804821af8017b1a7004492140037b1710047b1818047b1920048217f0017b1720034921280301951768047b17300349213803019517f00295182003501058df05821af802821b00038217f0027b172802330702330833097b1a200233005a0a64018217280251075082162833080101c856078477fc3e030000033e040800039783209934208173d443047a7464373a030000033a040800038477ff005207d38217200250105cf7349567002046274807821638020182191804821a20043308068217e80133005e0a04015317f7199577099777023308200001c887078177c8870732078217100464625107558217180482162833090101c856088488fc3e030000033e040800039793209934208183d443047a8464383a030000033a040800038488ff005208d35010627334956700204627480782123802821b30022807821b30020182174002ab27fafc8217d801510741821528955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e58217e0015010641834955700204627480701330733088210f0048215e8048216e0049511f8043200821a4002aba2d9fa289d3307062823330702281e3307281a33070528153307042810330701280b330703280633070701781720033307bc0201330ae01701330b68180133080b9519200328f3163307012823330703281e3307052819330704281433072810330702280b330706280633070701781758023307bc0201330ae01701330b00180133080b9519580228b0161407000000000000008082183002d47808aa7845821528955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e56467502086002233955700204627480782143802280782143802015204698217b00351074982174002510742821528955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e58217400250208800c8329557002046274807013307581401330ab81b01330bd81b013308239519df0428ec15330a330cb8003302002033060114090000000000000080821828958748278475fcc828087b1820027b192802280995aa01aa4a69ffcaca0782184002c87808828baa9bed828718d497077b1a3002ab9708520b5828da7b1b10027b181802828720013e040000039764209944208158d448047a543a040000038488ff005208e550208a002032821720024627480782143802821a3002330cb8008219280282181802821b1002510b88828708013e040000039764209944208158d448047a543a040000038488ff005208e550208c00d531821720024627480782143802821a3002330cb800821928022845ff33073309701b01281f013307080101330a401701330b60170133082b9519df0428dd1482172803821830033309701b0128f6003307081c01280d153307581b01284e0c9511e07b10187b15107b16086485829808647651081682971051074f8297645950208e00ca33289e003307011408e0ffffff1f000000ac584233091800037b19959848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e528433307011408e0ffffff1f000000ae58073307284a33091800037b19959848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e595571f9877058377330850209000062e8219959800204628480701887801330901db77097b687b69087b6510821018821510821608951120320052070764972806648728219511d064783307b018014911207b174911080149111008491118641728e3039511b87b1764173308aa003309d81801491128951b387b17387b18407b1908491110027b1b18491120019517089511b8827820827928827a827b08827c108277187b18207b192864187b1a7b1b087b1c107b17183307f818017b18307b17384711409517303300ea00281d209511b87b10407b15387b1630c88909ac895164758277330808977a01e48908e4a80657063e5107108258087b18187b1728330701017b1720641795191864685020920048fe82175207238217087b567b5708821040821538821630951148320033073309c018012803ff8217088218103309c0180128f5fe9511b07b10487b15407b16386486828b087b1710510b2d3305826795780864b901828a9599ffc8a5059588105209f582681851082a56150f1582770851073c280b8267185107343305013e020000038f5293253a02000003975501015705ce0051051f3307011408e0ffffff1f000000ae5814645728a7fe3305330701285733070128527b1b0833091800037b19959848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e595571f98770583773308502094000a2c82199598002046284807821b085107a27b15187b17204911283308801801951718646950209600a609520725821718821820821928821a107ba77ba8087ba91082104882154082163895115032003307180501330a281901330b48190133085695193728a211330710190128cefd8289088287829c183308cd1301330905320c82785108549511f07b10087b158277083305180003955848278488fc330901013e04000003979420994420818ad44a047a843a0400000384aaff00520ae550209c00fe2d95570020462748078210088215951110320032009511e07b10187b15107b1608649564768277826a10c9a707ac972b7b1a826708c8a70764595020a000e0348217c875057b65103307821018821510821608951120320064677b1864a864595020a200a1fd8218826a1028c59511d07b10287b15207b161864868275108b6c0b552680000a3309012813520c08330902280b8b68108e88958903018278c9580a6458ac9a15827b08330a8000c8b808aea6337886289c007b171082171064587b19087b1c5020a6003cfd821c821908821710827810827b08330a8000c8b808aca6d3520c18986b06846a3f86bbc00086aa8000788b788a0128588b6a10520a25986c0c976a34846b3f86cce00098aa3a86bb800095aa8000788c788a01788b022830986212976a2e976b34846c3f8626f00098aa3a98bb3a86cc800095aa800095bb80007886788a01788b02788c0301c895057b75103307821028821520821618951130320064893308801801289807827728179511e87b177b18084711100164175020ac00981c009511b07b10487b15407b16387b180833035409017b175327e8030001330914491118102714084b598638d6c56d347b181033007b1433047f96980001647a821710d67a07951c37c89c0c98770b821818ca870bbfba05975b3098bb32ca0b0b98b61098bb1196bb648466fe07bfb505c83606975531985530c835057c6b7c66017c587c550178cbe978c6ea78c8eb9592fc78c5ec6429aca4a654170939977830951b24988832c82b0c96887b14988811968964bf9707977731987730c837077c797c77019522fec82b0b78b978c7ff648701821851080651071b97770184771ec837077c77019522ff951824c82808788701951a249a2b14c82a0a33080182170833095020ae001c82104882154082163895115032003302145617098428b59511987b10607b15587b165080741033062b33000000118443000020983c15974528c8cb027b19305705414911287e751494302b7b1b207b1a18ac52688275827608645764686409821a28821b305020b00023025107713307010182106082155882165095116832007b1828330051091ac88909648c017dc695cc018966c0856501c85000ab9cf2c80202330000001133062b7e7514db36007b1b207b1a18ae529e974827570839c9250c97482198883e97442b7b10105a18019d005208db00330a28d800826c186457821818821920821060821558821650951168320c7b1238827910827a8278087b1984990000e09f8699300000207b17087a79107b1a4864a77b18406409821a28821b305020b20064016478330701520843ff3306821738c975076e75016e67ae5739821740827920956601330830821748b49002b4005107e7330701821060821558821650951168320064ca5218024497c830988a31283a821740827a18821748821818821920b4a002b60064783307015208e0fe330782180882197b8910821060821558821650951168320064ca017b1c08330582787b18488277087b174098472b7b17387b1a6ea6016e57ae672b821740827920955501821748821838b49002b8005107e7330701821060821558821650951168320082154864578216406468821910821a28821b305020ba007c647833070152085cfe826a186457821818821920b4a002bc00510714330701821060821558821650951168320033058217088218c987076e76016e57ae672b821740827920955501821748821838b49002be005107e73307018210608215588216509511683200330782106082155882165095116832009511d87b10207b15187b16106486330c0000116475aac92d826c20645764987b1b087b1ab4c002c000821a821b085107143307018210208215188216109511283200510a1a826c18645764a864b9821020821518821610951128320c330782102082151882161095112832009511a07b10587b15507b16487b1928807b1084b9000000187b18105109060397b8235708398212285512208a00821a1033045102ce01c82a09017da895aa018988c0858801c88404ab9af28212287b12287e7914ac94b60128c9027e7816821a285108860164b464733309821610c86a0a330be000330cf0003302281595650101c92607959901c975026456aa986d01aaa65d017d655905e78455ff00acb50bacc50d95650428db95650228d695650328d17b1b7b17088219109595078455f8c89206c95606846a073308aa95147d9b95990189bbc085bb01c8b808ab59f23309510a21846bf8c8b50cc8ca0ac85b0b017dbc95bb0189ccc085cc01c8c909ababf2986603c88904140c0101010101010101140aff00ff00ff00ff007b1a20140901000100010001007b19182839821b3097b703821640c9b60684b003821a20d2a508985908821338c87305d2a907c88707821818ca8707987730c87404821228520065015106ad0164593307c000e676087b16407b15387b18303305531604b198820297220501829b829008829710829a1885b3ff98bb06983607d46b0b8506ff980806986607d468088576ff987706986607d4670785a6ff98aa06986607d46a0ad2cb0bc85b0b9522e0d2c808d2c707d2ca0ac88707c8b707c87a059599205202a7284aff330295842815c998096437644bc9980428096437644b9584017b12287e7914ae941b01330cc9490697b92198993e330a0197b82bad9a0a510914646c280f330a02aba90a976930989c31017b1620330682797b19408277087b173898882b7b18307b1c186ec5016e67ae571c821738827920956601821740821830b49002c2005107e72847821738827a18821740821810821928b4a002c4003308015207323306821720821818c987076e75016e67ae57a700821738827920956601821740821830b49002c6005107e6330801013307018210588215508216489511603200330984b7fc00977703c87303970b030182379533088578ff987706988807d48707d2c70795bbf8c87909520be6821820d28907989908d29808c88707821818ca8707987730c8740482122801821708821b7b12287e7914ac94edfe8278088277828c18821810821928821058821550821648951160320c330782105882155082164895116032009511c064783307190901951a304911207b17304911382b7b1a49110801491110084911186417287cf88279088277829c18320c9511a07b10587b15507b1648829620140a200000e0000000007b17307b18387b1a405106f80082982851087c01330c978b04978a0695673082957b198299107b19189588ffc9ba0a978804c86a0a7b1a089888049588017b18109555080182597b17207b1c28510918821a388217308258f882aa18b4a002ca00520751017e67105107315217013b826718977704821818c887077e78087e67511702225217012c826708977704821918c897077e7908281d7e68127e67521702e63309281033087e67521702da28f47e690201826720816a28977704821b18c87b0b82b782bb087a1a40791844791946951830b4b002cc00821c285207d60095cc01821708821620d367073308309378c88607955510821810abc84bff28810082971851078800330a82967b198295109778049577ff9777049877049577017b1710c858087b1818955710956608017b1a2882697b1720510917821a388217308268f882aa18b4a002ce005207668257825908951830b49002d000821a2852075395aa01821718821520d357078e77977704c85707956610821810aba8b48219821a10829708ac7a0d2839330a829708540732829897a904821730821a38c88909829882990882aa18b4a002d2005107143307018210588215508216489511603200330782105882155082164895116032009511b87b10407b15387b16307b1808817933035409017b19330a0a5329e8037d951c2f49111810274911107b141404000000005917b7d133007f96980001649b979920d6490998992d821718ca7902bf2b05975830988832821710ca78089886109888119688648466fe07bf8505c83606975531985530c835057c687c66017c577c550178c8fd78c6fe78c7ff78c595aafc95ccfcacb0a954190939979730951626987732c8a60c96777b14987711967864bf8909979931989930c839097c987c990195aafec8a606786878c9ff647901821751070651091b97990184991ec839097c970195aaff951826c8a8087887019517269aab0ac87a0a33080182170833095020d600b5f682104082153882163095114832009511c87b10307b15287b16205618271d978a02330b680001c8ba0a81aac8ba0a320a33085c30280501330a5caba80a33085c5c28f800849901510912015428ff020e016475648664875020dc00c4025107080164698663018b6a143308080901976c2c976730976634979b3884990f33027d95100833045cc88a0a98cc3c98773c98663c98bb3cc88909c88c0cc88707c88606c8b8087caa7ccc7c777c6647110846110a781a0b781c0c693c98cc027c887c9978170d78160e78180f95c7fec80c0cc870007804461cff75460c7b78191078121181180881190c7f1a10330b0a7a587a5904795a0878570a785b0b821030821528821620951138320033085c6e282b849900015109f00133085c27281d33085c72281733085c7428119799289899385109d40133085c2201330900027a7847170447170647170879790a8210308215288216209511383200531820970055187f0c28a20164576468018b8910520921330629016e823303061001330a561001330c781101330928330b220128268b8911520906013306e6016e823303f80c01330a500d01330c200e0133092c330bd000017b1664756427648664383300e8009511d87b10207b15187b16107b1c33038216287b1608987008979901c8980c8476ff00283402646864796457520918018683018b8b14330a08090197852c978930978634978c3884880f33027d95101433045cc8ab0b98553c98993c98663c98cc3cc8a808c8a505c8a909c8a606c8ca0a7cbb7c557c997c66471114461116781b1778151869359855027caa7c8878191978161a781a1b9559fec80505c8900078044615ff7546057b78181c78121d811814811a187f1b1c330c0a7a787a7a04797b0878790a787c0b8210308215288216209511383200848afeff1f513a1eb80254ff8489e0ff1f5139e0a60248ff8389c048fd5619f93cff83895031fd5619f132ff838a1014fd330bf0acab25ff83890008fd56295df61cff83890000fd56291dfa11ff8389b0ecfc5619fa06ff838900fff1330aaf22f5ac9af7fe5538f0010ef3fe3309800078797a78048210308215288216209511383200330a113308e8120188792f1801949a97a902c88909819b208a790b8abb0bd8b90b85bb0197bb03d4ba0a97ab02c88b0b81bb108abb0bd8b90b85bb0197bb02d4ba0a97ab02c88b0b81bb088abb0bd8b90b85bb0197bb01d4ba0a97ab02c88b0b81bb048abb0bd8b90b85bb01c8ba0a97ab02c88b0b81bb048abb0bd8b90b85bb01c8ba0a97ab02c88b0b81bb8abb0bd39b0cd89b0988cb01c8a909c89b0b97ba02c88a0a80a8988815561b201880a904989915520b13858affc89a0a520a1c28383309ef020180aafc97aa2b98ab2b858affc89a0a510a22330abfb7079599ff330be6050101c88b0c7cccbeca0aaca709958801ab89f284870132007c847c8901958802c89302ab0421ac328d00ac2b940064a4c8a3030151091a7c359533019599ffab65f52861ac40116423abc8cf28096423644aabc8c6821408821cc8c404330801017dc995ca0157090d64acbf970759071e2821aa4a90007cca0195cc0284997f979908d4a909bf9707570709858801ab4cd28487018210208215188216109511283200330782102082151882161095112832003309b81a0164376428284f642764b89511a07b177b180864173308aa00951908330ae81a014911307b17407b18487b19507b18589517407b1a10491118027b1720491128023308b81a01951710287cf03307d01a0128d2f79511a07b177b180864173308aa00951a08330b081b014911307b17407b18487b1a507b18589517407b1b10491118027b17204911280295171064982836f09511907b177b18087b19107b1a18641233086001951310330ac800330c9019014911409519507b12507b18587b13607b1a687b1c20491128027b19304911380295172064b828eeef9511b0647833077b1401641a330b6001330cf808017b174911081b4911307b1a407b1b489519407b1c10491118017b19204911280195171028b3ef0000951118ff7b10e0007b15d8007b16d0007b197b1a0853280101137d7c0001581cbf1d3302000128237b17107b181849112001491128ae982a280e017d7cff00581cbfda003302ff0001330c120c017b17107b12187b1c2049112805ac98eb00aca8e700ae9a5864133308aa00951908951210330c6001951a203307e019017b1380007b1888007b1990007b1898004911707b12a0007b1ca8007b1ab0007b1cb800951980007b1750491158047b19604911680495175064b828f6ee510912ae890fc8970c7dcc89ccc0dbc90a017b1a30ae8a2601330903e49a029522fd95ac01ac2cc601c92c09c87a0a3303c00151090e7dac9599ff95aaffad3cf5c829095209a600647a017da88487ff005708ab007a173c330a01281e017d7cfe0089ccc085cc0195c2fd00c8270c7dc35813bf6e330c120c017b17107b12187b1c2049112805ae981dffd89807951840330caa0095161033056001db790a9517203309701a017b1880007b1c88007b1690007b1598007b17a0007b15a8004911707b1a40951880007b1950491158037b18604911680395175064b82817ee3309642a3300ec00285efeae894ec8970a7da25812bf4eab8955ff28417cac0184781f84cc3f5427df00447ca20297c306842c3fd4c30c5327f0003b7ca70397883d97cc0698882b84773fd4c707d4870751370000110b2822ab890864b72814f5648a3300ec0028fffd978706d4c707280997870cd4c707017a173c552780000a330a01281698780b520808330a02280b9877108e7a95aa0301c89a0a951230330caa0095143c3308ee009513403307fa00951010330660019515207b19407b1a483309201a017b1280007b1c88007b1490007b1898004911707b13a0007b17a8007b10b0007b16b8007b15c0007b16c800951780007b1950491158057b17604911680595175064b8280bed3309a01a01642764c8288efc9511c87b10307b15287b16208285088289825a2064763308277b190864977b1ab4a002f00051071433070182103082152882162095113832008168951714330901019516145020f2004af77c1714522780002881181882150864578219b49002f40051074133070182103082152882162095113832007c181e7c191f825a18c98909c868088215086457b4a002f600510714330701821030821528821620951138320033082764578219b49002f8008210308215288216209511383200951160ff7b1098007b1590007b16880064858088109789267b1757092d97882557088600827764585020fc002cec5107d7003307018210980082159000821688009511a0003200827a33097f95170801649898ab04c8790984aa0f88ac0a86a63095aa57dbc60a789a9589ff64ba520be2958801c89707957a019a8b8100330839170133090264575020fe00ebec5107773307018210980082159000821688009511a0003200827a33097f95170801649898ab04c8790984aa0f88ac0a86a63095aa37dbc60a789a9589ff64ba520be2958801c89707957a019a8b810033083917013309026457502000018cec5107183307018210980082159000821688009511a00032008258088257828a183308d50801330902b4a00202015107183307018210980082159000821688009511a000320080571097782657082897772557076382178277086458502004010ceb8210980082159000821688009511a00032008217827a0833097f95170801649898ab04c8790984aa0f88ac0a86a63095aa57dbc60a789a9589ff64ba520be2958801c89707957a019a8b8100330839170128428217827a0833097f95170801649898ab04c8790984aa0f88ac0a86a63095aa37dbc60a789a9589ff64ba520be2958801c89707957a019a8b81003308391701013309026457502006018deb8210980082159000821688009511a00032009511b07b10487b15407b16387b1808827533035409017b15330a145325e8037c951c37491118102714074b598638d6c56d347b171033007b1433047f96980001645b821710d6750598550b821718ca7502bf2b09979830988832ca08089886109888119688648466fe07bf8909c83606979931989930c839097c687c66017c977c990178c8fd78c6fe78c7ff78c995aafc95ccfcacb4ac54150939975730951b24987732c8ab0c96777b14987711967864bf8505975531985530c835057c587c590195aafec8ab0b78b878c9ff647501821751070651051b97550184551ec853037c370195aaff951824c8a8087887019517249aab14c87a0a330801821708330950200a0174ea8210488215408216389511503200951178ff7b1080006482817a33097f64130164988bab04c8390984aa0f88ac0a86a73095aa57dbc70a789a9589ff64ba520be2958801c83909959a019a8b81003308391701330902642750200e0116ea821080009511880032009511b87b10407b15387b16306475827a33070497a801e478063308b800d68609330752095a9669b8003308f1140bf8ffffffffffff7fac9b46520a073307281382570896a8b8007b17187b1828330708017b17206417330808951a18502010013682175207218217087b567b570882104082153882163095114832003309881b012847e48217088218103309881b012839e49511d87b10207b15187b1610648682a808647551081e82a71051077e82a77b15086495502012011a17645982150828d20051097d3307011408e0ffffff1f000000ac98697b1508330a1800037b1a95a848278488fc013e04000003977420994420818ad44a047a843a0400000384aaff00520ae595971f987705837733086495502014018c116459821a95a8002046284807821508286b5109173307011408e0ffffff1f000000ae980b3307285464672850330a1800037b1a95a848278488fc013e04000003977420994420818ad44a047a843a0400000384aaff00520ae595971f987705837733087b1908502016012011821908821a95a800204628480701887801db77067b587b56087b591082102082151882161095112832009511907b10687b15607b16587b1720828a828908828810330b9ca7874f847701330c5207179ca750b000847701649b648c510708330c649b017b18087b19103306f73307330833097b1a18330018010a01016475ae672257059e01821a1851053b3307011408e0ffffff1f000000ae5832645728d1e25215ff3d01140700000000000000808218207b870182106882156082165895117032003307490101284f3306180003956848278488fc013e040000039774209944208189d449047a843a040000038499ff005209e595571f987705837733080150201a011a109568002046284807821a1851078a7b1701330b9ca8874f848801330c5208149ca850b000848801510835330c821b10013306f73308645933001c010a0101ae67358217207b7582187b78087b75108210688215608216589511703200821b10821c083306f73308645933001e010a0101ac67d15217ff8500140800000000000000808217207b78510528ff3305180003955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e58217502020011112955700204627480782106882156082165895117032003307581c014911487b172849113001491138084911403308681c019517282853e53307581c014911487b172849113001491138084911403308681c019517282832e53307a01b01282be1951178ff7b1080007b15787b167064856476648750202201a9bb84770152070f825c08978720987a20aeac1f140700000000000000807b67821080008215788216709511880032008388510809017b16083308330b825649111801330900407b151001e69a05c9b8077b1c387b1a30ac5745ac5c9100c85b077b1720821718c8b7077b182864686459502024014318821920821828821a30821c38c95c0cc85606c95a0a8215107b567b5c08649b520ab528aa00c85b095709d0007b167b1b285208073307280f8217187b17587b1868330701017b1760951740330801951a5864965020260105fc8217405207a6008217487b17186468821c388216821b28ae5c77ff821608510826ff82171851071fff3305180003955748278477fc330801013e040000039784209944208179d449047a743a040000038499ff005209e5821718502028016910955700204627480728dcfe330949111801281314070000000000000080821608aa78c4fe7b688217187b67087b69108210800082157882167095118800320033073309701b01288fdf8217488218503309701b012881df51092a827b0001330aff0001acba1e7c8cc87b0b78bc827b000195880195bb019599ff7b7b00015209e5330732009511a0fe7b1058017b155001330833097b17330a961401330701330b150a64330501492108013308ab140195170833090f50202c01a16417330830013309f80801492130017b1740017b184801951740017b1910017b1518017b1720017b1528019517089518100133002e0164893308281b012878ea821b0801330701951a08330833090a6400951170ff7b1088007b1580007b16788277828908828582787b188276087b1910829a18330844090133090c64577b1a08b4a002320133080152075495671095681433096001330ad400330b601901330c034911387b16487b19507b17587b1a607b18687b1a709517487b1b187b1c207b17287b1c309519186457821610646850203401e5e951071a330801016487821088008215800082167895119000320033085009013309026457821a08b4a00236013308015207d882188287287b17708287207b17688287187b17608287107b17588287087b175082887b1848951948645764685020380181e982108800821580008216789511900032009511f8481104552880000d951905781804287f8b890b52091c951906988a0684883f86aac00086888000781a0478180528608b8910520929951907988a0c978b3484883f86aae00098bb3a8688800095bb8000781a04781b057818062834951908988a12978b2e978c3484883f86aaf00098bb3a98cc3a8688800095bb800095cc8000781a04781b05781c0678180701827b0001951804330aff0001acba1b7c8cc87b0b78bc827b000195880195bb017b7b0001ab98e83307951108320064893308281b0128a5e8951110ff7b10e8007b15e0007b16d8004921a0009517b8009518a00050203e01d1f98218b80014070000000000000080aa78b4028217c80053170893028479f85119088b0251191086028479fe5119187e029579e653190876029579de5319226e029579bc53191466029579a85319065e02957aa2531a105602957a92531a104e0295778254170746028217c0007c796a7c7a6b7c7b6c7c74187c70197c7c1e7b1c98007c7c1f7b1c90007c7c6d8aaa088abb10d4a9097c7a207b1a80007c7a217b1a787c72427c73437c7a567b1a88007c7a577b1a707c7a5c7b1a487c7a5d7b1a407c757a7c7a7b7b1a387c7a7c7b1a307c7a7d7b1a287c7a82007b1a687c7a83007b1a607c7a84007b1a587c7a85007b1a508aca18d4ba0ad49a0651085c7b157b10087b14107b13187b12203305180003955848278488fc330901013e04000003979420994420818ad44a047a843a0400000384aaff00520ae550204001390c95570020462748078212208213188214108210088215018467015207e900330782150133083c5106220133090810c3690952091701821790008a7708821880008a88108219788a9918821a7097aa08821b9800d4b707d49808d4870782198800d4a9096478510907c39708015208a1008218608a88088219588a9910821a508aaa18821b68d4b808d4a909d49808ae788900821740977708821948d497079678abaa6e88562855557d970808d44808978901c898086e88ab87748217388a77088218308a88108219288a9918d45707d49808d487075227800060390810000333090001ae985c973808d428083d081000038210e8008215e0008216d8009511f0003200330765150133081d283d3307be150133083528333307cb160133083328293307f31501330832281f33079e160133082d28153307671601330837280b3307251601330842017b17b8007b18c0003307ba1401330a201c01330b401c0133081f9519b8002816ee3307581401330ab81b01330bd81b013308239519d70028fded3307f01b01283dee9511907b10687b15607b1658828a08828982758276087b1a2882aa203308227b193064977b1a18b4a002440133080152077f0251062602491108330a3302a149111001000133032233045c7b1564687b16202813330a0101c95808c89a0a8216205108d9013307c885097b194001c875097c9b95bc81ac2c12aa3b0faa4b0c957701ab78ec28b1017d988486ff0057080895950128547c9b0184681f84bb3f330cdf00ae6c307c9c0297bb0684cc3fd4cb0b330cf000acc6289595047c990397883d98882b97bb0684993fd4b909d489062817959502978806d4b806280c95950397880cd48b0601c8a7077b173895174c646882191050204601c2e77c174c51278000167c17567c1857bf78088487ff00521701273302a133032233045c82184082193833078000ac762cff98670b52072a330a022823ff821c08821a38acca91018218c8c808510c21821720ae7c177d875a17bf162879019866108e6a95aa0328f7feab7c6b01510a1b821720ae7a118217c8a7077d775917c00b285301ab7a5001821728827b18c9ca098217307b1b08b4b0024801520727017c174c5227800019811850821730821918b490024a01510724280a017c18567c1957c9890995174cc87808821730821a08b4a0024c015207ed0082184055268000223302a133032233045c8219389597017b170833078000ac7661fe2835ff98670b3302a133032233045c8219385207149597027b170833078000ac763efe2812ff9867108e77957703c897077b170833078000ac7625fe28f9fec88a0a01821908ac9aa500821551091aae6920c895077d77330bbf6498ad7b19289300330828273308520a103306281e6468ab698100510af6ae6a10c8a5077d775817bf6f64a62805ab6a67821728827a18c98609c858088215306457b4a0024e013308015207133308226457821918b490025001647801648782106882156082165895117032003307018210688215608216589511703200330bb01901821782182064c93300ec002898eb82152805648901330bc81901645764683300ec002881eb951168ff7b1090007b1588007b168000827a088279552a03048100510a7b0182877b17288287087b172033075c017b17183307f808017b171033060817017b1608017b1a307b19389517387b17708217187b17783305018217087b17607b15688217107b17407b15489517707b17507b155864969519408217288218205020540140e252071601821a3095aaff956901520ab22803017b1a3082877b17288287087b17203306000233075c017b17183307f808017b171033070817017b17087b196495017b15389517387b17708217187b17788217087b1760491168018217107b1740491148019517707b17504911580195194082172882182050205601c3e15207ad009566ff9555015206ba821720827a183308fe1601330903821728b4a0025801821830821933050152076ec89808958600fe5106617b1830958701fe017b163864769517387b17708217187b17783305018217087b17607b15688217107b17407b15489517707b17507b155895194082172882182050205a0145e152071b3305510616821830d386078e77c86707ab86af28053305016457821090008215880082168000951198003200330701821090008215880082168000951198003200951178ff7b108000648282777c7a33097f641301649898ab0484aa0fc8390988ac0a86a73095aa57dbc70a789a9589ff64ba520be2958801c83909959a019a8b81003308391701330902642750205e0152da82108000951188003200827a827908648764a82816dd9511887b10707b15687b16607b180833098000330a1f647b015109527dbc95bb019599ffadcaf5510cf2951a50330b5201330ca01c013306029515407b1750491158087b1a407b1b4882180882878288087b1c107b16187b15204911280149113095191033006401282be0330a01c8a7097c9951090f95aa0133098000ab9af1280864a9510a6801330033060f957507957a089a93330c120b017b1ce469048455f8140680808080808080809544f1c975082809950001ae903501c8070c7dcc570c42bf080c84cc07520cebae401cc80a0cc80705825582ccd45c0cd26c0c520c09950010ac40eaae907d9a05c87000017d0c570c6f9555ff950001ab53f528ed0084c5ff00821cc85c0c7ccc511c0436511c0313521c0215ff950001ac9086002809ff950c01ae9c03ffc87c0c7cc25125e0003c5225ed004e6c2c581c9f6028eafe950c01ae9ce4fec87c0c7ccc5125f000295225f4005d6ccc581c8f6628cbfe9a5001ac9056ff288300842ce000512ca0002c28b5fe95cc70ff531c304528aafe955c1fff531c0c0e845cfe00522cee009bfe6c2c591cc093fe950002ae908bfec8070c7dcc330bbfafcb3a287cfe95550fff56150275fe6ccc591cc06efe950c02ae9c66fec87c0c7dcc5a1cbf5dfe950003ae9055fec8070c7dcc330bc0afbc49fe950001ac90d3fe7b17407b194895174033096001330a801c01330b02491130951c503306017b17507b195882180882878288087b1a107b1b187b1c207b1628951910502064016cde821070821568821660951178320033086827033307657795881f8488e0ae8714c97807657933075109270352080928200351086b33076577c9870756171f0d9a7920659751070a033307013a0a4827033a0c502703140b0000000000000080d4ba0a140b0000000020000000d4cb0b330c000000043e081826033e0a4827033e0b5027033d0758270348087a8c0448180848180c3d0814260328b502330728b0029511b87b10407b15387b16307b182833061800039568002082880006510889028b781952084201837501647c647a54177e2969588d8918978807c6950b9099ff84ba7fe095098e9cc98a0a95a8ff07958a8004c8ac0c017b152095690020829b38078bc006d00b0584550151051a97090395680020c89808828808069bc9ffd298085208219500019b08ffd2b8085108a0006a8097090395680020c8980882880806016a8b8a0806d4b808978802c88605805a80a804988801ae87ac007b16109569002082920006857cffbe8c0c81964007bf2a038b38059588017b18c878087b1c188ec9be98087b1608ae862733096599978c25c9290998c420ae490bc994096599510962821c1095c900207a9840070180a80881a48439e0c8290a7a5851085748180c8215188216102882008ba006d00b0884880151082197080395690020c898088288080684a93f330affcf9a09d2980852084fff9500019b08ffd2b808520831ff3307018210408215388216309511483200970903821610956c0020c8c909829c08063303fedcb308d2c8087b9808068215185208169562002082283807dc0309d298087b283807018218088219d8890b82102097080197ac2095a2208a790595880198cc207ac47ac804c8920c5105920064b4645854157f2a330700000002e67507697a8da818c6870784777f97aa07c9a7079577ff0795788004958801018388ff975a0197c720978b02c86b0b80b57abc8b830684883f9877207a707a7a047a750848170c5105077a5c0c01973703956b0020c8b707827b08069b8801d4b8087b780806956a002082a738079b3801d487077ba738078215186450644b019567002081780006975705bec707838a00000040821828d2b808aea70c9777209877207a70019727209877205108d8fe3308821040821538821630951148280009647550206601c6fc6479645752096ffd28b0fe9511e87b10107b15087b169577e03304180003977a20954800208289000698aa2080a80481a3bf970a988201541a1fcc00973a05bfa70a97ac2098cc2080c004840b01520bb5009808013309000000025640000000041651002a6489552000010a9589ff2820699b8db618c6690984997f97bb07c9b9099599ff07959980042805330901979b2098bb1ec8b406816b81c380c508abab0d7a6551051648150c284780c90c7a950851053f7a590c28398b9a0697ab0395450020c85b0b82b5080684963f3309fedc6906d265057bb50806520516954c002082cb3807dca909d2b9097bc93807019549002082990006be2802970004bf070701839b00000040972c05be7c0caebccd0097ca2098aa2080a9048498015208bd009890013305000000025649000000043351090e640555290001279509ff0197952098551ec85406816880a508abc83a7a6551054648150cbe2002287f69588d8918c6950984997f978807c989099598ff079589800497952098551ec85406816880a508aac8cc80a90c7a95085105497a590cbe200228448b9a06330c002097a803c8c405c858088285080684963f3309fedc6906d265057b85080652051a954c002082c83807dca909d298087bc83807be20022806be20020151028900642954127f2a330800000002e68208698a8da918c6980884887f97aa07c9a8089588ff079589800495990101839aff97280197752097a902c84909809c7a978ba90684aa3f9855207a537a58047a5c0848150c510c077ac70c01979803954c0020c8c808828c08069baa01d4ca0a7b8a080695440020824838079b9901d498087b48380701972805be8707aeb70c9777209877207a720182101082150882169511183200330801140ae0ffffff1f000000ae9a07330732003303180003953a482784aafc013e0400000397842099442081abd44b047aa43a0400000384bbff00520be59511b07b10487b15407b1638807ae495991f989405834898aa0195a6ffaa86d400ae68789572e0972b2098bb2080ba0483490198aa01aea9bb00979c0195cc01953600207abc04816b0006330c0000004097a605becb0bbe2606bf9a00aeb60f02976520985520805c0484c8015208ff017b1b3098c401330b00000002564c000000044d01510c6201644b552c00013f0164389543ff2856017b13301409ffffffff00000000ab94357b17286487330850206801e0f95107379769259899206475821628646850206a01a303646750206c017afc645782133028189579e0979520985520805a0498a201ae281d8213300195380020462848078210488215408216389511503200821b3095ba002082a30006972c05833a00000040be9c0c7b1a28aeac8897cb2098bb2080b004840a01520a79ff980a017b1a18be2a0aaea86cff64b2834b0197b4057b14087b1a7b1b20d3ba0a642b7b1a10821a3095a20020821a08c89a0a812440077b1a08bf3a0a8ba205821a108eaac8a202ae2428330a65aac93a03972a2598a420ae430cc9340a65aa510a16ff821a3095a800207a824007013308000000028213308214205640000000047601510066015520000166018212189522ff287801643869ba8dac18c6cb0b84bb7f97aa07c9ab0b95baff0795a3800428076438330301973a2098aa1ec8a80b81ba805c08ab6a157abc510c26481c0c6483be0400821b30285f805b0c7abc086483510c4e7acb0cbe0400821b3028498b3606976a03958b0020c8ba0a82a50806843c3f6483330bfedccb0cd25c0c7bac0806520c1d953a002082ac3807dc6b0bd2cb0b7bab3807be0400821b302809be0400821b3001510099007b1b306434979c05c82c03640b54107f2a330800000002e68008698b8dba18c6a80884887f97bb07c9b8089588ff07958b800495bb010183b8ff970a01973c20978b02c84b0b80b57ab38b8606848b3f98cc207ac97aca047ac508481c0c5105077a530c019768036443954c0020c8c808828c08069bbb01d4cb0b7b8b0806954a002082a838079b6b01d4b8087ba83807821b3001c80909979905be2908aeb8e3fd9788209888207a8028d7fd3302282082181801698a8da618c6680884887f97aa07c9a8089588ff07958280040197282098881ec88306816880ba088210bf4003abc80d7a6a510a16481a0c284d80bb0c7aba08510a457aab0c283f8b2a0697a803821c3095cc0020c8c80882860806842b3f330cfedcbc0bd26b0b7b8b0806520b198218309584002082483807dcac0ad2a8087b483807018214209748019588017a580451039500643a54137f2a330800000002e68308698b8dba18c6a80884887f97bb07c9b8089588ff07958a800495aa010183a8ff973201821608976520978a02821b30c8ba0a80ac7aa68b8a06848b3f9855207a547a52047a5c0848150c510c077ac60c0197a80333020020821630c8260cc8c808828c08069bbb01d4cb0b7b8b0806c82606826838079baa01d4a8087b6838072805644301970805be8909821828ae8996fc9799209899207a93821330288bfc9511c07b10387b15307b16286474531910598d4a84a207c82407ae741a642b64466485017c5a95bbff786a956601955501520bf3c82800c92909849bf8840207c8b70a52024caea71364080182867b76957708958808aca7f6c8b008849907c89a0bacba0d281a644ac8940baeb4137c8b9599ff78ab95aa019588015209f36447821038821530821628951140320033034911209a2508951820845c01d428087b1818520c57845602520661845804520871821c20972503957808c920067b1510aea87e8d588488387b1801826308956608821810d08c0c9575088218cf8308d4c808957c107b1c187b7864577b1608643c821818aca8d7284f7c0882161878683303018456025106a5c830087f88821618c836067968953302845804510895c830088188821618c863037a38821c20972503957808c920067b1510aca88864c37b16086475017b1518821610330782180895880849112055120446840502646252054d821518840c01510c11c878067c6c951620d47606786c01821820d0230c8d26846638cf6808d4c8087b58c8b008849907c89a0bacbad8fe28e4fe818c7a1c2033070484050264625105b9c8780c7fcc951520d47505795c957702821518840c01520ca928b4531910608d7a84a207c8270baeb713642c647a0178a895ccff95aa01520cf8c92909849af8c8ba0aaeab22978238140c0001010101000000d6c202972c20d42c0c017bbc95bb08acabfb849907c89a0baeba0e78a89599ff95aa015209f83200647ac8970bacb7ee28f62149524a52210920a1c8909442a404900a4586a41422498522262949929224a9244992644aa1a4244985242149a5104a9252529224a9a44424499224499224499224294992544a494a52929294a424259924494a4a8592a424499229490a8988a8904400294922129224499224499224499294a494a424252949494a522213a9902449922499484824244a482489a24a1493248924a90a4992942491a49264228588489244922409910c49294412228610922421a9286212898890888888244942925469422443520a9142c490249244024809912129854852211129129151924c522149448a1412512425922129854842225a444485448484448864484a21929088880a491211214911921421492222224222121191888810112222551a924886a4142285440c112102481291880849528428491240024848494a22499244944844444444444422914824128948221149241289449228a2888848a22422114924254992244992244924499224499224491249922449922449924492244992244922922449242949922491244992241289449224491289249124912492248924912449922449924424221189484422922449244944121111294922491249922449221249922449922449922449922411892449229148244912894422119148442449922491244992449224912449249124128944244922914824494422914824128944444412111111442222922422114992882489482491482492884822914824119144924422914822224924492412894422914824128944229148241289888848448a4424928844221249241249221289482491482489482422914424129148221289482411894424928814894412111191884844242211894844221291244924121191442449222292242949922449124922499248922449248924492249244912492249924812499244924892249244922491249224892491244924892449224924491249224992481249924492489224924492249124922489249124492489888824924444441249222222892411119144924892249224492449922449229248442422119188444422121189484424221191884444221211894844242211918844442292481249124912894422914822892492482289241289442211111111111111114991a224252949494a52921219111521922412114946494424a1041045844800111111494444922489489289089188482422914424129148241289441289442289442291442291482212899444224a924844444444444444444444444444448c888888888888888888888888888888884822424444922815111911891011115112012422121129a244440049004924111125929244524486a4142221222222229221298548888888884412512443520a918488881411111121122142041045ac24898844444448a4918494222222924c42484a8510294444448c641242522a84484a22222211222222121111111111111121224484888888888888081121224444a4228d249310925221442244c488441a4948294a249310925221444244443122924886a41422112256444411a55429a59411424824a5942aa58c1042220144924886a414220511514c24924886a4142211440c212452440248241125122949244564484a2112444444442443520a91202222228584101289080909499252920a9200128a0c4929444a00a95064484a21925450c424254952aa52481129a908499248244949924a12922492244925499224530a4a4a92544812922425a9b42449222969a890910490524a094586a414224905452449420a9224254952082149484a4825292192212985481091524952252929282949aa0a4a4952125252ca521249494a4149124992885292241251529244121125999424155229513049120a120248044b92244992249224a524494a244924495292d2922449d2244921494a94244908499224494a9504c92429252d49929024499224095152922425a52048524a8212252d499290244945529224942825496a2a49524b92242449524a494289948424492aa52549129224a92429490a2595424992a4a44a924a929224091249428a2c49922492449544542629a24492522a4992949224254996244902082001a424499224499244144952244b9224499224499224499224552a559328499254924c2549d29224092949129292b44892846492a422c9922449922449924c49a424495249520a49a2889494922401248944492a499224c9922449882492244524494a51156592a424494824494a12495449922449a62449424a42922425494a52494952129224a92429495228212104102c4992244912499252929424922492242949694992246992a420494a924848299244124115444948922449922449aa92a4549224459224294992a428228aa224629424495288d4240a21244a442184c4aa22492a4912a92209499224499224495592944a92a44892242549921402211042082111821084104204a2244909a12449922449922449922449922449929424495222499262922425614a92549244942949295552b2a4a44892244992a492a450554a91902449221192909422214992442225252912214992442225852214499248244744941011259124420c492222451209111189888844222549c9449244922949a24524892452842411492492101111914422915291a4889284240949922428a5485232212529651289442249888848444444241229a192242925159224a9448284a44292242549524892a454489222224a4aa282484444254b9224a5248990822422a29225499252924448411211514a484822224a92948288884ab22449524a920895644992a4942411a62022a22449a184840012c192244992249224a5242949244924495292d2922449d2244941921451694992a4942411521051925492488904905422c9948492922485242149524a520a4a9200925064484a2192544145a4248054852243520a91a412a4884949929424292952422a35499568892401a48400d224a9142243520a91a40411498b949094a96852499294a4a20901a408910c4929440a2292a490442424218948484222922a4892248022898a5411992489244905499224a52451524932490a1249299148844886a414220922521440a224914485242189985224895444542129119250848888888844141249352252aa2421252449444892244992a414248b481452425292244952524144a22424254922929224894424294912494424c9484c299248a9908888880822808890101212121212121212224992482449224912499224499248241249929224443224a510092292a48990884424922491245232922449924892282449499224490891244444145248218514524821234208898410120949925492a48444a40449a54c12255392242992924492249224495292924c52828424114992882449a22429929248528a44922444429290244992100949924424499224222949443251925252a58a24452549524852424b9292248552a41a2a12119182284924246492244992244949824892a424110909a94992249248224910499290849424929892244992244912a42425b5888812115144959624494a4912210511954a9224d2942489902492942491449a924845490a25012425529224a92449ca9224518a088924520845122984228d4448442222282452920885440a89944824899024494a9224489242254945924aa4480801248004218450929092240911914845499294884c4492241249924489c492a4241291449292925492a424624a4a92244922912491249a2425914812494412539224494954042949129149924a5292642291444412a911499024691225490a2a92a48444242521499248104821952489a8492a2929494a2291249188244624192491249120904226292949254922929424494a24892489442491328a20254922324952494a924c249288486292a4492901a44264484a219224494a22499244128920492249220902a2a02401241524290505a524493222494a448224491249a2242525914492945492a448c42009882049254922aa494a4a4a4a4a929248a4249188249292919222484992884c92549292245324928848229344522a4b92444c524a4a4a929244224922894862924411a42449442649924a5292642249249148a22689a424498a92544b499224492b499254524a5292549224499224494a93242549a5a45292949424252549529a94245292a4a449494a1229494949495294a496244912409225494a5249 +TRACE [ ecalli] memwrite 0xfeff0000 len=4 <- 0x2b86c101 +TRACE [ ecalli] start pc=0 gas=100000 r00=0xffff0000 r01=0xfefe0000 r07=0xfeff0000 r08=0x4 +TRACE [ ecalli] ecalli=1 pc=17451 gas=99962 r00=0x118 r01=0xfefdf9a8 r05=0x4 r06=0xfffffffffffffff7 +TRACE [ ecalli] setreg r07 <- 0x86 +TRACE [ ecalli] setgas <- 99952 +TRACE [ ecalli] ecalli=1 pc=17651 gas=99709 r00=0x11c r01=0xfefdf9a8 r02=0x327a0 r03=0x25 r05=0x86 r06=0xfffffffffffffff7 r07=0x327a0 r09=0x86 +TRACE [ ecalli] memwrite 0x000327a0 len=134 <- 0x0a00000000000000010000000000000064000000000000000200200000000c000000809698000000000080f0fa020000000000ca9a3b00000000002d3101000000000800100008000300180000000300080006005000040080000500060000fa00008070d20000093d0004000000000c00000204000000c0000080000000000c00000a000000 +TRACE [ ecalli] setreg r07 <- 0x86 +TRACE [ ecalli] setgas <- 99699 +TRACE [ ecalli] ecalli=100 pc=2281 gas=98232 r00=0x36 r01=0xfefdfb08 r02=0x1 r04=0x327d2 r05=0x327a0 r06=0x68 r07=0x2 r10=0x327a0 r11=0x38 r12=0x9e +TRACE [ ecalli] memread 0x000327a0 len=56 -> 0x5468697320697320416363756d756c61746520696e2074686520546573742053657276696365203663316820776974682031206974656d73 +TRACE [ ecalli] setreg r07 <- 0xfffffffffffffffe +TRACE [ ecalli] setgas <- 98222 +TRACE [ ecalli] ecalli=1 pc=17451 gas=98057 r00=0x118 r01=0xfefdfa98 r02=0x2000000 r04=0x32018 r05=0x327a0 r06=0xfffffffffffffff7 r10=0xe +TRACE [ ecalli] setreg r07 <- 0x98 +TRACE [ ecalli] setgas <- 98047 +TRACE [ ecalli] ecalli=1 pc=17651 gas=97768 r00=0x11c r01=0xfefdfa98 r03=0x25 r04=0x1 r05=0x98 r06=0xfffffffffffffff7 r07=0x327a0 r09=0x98 r10=0xe +TRACE [ ecalli] memwrite 0x000327a0 len=152 <- 0x01002a762f984bcb960c32e78fb31b648c3bab5b8ebc0ac2b945f936d30cdc4bec9c51f9df1925c1adae33dc75126996fe1200fe80fcc27e70c177fa259081296236bf52de5ab6e18f3a407ae0952c67b624a56a6eafd7c40feb2ade331a18b4c929038a8a7fea081877c200733e9aad0bdf3c1f43ea76cf5c3235d70dce8c58a84ac1a0860010efd36b4ce7a69729081481c50119dbe400 +TRACE [ ecalli] setreg r07 <- 0x98 +TRACE [ ecalli] setgas <- 97758 +TRACE [ ecalli] ecalli=4 pc=8301 gas=96005 r00=0x4e r01=0xfefdfb08 r02=0x32918 r03=0x10 r04=0x32018 r05=0x2748 r07=0x102b8 r08=0x4 r09=0x32940 r10=0x10 r11=0x8000000000000000 r12=0x32860 +TRACE [ ecalli] memread 0x000102b8 len=4 -> 0x6c617374 +TRACE [ ecalli] memread 0x00032940 len=16 -> 0xefd36b4ce7a69729081481c50119dbe4 +TRACE [ ecalli] setreg r07 <- 0xffffffffffffffff +TRACE [ ecalli] setgas <- 95995 +TRACE [ ecalli] HALT pc=9102 gas=95636 r00=0xffff0000 r01=0xfefe0000 r02=0x2000000 r04=0x32018 r09=0x2000000000 r10=0x8000000000000000 r11=0x40032780 +# Subtest: accumulate tests [1/1] + # Subtest: [ananas] stf/accumulate/tiny/process_one_immediate_report-1.json + ok 1 - [ananas] stf/accumulate/tiny/process_one_immediate_report-1.json + --- + duration_ms: 26.150416 + type: 'test' + ... + 1..1 +ok 1 - accumulate tests [1/1] + --- + duration_ms: 26.7135 + type: 'suite' + ... +1..1 +# tests 1 +# suites 1 +# pass 1 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 29.853542 diff --git a/tests/trace-loading.spec.ts b/tests/trace-loading.spec.ts new file mode 100644 index 00000000..3a448437 --- /dev/null +++ b/tests/trace-loading.spec.ts @@ -0,0 +1,107 @@ +import { test, expect, Page } from "@playwright/test"; +import { FIXTURES, getFixturePath } from "./utils/fixtures"; + +/** + * Upload a file using the file chooser dialog. + * react-dropzone's hidden input doesn't respond to Playwright's setInputFiles, + * so we click the "Upload file" button and use the file chooser event instead. + */ +async function uploadFile(page: Page, filePath: string) { + const uploadButton = page.getByRole("button", { name: "Upload file" }); + await expect(uploadButton).toBeVisible({ timeout: 5000 }); + + const [fileChooser] = await Promise.all([page.waitForEvent("filechooser"), uploadButton.click()]); + await fileChooser.setFiles(filePath); +} + +test.describe("Trace file loading", () => { + test("should load io-trace-output.log without crashing", async ({ page }) => { + await page.goto("/", { waitUntil: "domcontentloaded" }); + await page.evaluate(() => window.localStorage.clear()); + + const tracePath = getFixturePath(FIXTURES.IO_TRACE_OUTPUT); + await uploadFile(page, tracePath); + + // Wait for load button to be ready instead of fixed timeout + const loadButton = page.getByTestId("load-button"); + await expect(loadButton).toBeVisible({ timeout: 5000 }); + await expect(loadButton).toBeEnabled({ timeout: 5000 }); + + await loadButton.click(); + + // Wait for program status or error boundary with race condition + const programStatus = page.getByTestId("program-status"); + const errorBoundary = page.locator("text=Something went wrong"); + + // Wait for either the program to load or an error to appear + await Promise.race([ + expect(programStatus).toBeVisible({ timeout: 10000 }), + expect(errorBoundary).toBeVisible({ timeout: 10000 }), + ]); + + // Check if error boundary appeared + const hasError = await errorBoundary.isVisible().catch(() => false); + if (hasError) { + const errorMessage = await page.locator("pre").first().textContent(); + console.error("Error boundary caught after load:", errorMessage); + throw new Error(`App crashed after load with error: ${errorMessage}`); + } + + // Verify program status is visible + await expect(programStatus).toBeVisible(); + }); + + test("should run io-trace-output.log program without crashing", async ({ page }) => { + await page.goto("/", { waitUntil: "domcontentloaded" }); + await page.evaluate(() => window.localStorage.clear()); + + const tracePath = getFixturePath(FIXTURES.IO_TRACE_OUTPUT); + await uploadFile(page, tracePath); + + // Wait for load button to be ready + const loadButton = page.getByTestId("load-button"); + await expect(loadButton).toBeVisible({ timeout: 5000 }); + await expect(loadButton).toBeEnabled({ timeout: 5000 }); + await loadButton.click(); + + // Wait for program status to appear + const programStatus = page.getByTestId("program-status"); + await expect(programStatus).toBeVisible({ timeout: 10000 }); + + // Now click Run + const runButton = page.getByRole("button", { name: "Run" }); + await expect(runButton).toBeVisible({ timeout: 5000 }); + await runButton.click(); + + // Wait for either host call dialog, program status, or error boundary + const hostCallDialog = page.locator('[role="dialog"]'); + const errorBoundary = page.locator("text=Something went wrong"); + + await Promise.race([ + expect(hostCallDialog).toBeVisible({ timeout: 10000 }), + expect(errorBoundary).toBeVisible({ timeout: 10000 }), + expect(programStatus).toBeVisible({ timeout: 10000 }), + ]); + + // Check for errors + const hasError = await errorBoundary.isVisible().catch(() => false); + if (hasError) { + const errorMessage = await page.locator("pre").first().textContent(); + const stackTrace = await page + .locator("pre") + .nth(1) + .textContent() + .catch(() => ""); + console.error("Error boundary caught after Run:", errorMessage); + console.error("Stack trace:", stackTrace); + throw new Error(`App crashed after Run with error: ${errorMessage}`); + } + + // Program should either be showing host call dialog or program status + const isDialogVisible = await hostCallDialog.isVisible().catch(() => false); + if (!isDialogVisible) { + // If no dialog, program should have finished or be in some valid state + await expect(programStatus).toBeVisible(); + } + }); +}); diff --git a/tests/utils/actions.ts b/tests/utils/actions.ts index 4e5be351..0db99574 100644 --- a/tests/utils/actions.ts +++ b/tests/utils/actions.ts @@ -3,6 +3,7 @@ import { Page } from "@playwright/test"; export const openDebugger = async (page: Page) => { await page.goto("/", { waitUntil: "domcontentloaded" }); await page.evaluate(() => window.localStorage.clear()); + await page.reload(); }; export const openProgram = async (page: Page, name: string) => { @@ -22,21 +23,26 @@ export const selectPVM = async (page: Page, pvmType: string) => { await page.waitForSelector('[role="dialog"]'); // Locate all options in the multi-select - const allPvmOptions = await page.locator('div[role="option"]'); - - // Check for selected options - const selectedPvmOptions = allPvmOptions.locator(".bg-brand"); // Adjust the class name as needed - const selectedPvmCount = await selectedPvmOptions.count(); - - for (let i = 0; i < selectedPvmCount; i++) { - const pvm = selectedPvmOptions.nth(i); - await pvm.click(); // Click to unselect the checkbox + const allPvmOptions = page.locator('div[role="option"]'); + const count = await allPvmOptions.count(); + + for (let i = 0; i < count; i++) { + const option = allPvmOptions.nth(i); + const text = await option.textContent(); + const isTarget = text?.match(new RegExp(pvmType, "i")); + const isSelected = (await option.locator(".bg-brand").count()) > 0; + + if (isTarget) { + if (!isSelected) { + await option.click(); + } + } else { + if (isSelected) { + await option.click(); + } + } } - const pvmOption = page.locator('div[role="option"]', { hasText: new RegExp(pvmType, "i") }); - await pvmOption.waitFor({ state: "visible" }); - await pvmOption.click(); - await page.waitForTimeout(1000); await page.locator("html").click(); }; diff --git a/tests/utils/fixtures.ts b/tests/utils/fixtures.ts new file mode 100644 index 00000000..a35b332c --- /dev/null +++ b/tests/utils/fixtures.ts @@ -0,0 +1,63 @@ +import * as path from "path"; +import * as fs from "fs"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Path to the test fixtures directory + */ +export const FIXTURES_DIR = path.join(__dirname, ".."); + +/** + * Get the path to a fixture file + * @param fileName The name of the fixture file + * @returns The full path to the fixture file + */ +export function getFixturePath(fileName: string): string { + return path.join(FIXTURES_DIR, fileName); +} + +/** + * Check if a fixture file exists + * @param fileName The name of the fixture file + * @returns True if the fixture exists, false otherwise + */ +export function fixtureExists(fileName: string): boolean { + const filePath = getFixturePath(fileName); + return fs.existsSync(filePath); +} + +/** + * Read a fixture file as a string + * @param fileName The name of the fixture file + * @returns The file contents as a string + * @throws Error if the fixture does not exist + */ +export function readFixture(fileName: string): string { + const filePath = getFixturePath(fileName); + if (!fs.existsSync(filePath)) { + throw new Error(`Fixture not found: ${fileName} at ${filePath}`); + } + return fs.readFileSync(filePath, "utf-8"); +} + +/** + * Read a fixture file as a Uint8Array + * @param fileName The name of the fixture file + * @returns The file contents as a Uint8Array + * @throws Error if the fixture does not exist + */ +export function readFixtureBytes(fileName: string): Uint8Array { + const filePath = getFixturePath(fileName); + if (!fs.existsSync(filePath)) { + throw new Error(`Fixture not found: ${fileName} at ${filePath}`); + } + return new Uint8Array(fs.readFileSync(filePath)); +} + +// Common fixture file names +export const FIXTURES = { + IO_TRACE_OUTPUT: "io-trace-output.log", +} as const;