diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 2d81f4ecb..b483725d2 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -12,8 +12,11 @@ For more details go to [emsesp.org](https://emsesp.org/). ## Fixed - signed value for solarInfuence [#3077](https://github.com/emsesp/EMS-ESP32/issues/3077) +- TLS support with 4MB boards without PSRAM ## Changed - various memory optimizations [#3083](https://github.com/emsesp/EMS-ESP32/issues/3083) +- Scheduler name is mandatory +- Scheduler with type "Immediate", click Execute does not run the command [#3092](https://github.com/emsesp/EMS-ESP32/issues/3092) - network fallback to AP only after start [#3090](https://github.com/emsesp/EMS-ESP32/issues/3090) diff --git a/Makefile b/Makefile index 40ce7f991..01ce71667 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,6 @@ CXX_STANDARD := -std=gnu++20 #---------------------------------------------------------------------- DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500 -DEFINES += -DNO_TLS_SUPPORT DEFINES += $(ARGS) DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\" diff --git a/boards/c3_mini_4M.json b/boards/c3_mini_4M.json index 96b9906df..7f19d1662 100644 --- a/boards/c3_mini_4M.json +++ b/boards/c3_mini_4M.json @@ -5,7 +5,6 @@ }, "core": "esp32", "extra_flags": [ - "-DNO_TLS_SUPPORT", "-DARDUINO_LOLIN_C3_MINI", "-DARDUINO_USB_MODE=1", "-DARDUINO_USB_CDC_ON_BOOT=1" diff --git a/boards/s2_4M_P.json b/boards/s2_4M_P.json index 0df1c4b64..8baa60e01 100644 --- a/boards/s2_4M_P.json +++ b/boards/s2_4M_P.json @@ -6,7 +6,6 @@ "core": "esp32", "extra_flags": [ "-DBOARD_HAS_PSRAM", - "-DNO_TLS_SUPPORT", "-DARDUINO_USB_CDC_ON_BOOT=1", "-DARDUINO_USB_MODE=0" ], diff --git a/boards/s_16M.json b/boards/s_16M.json index b346a31cc..664d2bd61 100644 --- a/boards/s_16M.json +++ b/boards/s_16M.json @@ -1,7 +1,6 @@ { "build": { "core": "esp32", - "extra_flags": "-DNO_TLS_SUPPORT", "f_cpu": "240000000L", "f_flash": "40000000L", "flash_mode": "dio", diff --git a/boards/s_4M.json b/boards/s_4M.json index 8658bd3c9..8978e2625 100644 --- a/boards/s_4M.json +++ b/boards/s_4M.json @@ -1,7 +1,6 @@ { "build": { "core": "esp32", - "extra_flags": "-DNO_TLS_SUPPORT", "f_cpu": "240000000L", "f_flash": "40000000L", "flash_mode": "dio", diff --git a/boards/seeed_xiao_esp32c6.json b/boards/seeed_xiao_esp32c6.json index a3de41d41..224f0e215 100644 --- a/boards/seeed_xiao_esp32c6.json +++ b/boards/seeed_xiao_esp32c6.json @@ -2,7 +2,6 @@ "build": { "core": "esp32", "extra_flags": [ - "-DNO_TLS_SUPPORT", "-DARDUINO_XIAO_ESP32C6", "-DARDUINO_USB_MODE=1", "-DARDUINO_USB_CDC_ON_BOOT=1" diff --git a/interface/package.json b/interface/package.json index 2e96ec718..f4609e506 100644 --- a/interface/package.json +++ b/interface/package.json @@ -38,7 +38,7 @@ "react": "^19.2.6", "react-dom": "^19.2.6", "react-icons": "^5.6.0", - "react-router": "^7.15.1", + "react-router": "^7.16.0", "react-toastify": "^11.1.0", "typesafe-i18n": "^5.27.1", "typescript": "^6.0.3" @@ -50,15 +50,15 @@ "@types/node": "^25.9.1", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", - "concurrently": "^9.2.1", + "concurrently": "^10.0.0", "eslint": "^10.4.0", "eslint-config-prettier": "^10.1.8", "prettier": "^3.8.3", "rollup-plugin-visualizer": "^7.0.1", - "terser": "^5.47.1", - "typescript-eslint": "^8.59.4", - "vite": "^8.0.13", + "terser": "^5.48.0", + "typescript-eslint": "^8.60.0", + "vite": "^8.0.14", "vite-plugin-imagemin": "^0.6.1" }, - "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800" + "packageManager": "pnpm@10.34.1+sha512.b58fbde6dca66a929538021581f648b4570b6ca19b18e7cbd7f2c07a7b24454155388dacdf08f2af3678e88a6d1fe04f9d609df24bf51735a060ea041b374ab7" } diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 5c44848cd..47b2c7e77 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -54,8 +54,8 @@ importers: specifier: ^5.6.0 version: 5.6.0(react@19.2.6) react-router: - specifier: ^7.15.1 - version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^7.16.0 + version: 7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react-toastify: specifier: ^11.1.0 version: 11.1.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -71,7 +71,7 @@ importers: version: 10.0.1(eslint@10.4.0) '@preact/preset-vite': specifier: ^2.10.5 - version: 2.10.5(@babel/core@7.29.0)(preact@10.29.2)(rollup@4.59.0)(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1)) + version: 2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.2 version: 6.0.2(prettier@3.8.3) @@ -85,8 +85,8 @@ importers: specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.15) concurrently: - specifier: ^9.2.1 - version: 9.2.1 + specifier: ^10.0.0 + version: 10.0.0 eslint: specifier: ^10.4.0 version: 10.4.0 @@ -98,19 +98,19 @@ importers: version: 3.8.3 rollup-plugin-visualizer: specifier: ^7.0.1 - version: 7.0.1(rolldown@1.0.1)(rollup@4.59.0) + version: 7.0.1(rolldown@1.0.2) terser: - specifier: ^5.47.1 - version: 5.47.1 + specifier: ^5.48.0 + version: 5.48.0 typescript-eslint: - specifier: ^8.59.4 - version: 8.59.4(eslint@10.4.0)(typescript@6.0.3) + specifier: ^8.60.0 + version: 8.60.0(eslint@10.4.0)(typescript@6.0.3) vite: - specifier: ^8.0.13 - version: 8.0.13(@types/node@25.9.1)(terser@5.47.1) + specifier: ^8.0.14 + version: 8.0.14(@types/node@25.9.1)(terser@5.48.0) vite-plugin-imagemin: specifier: ^0.6.1 - version: 0.6.1(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1)) + version: 0.6.1(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)) packages: @@ -122,101 +122,101 @@ packages: '@alova/shared@1.3.2': resolution: {integrity: sha512-1XvDLWgYpVZ99MmLl1f3Fw4T6S6pPYk5afz5cwRVjuq8JXEGsDn9IygDKfvRyWqkqCBx7Jif07LIct1O+MVEow==} - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.3': - resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.3': - resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-development@7.27.1': - resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + '@babel/plugin-transform-react-jsx-development@7.29.7': + resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.28.6': - resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + '@babel/plugin-transform-react-jsx@7.29.7': + resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@emnapi/core@1.10.0': @@ -475,8 +475,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-project/types@0.130.0': - resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.132.0': + resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -504,97 +504,97 @@ packages: preact: ^10.4.0 || ^11.0.0-0 vite: '>=2.0.0' - '@rolldown/binding-android-arm64@1.0.1': - resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} + '@rolldown/binding-android-arm64@1.0.2': + resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.1': - resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} + '@rolldown/binding-darwin-arm64@1.0.2': + resolution: {integrity: sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.1': - resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} + '@rolldown/binding-darwin-x64@1.0.2': + resolution: {integrity: sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.1': - resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} + '@rolldown/binding-freebsd-x64@1.0.2': + resolution: {integrity: sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': - resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + resolution: {integrity: sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.1': - resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} + '@rolldown/binding-linux-arm64-gnu@1.0.2': + resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.1': - resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} + '@rolldown/binding-linux-arm64-musl@1.0.2': + resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.1': - resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.1': - resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.2': + resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.1': - resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} + '@rolldown/binding-linux-x64-gnu@1.0.2': + resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.1': - resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} + '@rolldown/binding-linux-x64-musl@1.0.2': + resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.1': - resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} + '@rolldown/binding-openharmony-arm64@1.0.2': + resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.1': - resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} + '@rolldown/binding-wasm32-wasi@1.0.2': + resolution: {integrity: sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.1': - resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} + '@rolldown/binding-win32-arm64-msvc@1.0.2': + resolution: {integrity: sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.1': - resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} + '@rolldown/binding-win32-x64-msvc@1.0.2': + resolution: {integrity: sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -615,144 +615,6 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} - cpu: [x64] - os: [win32] - '@sindresorhus/is@0.7.0': resolution: {integrity: sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==} engines: {node: '>=4'} @@ -789,9 +651,6 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -857,63 +716,63 @@ packages: '@types/svgo@2.6.4': resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} - '@typescript-eslint/eslint-plugin@8.59.4': - resolution: {integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==} + '@typescript-eslint/eslint-plugin@8.60.0': + resolution: {integrity: sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.4 + '@typescript-eslint/parser': ^8.60.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.59.4': - resolution: {integrity: sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==} + '@typescript-eslint/parser@8.60.0': + resolution: {integrity: sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.4': - resolution: {integrity: sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==} + '@typescript-eslint/project-service@8.60.0': + resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.59.4': - resolution: {integrity: sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==} + '@typescript-eslint/scope-manager@8.60.0': + resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.59.4': - resolution: {integrity: sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==} + '@typescript-eslint/tsconfig-utils@8.60.0': + resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.59.4': - resolution: {integrity: sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==} + '@typescript-eslint/type-utils@8.60.0': + resolution: {integrity: sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.59.4': - resolution: {integrity: sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==} + '@typescript-eslint/types@8.60.0': + resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.59.4': - resolution: {integrity: sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==} + '@typescript-eslint/typescript-estree@8.60.0': + resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.4': - resolution: {integrity: sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==} + '@typescript-eslint/utils@8.60.0': + resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.59.4': - resolution: {integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==} + '@typescript-eslint/visitor-keys@8.60.0': + resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -937,10 +796,6 @@ packages: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} engines: {node: '>=0.10.0'} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -998,8 +853,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.31: - resolution: {integrity: sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==} + baseline-browser-mapping@2.10.32: + resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} engines: {node: '>=6.0.0'} hasBin: true @@ -1029,11 +884,11 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} @@ -1112,9 +967,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} cliui@9.0.1: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} @@ -1148,9 +1003,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@9.2.1: - resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} - engines: {node: '>=18'} + concurrently@10.0.0: + resolution: {integrity: sha512-DRrk10z3sVPpguNe8od2cGNqZGqbT15rwAnxD4dG3b78mdNNb/gJyr8T834Oj518WcBmTktrt4FhdwZn09ZWSg==} + engines: {node: '>=22'} hasBin: true config-chain@1.1.13: @@ -1330,15 +1185,12 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.360: - resolution: {integrity: sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==} + electron-to-chromium@1.5.363: + resolution: {integrity: sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -1360,8 +1212,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} esbuild-android-64@0.14.54: @@ -1824,8 +1676,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.3: - resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} he@1.2.0: @@ -1949,10 +1801,6 @@ packages: resolution: {integrity: sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-gif@3.0.0: resolution: {integrity: sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw==} engines: {node: '>=6'} @@ -2312,8 +2160,9 @@ packages: node-html-parser@6.1.13: resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} - node-releases@2.0.44: - resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -2589,8 +2438,8 @@ packages: react-is@19.2.6: resolution: {integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==} - react-router@7.15.1: - resolution: {integrity: sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==} + react-router@7.16.0: + resolution: {integrity: sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -2651,10 +2500,6 @@ packages: resolution: {integrity: sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==} engines: {node: '>= 0.10'} - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2676,8 +2521,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rolldown@1.0.1: - resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} + rolldown@1.0.2: + resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2694,11 +2539,6 @@ packages: rollup: optional: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} @@ -2742,8 +2582,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.0: - resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} engines: {node: '>=10'} hasBin: true @@ -2770,8 +2610,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + shell-quote@1.8.4: + resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} engines: {node: '>= 0.4'} signal-exit@3.0.7: @@ -2843,10 +2683,6 @@ packages: resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} engines: {node: '>=0.10.0'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -2858,10 +2694,6 @@ packages: resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} engines: {node: '>=0.10.0'} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -2896,6 +2728,10 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} engines: {node: '>=0.8.0'} @@ -2904,10 +2740,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2929,8 +2761,8 @@ packages: resolution: {integrity: sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA==} engines: {node: '>=4'} - terser@5.47.1: - resolution: {integrity: sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==} + terser@5.48.0: + resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} engines: {node: '>=10'} hasBin: true @@ -2995,8 +2827,8 @@ packages: peerDependencies: typescript: '>=3.5.1' - typescript-eslint@8.59.4: - resolution: {integrity: sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==} + typescript-eslint@8.60.0: + resolution: {integrity: sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -3059,8 +2891,8 @@ packages: peerDependencies: vite: 5.x || 6.x || 7.x || 8.x - vite@8.0.13: - resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==} + vite@8.0.14: + resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3102,8 +2934,8 @@ packages: yaml: optional: true - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.21: + resolution: {integrity: sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==} engines: {node: '>= 0.4'} which@1.3.1: @@ -3119,10 +2951,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - wrap-ansi@9.0.2: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} @@ -3152,18 +2980,10 @@ packages: resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} engines: {node: '>= 6'} - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - yargs@18.0.0: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} @@ -3187,25 +3007,25 @@ snapshots: '@alova/shared@1.3.2': {} - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.3': {} + '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.0': + '@babel/core@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.3 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -3215,108 +3035,108 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.29.1': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': + '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.29.3 - '@babel/helper-validator-option': 7.27.1 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} - '@babel/helpers@7.29.2': + '@babel/helpers@7.29.7': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/parser@7.29.3': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/runtime@7.29.2': {} + '@babel/runtime@7.29.7': {} - '@babel/template@7.28.6': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.3 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.29.0': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@emnapi/core@1.10.0': dependencies: @@ -3336,8 +3156,8 @@ snapshots: '@emotion/babel-plugin@11.13.5': dependencies: - '@babel/helper-module-imports': 7.28.6 - '@babel/runtime': 7.29.2 + '@babel/helper-module-imports': 7.29.7 + '@babel/runtime': 7.29.7 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -3368,7 +3188,7 @@ snapshots: '@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 @@ -3394,7 +3214,7 @@ snapshots: '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 '@emotion/react': 11.14.0(@types/react@19.2.15)(react@19.2.6) @@ -3498,7 +3318,7 @@ snapshots: '@mui/icons-material@9.0.1(@mui/material@9.0.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@mui/material': 9.0.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 optionalDependencies: @@ -3506,7 +3326,7 @@ snapshots: '@mui/material@9.0.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@mui/core-downloads-tracker': 9.0.1 '@mui/system': 9.0.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6) '@mui/types': 9.0.0(@types/react@19.2.15) @@ -3527,7 +3347,7 @@ snapshots: '@mui/private-theming@9.0.1(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@mui/utils': 9.0.1(@types/react@19.2.15)(react@19.2.6) prop-types: 15.8.1 react: 19.2.6 @@ -3536,7 +3356,7 @@ snapshots: '@mui/styled-engine@9.0.0(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 '@emotion/sheet': 1.4.0 @@ -3549,7 +3369,7 @@ snapshots: '@mui/system@9.0.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@mui/private-theming': 9.0.1(@types/react@19.2.15)(react@19.2.6) '@mui/styled-engine': 9.0.0(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.6))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6) '@mui/types': 9.0.0(@types/react@19.2.15) @@ -3565,13 +3385,13 @@ snapshots: '@mui/types@9.0.0(@types/react@19.2.15)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 optionalDependencies: '@types/react': 19.2.15 '@mui/utils@9.0.1(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@mui/types': 9.0.0(@types/react@19.2.15) '@types/prop-types': 15.7.15 clsx: 2.1.1 @@ -3600,23 +3420,23 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.132.0': {} '@popperjs/core@2.11.8': {} - '@preact/preset-vite@2.10.5(@babel/core@7.29.0)(preact@10.29.2)(rollup@4.59.0)(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1))': + '@preact/preset-vite@2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) - '@prefresh/vite': 2.4.12(preact@10.29.2)(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1)) - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) - babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.0) + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7) + '@prefresh/vite': 2.4.12(preact@10.29.2)(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)) + '@rollup/pluginutils': 5.3.0 + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7) debug: 4.4.3 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.0.13(@types/node@25.9.1)(terser@5.47.1) - vite-prerender-plugin: 0.5.13(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1)) + vite: 8.0.14(@types/node@25.9.1)(terser@5.48.0) + vite-prerender-plugin: 0.5.13(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)) zimmerframe: 1.1.4 transitivePeerDependencies: - preact @@ -3631,65 +3451,65 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@10.29.2)(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1))': + '@prefresh/vite@2.4.12(preact@10.29.2)(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7 '@prefresh/babel-plugin': 0.5.3 '@prefresh/core': 1.5.10(preact@10.29.2) '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.29.2 - vite: 8.0.13(@types/node@25.9.1)(terser@5.47.1) + vite: 8.0.14(@types/node@25.9.1)(terser@5.48.0) transitivePeerDependencies: - supports-color - '@rolldown/binding-android-arm64@1.0.1': + '@rolldown/binding-android-arm64@1.0.2': optional: true - '@rolldown/binding-darwin-arm64@1.0.1': + '@rolldown/binding-darwin-arm64@1.0.2': optional: true - '@rolldown/binding-darwin-x64@1.0.1': + '@rolldown/binding-darwin-x64@1.0.2': optional: true - '@rolldown/binding-freebsd-x64@1.0.1': + '@rolldown/binding-freebsd-x64@1.0.2': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.1': + '@rolldown/binding-linux-arm64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.1': + '@rolldown/binding-linux-arm64-musl@1.0.2': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.1': + '@rolldown/binding-linux-ppc64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.1': + '@rolldown/binding-linux-s390x-gnu@1.0.2': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.1': + '@rolldown/binding-linux-x64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-x64-musl@1.0.1': + '@rolldown/binding-linux-x64-musl@1.0.2': optional: true - '@rolldown/binding-openharmony-arm64@1.0.1': + '@rolldown/binding-openharmony-arm64@1.0.2': optional: true - '@rolldown/binding-wasm32-wasi@1.0.1': + '@rolldown/binding-wasm32-wasi@1.0.2': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.1': + '@rolldown/binding-win32-arm64-msvc@1.0.2': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.1': + '@rolldown/binding-win32-x64-msvc@1.0.2': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -3699,88 +3519,11 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.2 - '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + '@rollup/pluginutils@5.3.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.4 - optionalDependencies: - rollup: 4.59.0 - - '@rollup/rollup-android-arm-eabi@4.59.0': - optional: true - - '@rollup/rollup-android-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-x64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.59.0': - optional: true - - '@rollup/rollup-openbsd-x64@4.59.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.59.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.59.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.59.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.59.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.59.0': - optional: true '@sindresorhus/is@0.7.0': {} @@ -3795,10 +3538,10 @@ snapshots: '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)': dependencies: - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.3 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 javascript-natural-sort: 0.7.1 lodash-es: 4.18.1 minimatch: 9.0.9 @@ -3814,9 +3557,6 @@ snapshots: '@types/esrecurse@4.3.1': {} - '@types/estree@1.0.8': - optional: true - '@types/estree@1.0.9': {} '@types/glob@7.2.0': @@ -3891,14 +3631,14 @@ snapshots: dependencies: '@types/node': 25.9.1 - '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.59.4 - '@typescript-eslint/type-utils': 8.59.4(eslint@10.4.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.4(eslint@10.4.0)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.4 + '@typescript-eslint/parser': 8.60.0(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/type-utils': 8.60.0(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.0(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.60.0 eslint: 10.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -3907,41 +3647,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3)': + '@typescript-eslint/parser@8.60.0(eslint@10.4.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.59.4 - '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.4 + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 eslint: 10.4.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.4(typescript@6.0.3)': + '@typescript-eslint/project-service@8.60.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@6.0.3) - '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@6.0.3) + '@typescript-eslint/types': 8.60.0 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.59.4': + '@typescript-eslint/scope-manager@8.60.0': dependencies: - '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/visitor-keys': 8.59.4 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 - '@typescript-eslint/tsconfig-utils@8.59.4(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.60.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.59.4(eslint@10.4.0)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.60.0(eslint@10.4.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.4(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.0(eslint@10.4.0)(typescript@6.0.3) debug: 4.4.3 eslint: 10.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -3949,37 +3689,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.59.4': {} + '@typescript-eslint/types@8.60.0': {} - '@typescript-eslint/typescript-estree@8.59.4(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.60.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.59.4(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@6.0.3) - '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/visitor-keys': 8.59.4 + '@typescript-eslint/project-service': 8.60.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@6.0.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.0 + semver: 7.8.1 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.4(eslint@10.4.0)(typescript@6.0.3)': + '@typescript-eslint/utils@8.60.0(eslint@10.4.0)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0) - '@typescript-eslint/scope-manager': 8.59.4 - '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) eslint: 10.4.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.59.4': + '@typescript-eslint/visitor-keys@8.60.0': dependencies: - '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/types': 8.60.0 eslint-visitor-keys: 5.0.1 acorn-jsx@5.3.2(acorn@8.16.0): @@ -4002,8 +3742,6 @@ snapshots: ansi-regex@2.1.1: {} - ansi-regex@5.0.1: {} - ansi-regex@6.2.2: {} ansi-styles@2.2.1: {} @@ -4032,13 +3770,13 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 cosmiconfig: 7.1.0 resolve: 1.22.12 - babel-plugin-transform-hook-names@1.0.2(@babel/core@7.29.0): + babel-plugin-transform-hook-names@1.0.2(@babel/core@7.29.7): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7 balanced-match@1.0.2: {} @@ -4046,7 +3784,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.31: {} + baseline-browser-mapping@2.10.32: {} bin-build@3.0.0: dependencies: @@ -4088,12 +3826,12 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.14: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.0: + brace-expansion@2.1.1: dependencies: balanced-match: 1.0.2 @@ -4107,10 +3845,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.31 + baseline-browser-mapping: 2.10.32 caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.360 - node-releases: 2.0.44 + electron-to-chromium: 1.5.363 + node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-alloc-unsafe@1.1.0: {} @@ -4193,11 +3931,7 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + chalk@5.6.2: {} cliui@9.0.1: dependencies: @@ -4225,14 +3959,14 @@ snapshots: concat-map@0.0.1: {} - concurrently@9.2.1: + concurrently@10.0.0: dependencies: - chalk: 4.1.2 + chalk: 5.6.2 rxjs: 7.8.2 - shell-quote: 1.8.3 - supports-color: 8.1.1 + shell-quote: 1.8.4 + supports-color: 10.2.2 tree-kill: 1.2.2 - yargs: 17.7.2 + yargs: 18.0.0 config-chain@1.1.13: dependencies: @@ -4394,7 +4128,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 csstype: 3.2.3 dom-serializer@1.4.1: @@ -4468,12 +4202,10 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.360: {} + electron-to-chromium@1.5.363: {} emoji-regex@10.6.0: {} - emoji-regex@8.0.0: {} - end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -4490,7 +4222,7 @@ snapshots: es-errors@1.3.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -4844,18 +4576,18 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.3 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-proxy@2.1.0: dependencies: @@ -4982,7 +4714,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.3: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -5092,7 +4824,7 @@ snapshots: is-core-module@2.16.2: dependencies: - hasown: 2.0.3 + hasown: 2.0.4 is-cwebp-readable@3.0.0: dependencies: @@ -5104,8 +4836,6 @@ snapshots: is-finite@1.1.0: {} - is-fullwidth-code-point@3.0.0: {} - is-gif@3.0.0: dependencies: file-type: 10.11.0 @@ -5144,7 +4874,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.21 is-utf8@0.2.1: {} @@ -5376,11 +5106,11 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.15 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.1 minimist@1.2.8: {} @@ -5402,7 +5132,7 @@ snapshots: css-select: 5.2.2 he: 1.2.0 - node-releases@2.0.44: {} + node-releases@2.0.46: {} normalize-package-data@2.5.0: dependencies: @@ -5529,7 +5259,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.29.7 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -5648,7 +5378,7 @@ snapshots: react-is@19.2.6: {} - react-router@7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-router@7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: cookie: 1.1.1 react: 19.2.6 @@ -5664,7 +5394,7 @@ snapshots: react-transition-group@4.4.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -5678,7 +5408,7 @@ snapshots: react-window@1.8.11(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 memoize-one: 5.2.1 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) @@ -5717,8 +5447,6 @@ snapshots: replace-ext@1.0.1: {} - require-directory@2.1.1: {} - resolve-from@4.0.0: {} resolve@1.22.12: @@ -5738,68 +5466,35 @@ snapshots: dependencies: glob: 7.2.3 - rolldown@1.0.1: + rolldown@1.0.2: dependencies: - '@oxc-project/types': 0.130.0 + '@oxc-project/types': 0.132.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.1 - '@rolldown/binding-darwin-arm64': 1.0.1 - '@rolldown/binding-darwin-x64': 1.0.1 - '@rolldown/binding-freebsd-x64': 1.0.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 - '@rolldown/binding-linux-arm64-gnu': 1.0.1 - '@rolldown/binding-linux-arm64-musl': 1.0.1 - '@rolldown/binding-linux-ppc64-gnu': 1.0.1 - '@rolldown/binding-linux-s390x-gnu': 1.0.1 - '@rolldown/binding-linux-x64-gnu': 1.0.1 - '@rolldown/binding-linux-x64-musl': 1.0.1 - '@rolldown/binding-openharmony-arm64': 1.0.1 - '@rolldown/binding-wasm32-wasi': 1.0.1 - '@rolldown/binding-win32-arm64-msvc': 1.0.1 - '@rolldown/binding-win32-x64-msvc': 1.0.1 + '@rolldown/binding-android-arm64': 1.0.2 + '@rolldown/binding-darwin-arm64': 1.0.2 + '@rolldown/binding-darwin-x64': 1.0.2 + '@rolldown/binding-freebsd-x64': 1.0.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.2 + '@rolldown/binding-linux-arm64-gnu': 1.0.2 + '@rolldown/binding-linux-arm64-musl': 1.0.2 + '@rolldown/binding-linux-ppc64-gnu': 1.0.2 + '@rolldown/binding-linux-s390x-gnu': 1.0.2 + '@rolldown/binding-linux-x64-gnu': 1.0.2 + '@rolldown/binding-linux-x64-musl': 1.0.2 + '@rolldown/binding-openharmony-arm64': 1.0.2 + '@rolldown/binding-wasm32-wasi': 1.0.2 + '@rolldown/binding-win32-arm64-msvc': 1.0.2 + '@rolldown/binding-win32-x64-msvc': 1.0.2 - rollup-plugin-visualizer@7.0.1(rolldown@1.0.1)(rollup@4.59.0): + rollup-plugin-visualizer@7.0.1(rolldown@1.0.2): dependencies: open: 11.0.0 picomatch: 4.0.4 source-map: 0.7.6 yargs: 18.0.0 optionalDependencies: - rolldown: 1.0.1 - rollup: 4.59.0 - - rollup@4.59.0: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 - optional: true + rolldown: 1.0.2 run-applescript@7.1.0: {} @@ -5833,7 +5528,7 @@ snapshots: semver@6.3.1: {} - semver@7.8.0: {} + semver@7.8.1: {} set-cookie-parser@2.7.2: {} @@ -5858,7 +5553,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.3: {} + shell-quote@1.8.4: {} signal-exit@3.0.7: {} @@ -5919,12 +5614,6 @@ snapshots: strict-uri-encode@1.1.0: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - string-width@7.2.0: dependencies: emoji-regex: 10.6.0 @@ -5939,10 +5628,6 @@ snapshots: dependencies: ansi-regex: 2.1.1 - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -5971,16 +5656,14 @@ snapshots: stylis@4.2.0: {} + supports-color@10.2.2: {} + supports-color@2.0.0: {} supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} svgo@2.8.2: @@ -6010,7 +5693,7 @@ snapshots: temp-dir: 1.0.0 uuid: 3.4.0 - terser@5.47.1: + terser@5.48.0: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.16.0 @@ -6070,12 +5753,12 @@ snapshots: dependencies: typescript: 6.0.3 - typescript-eslint@8.59.4(eslint@10.4.0)(typescript@6.0.3): + typescript-eslint@8.60.0(eslint@10.4.0)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0)(typescript@6.0.3) - '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.4(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.60.0(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.0(eslint@10.4.0)(typescript@6.0.3) eslint: 10.4.0 typescript: 6.0.3 transitivePeerDependencies: @@ -6121,7 +5804,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-imagemin@0.6.1(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1)): + vite-plugin-imagemin@0.6.1(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)): dependencies: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -6146,11 +5829,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 pathe: 0.2.0 - vite: 8.0.13(@types/node@25.9.1)(terser@5.47.1) + vite: 8.0.14(@types/node@25.9.1)(terser@5.48.0) transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.13(vite@8.0.13(@types/node@25.9.1)(terser@5.47.1)): + vite-prerender-plugin@0.5.13(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -6158,21 +5841,21 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0 - vite: 8.0.13(@types/node@25.9.1)(terser@5.47.1) + vite: 8.0.14(@types/node@25.9.1)(terser@5.48.0) - vite@8.0.13(@types/node@25.9.1)(terser@5.47.1): + vite@8.0.14(@types/node@25.9.1)(terser@5.48.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rolldown: 1.0.1 + rolldown: 1.0.2 tinyglobby: 0.2.16 optionalDependencies: '@types/node': 25.9.1 fsevents: 2.3.3 - terser: 5.47.1 + terser: 5.48.0 - which-typed-array@1.1.20: + which-typed-array@1.1.21: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 @@ -6192,12 +5875,6 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 @@ -6221,20 +5898,8 @@ snapshots: yaml@1.10.3: {} - yargs-parser@21.1.1: {} - yargs-parser@22.0.0: {} - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - yargs@18.0.0: dependencies: cliui: 9.0.1 diff --git a/interface/pnpm-workspace.yaml b/interface/pnpm-workspace.yaml deleted file mode 100644 index fbf09cf33..000000000 --- a/interface/pnpm-workspace.yaml +++ /dev/null @@ -1,16 +0,0 @@ -allowBuilds: - cwebp-bin: true - esbuild: true - gifsicle: true - jpegtran-bin: true - mozjpeg: true - optipng-bin: true - pngquant-bin: true -onlyBuiltDependencies: - - cwebp-bin - - esbuild - - gifsicle - - jpegtran-bin - - mozjpeg - - optipng-bin - - pngquant-bin diff --git a/interface/src/app/main/Scheduler.tsx b/interface/src/app/main/Scheduler.tsx index d85183a12..1bb612854 100644 --- a/interface/src/app/main/Scheduler.tsx +++ b/interface/src/app/main/Scheduler.tsx @@ -240,7 +240,7 @@ const Scheduler = () => { .filter((si: ScheduleItem) => !si.deleted) .sort((a: ScheduleItem, b: ScheduleItem) => a.flags - b.flags); - const dayBox = (si: ScheduleItem, flag: number) => { + const dayBox = (si: ScheduleItem, flag: number, isLast = false) => { const dayIndex = Math.log(flag) / LOG_2; const isActive = (si.flags & flag) === flag; @@ -251,7 +251,7 @@ const Scheduler = () => { {dow[dayIndex]} - + {!isLast && } ); }; @@ -297,14 +297,15 @@ const Scheduler = () => { {tableList.map((si: ScheduleItem) => ( editScheduleItem(si)}> - + {si.flags !== ScheduleFlag.SCHEDULE_IMMEDIATE && ( + + )} - {si.flags > SCHEDULE_FLAG_THRESHOLD ? ( scheduleType(si) ) : ( @@ -315,7 +316,7 @@ const Scheduler = () => { {dayBox(si, ScheduleFlag.SCHEDULE_THU)} {dayBox(si, ScheduleFlag.SCHEDULE_FRI)} {dayBox(si, ScheduleFlag.SCHEDULE_SAT)} - {dayBox(si, ScheduleFlag.SCHEDULE_SUN)} + {dayBox(si, ScheduleFlag.SCHEDULE_SUN, true)} )} diff --git a/interface/src/app/main/SchedulerDialog.tsx b/interface/src/app/main/SchedulerDialog.tsx index d68d9b727..c3b52874c 100644 --- a/interface/src/app/main/SchedulerDialog.tsx +++ b/interface/src/app/main/SchedulerDialog.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; import AddIcon from '@mui/icons-material/Add'; import CancelIcon from '@mui/icons-material/Cancel'; @@ -20,7 +21,9 @@ import { Typography } from '@mui/material'; +import { callAction } from '@/api/app'; import { dialogStyle } from 'CustomTheme'; +import { useRequest } from 'alova/client'; import type Schema from 'async-validator'; import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, ValidatedTextField } from 'components'; @@ -128,8 +131,15 @@ const SchedulerDialog = ({ await handleSave(editItem); }; - const saveandactivate = async () => { - await handleSave({ ...editItem, active: true }); + const { send: executeSchedule } = useRequest( + (id: string) => callAction({ action: 'executeSchedule', param: id }), + { immediate: false } + ).onError((error) => { + toast.error(String(error.error?.message || 'An error occurred')); + }); + + const execute = async () => { + await executeSchedule(editItem.name); }; const remove = () => { @@ -351,7 +361,7 @@ const SchedulerDialog = ({ {creating ? LL.ADD(0) : LL.UPDATE()} - {isImmediateSchedule && editItem.cmd !== '' && ( + {isImmediateSchedule && !creating && editItem.cmd !== '' && ( - {!downloadOnly && ( - - )} + ); @@ -430,7 +426,6 @@ const Version = () => { useState(false); const [fetchDevVersion, setFetchDevVersion] = useState(false); - const [downloadOnly, setDownloadOnly] = useState(false); const [showVersionInfo, setShowVersionInfo] = useState(0); // 1 = stable, 2 = dev, 3 = partition const [firmwareSize, setFirmwareSize] = useState(0); @@ -460,16 +455,7 @@ const Version = () => { toast.error(String(error.error?.message || 'An error occurred')); }); - const { - data, - send: loadData, - error - } = useRequest(SystemApi.readSystemStatus).onSuccess((event) => { - const systemData = event.data as VersionData; - if (systemData.arduino_version.startsWith('Tasmota')) { - setDownloadOnly(true); - } - }); + const { data, send: loadData, error } = useRequest(SystemApi.readSystemStatus); const { send: sendUploadURL } = useRequest( (url: string) => callAction({ action: 'uploadURL', param: url }), @@ -842,7 +828,6 @@ const Version = () => { latestVersion={latestVersion} latestDevVersion={latestDevVersion} upgradeImportantMessageType={upgradeImportantMessageType} - downloadOnly={downloadOnly} platform={platform} LL={LL} onClose={closeInstallDialog} diff --git a/lib/espMqttClient/src/Transport/ClientSecureSync.cpp b/lib/espMqttClient/src/Transport/ClientSecureSync.cpp index aa9e30f13..a61090f67 100644 --- a/lib/espMqttClient/src/Transport/ClientSecureSync.cpp +++ b/lib/espMqttClient/src/Transport/ClientSecureSync.cpp @@ -6,8 +6,6 @@ For a copy, see or the LICENSE file. */ -#ifndef NO_TLS_SUPPORT - #include "ClientSecureSync.h" #include #include "../Config.h" @@ -66,5 +64,3 @@ bool ClientSecureSync::disconnected() { } } // namespace espMqttClientInternals - -#endif \ No newline at end of file diff --git a/lib/espMqttClient/src/Transport/ClientSecureSync.h b/lib/espMqttClient/src/Transport/ClientSecureSync.h index 84c51f761..c949eec54 100644 --- a/lib/espMqttClient/src/Transport/ClientSecureSync.h +++ b/lib/espMqttClient/src/Transport/ClientSecureSync.h @@ -8,7 +8,6 @@ the LICENSE file. #pragma once -#ifndef NO_TLS_SUPPORT // #include "esp_tls.h" #include @@ -34,5 +33,3 @@ class ClientSecureSync : public Transport { }; } // namespace espMqttClientInternals - -#endif \ No newline at end of file diff --git a/lib/espMqttClient/src/espMqttClient.cpp b/lib/espMqttClient/src/espMqttClient.cpp index 6649487cc..c2c12118f 100644 --- a/lib/espMqttClient/src/espMqttClient.cpp +++ b/lib/espMqttClient/src/espMqttClient.cpp @@ -34,36 +34,26 @@ espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core) } espMqttClientSecure & espMqttClientSecure::setInsecure() { -#ifndef NO_TLS_SUPPORT _client.client.setInsecure(); -#endif return *this; } espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) { -#ifndef NO_TLS_SUPPORT _client.client.setCACert(rootCA); -#endif return *this; } espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) { -#ifndef NO_TLS_SUPPORT _client.client.setCertificate(clientCa); -#endif return *this; } espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) { -#ifndef NO_TLS_SUPPORT _client.client.setPrivateKey(privateKey); -#endif return *this; } espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) { -#ifndef NO_TLS_SUPPORT -#endif return *this; } diff --git a/lib/espMqttClient/src/espMqttClient.h b/lib/espMqttClient/src/espMqttClient.h index b31a8ea64..41d2c516b 100644 --- a/lib/espMqttClient/src/espMqttClient.h +++ b/lib/espMqttClient/src/espMqttClient.h @@ -65,11 +65,7 @@ class espMqttClientSecure : public MqttClientSetup { espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey); protected: -#ifndef NO_TLS_SUPPORT espMqttClientInternals::ClientSecureSync _client; -#else - espMqttClientInternals::ClientSync _client; -#endif }; #endif diff --git a/lib_standalone/ESP_SSLClient.h b/lib_standalone/ESP_SSLClient.h new file mode 100644 index 000000000..131f10230 --- /dev/null +++ b/lib_standalone/ESP_SSLClient.h @@ -0,0 +1,72 @@ +// standalone stub for ESP_SSLClient (BearSSL) - no-op TLS on host build +// Inherits from Stream so it gets print/println via Print, matching the real API. + +#ifndef ESP_SSLClient_h +#define ESP_SSLClient_h + +#include +#include + +#include "Arduino.h" +#include "Stream.h" +#include "WiFiClient.h" +#include + +class ESP_SSLClient : public Stream { + public: + ESP_SSLClient() = default; + + void setInsecure() { + } + void setCACert(const char *) { + } + void setCertificate(const char *) { + } + void setPrivateKey(const char *) { + } + void setBufferSizes(int, int) { + } + void setSessionTimeout(int) { + } + void setClient(WiFiClient *) { + } + void setClient(WiFiClient *, bool) { + } + + int connect(IPAddress, uint16_t) { + return 0; + } + int connect(const char *, uint16_t) { + return 0; + } + bool connected() { + return false; + } + + // Stream / Print overrides + int available() override { + return 0; + } + int read() override { + return -1; + } + int read(uint8_t *, size_t) { + return 0; + } + int peek() override { + return -1; + } + size_t write(uint8_t) override { + return 0; + } + size_t write(const uint8_t *, size_t) override { + return 0; + } + void flush() override { + } + + void stop() { + } +}; + +#endif // ESP_SSLClient_h diff --git a/lib_standalone/HTTPClient.h b/lib_standalone/HTTPClient.h deleted file mode 100644 index c52df11c6..000000000 --- a/lib_standalone/HTTPClient.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef HTTPClient_H_ -#define HTTPClient_H_ - -#include "WString.h" - -class HTTPClient { - public: - bool begin(String url) { - return true; - }; - void end(void) {}; - int GET() { - return 200; - }; - int POST(String payload) { - return 200; - }; - void addHeader(const String & name, const String & value, bool first = false, bool replace = true) {}; - String getString(void) { - return "Hello, World!"; - }; -}; - -#endif /* HTTPClient_H_ */ diff --git a/lib_standalone/WiFiClient.h b/lib_standalone/WiFiClient.h new file mode 100644 index 000000000..ab43e928e --- /dev/null +++ b/lib_standalone/WiFiClient.h @@ -0,0 +1,49 @@ +// standalone stub for WiFiClient (no-op networking on host build) + +#ifndef WiFiClient_h +#define WiFiClient_h + +#include +#include + +#include "Arduino.h" +#include + +class WiFiClient { + public: + WiFiClient() = default; + + int connect(IPAddress, uint16_t) { + return 0; + } + int connect(const char *, uint16_t) { + return 0; + } + bool connected() { + return false; + } + int available() { + return 0; + } + int read() { + return -1; + } + int read(uint8_t *, size_t) { + return 0; + } + size_t write(uint8_t) { + return 0; + } + size_t write(const uint8_t *, size_t) { + return 0; + } + void stop() { + } + + // ESP32 socket option passthrough (e.g. TCP_NODELAY) + int setSocketOption(int, int, const void *, size_t) { + return 0; + } +}; + +#endif // WiFiClient_h diff --git a/lib_standalone/lwip/sockets.h b/lib_standalone/lwip/sockets.h new file mode 100644 index 000000000..6bae137d2 --- /dev/null +++ b/lib_standalone/lwip/sockets.h @@ -0,0 +1,10 @@ +// standalone stub for +// pulls in POSIX equivalents on the host build for things like IPPROTO_TCP / TCP_NODELAY. + +#ifndef LWIP_SOCKETS_STUB_H_ +#define LWIP_SOCKETS_STUB_H_ + +#include +#include + +#endif // LWIP_SOCKETS_STUB_H_ diff --git a/mock-api/package.json b/mock-api/package.json index e02d47946..a854e8600 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -15,5 +15,5 @@ "itty-router": "^5.0.23", "prettier": "^3.8.3" }, - "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800" + "packageManager": "pnpm@10.34.1+sha512.b58fbde6dca66a929538021581f648b4570b6ca19b18e7cbd7f2c07a7b24454155388dacdf08f2af3678e88a6d1fe04f9d609df24bf51735a060ea041b374ab7" } diff --git a/mock-api/pnpm-lock.yaml b/mock-api/pnpm-lock.yaml index 1ddedb1b1..0f5353a97 100644 --- a/mock-api/pnpm-lock.yaml +++ b/mock-api/pnpm-lock.yaml @@ -26,41 +26,41 @@ importers: packages: - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.3': - resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@jridgewell/gen-mapping@0.3.13': @@ -112,8 +112,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -177,52 +177,52 @@ packages: snapshots: - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/generator@7.29.1': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/parser@7.29.3': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/template@7.28.6': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.3 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.29.0': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -248,10 +248,10 @@ snapshots: '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)': dependencies: - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.3 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 javascript-natural-sort: 0.7.1 lodash-es: 4.18.1 minimatch: 9.0.9 @@ -264,7 +264,7 @@ snapshots: balanced-match@1.0.2: {} - brace-expansion@2.1.0: + brace-expansion@2.1.1: dependencies: balanced-match: 1.0.2 @@ -295,7 +295,7 @@ snapshots: minimatch@9.0.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.1 ms@2.1.3: {} diff --git a/mock-api/restServer.ts b/mock-api/restServer.ts index fb9bed64a..18ace9b04 100644 --- a/mock-api/restServer.ts +++ b/mock-api/restServer.ts @@ -393,6 +393,12 @@ function custom_support() { }; } +// run a schedule +function executeSchedule(name: string) { + console.log('executing schedule', name); + return status(200); +} + // called by Action endpoint upgradeImportantMessages function upgradeImportantMessages(version: string) { // 0 is do nothing @@ -5220,6 +5226,9 @@ router } else if (action === 'upgradeImportantMessages') { // check upgrade important messages return upgradeImportantMessages(content.param); + } else if (action === 'executeSchedule') { + // execute schedule + return executeSchedule(content.param); } } return status(404); // cmd not found diff --git a/platformio.ini b/platformio.ini index 92fcddb7d..e6c7d83f7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -159,6 +159,9 @@ build_flags = extends = espressif32_base_4M board_build.app_partition_name = app0 board = seeed_xiao_esp32c6 +build_flags = + ${common.build_flags} + -DEMSESP_EN_ONLY ; foundation for building and testing natively, standalone without an ESP32 ; use the `standalone` environment instead of `native` for testing @@ -169,7 +172,6 @@ build_flags = build_src_flags = -DEMSESP_STANDALONE -DEMSESP_TEST -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 - -DNO_TLS_SUPPORT -std=gnu++20 -Og -ggdb -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces @@ -208,7 +210,6 @@ build_src_flags = -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_UNITY -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 - -DNO_TLS_SUPPORT -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" -std=gnu++20 -Og -ggdb -Wall -Wextra diff --git a/src/ESP32React/MqttSettingsService.cpp b/src/ESP32React/MqttSettingsService.cpp index c371a5438..244486790 100644 --- a/src/ESP32React/MqttSettingsService.cpp +++ b/src/ESP32React/MqttSettingsService.cpp @@ -41,7 +41,6 @@ void MqttSettingsService::startClient() { delete _mqttClient; _mqttClient = nullptr; } -#ifndef NO_TLS_SUPPORT if (_state.enableTLS) { isSecure = true; if (emsesp::EMSESP::system_.PSram() == 0) { @@ -62,7 +61,6 @@ void MqttSettingsService::startClient() { }); return; } -#endif isSecure = false; if (emsesp::EMSESP::system_.PSram() == 0) { _mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO); @@ -164,7 +162,6 @@ bool MqttSettingsService::configureMqtt() { } _reconfigureMqtt = false; -#ifndef NO_TLS_SUPPORT if (_state.enableTLS) { if (_state.rootCA == "insecure") { #if defined(EMSESP_DEBUG) @@ -189,7 +186,6 @@ bool MqttSettingsService::configureMqtt() { static_cast(_mqttClient)->setWill(will_topic, 1, true, "offline"); // QOS 1, retain return _mqttClient->connect(); } -#endif static_cast(_mqttClient)->setServer(_state.host.c_str(), _state.port); if (_state.username.length() > 0) { static_cast(_mqttClient)->setCredentials(_state.username.c_str(), _state.password.length() > 0 ? _state.password.c_str() : nullptr); @@ -205,10 +201,8 @@ bool MqttSettingsService::configureMqtt() { } void MqttSettings::read(MqttSettings & settings, JsonObject root) { -#ifndef NO_TLS_SUPPORT - root["enableTLS"] = settings.enableTLS; - root["rootCA"] = settings.rootCA; -#endif + root["enableTLS"] = settings.enableTLS; + root["rootCA"] = settings.rootCA; root["enabled"] = settings.enabled; root["host"] = settings.host; root["port"] = settings.port; @@ -244,12 +238,8 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings) MqttSettings newSettings; bool changed = false; -#ifndef NO_TLS_SUPPORT - newSettings.enableTLS = root["enableTLS"]; - newSettings.rootCA = root["rootCA"] | ""; -#else - newSettings.enableTLS = false; -#endif + newSettings.enableTLS = root["enableTLS"]; + newSettings.rootCA = root["rootCA"] | ""; newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; newSettings.host = root["host"] | FACTORY_MQTT_HOST; newSettings.port = static_cast(root["port"] | FACTORY_MQTT_PORT); @@ -375,7 +365,6 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings) emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat); } -#ifndef NO_TLS_SUPPORT // strip down to certificate only newSettings.rootCA.replace("\r", ""); newSettings.rootCA.replace("\n", ""); @@ -388,7 +377,6 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings) if (newSettings.enableTLS != settings.enableTLS || newSettings.rootCA != settings.rootCA) { changed = true; } -#endif // save the new settings settings = newSettings; diff --git a/src/ESP32React/NetworkStatus.cpp b/src/ESP32React/NetworkStatus.cpp index 8f99cfa74..75469892b 100644 --- a/src/ESP32React/NetworkStatus.cpp +++ b/src/ESP32React/NetworkStatus.cpp @@ -2,10 +2,6 @@ #include -#ifdef NO_TLS_SUPPORT -#include "lwip/dns.h" -#endif - NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) { securityManager->addEndpoint(server, NETWORK_STATUS_SERVICE_PATH, AuthenticationPredicates::IS_AUTHENTICATED, [this](AsyncWebServerRequest * request) { networkStatus(request); diff --git a/src/core/emsdevicevalue.cpp b/src/core/emsdevicevalue.cpp index ea5db353e..8db02d339 100644 --- a/src/core/emsdevicevalue.cpp +++ b/src/core/emsdevicevalue.cpp @@ -110,10 +110,12 @@ DeviceValue::DeviceValue(uint8_t device_type, const char * DeviceValue::DeviceValueUOM_s[] = { F_(uom_blank), // 0 - F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], - F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), - F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_mbar), - F_(uom_lh), F_(uom_ctkwh), F_(uom_hz), F_(uom_blank) + F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], F_(uom_ua), + F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm), + F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_mbar), F_(uom_lh), F_(uom_ctkwh), F_(uom_hz), + F_(uom_blank), // connectivity + F_(uom_blank), // timestamp + F_(uom_blank) // blank }; diff --git a/src/core/emsdevicevalue.h b/src/core/emsdevicevalue.h index 20af17955..b7b1a016c 100644 --- a/src/core/emsdevicevalue.h +++ b/src/core/emsdevicevalue.h @@ -77,7 +77,8 @@ class DeviceValue { LH, // 25 - l/h - volume flow rate CTKWH, // 26 - ct/kWh - monetary HERTZ, // 27 - Hz - frequency - CONNECTIVITY // 28 - used in HA - connectivity + CONNECTIVITY, // 28 - used in HA - connectivity + TIMESTAMP, // 29 - used in HA - timestamp }; // TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index f0393e6e0..80aa841b1 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -1757,6 +1757,12 @@ void EMSESP::start() { // start network services. This will initialise WiFi or Ethernet depending on the settings. network_.begin(); + // start the core web services, as this loads the settings from the filesystem + // this will also handle any MQTT subscriptions + webCustomizationService.begin(); // load the customizations + webSchedulerService.begin(); // load the scheduler events + webCustomEntityService.begin(); // load the custom telegram reads + // perform any system upgrades if (!factory_settings) { if (system_.check_upgrade()) { @@ -1771,10 +1777,6 @@ void EMSESP::start() { }; LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), EMSESP_TRANSLATION_COUNT, system_.languages_string().c_str()); - webCustomizationService.begin(); // load the customizations - webSchedulerService.begin(); // load the scheduler events - webCustomEntityService.begin(); // load the custom telegram reads - // start telnet service if it's enabled // default idle is 10 minutes, default write timeout is 0 (automatic) // note, this must be started after the network/wifi for ESP32 otherwise it'll crash diff --git a/src/core/locale_common.h b/src/core/locale_common.h index 79587647e..be2db4821 100644 --- a/src/core/locale_common.h +++ b/src/core/locale_common.h @@ -272,6 +272,8 @@ MAKE_WORD_CUSTOM(uom_l, "l") MAKE_WORD_CUSTOM(uom_kmin, "K*min") MAKE_WORD_CUSTOM(uom_k, "K") MAKE_WORD_CUSTOM(uom_volts, "V") +MAKE_WORD_CUSTOM(uom_connectivity, "connectivity") +MAKE_WORD_CUSTOM(uom_timestamp, "timestamp") MAKE_WORD_CUSTOM(uom_mbar, "mbar") MAKE_WORD_CUSTOM(uom_lh, "l/h") MAKE_WORD_CUSTOM(uom_ctkwh, "ct/kWh") diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index 9477571c3..3ba402013 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -567,8 +567,6 @@ void Mqtt::ha_status() { #endif publish_system_ha_sensor_config(DeviceValueType::STRING, "EMS Bus", "bus_status", DeviceValueUOM::NONE); - publish_system_ha_sensor_config(DeviceValueType::STRING, "Uptime", "uptime", DeviceValueUOM::NONE); - publish_system_ha_sensor_config(DeviceValueType::INT8, "Uptime (sec)", "uptime_sec", DeviceValueUOM::SECONDS); publish_system_ha_sensor_config(DeviceValueType::INT8, "Free memory", "freemem", DeviceValueUOM::KB); publish_system_ha_sensor_config(DeviceValueType::INT8, "Max alloc", "max_alloc", DeviceValueUOM::KB); publish_system_ha_sensor_config(DeviceValueType::INT8, "MQTT fails", "mqttfails", DeviceValueUOM::NONE); @@ -585,8 +583,10 @@ void Mqtt::ha_status() { if (!EMSESP::network_.ethernet_connected()) { publish_system_ha_sensor_config(DeviceValueType::INT16, "WiFi reconnects", "wifireconnects", DeviceValueUOM::NONE); } - // This one comes from the info MQTT topic - and handled in the publish_ha_sensor_config function + + // These come from the info MQTT topic - and handled in the publish_ha_sensor_config function publish_system_ha_sensor_config(DeviceValueType::STRING, "Version", "version", DeviceValueUOM::NONE); + publish_system_ha_sensor_config(DeviceValueType::STRING, "Boot time", "bootTime", DeviceValueUOM::TIMESTAMP); } // add sub or pub task to the queue. @@ -1061,15 +1061,19 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // add state_topic and it's value_template. This is not needed for commands, only sensors if (type != DeviceValueType::CMD || is_sensor) { - // state topic, except for commands - char stat_t[MQTT_TOPIC_MAX_SIZE]; - // This is where we determine which MQTT topic to pull the data from - // There is one exception for DeviceType::SYSTEM, which uses the heartbeat topic, and when fetching the version we want to take this from the info topic instead - if ((device_type == EMSdevice::DeviceType::SYSTEM) && (strncmp(entity, "version", 7) == 0)) { - snprintf(stat_t, sizeof(stat_t), "~/%s", F_(info)); - } else { - snprintf(stat_t, sizeof(stat_t), "~/%s", tag_to_topic(device_type, tag).c_str()); + char stat_t[MQTT_TOPIC_MAX_SIZE]; // state topic, except for commands + snprintf(stat_t, sizeof(stat_t), "~/%s", tag_to_topic(device_type, tag).c_str()); + + // Override - there are exceptions for DeviceType::SYSTEM, which uses the heartbeat topic + // and when fetching the version and bootTime we want to take this from the info topic instead + if (device_type == EMSdevice::DeviceType::SYSTEM) { + // handle the exceptions + if (strncmp(entity, "version", 7) == 0) { + snprintf(stat_t, sizeof(stat_t), "~/%s", F_(info)); + } else if (strncmp(entity, "bootTime", 8) == 0) { + snprintf(stat_t, sizeof(stat_t), "~/%s", F_(info)); + } } doc["stat_t"] = stat_t; @@ -1095,7 +1099,14 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery if (discovery_type() == discoveryType::HOMEASSISTANT) { - doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}"; + if (uom == DeviceValueUOM::TIMESTAMP) { + // special case for timestamp, using "value_template": "{{ (value_json.bootTime | as_datetime).isoformat() }}", + char val_tpl[100]; + snprintf(val_tpl, sizeof(val_tpl), "{{ (value_json.%s | as_datetime).isoformat() }}", entity); + doc["val_tpl"] = val_tpl; + } else { + doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}"; + } add_ha_avty_section(doc.as(), stat_t, val_cond); // adds availability section } else { // Domoticz doesn't support value templates, so we just use the value directly @@ -1166,8 +1177,13 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8 doc[uom_ha] = "L/h"; } else if (uom == DeviceValueUOM::L) { doc[uom_ha] = "L"; + } else if (uom == DeviceValueUOM::TIMESTAMP) { + // do nothing } else if (uom != DeviceValueUOM::NONE) { - doc[uom_ha] = EMSdevice::uom_to_string(uom); // use default + auto uom_str = EMSdevice::uom_to_string(uom); + if (strlen(uom_str)) { + doc[uom_ha] = uom_str; + } } else if (discovery_type() != discoveryType::HOMEASSISTANT) { doc[uom_ha] = " "; // Domoticz uses " " for a no-uom } @@ -1259,6 +1275,10 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8 doc[sc_ha] = sc_ha_measurement; doc[dc_ha] = "connectivity"; break; + case DeviceValueUOM::TIMESTAMP: + doc[sc_ha] = sc_ha_measurement; + doc[dc_ha] = "timestamp"; + break; case DeviceValueUOM::MV: case DeviceValueUOM::VOLTS: doc[sc_ha] = sc_ha_measurement; diff --git a/src/core/shuntingYard.cpp b/src/core/shuntingYard.cpp index da3e6df10..f15c54fc7 100644 --- a/src/core/shuntingYard.cpp +++ b/src/core/shuntingYard.cpp @@ -22,6 +22,9 @@ #include "shuntingYard.h" +#include +#include + namespace emsesp { // find tokens - optimized to reduce string allocations @@ -683,6 +686,94 @@ std::string calculate(const std::string & expr) { return result; } +// perform an HTTP/HTTPS request; returns the HTTP status code (0 on failure or unsupported scheme) +// the response headers are always stripped, so `result` contains only the body +int http_request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result) { + int httpResult = 0; + const bool is_post = value.length() || Helpers::toLower(method) == "post"; + const auto lower_url = Helpers::toLower(url.c_str()); + + const bool is_https = lower_url.starts_with("https://"); + if (!is_https && !lower_url.starts_with("http://")) { + return 0; // unsupported scheme + } + + WiFiClient * basic_client = new WiFiClient; + ESP_SSLClient * ssl_client = new ESP_SSLClient; + if (is_https) { + ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert); + // NOTE: 1 KB RX buffer is fine for small JSON-style endpoints used by the scheduler/shunting-yard, + // but it is NOT enough for servers that send full-size TLS records (>1 KB), e.g. GitHub release + // assets / large CDN responses. Such servers do not negotiate max_fragment_length, so the body + // can't be decoded and reads return 0. If this path is ever used to fetch large or CDN-hosted + // payloads, bump the RX buffer to 16384 (see uploadFirmwareURL in core/system.cpp for reference). + ssl_client->setBufferSizes(1024, 1024); + ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds) + } + ssl_client->setClient(basic_client, is_https); // enableSSL = false for plain HTTP + + url.replace(0, is_https ? 8 : 7, ""); + std::string host = url; + auto index = url.find_first_of('/'); + if (index != std::string::npos) { + host = url.substr(0, index); + url.replace(0, index, ""); + } else { + url = "/"; + } + + const uint16_t port = is_https ? 443 : 80; + if (ssl_client->connect(host.c_str(), port)) { + bool content_set = false; + ssl_client->print(is_post ? "POST " : "GET "); + ssl_client->print(url.c_str()); + ssl_client->println(" HTTP/1.1"); + ssl_client->print("Host: "); + ssl_client->println(host.c_str()); + for (JsonPairConst p : headers) { + content_set |= (Helpers::toLower(p.key().c_str()) == "content-type"); + ssl_client->print(p.key().c_str()); + ssl_client->print(": "); + ssl_client->println(p.value().as().c_str()); + } + if (is_post) { + if (!content_set) { + ssl_client->print("Content-Type: "); + ssl_client->println(value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain); + } + ssl_client->print("Content-Length: "); + ssl_client->println(value.length()); + ssl_client->println("Connection: close"); + ssl_client->print("\r\n"); + ssl_client->print(value.c_str()); + } else { + ssl_client->println("Connection: close"); + } + auto ms = millis(); + while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) { + delay(0); + } + while (ssl_client->available()) { + result += (char)ssl_client->read(); + } + ssl_client->stop(); + index = result.find_first_of(' '); + if (index != std::string::npos) { + httpResult = stoi(result.substr(index + 1, 3)); + } + index = result.find("\r\n\r\n"); + if (index != std::string::npos) { + result.replace(0, index + 4, ""); + } + } else { + EMSESP::logger().warning("%s connection failed", is_https ? "HTTPS" : "HTTP"); + } + delete ssl_client; + delete basic_client; + + return httpResult; +} + // check for multiple instances of ? : std::string compute(const std::string & expr) { std::string expr_new = expr; @@ -723,119 +814,10 @@ std::string compute(const std::string & expr) { keys_s = p.key().c_str(); } } - bool content_set = false; - std::string value = doc[value_s] | ""; - std::string method = doc[method_s] | "GET"; - if (value.length()) { - method = "POST"; - } + std::string value = doc[value_s] | ""; + std::string method = doc[method_s] | "GET"; std::string result; - int httpResult = 0; -#ifndef NO_TLS_SUPPORT - if (Helpers::toLower(url.c_str()).starts_with("https://")) { - WiFiClient * basic_client = new WiFiClient; - ESP_SSLClient * ssl_client = new ESP_SSLClient; - ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert); - ssl_client->setBufferSizes(1024, 1024); - ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds) - url.replace(0, 8, ""); - std::string host = url; - auto index = url.find_first_of('/'); - if (index != std::string::npos) { - host = url.substr(0, index); - url.replace(0, index, ""); - } - /* - index = host.find_first_of('@'); - std::string auth; - if (index != std::string::npos) { - auth = base64::encode(host.substr(0, index)); - host.replace(0, index, ""); - } - */ - ssl_client->setClient(basic_client); - if (ssl_client->connect(host.c_str(), 443)) { - if (value.length() || Helpers::toLower(method) == "post") { - ssl_client->print("POST "); - ssl_client->print(url.c_str()); - ssl_client->println(" HTTP/1.1"); - ssl_client->print("Host: "); - ssl_client->println(host.c_str()); - for (JsonPair p : doc[header_s].as()) { - content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type"); - ssl_client->print(p.key().c_str()); - ssl_client->print(": "); - ssl_client->println(p.value().as().c_str()); - } - if (!content_set) { - ssl_client->print("Content-Type: "); - if (value.starts_with('{')) { - ssl_client->println(asyncsrv::T_application_json); - } else { - ssl_client->println(asyncsrv::T_text_plain); - } - } - ssl_client->print("Content-Length: "); - ssl_client->println(value.length()); - ssl_client->println("Connection: close"); - ssl_client->print("\r\n"); - ssl_client->print(value.c_str()); - } else { - ssl_client->print("GET "); - ssl_client->print(url.c_str()); - ssl_client->println(" HTTP/1.1"); - ssl_client->print("Host: "); - ssl_client->println(host.c_str()); - for (JsonPair p : doc[header_s].as()) { - ssl_client->print(p.key().c_str()); - ssl_client->print(": "); - ssl_client->println(p.value().as().c_str()); - } - ssl_client->println("Connection: close"); - } - auto ms = millis(); - while (!ssl_client->available() && millis() - ms < 3000) { - delay(0); - } - while (ssl_client->available()) { - result += (char)ssl_client->read(); - } - ssl_client->stop(); - index = result.find_first_of(' '); - if (index != std::string::npos) { - httpResult = stoi(result.substr(index + 1, 3)); - } - index = result.find("\r\n\r\n"); - if (index != std::string::npos) { - result.replace(0, index + 4, ""); - } - } - delete ssl_client; - delete basic_client; - } else -#endif - if (Helpers::toLower(url.c_str()).starts_with("http://")) { - HTTPClient * http = new HTTPClient; - if (http->begin(url.c_str())) { - for (JsonPair p : doc[header_s].as()) { - http->addHeader(p.key().c_str(), p.value().as().c_str()); - content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type"); - } - if (value.length() || Helpers::toLower(method) == "post") { - if (!content_set) { - http->addHeader("Content-Type", value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain); - } - httpResult = http->POST(value.c_str()); - } else { - httpResult = http->GET(); // normal GET - } - if (httpResult > 0) { - result = http->getString().c_str(); - } - } - http->end(); - delete http; - } + int httpResult = http_request(url, method, value, doc[header_s].as(), result); if (httpResult == 200) { std::string key = doc[key_s] | ""; JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body diff --git a/src/core/shuntingYard.h b/src/core/shuntingYard.h index ce9f4af74..23e8486ec 100644 --- a/src/core/shuntingYard.h +++ b/src/core/shuntingYard.h @@ -21,7 +21,6 @@ #ifndef EMSESP_SHUNTING_YARD_H_ #define EMSESP_SHUNTING_YARD_H_ -#include #include #include @@ -85,6 +84,8 @@ std::string calculate(const std::string & expr); // check for multiple instances of ? : std::string compute(const std::string & expr); +int http_request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result); + #endif } // namespace emsesp \ No newline at end of file diff --git a/src/core/system.cpp b/src/core/system.cpp index 1fca50f8a..b49b8b8fd 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -29,7 +29,6 @@ #include #endif -#include #include #include "firmwareVersion.h" @@ -38,7 +37,7 @@ #include "../test/test.h" #endif -#ifndef NO_TLS_SUPPORT +#ifndef EMSESP_STANDALONE #define ENABLE_SMTP #define USE_ESP_SSLCLIENT #define READYCLIENT_SSL_CLIENT ESP_SSLClient @@ -138,7 +137,7 @@ bool System::command_sendmail(const char * value, const int8_t id) { bool success = false; -#ifndef NO_TLS_SUPPORT +#ifndef EMSESP_STANDALONE WiFiClient * basic_client = new WiFiClient; ESP_SSLClient * ssl_client = new ESP_SSLClient; ReadyClient * r_client = new ReadyClient(*ssl_client); @@ -1523,14 +1522,27 @@ bool System::check_upgrade() { // changes going to v3.9 from an earlier version if (settings_version.major() == 3 && settings_version.minor() < 9) { #ifndef EMSESP_STANDALONE + // AP_MODE_ALWAYS has been removed EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) { if (apSettings.provisionMode == 0) { - apSettings.provisionMode = AP_MODE_DISCONNECTED; // AP_MODE_ALWAYS has been removed + apSettings.provisionMode = AP_MODE_DISCONNECTED; // AP_MODE_DISCONNECTED is the new default LOG_INFO("Upgrade: Setting AP provision mode to auto"); return StateUpdateResult::CHANGED; } return StateUpdateResult::UNCHANGED; }); + // Scheduler name is now mandatory, update FS + uint8_t i = 0; + bool schedule_changed = false; + EMSESP::webSchedulerService.update([&](WebScheduler & scheduler) { + for (ScheduleItem & scheduleItem : scheduler.scheduleItems) { + if (scheduleItem.name[0] == '\0') { + snprintf(scheduleItem.name, sizeof(scheduleItem.name), "schedule_%d", i++); + schedule_changed = true; + } + } + return schedule_changed ? StateUpdateResult::CHANGED : StateUpdateResult::UNCHANGED; + }); #endif } @@ -2956,65 +2968,252 @@ bool System::uploadFirmwareURL(const char * url) { Shell::loop_all(); // flush log buffers so latest messages are shown in console - // Configure temporary client - HTTPClient http; - http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // important for GitHub 302's - http.setTimeout(8000); - http.useHTTP10(true); // use HTTP/1.0 for update since the update handler does not support any transfer Encoding - http.begin(saved_url); + String scheme = saved_url.substring(0, 8); + scheme.toLowerCase(); + const bool is_https = scheme.startsWith("https://"); + const int scheme_len = is_https ? 8 : 7; // "https://" vs "http://" - // start a connection, returns -1 if fails - int httpCode = http.GET(); - if (httpCode != HTTP_CODE_OK) { - LOG_ERROR("Firmware upload failed - HTTP code %d", httpCode); - http.end(); - return false; // error + WiFiClient basic_client; + ESP_SSLClient ssl_client; + + Stream * stream = nullptr; + int firmware_size = 0; + + if (is_https) { + ssl_client.setInsecure(); // no CA validation, matches the rest of the project + // BearSSL needs a receive buffer large enough to hold one full TLS record. + // GitHub's release-assets CDN sends standard up-to-16 KB records and does NOT + // negotiate max_fragment_length, so a small (e.g. 1 KB) RX buffer makes the + // body unreadable (headers still fit one small record, hence Content-Length + // looks fine, but the first body record cannot be decoded). 16384 + overhead + // is the safe value the library itself uses by default; we go a bit smaller + // to be friendlier to 4 MB / no-PSRAM boards while still big enough for any + // record the CDN actually sends in practice. + ssl_client.setBufferSizes(16384, 1024); + ssl_client.setSessionTimeout(120); + } + basic_client.setTimeout(15000); // socket-level read timeout + ssl_client.setTimeout(15000); // Stream::readBytes timeout used by Update + ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP + + const uint16_t port = is_https ? 443 : 80; + String url_remain = saved_url.substring(scheme_len); + int redirect_count = 0; + + while (true) { + // split url_remain into host and path + String host; + String path; + int s = url_remain.indexOf('/'); + if (s < 0) { + host = url_remain; + path = "/"; + } else { + host = url_remain.substring(0, s); + path = url_remain.substring(s); + } + + LOG_DEBUG("Connecting to %s", host.c_str()); + if (!ssl_client.connect(host.c_str(), port)) { + LOG_ERROR("Firmware upload failed - connection failed"); + return false; + } + + // send a minimal HTTP/1.0 GET so we don't have to deal with chunked encoding + ssl_client.print("GET "); + ssl_client.print(path); + ssl_client.println(" HTTP/1.0"); + ssl_client.print("Host: "); + ssl_client.println(host); + ssl_client.println("User-Agent: EMS-ESP"); + ssl_client.println("Connection: close"); + ssl_client.print("\r\n"); + + // wait for the first byte (up to 8s, matching the previous HTTP timeout) + uint32_t ms = millis(); + while (ssl_client.connected() && !ssl_client.available() && millis() - ms < 8000) { + delay(1); + } + + // parse status line: "HTTP/1.x CODE TEXT" + String status_line = ssl_client.readStringUntil('\n'); + int sp = status_line.indexOf(' '); + int http_code = (sp >= 0) ? status_line.substring(sp + 1, sp + 4).toInt() : 0; + + // parse response headers, looking for Content-Length and Location + int content_length = -1; + String location; + while (ssl_client.connected() || ssl_client.available()) { + String line = ssl_client.readStringUntil('\n'); + line.trim(); + if (line.isEmpty()) { + break; // end of headers + } + int colon = line.indexOf(':'); + if (colon < 0) { + continue; + } + String name = line.substring(0, colon); + name.toLowerCase(); + String val = line.substring(colon + 1); + val.trim(); + if (name == "content-length") { + content_length = val.toInt(); + } else if (name == "location") { + location = val; + } + } + + // follow redirects manually (GitHub releases redirect to objects.githubusercontent.com) + if (http_code == 301 || http_code == 302 || http_code == 303 || http_code == 307 || http_code == 308) { + ssl_client.stop(); + if (location.isEmpty() || ++redirect_count > 5) { + LOG_ERROR("Firmware upload failed - too many redirects"); + return false; + } + String lower_loc = location; + lower_loc.toLowerCase(); + if (lower_loc.startsWith("https://") || lower_loc.startsWith("http://")) { + // scheme-changing redirect is not supported - the SSL state is + // baked in at setClient() time and we don't want to re-init mid-flight + const bool new_is_https = lower_loc.startsWith("https://"); + if (new_is_https != is_https) { + LOG_ERROR("Firmware upload failed - cross-scheme redirect to %s", location.c_str()); + return false; + } + url_remain = location.substring(new_is_https ? 8 : 7); + } else if (location.startsWith("/")) { + url_remain = host + location; // relative redirect, same host + } else { + LOG_ERROR("Firmware upload failed - unsupported redirect to %s", location.c_str()); + return false; + } + LOG_DEBUG("Following redirect to %s", url_remain.c_str()); + continue; + } + + if (http_code != 200) { + ssl_client.stop(); + LOG_ERROR("Firmware upload failed - HTTP code %d", http_code); + return false; + } + + if (content_length <= 0) { + ssl_client.stop(); + LOG_ERROR("Firmware upload failed - missing Content-Length"); + return false; + } + + // wait for the first byte of the body so the read loop sees real data + // (headers and body may arrive in separate TLS records) + uint32_t body_wait = millis(); + while (ssl_client.connected() && !ssl_client.available() && millis() - body_wait < 8000) { + delay(1); + } + if (!ssl_client.available()) { + ssl_client.stop(); + LOG_ERROR("Firmware upload failed - no body received"); + return false; + } + + stream = &ssl_client; + firmware_size = content_length; + break; } - int firmware_size = http.getSize(); - // check we have a valid size - if (firmware_size < 2097152) { // 2MB or greater is required + if (firmware_size < 1677721) { // 1.6MB or greater is required LOG_ERROR("Firmware upload failed - invalid size"); - http.end(); return false; // error } // check we have enough space for the upload in the ota partition if (!Update.begin(firmware_size)) { LOG_ERROR("Firmware upload failed - no space"); - http.end(); return false; // error } - LOG_INFO("Firmware uploading (size: %d KB). Please wait...", firmware_size / 1024); + LOG_INFO("Firmware uploading (size: %d KB) over %s. Please wait...", firmware_size / 1024, is_https ? "HTTPS" : "HTTP"); Shell::loop_all(); // flush log buffers so latest messages are shown in console // we're about to start the upload, set the status so the Web System Monitor spots it EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING); - // set a callback so we can monitor progress in the WebUI - Update.onProgress([](size_t progress, size_t total) { EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING + (progress * 100 / total)); }); + // explicit chunked read loop instead of Update.writeStream(): + constexpr size_t CHUNK_SIZE = 1024; + constexpr uint32_t READ_TIMEOUT_MS = 30000; // overall stall timeout per chunk + uint8_t buf[CHUNK_SIZE]; + size_t total_read = 0; + bool magic_ok = false; + int last_pct = -1; - // get tcp stream and send it to Updater - WiFiClient * stream = http.getStreamPtr(); - if (Update.writeStream(*stream) != firmware_size) { - LOG_ERROR("Firmware upload failed - size differences"); - http.end(); - EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); - return false; // error + while (total_read < (size_t)firmware_size) { + // wait for some data or for the connection to drop + uint32_t wait_start = millis(); + while (!stream->available()) { + if (!ssl_client.connected()) { + break; + } + if (millis() - wait_start > READ_TIMEOUT_MS) { + break; + } + delay(1); + } + + if (!stream->available()) { + LOG_ERROR("Firmware upload failed - read stalled at %u of %d bytes", (unsigned)total_read, firmware_size); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); + return false; + } + + size_t want = (size_t)firmware_size - total_read; + if (want > CHUNK_SIZE) { + want = CHUNK_SIZE; + } + + size_t n = stream->readBytes(buf, want); + if (n == 0) { + LOG_ERROR("Firmware upload failed - read returned 0 at %u of %d bytes", (unsigned)total_read, firmware_size); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); + return false; + } + + // verify the ESP image magic byte the very first time so we fail fast with a + // clear message if the URL points at the wrong asset (HTML, archive, ...) + if (!magic_ok) { + if (buf[0] != 0xE9) { + LOG_ERROR("Firmware upload failed - bad magic byte 0x%02X (expected 0xE9, not an ESP32 firmware image?)", buf[0]); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); + return false; + } + magic_ok = true; + } + + if (Update.write(buf, n) != n) { + LOG_ERROR("Firmware upload failed - flash write error at %u of %d bytes: %s", (unsigned)total_read, firmware_size, Update.errorString()); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); + return false; + } + + total_read += n; + + // update the WebUI status, but only when the percentage actually changes + int pct = (int)(total_read * 100 / (size_t)firmware_size); + if (pct != last_pct) { + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING + pct); + last_pct = pct; + } + + yield(); } if (!Update.end(true)) { - LOG_ERROR("Firmware upload failed - general error"); - http.end(); + LOG_ERROR("Firmware upload failed - %s", Update.errorString()); EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); return false; // error } - // finished with upload - http.end(); saved_url.clear(); // prevent from downloading again LOG_INFO("Firmware uploaded successfully. Restarting..."); EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); diff --git a/src/emsesp_version.h b/src/emsesp_version.h index 4750696f3..808c5e03b 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.9.0-dev.9" +#define EMSESP_APP_VERSION "3.9.0-dev.10" diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 00e87c303..beb487376 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -478,7 +478,7 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) { } } - // show scheduler, with name, on/off + // show scheduler, with name, on/off, unless it's of type SCHEDULE_IMMEDIATE if (EMSESP::webSchedulerService.count_entities(true)) { JsonObject obj = nodes.add(); obj["id"] = EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID; // it's unique id @@ -488,8 +488,8 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) { EMSESP::webSchedulerService.read([&](const WebScheduler & webScheduler) { for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) { - // only add if we have a name - we don't need a u (UOM) for this - if (scheduleItem.name[0] != '\0') { + // only add if we have a name and it's not of type SCHEDULE_IMMEDIATE - we don't need a u (UOM) for this + if (scheduleItem.name[0] != '\0' && scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) { JsonObject node = nodes.add(); node["id"] = (EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID * 100) + count++; diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 3eaf73715..0f05283c4 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -82,7 +82,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu EMSESP::webSchedulerService.ha_reset(); // build up the list of schedule items - auto scheduleItems = root["schedule"].as(); + auto scheduleItems = root["schedule"].as(); for (const JsonObject schedule : scheduleItems) { // create each schedule item, overwriting any previous settings // ignore the id (as this is only used in the web for table rendering) @@ -137,6 +137,7 @@ bool WebSchedulerService::command_setvalue(const char * value, const int8_t id, if (EMSESP::mqtt_.get_publish_onchange(0)) { publish(); } + // save new state to nvs #2946 if (scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) { char key[sizeof(scheduleItem.name) + 2]; @@ -343,6 +344,7 @@ uint8_t WebSchedulerService::count_entities(bool cmd_only) { } // execute scheduled command +// return true if successful, false if not bool WebSchedulerService::command(const char * name, const std::string & command, const std::string & data) { std::string cmd = Helpers::toLower(command); @@ -365,130 +367,10 @@ bool WebSchedulerService::command(const char * name, const std::string & command std::string value = doc["value"] | data; // extract value if its in the command, or take the data std::string method = doc["method"] | "GET"; // default GET commands(value, false); - if (value.length()) { - method = "POST"; - } - std::string result; - int httpResult = 0; -#ifndef NO_TLS_SUPPORT - if (Helpers::toLower(url.c_str()).starts_with("https://")) { - WiFiClient * basic_client = new WiFiClient; - ESP_SSLClient * ssl_client = new ESP_SSLClient; - ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert); - ssl_client->setBufferSizes(1024, 1024); - ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds) - url.replace(0, 8, ""); - std::string host = url; - auto index = url.find_first_of('/'); - if (index != std::string::npos) { - host = url.substr(0, index); - url.replace(0, index, ""); - } - // EMSESP::logger().debug("Host: %s, URL: %s", host.c_str(), url.c_str()); - ssl_client->setClient(basic_client); - if (ssl_client->connect(host.c_str(), 443)) { - if (value.length() || Helpers::toLower(method) == "post") { - // EMSESP::logger().debug("POST %s HTTP/1.1", url.c_str()); - ssl_client->print("POST "); - ssl_client->print(url.c_str()); - ssl_client->println(" HTTP/1.1"); - ssl_client->print("Host: "); - ssl_client->println(host.c_str()); - bool content_set = false; - for (JsonPair p : doc["header"].as()) { - content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type"); - ssl_client->print(p.key().c_str()); - ssl_client->print(": "); - ssl_client->println(p.value().as().c_str()); - } - if (!content_set) { - ssl_client->print("Content-Type: "); - if (value.starts_with('{')) { - ssl_client->println(asyncsrv::T_application_json); - } else { - ssl_client->println(asyncsrv::T_text_plain); - } - } - ssl_client->print("Content-Length: "); - ssl_client->println(value.length()); - ssl_client->println("Connection: close"); - ssl_client->print("\r\n"); - ssl_client->print(value.c_str()); - } else { - // EMSESP::logger().debug("GET %s HTTP/1.1", url.c_str()); - ssl_client->print("GET "); - ssl_client->print(url.c_str()); - ssl_client->println(" HTTP/1.1"); - ssl_client->print("Host: "); - ssl_client->println(host.c_str()); - for (JsonPair p : doc["header"].as()) { - ssl_client->print(p.key().c_str()); - ssl_client->print(": "); - ssl_client->println(p.value().as().c_str()); - } - ssl_client->println("Connection: close"); - } - auto ms = millis(); - while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) { - delay(0); - } - while (ssl_client->available()) { - result += (char)ssl_client->read(); - } - ssl_client->stop(); - // EMSESP::logger().debug("HTTPS response: %s", result.c_str()); - index = result.find_first_of(' '); - if (index != std::string::npos) { - httpResult = stoi(result.substr(index + 1, 3)); - // EMSESP::logger().debug("HTTPS code: %i", httpResult); - } - index = result.find("\r\n\r\n"); - if (index != std::string::npos) { - result.replace(0, index + 4, ""); - // EMSESP::logger().debug("HTTPS response: %s", result.c_str()); - } - } else { - EMSESP::logger().warning("HTTPS connection failed"); - } - delete ssl_client; - delete basic_client; - // check HTTP return code - if (httpResult != 200) { - EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult); - return false; - } - return true; - } else -#endif - if (Helpers::toLower(url.c_str()).starts_with("http://")) { - HTTPClient * http = new HTTPClient; - if (http->begin(url.c_str())) { - bool content_set = false; - for (JsonPair p : doc["header"].as()) { - http->addHeader(p.key().c_str(), p.value().as().c_str()); - content_set |= p.key() == "content-type"; - } - // if there is data, force a POST - if (Helpers::toLower(method) == "post") { // we have all lowercase - if (!content_set) { - // http->addHeader("Content-Type", value.find_first_of('{') != std::string::npos ? "application/json" : "text/plain"); - if (value.starts_with('{')) { - http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON - } else { - http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_text_plain, false); // auto-set to JSON - } - } - httpResult = http->POST(value.c_str()); - } else { - httpResult = http->GET(); // normal GET - if (httpResult > 0) { - result = http->getString().c_str(); - } - } - } - http->end(); - delete http; - // check HTTP return code + auto lower_url = Helpers::toLower(url.c_str()); + if (lower_url.starts_with("http://") || lower_url.starts_with("https://")) { + std::string result; + int httpResult = http_request(url, method, value, doc["header"].as(), result); if (httpResult != 200) { EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult); return false; @@ -598,7 +480,7 @@ void WebSchedulerService::loop() { for (ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) { command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())); - scheduleItem.active = false; + // scheduleItem.active = false; publish_single(scheduleItem.name, false); if (EMSESP::mqtt_.get_publish_onchange(0)) { publish(); @@ -659,6 +541,18 @@ void WebSchedulerService::loop() { } } +// execute a schedule item immediately +bool WebSchedulerService::executeSchedule(const char * name) { + for (ScheduleItem & scheduleItem : *scheduleItems_) { + if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE && strcmp(scheduleItem.name, name) == 0) { + EMSESP::logger().info("Executing schedule '%s'", name); + return command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())); + } + } + EMSESP::logger().warning("Schedule '%s' not found", name); + return false; // not found +} + // process schedules async void WebSchedulerService::scheduler_task(void * pvParameters) { while (1) { @@ -681,11 +575,11 @@ void WebSchedulerService::load_test_data() { // test 1 auto si = ScheduleItem(); si.active = true; - si.flags = 1; + si.flags = 1; // day schedule si.time = "12:00"; si.cmd = "system/fetch"; si.value = "10"; - strcpy(si.name, "test_scheduler"); + strcpy(si.name, "test_scheduler1"); si.elapsed_min = 0; si.retry_cnt = 0xFF; // no startup retries @@ -694,11 +588,11 @@ void WebSchedulerService::load_test_data() { // test 2 si = ScheduleItem(); si.active = false; - si.flags = 1; + si.flags = SCHEDULEFLAG_SCHEDULE_IMMEDIATE; // immediate si.time = "13:00"; si.cmd = "system/message"; si.value = "20"; - strcpy(si.name, ""); // to make sure its excluded from Dashboard + strcpy(si.name, "test_scheduler2"); // to make sure its excluded from Dashboard si.elapsed_min = 0; si.retry_cnt = 0xFF; // no startup retries diff --git a/src/web/WebSchedulerService.h b/src/web/WebSchedulerService.h index 0d1ad2fa6..af48d5a82 100644 --- a/src/web/WebSchedulerService.h +++ b/src/web/WebSchedulerService.h @@ -38,14 +38,14 @@ // bit flags for the schedule items. Matches those in interface/src/app/main/SchedulerDialog.tsx // 0-127 (0->0x7F) is day schedule -// 128/0x80 is timer -// 129/0x81 is on change -// 130/0x82 is on condition -// 132/0x84 is immediate +// 128 (0x80) is timer +// 129 (0x81) is on change +// 130 (0x82) is on condition +// 132 (0x84) is immediate #define SCHEDULEFLAG_SCHEDULE_TIMER 0x80 // 7th bit for Timer #define SCHEDULEFLAG_SCHEDULE_ONCHANGE 0x81 // 7th+1st bit for OnChange #define SCHEDULEFLAG_SCHEDULE_CONDITION 0x82 // 7th+2nd bit for Condition -#define SCHEDULEFLAG_SCHEDULE_IMMEDIATE 0x84 // 7th+3rd bit for Condition +#define SCHEDULEFLAG_SCHEDULE_IMMEDIATE 0x84 // 7th+3rd bit for Immediate #define MAX_STARTUP_RETRIES 3 // retry the start-up commands x times @@ -54,7 +54,7 @@ namespace emsesp { class ScheduleItem { public: boolean active; - uint8_t flags; + uint8_t flags; // bit flags, see SCHEDULEFLAG_* defines uint16_t elapsed_min; // total mins from 00:00 stringPSRAM time; // HH:MM stringPSRAM cmd; @@ -88,6 +88,8 @@ class WebSchedulerService : public StatefulService { uint8_t count_entities(bool cmd_only = false); bool onChange(const char * cmd); + bool executeSchedule(const char * name); + std::string get_metrics_prometheus(); std::string raw_value; diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 93d3f956b..38769e33d 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -83,19 +83,15 @@ void WebSettings::read(WebSettings & settings, JsonObject root) { root["modbus_max_clients"] = settings.modbus_max_clients; root["modbus_timeout"] = settings.modbus_timeout; root["developer_mode"] = settings.developer_mode; -#ifndef NO_TLS_SUPPORT - root["email_enabled"] = settings.email_enabled; -#else - root["email_enabled"] = false; -#endif - root["email_security"] = settings.email_security; - root["email_server"] = settings.email_server; - root["email_port"] = settings.email_port; - root["email_login"] = settings.email_login; - root["email_pass"] = settings.email_pass; - root["email_sender"] = settings.email_sender; - root["email_recp"] = settings.email_recp; - root["email_subject"] = settings.email_subject; + root["email_enabled"] = settings.email_enabled; + root["email_security"] = settings.email_security; + root["email_server"] = settings.email_server; + root["email_port"] = settings.email_port; + root["email_login"] = settings.email_login; + root["email_pass"] = settings.email_pass; + root["email_sender"] = settings.email_sender; + root["email_recp"] = settings.email_recp; + root["email_subject"] = settings.email_subject; } // call on initialization and also when settings are updated/saved via web or console diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 7523241a6..fbc8f99e3 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -20,7 +20,8 @@ #ifndef EMSESP_STANDALONE #include -#include +#include +#include #endif namespace emsesp { @@ -227,6 +228,8 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json) EMSESP::mqtt_.reset_mqtt(); } else if (action == "upgradeImportantMessages") { root["upgradeImportantMessageType"] = upgradeImportantMessages(param); + } else if (action == "executeSchedule") { + ok = EMSESP::webSchedulerService.executeSchedule(param.c_str()); } #if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY) @@ -412,30 +415,91 @@ bool WebStatusService::refresh_versions_cache() { #ifdef EMSESP_STANDALONE return false; #else - HTTPClient http; - http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - http.setTimeout(5000); - http.useHTTP10(true); - - if (!http.begin(EMSESP_VERSIONS_URL)) { + // detect scheme from EMSESP_VERSIONS_URL (case-insensitive). One code path for HTTP and HTTPS, + // using ESP_SSLClient as a plain TCP passthrough when SSL is disabled. + String url = EMSESP_VERSIONS_URL; + String lower = url; + lower.toLowerCase(); + const bool is_https = lower.startsWith("https://"); + if (!is_https && !lower.startsWith("http://")) { #if defined(EMSESP_DEBUG) - EMSESP::logger().debug("versions.json: failed to start HTTPS request"); + EMSESP::logger().debug("versions.json: unsupported scheme"); +#endif + return false; + } + const int scheme_len = is_https ? 8 : 7; + + WiFiClient basic_client; + ESP_SSLClient ssl_client; + if (is_https) { + ssl_client.setInsecure(); + ssl_client.setBufferSizes(16384, 1024); // versions.json fits easily but TLS records may be full-size + ssl_client.setSessionTimeout(120); + } + basic_client.setTimeout(5000); + ssl_client.setTimeout(5000); + ssl_client.setClient(&basic_client, is_https); + + // split into host and path + String rest = url.substring(scheme_len); + String host; + String path; + int s = rest.indexOf('/'); + if (s < 0) { + host = rest; + path = "/"; + } else { + host = rest.substring(0, s); + path = rest.substring(s); + } + + if (!ssl_client.connect(host.c_str(), is_https ? 443 : 80)) { +#if defined(EMSESP_DEBUG) + EMSESP::logger().debug("versions.json: connection failed"); #endif return false; } - int httpCode = http.GET(); - if (httpCode != HTTP_CODE_OK) { + // minimal HTTP/1.0 GET so we don't have to handle chunked encoding + ssl_client.print("GET "); + ssl_client.print(path); + ssl_client.println(" HTTP/1.0"); + ssl_client.print("Host: "); + ssl_client.println(host); + ssl_client.println("User-Agent: EMS-ESP"); + ssl_client.println("Connection: close"); + ssl_client.print("\r\n"); + + // wait for the first byte + uint32_t ms = millis(); + while (ssl_client.connected() && !ssl_client.available() && millis() - ms < 5000) { + delay(1); + } + + // parse status line + String status_line = ssl_client.readStringUntil('\n'); + int sp = status_line.indexOf(' '); + int http_code = (sp >= 0) ? status_line.substring(sp + 1, sp + 4).toInt() : 0; + if (http_code != 200) { + ssl_client.stop(); #if defined(EMSESP_DEBUG) - EMSESP::logger().debug("versions.json: HTTP error code %d", httpCode); + EMSESP::logger().debug("versions.json: HTTP error code %d", http_code); #endif - http.end(); return false; } + // skip headers + while (ssl_client.connected() || ssl_client.available()) { + String line = ssl_client.readStringUntil('\n'); + line.trim(); + if (line.isEmpty()) { + break; + } + } + JsonDocument doc(PSRAM_DOC); - DeserializationError err = deserializeJson(doc, http.getStream()); - http.end(); + DeserializationError err = deserializeJson(doc, ssl_client); + ssl_client.stop(); if (err) { #if defined(EMSESP_DEBUG) EMSESP::logger().debug("versions.json: parse error");