From e307bcb276671708289a1a363410692311fbd98d Mon Sep 17 00:00:00 2001 From: SpaceFox1 <44732812+SpaceFox1@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:31:38 -0300 Subject: [PATCH 1/9] feat: split phone camera handling to it's own component --- frontend/package-lock.json | 5666 +++++++++++----------- frontend/package.json | 60 +- frontend/src/app.html | 23 +- frontend/src/lib/global.css | 20 + frontend/src/lib/interfaces/Quality.ts | 1 + frontend/src/lib/utils/phoneUtils.ts | 28 + frontend/src/routes/+layout.svelte | 10 +- frontend/src/routes/stream/+page.svelte | 452 +- frontend/src/routes/stream/camera.svelte | 226 + frontend/src/routes/view/+page.svelte | 2 +- frontend/static/icons/fullscreenicon.svg | 4 + 11 files changed, 3361 insertions(+), 3131 deletions(-) create mode 100644 frontend/src/lib/global.css create mode 100644 frontend/src/lib/interfaces/Quality.ts create mode 100644 frontend/src/lib/utils/phoneUtils.ts create mode 100644 frontend/src/routes/stream/camera.svelte create mode 100644 frontend/static/icons/fullscreenicon.svg diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cbce1ee..3aa1eed 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,2835 +1,2835 @@ { - "name": "frontend", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "frontend", - "version": "0.0.1", - "devDependencies": { - "@eslint/compat": "^2.0.4", - "@eslint/js": "^10.0.1", - "@sveltejs/adapter-static": "^3.0.10", - "@sveltejs/kit": "^2.57.0", - "@sveltejs/vite-plugin-svelte": "^7.0.0", - "@types/node": "^22", - "eslint": "^10.2.0", - "eslint-plugin-svelte": "^3.17.0", - "globals": "^17.4.0", - "svelte": "^5.55.2", - "svelte-check": "^4.4.6", - "typescript": "^6.0.2", - "typescript-eslint": "^8.58.1", - "vite": "^8.0.7" - } - }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/compat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.5.tgz", - "integrity": "sha512-IbHDbHJfkVNv6xjlET8AIVo/K1NQt7YT4Rp6ok/clyBGcpRx1l6gv0Rq3vBvYfPJIZt6ODf66Zq08FJNDpnzgg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "peerDependencies": { - "eslint": "^8.40 || 9 || 10" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.124.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", - "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", - "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", - "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.9.2", - "@emnapi/runtime": "1.9.2", - "@napi-rs/wasm-runtime": "^1.1.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", - "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sveltejs/acorn-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", - "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8.9.0" - } - }, - "node_modules/@sveltejs/adapter-static": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", - "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@sveltejs/kit": "^2.0.0" - } - }, - "node_modules/@sveltejs/kit": { - "version": "2.57.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.57.1.tgz", - "integrity": "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@sveltejs/acorn-typescript": "^1.0.5", - "@types/cookie": "^0.6.0", - "acorn": "^8.14.1", - "cookie": "^0.6.0", - "devalue": "^5.6.4", - "esm-env": "^1.2.2", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "mrmime": "^2.0.0", - "set-cookie-parser": "^3.0.0", - "sirv": "^3.0.0" - }, - "bin": { - "svelte-kit": "svelte-kit.js" - }, - "engines": { - "node": ">=18.13" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": "^5.3.3 || ^6.0.0", - "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.0.0.tgz", - "integrity": "sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deepmerge": "^4.3.1", - "magic-string": "^0.30.21", - "obug": "^2.1.0", - "vitefu": "^1.1.2" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24" - }, - "peerDependencies": { - "svelte": "^5.46.4", - "vite": "^8.0.0-beta.7 || ^8.0.0" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", - "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", - "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/type-utils": "8.58.2", - "@typescript-eslint/utils": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.58.2", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", - "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", - "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.2", - "@typescript-eslint/types": "^8.58.2", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", - "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", - "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", - "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/utils": "8.58.2", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", - "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", - "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.58.2", - "@typescript-eslint/tsconfig-utils": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", - "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", - "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.58.2", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/aria-query": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", - "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devalue": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", - "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", - "dev": true, - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", - "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.4", - "@eslint/config-helpers": "^0.5.4", - "@eslint/core": "^1.2.0", - "@eslint/plugin-kit": "^0.7.0", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-svelte": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.17.0.tgz", - "integrity": "sha512-sF6wgd5FLS2P8CCaOy2HdYYYEcZ6TwL251dLHUkNmtLnWECk1Dwc+j6VeulmmnFxr7Xs0WNtjweOA+bJ0PnaFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.6.1", - "@jridgewell/sourcemap-codec": "^1.5.0", - "esutils": "^2.0.3", - "globals": "^16.0.0", - "known-css-properties": "^0.37.0", - "postcss": "^8.4.49", - "postcss-load-config": "^3.1.4", - "postcss-safe-parser": "^7.0.0", - "semver": "^7.6.3", - "svelte-eslint-parser": "^1.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": "^8.57.1 || ^9.0.0 || ^10.0.0", - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-svelte/node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esm-env": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrap": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.5.tgz", - "integrity": "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "peerDependencies": { - "@typescript-eslint/types": "^8.2.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/types": { - "optional": true - } - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", - "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-reference": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", - "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/known-css-properties": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", - "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-safe-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", - "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", - "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.29" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/rolldown": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", - "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.124.0", - "@rolldown/pluginutils": "1.0.0-rc.15" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-x64": "1.0.0-rc.15", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" - } - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", - "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sirv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/svelte": { - "version": "5.55.4", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.4.tgz", - "integrity": "sha512-q8DFohk6vUswSng95IZb9nzWJnbINZsK7OiM1snAa3qCjJBL0ZQpvMyAaVXjUukdM75J/m8UE8xwqat8Ors/zQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "@jridgewell/sourcemap-codec": "^1.5.0", - "@sveltejs/acorn-typescript": "^1.0.5", - "@types/estree": "^1.0.5", - "@types/trusted-types": "^2.0.7", - "acorn": "^8.12.1", - "aria-query": "5.3.1", - "axobject-query": "^4.1.0", - "clsx": "^2.1.1", - "devalue": "^5.6.4", - "esm-env": "^1.2.1", - "esrap": "^2.2.4", - "is-reference": "^3.0.3", - "locate-character": "^3.0.0", - "magic-string": "^0.30.11", - "zimmerframe": "^1.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/svelte-check": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.6.tgz", - "integrity": "sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "chokidar": "^4.0.1", - "fdir": "^6.2.0", - "picocolors": "^1.0.0", - "sade": "^1.7.4" - }, - "bin": { - "svelte-check": "bin/svelte-check" - }, - "engines": { - "node": ">= 18.0.0" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": ">=5.0.0" - } - }, - "node_modules/svelte-eslint-parser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.6.0.tgz", - "integrity": "sha512-qoB1ehychT6OxEtQAqc/guSqLS20SlA53Uijl7x375s8nlUT0lb9ol/gzraEEatQwsyPTJo87s2CmKL9Xab+Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.0", - "postcss": "^8.4.49", - "postcss-scss": "^4.0.9", - "postcss-selector-parser": "^7.0.0", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0", - "pnpm": "10.30.3" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/svelte-eslint-parser/node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/svelte-eslint-parser/node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", - "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.2", - "@typescript-eslint/parser": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/utils": "8.58.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", - "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.15", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "dev": true, - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zimmerframe": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", - "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", - "dev": true, - "license": "MIT" - } - } + "name": "frontend", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.1", + "devDependencies": { + "@eslint/compat": "^2.0.4", + "@eslint/js": "^10.0.1", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.57.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@types/node": "^22", + "eslint": "^10.2.0", + "eslint-plugin-svelte": "^3.17.0", + "globals": "^17.4.0", + "svelte": "^5.55.2", + "svelte-check": "^4.4.6", + "typescript": "^6.0.2", + "typescript-eslint": "^8.58.1", + "vite": "^8.0.7" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/compat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.5.tgz", + "integrity": "sha512-IbHDbHJfkVNv6xjlET8AIVo/K1NQt7YT4Rp6ok/clyBGcpRx1l6gv0Rq3vBvYfPJIZt6ODf66Zq08FJNDpnzgg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^8.40 || 9 || 10" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-static": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.57.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.57.1.tgz", + "integrity": "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.6.4", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.0.0.tgz", + "integrity": "sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "magic-string": "^0.30.21", + "obug": "^2.1.0", + "vitefu": "^1.1.2" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", + "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-svelte": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.17.0.tgz", + "integrity": "sha512-sF6wgd5FLS2P8CCaOy2HdYYYEcZ6TwL251dLHUkNmtLnWECk1Dwc+j6VeulmmnFxr7Xs0WNtjweOA+bJ0PnaFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.6.1", + "@jridgewell/sourcemap-codec": "^1.5.0", + "esutils": "^2.0.3", + "globals": "^16.0.0", + "known-css-properties": "^0.37.0", + "postcss": "^8.4.49", + "postcss-load-config": "^3.1.4", + "postcss-safe-parser": "^7.0.0", + "semver": "^7.6.3", + "svelte-eslint-parser": "^1.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": "^8.57.1 || ^9.0.0 || ^10.0.0", + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-svelte/node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrap": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.5.tgz", + "integrity": "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/known-css-properties": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "5.55.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.4.tgz", + "integrity": "sha512-q8DFohk6vUswSng95IZb9nzWJnbINZsK7OiM1snAa3qCjJBL0ZQpvMyAaVXjUukdM75J/m8UE8xwqat8Ors/zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.6.4", + "esm-env": "^1.2.1", + "esrap": "^2.2.4", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.6.tgz", + "integrity": "sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte-eslint-parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.6.0.tgz", + "integrity": "sha512-qoB1ehychT6OxEtQAqc/guSqLS20SlA53Uijl7x375s8nlUT0lb9ol/gzraEEatQwsyPTJo87s2CmKL9Xab+Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.0", + "postcss": "^8.4.49", + "postcss-scss": "^4.0.9", + "postcss-selector-parser": "^7.0.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0", + "pnpm": "10.30.3" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/svelte-eslint-parser/node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/svelte-eslint-parser/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } } diff --git a/frontend/package.json b/frontend/package.json index 815ba61..ee07487 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,31 +1,31 @@ { - "name": "frontend", - "private": true, - "version": "0.0.1", - "type": "module", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "eslint ." - }, - "devDependencies": { - "@eslint/compat": "^2.0.4", - "@eslint/js": "^10.0.1", - "@sveltejs/adapter-static": "^3.0.10", - "@sveltejs/kit": "^2.57.0", - "@sveltejs/vite-plugin-svelte": "^7.0.0", - "@types/node": "^22", - "eslint": "^10.2.0", - "eslint-plugin-svelte": "^3.17.0", - "globals": "^17.4.0", - "svelte": "^5.55.2", - "svelte-check": "^4.4.6", - "typescript": "^6.0.2", - "typescript-eslint": "^8.58.1", - "vite": "^8.0.7" - } -} + "name": "frontend", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "eslint ." + }, + "devDependencies": { + "@eslint/compat": "^2.0.4", + "@eslint/js": "^10.0.1", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.57.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@types/node": "^22", + "eslint": "^10.2.0", + "eslint-plugin-svelte": "^3.17.0", + "globals": "^17.4.0", + "svelte": "^5.55.2", + "svelte-check": "^4.4.6", + "typescript": "^6.0.2", + "typescript-eslint": "^8.58.1", + "vite": "^8.0.7" + } +} \ No newline at end of file diff --git a/frontend/src/app.html b/frontend/src/app.html index 6a2bb58..3173408 100644 --- a/frontend/src/app.html +++ b/frontend/src/app.html @@ -1,12 +1,15 @@ - - - - - %sveltekit.head% - - -
%sveltekit.body%
- - + + + + + + %sveltekit.head% + + + +
%sveltekit.body%
+ + + \ No newline at end of file diff --git a/frontend/src/lib/global.css b/frontend/src/lib/global.css new file mode 100644 index 0000000..44e2bb2 --- /dev/null +++ b/frontend/src/lib/global.css @@ -0,0 +1,20 @@ +:root { + --background: #191919; + --accent-color: #54ff98; +} + +* { + padding: 0; + margin: 0; + box-sizing: border-box; +} + +body { + width: 100vw; + height: 100vh; + overflow: hidden; + background: var(--background); + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/frontend/src/lib/interfaces/Quality.ts b/frontend/src/lib/interfaces/Quality.ts new file mode 100644 index 0000000..fb94c75 --- /dev/null +++ b/frontend/src/lib/interfaces/Quality.ts @@ -0,0 +1 @@ +export type Quality = "high" | "medium" | "low"; \ No newline at end of file diff --git a/frontend/src/lib/utils/phoneUtils.ts b/frontend/src/lib/utils/phoneUtils.ts new file mode 100644 index 0000000..2c3f332 --- /dev/null +++ b/frontend/src/lib/utils/phoneUtils.ts @@ -0,0 +1,28 @@ +let wakeLock: WakeLockSentinel | null = null + +async function reacquireWakeLock() { + if (document.visibilityState === "visible") { + await preventScreenLock(); + } +} + +export async function preventScreenLock() { + try { + wakeLock = await navigator.wakeLock.request("screen"); + wakeLock.addEventListener("release", () => { + document.addEventListener("visibilitychange", reacquireWakeLock, { + once: true, + }); + }); + } catch { + /* empty */ + } +} + +export function toggleFullScreen() { + if (!document.fullscreenElement) { + document.body.requestFullscreen(); + } else { + document.exitFullscreen?.(); + } +} \ No newline at end of file diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 9cebde5..108f498 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -1,11 +1,15 @@ - + {@render children()} + + diff --git a/frontend/src/routes/stream/+page.svelte b/frontend/src/routes/stream/+page.svelte index 128079c..1e29649 100644 --- a/frontend/src/routes/stream/+page.svelte +++ b/frontend/src/routes/stream/+page.svelte @@ -1,4 +1,15 @@ - async function acquireWakeLock() { - try { - wakeLock = await navigator.wakeLock.request('screen'); - wakeLock.addEventListener('release', () => { - document.addEventListener('visibilitychange', reacquireWakeLock, { once: true }); - }); - } catch { /* empty */ } - } +{#if !ws} + +{:else} +
+ +
+ +
+
+ +{/if} - async function reacquireWakeLock() { - if (document.visibilityState === 'visible') { - await acquireWakeLock(); - } + \ No newline at end of file + diff --git a/frontend/src/routes/stream/camera.svelte b/frontend/src/routes/stream/camera.svelte new file mode 100644 index 0000000..7030c21 --- /dev/null +++ b/frontend/src/routes/stream/camera.svelte @@ -0,0 +1,226 @@ + + +
+
+
+ +
+ + diff --git a/frontend/src/routes/view/+page.svelte b/frontend/src/routes/view/+page.svelte index aaa6214..b644161 100644 --- a/frontend/src/routes/view/+page.svelte +++ b/frontend/src/routes/view/+page.svelte @@ -113,6 +113,6 @@ top: 0; left: 0; object-fit: cover; - background-color: #111; /* Dark background to prove the element is rendering */ + background-color: #111; } \ No newline at end of file diff --git a/frontend/static/icons/fullscreenicon.svg b/frontend/static/icons/fullscreenicon.svg new file mode 100644 index 0000000..bff5727 --- /dev/null +++ b/frontend/static/icons/fullscreenicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From e54e39cf09a0e28c97540245ba4bb6cee634c5c7 Mon Sep 17 00:00:00 2001 From: SpaceFox1 <44732812+SpaceFox1@users.noreply.github.com> Date: Sat, 18 Apr 2026 03:02:15 -0300 Subject: [PATCH 2/9] feat: Working zoom slider --- frontend/src/lib/utils/phoneUtils.ts | 2 + frontend/src/routes/stream/+page.svelte | 20 +-- frontend/src/routes/stream/camera.svelte | 160 ++++++++++++++++++++--- frontend/src/routes/view/+page.svelte | 77 +++++++---- frontend/static/icons/fullscreenexit.svg | 15 +++ 5 files changed, 220 insertions(+), 54 deletions(-) create mode 100644 frontend/static/icons/fullscreenexit.svg diff --git a/frontend/src/lib/utils/phoneUtils.ts b/frontend/src/lib/utils/phoneUtils.ts index 2c3f332..adef9ec 100644 --- a/frontend/src/lib/utils/phoneUtils.ts +++ b/frontend/src/lib/utils/phoneUtils.ts @@ -22,7 +22,9 @@ export async function preventScreenLock() { export function toggleFullScreen() { if (!document.fullscreenElement) { document.body.requestFullscreen(); + return true; } else { document.exitFullscreen?.(); + return false; } } \ No newline at end of file diff --git a/frontend/src/routes/stream/+page.svelte b/frontend/src/routes/stream/+page.svelte index 1e29649..9fb8194 100644 --- a/frontend/src/routes/stream/+page.svelte +++ b/frontend/src/routes/stream/+page.svelte @@ -229,12 +229,13 @@
-
- -
import type { Quality } from "$lib/interfaces/Quality"; import { preventScreenLock } from "$lib/utils/phoneUtils"; + import { onMount } from "svelte"; const QUALITY_PROFILES = { high: { width: 1920, height: 1080, frameRate: 30, bitrate: 8_000_000 }, @@ -21,6 +22,9 @@ zoom: 1, rotation: 0, }); + let videoWidth = $state(0); + let videoHeight = $state(0); + let zoomSlider: HTMLInputElement; let localStream: MediaStream | undefined = $state(); @@ -39,6 +43,8 @@ function applyZoom(val: number) { if (!localStream) return; + zoomValue = val; + updateZoomSlider(); const track = localStream.getVideoTracks()[0]; if (nativeZoomSupported) { @@ -94,6 +100,7 @@ } function handleTouchStart(e: TouchEvent) { + console.log(e.touches.length); if (e.touches.length === 2) { initialPinchDistance = Math.hypot( e.touches[0].clientX - e.touches[1].clientX, @@ -117,6 +124,24 @@ applyZoom(newZoom); } + export async function recalculateVideoDimensions(retryAttempt = 0) { + if (!localStream) return; + const settings = localStream.getVideoTracks()[0].getSettings(); + const previousWidth = videoWidth; + const previousHeight = videoHeight; + if ( + settings.width === previousWidth && + settings.height === previousHeight + ) { + // check again in 100ms in case the settings haven't updated yet + if (retryAttempt < 5) + setTimeout(() => recalculateVideoDimensions(retryAttempt + 1), 100); + return; + } + videoWidth = settings.width ?? 0; + videoHeight = settings.height ?? 0; + } + export async function startCamera() { try { await preventScreenLock(); @@ -138,6 +163,7 @@ const track = localStream.getVideoTracks()[0]; const caps = track.getCapabilities?.() || {}; + const settings = track.getSettings(); // @ts-expect-error chrome only feature if (caps.zoom) { @@ -147,6 +173,17 @@ // @ts-expect-error chrome only feature maxZoom = caps.zoom.max; } + + applyZoom(1); + + if (settings) { + videoWidth = settings.width ?? 0; + videoHeight = settings.height ?? 0; + } + screen.orientation.addEventListener("change", () => + recalculateVideoDimensions(), + ); + window.addEventListener("resize", () => recalculateVideoDimensions()); } catch (err) { console.error(err); alert("Could not start camera"); @@ -156,32 +193,75 @@ export function getVideoStream(): MediaStream | undefined { return localStream; } + + function updateZoomSlider() { + zoomSlider.min = minZoom as unknown as string; + zoomSlider.max = maxZoom as unknown as string; + zoomSlider.value = zoomValue as unknown as string; + const ratio = + ((Number(zoomSlider.value) - Number(zoomSlider.min)) / + (Number(zoomSlider.max) - Number(zoomSlider.min))) * + 100; + zoomSlider.style.background = `linear-gradient(0deg, var(--slider-bg-color) ${ratio}%, var(--deselected-bg-color) ${ratio}%)`; + } + + onMount(() => { + zoomSlider.addEventListener("input", () => { + applyZoom(Number(zoomSlider.value)); + }); + }); -
-
-
- +
+
+ +
+
+
+
+ +
diff --git a/frontend/src/routes/view/+page.svelte b/frontend/src/routes/view/+page.svelte index b644161..6e27427 100644 --- a/frontend/src/routes/view/+page.svelte +++ b/frontend/src/routes/view/+page.svelte @@ -3,34 +3,41 @@ const ICE_CONFIG = { iceServers: [ - { urls: 'stun:stun.l.google.com:19302' }, - { urls: 'stun:stun1.l.google.com:19302' }, - ] + { urls: "stun:stun.l.google.com:19302" }, + { urls: "stun:stun1.l.google.com:19302" }, + ], }; - let streamer = $derived(page.url.searchParams.get('streamer') || ''); + let streamer = $derived(page.url.searchParams.get("streamer") || ""); let preview: HTMLVideoElement; $effect(() => { - const wsProtocol = page.url.protocol === 'https:' ? 'wss:' : 'ws:'; - const internalSocket = new WebSocket(`${wsProtocol}//${page.url.host}/ws?role=viewer&watch_id=${streamer}`); - + const wsProtocol = page.url.protocol === "https:" ? "wss:" : "ws:"; + const internalSocket = new WebSocket( + `${wsProtocol}//${page.url.host}/ws?role=viewer&watch_id=${streamer}`, + ); + let internalPc: RTCPeerConnection | undefined = undefined; let candidateQueue: RTCIceCandidateInit[] = []; - internalSocket.addEventListener('error', (err) => console.error("WebSocket Error:", err)); + internalSocket.addEventListener("error", (err) => + console.error("WebSocket Error:", err), + ); - internalSocket.addEventListener('message', async (event) => { + internalSocket.addEventListener("message", async (event) => { const commandStr: string = event.data.toString(); const parts = commandStr.split(":"); const command = parts.shift(); const params = parts.join(":"); - + switch (command) { - case 'f': { // Received Offer + case "f": { + // Received Offer const paramsSplit = params.split(":"); paramsSplit.shift(); - const peerConnRemoteDescription = JSON.parse(paramsSplit.join(":").slice(1)); + const peerConnRemoteDescription = JSON.parse( + paramsSplit.join(":").slice(1), + ); if (internalPc) internalPc.close(); @@ -41,46 +48,64 @@ console.log("Track received!", e.track.kind); if (preview.srcObject !== e.streams[0]) { preview.srcObject = e.streams[0]; - preview.play().catch((err) => console.error("Error playing video:", err)); + preview + .play() + .catch((err) => console.error("Error playing video:", err)); } }; internalPc.onicecandidate = ({ candidate }) => { - if (candidate) internalSocket.send(`h:${streamer}:#${JSON.stringify(candidate)}`); + if (candidate) + internalSocket.send( + `h:${streamer}:#${JSON.stringify(candidate)}`, + ); }; // DIAGNOSTICS: Monitor the connection state internalPc.onconnectionstatechange = () => { if (!internalPc) return; console.log("WebRTC Connection State:", internalPc.connectionState); - if (internalPc.connectionState === 'failed') { - console.error("WebRTC connection failed. A TURN server might be required."); + if (internalPc.connectionState === "failed") { + console.error( + "WebRTC connection failed. A TURN server might be required.", + ); internalPc.close(); } }; - await internalPc.setRemoteDescription(new RTCSessionDescription(peerConnRemoteDescription)); - + await internalPc.setRemoteDescription( + new RTCSessionDescription(peerConnRemoteDescription), + ); + const answer = await internalPc.createAnswer(); await internalPc.setLocalDescription(answer); - internalSocket.send(`f:${streamer}:#${JSON.stringify(internalPc.localDescription)}`); + internalSocket.send( + `f:${streamer}:#${JSON.stringify(internalPc.localDescription)}`, + ); while (candidateQueue.length > 0) { const queuedCandidate = candidateQueue.shift(); if (queuedCandidate) { - await internalPc.addIceCandidate(new RTCIceCandidate(queuedCandidate)); + await internalPc.addIceCandidate( + new RTCIceCandidate(queuedCandidate), + ); } } break; } - case 'g': { // Received ICE Candidate + case "g": { + // Received ICE Candidate const candidateString = params.split(":"); candidateString.shift(); const candidate = JSON.parse(candidateString.join(":").slice(1)); - - if (internalPc && internalPc.remoteDescription && internalPc.remoteDescription.type) { + + if ( + internalPc && + internalPc.remoteDescription && + internalPc.remoteDescription.type + ) { await internalPc.addIceCandidate(new RTCIceCandidate(candidate)); } else { candidateQueue.push(candidate); @@ -93,11 +118,11 @@ return () => { internalSocket.close(); if (internalPc) internalPc.close(); - } + }; }); - + \ No newline at end of file + diff --git a/frontend/static/icons/fullscreenexit.svg b/frontend/static/icons/fullscreenexit.svg new file mode 100644 index 0000000..86fb86a --- /dev/null +++ b/frontend/static/icons/fullscreenexit.svg @@ -0,0 +1,15 @@ + + + + fullscreen_exit_line + + + + + + + + + + + \ No newline at end of file From 56c9ba934d7400a9c7e79b514a80836572632b7f Mon Sep 17 00:00:00 2001 From: SpaceFox1 <44732812+SpaceFox1@users.noreply.github.com> Date: Sat, 18 Apr 2026 20:56:47 -0300 Subject: [PATCH 3/9] feat: viewer, screen share and controller structure --- frontend/src/lib/global.css | 5 +- frontend/src/lib/viewer.svelte | 0 frontend/src/routes/controller/+page.svelte | 59 ++++++++ frontend/src/routes/stream/+page.svelte | 133 ++++++++++++++--- frontend/src/routes/stream/camera.svelte | 2 +- frontend/src/routes/stream/screen.svelte | 149 ++++++++++++++++++++ frontend/src/routes/view/+page.svelte | 71 +++++++++- frontend/static/icons/camera.svg | 4 + frontend/static/icons/mute.svg | 13 ++ frontend/static/icons/screenshare.svg | 4 + frontend/static/icons/unmute.svg | 2 + frontend/static/image/logo.png | Bin 0 -> 36497 bytes 12 files changed, 411 insertions(+), 31 deletions(-) create mode 100644 frontend/src/lib/viewer.svelte create mode 100644 frontend/src/routes/stream/screen.svelte create mode 100644 frontend/static/icons/camera.svg create mode 100644 frontend/static/icons/mute.svg create mode 100644 frontend/static/icons/screenshare.svg create mode 100644 frontend/static/icons/unmute.svg create mode 100644 frontend/static/image/logo.png diff --git a/frontend/src/lib/global.css b/frontend/src/lib/global.css index 44e2bb2..0d48a8c 100644 --- a/frontend/src/lib/global.css +++ b/frontend/src/lib/global.css @@ -1,6 +1,7 @@ :root { --background: #191919; --accent-color: #54ff98; + --black: #000; } * { @@ -10,8 +11,8 @@ } body { - width: 100vw; - height: 100vh; + width: 100dvw; + height: 100dvh; overflow: hidden; background: var(--background); display: flex; diff --git a/frontend/src/lib/viewer.svelte b/frontend/src/lib/viewer.svelte new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/routes/controller/+page.svelte b/frontend/src/routes/controller/+page.svelte index e69de29..c563633 100644 --- a/frontend/src/routes/controller/+page.svelte +++ b/frontend/src/routes/controller/+page.svelte @@ -0,0 +1,59 @@ +
+ +
+
+
+
+
+
+
+ +
+
+ + diff --git a/frontend/src/routes/stream/+page.svelte b/frontend/src/routes/stream/+page.svelte index 9fb8194..b5bd4f7 100644 --- a/frontend/src/routes/stream/+page.svelte +++ b/frontend/src/routes/stream/+page.svelte @@ -3,6 +3,7 @@ import type { Quality } from "$lib/interfaces/Quality"; import { toggleFullScreen } from "$lib/utils/phoneUtils"; import Camera from "./camera.svelte"; + import Screen from "./screen.svelte"; function stringToQuality(string: string): Quality { if (string === "3") return "low"; @@ -23,7 +24,8 @@ let ws: WebSocket | null = $state(null); // let sessionId: number | null = $state(null); // svelte-ignore non_reactive_update - let camera: Camera; + let sourceMedia: Camera | Screen; + let isCamera = $state(true); function setStreamBandwidth( peerConnection: RTCPeerConnection, @@ -52,7 +54,7 @@ } async function createPeerConnection(viewerSocketId: string) { - const localStream = camera.getVideoStream(); + const localStream = sourceMedia.getVideoStream(); if (!localStream) return; if (peerConnections[viewerSocketId]) { peerConnections[viewerSocketId].close(); @@ -66,7 +68,7 @@ pc.addTrack(track, localStream); } - const bandwidth = camera.getCurrentBandwidth(); + const bandwidth = sourceMedia.getCurrentBandwidth(); setStreamBandwidth(pc, bandwidth.maxBitrate, bandwidth.maxFramerate); pc.onicecandidate = ({ candidate }) => { @@ -86,7 +88,7 @@ } async function createOffer(viewerId: string) { - const localStream = camera.getVideoStream(); + const localStream = sourceMedia.getVideoStream(); if (!localStream) return; const pc = await createPeerConnection(viewerId); if (!pc) return; @@ -118,7 +120,7 @@ const wsProtocol = page.url.protocol === "https:" ? "wss:" : "ws:"; ws = new WebSocket(`${wsProtocol}//${page.url.host}/ws?role=streamer`); ws.onopen = async () => { - await camera.startCamera(); + await sourceMedia.start(); }; } @@ -194,7 +196,7 @@ case "i": { // Apply quality change - await camera.applyQuality(stringToQuality(params.split(":")[1])); + await sourceMedia.applyQuality(stringToQuality(params.split(":")[1])); break; } @@ -218,13 +220,30 @@ {#if !ws} - +
+ + +
{:else}
+ Company Logo
- + {#if isCamera} + + {:else} + + {/if} {/if} diff --git a/frontend/src/routes/stream/camera.svelte b/frontend/src/routes/stream/camera.svelte index 141e6bc..62772d0 100644 --- a/frontend/src/routes/stream/camera.svelte +++ b/frontend/src/routes/stream/camera.svelte @@ -142,7 +142,7 @@ videoHeight = settings.height ?? 0; } - export async function startCamera() { + export async function start() { try { await preventScreenLock(); const q = QUALITY_PROFILES[currentQuality]; diff --git a/frontend/src/routes/stream/screen.svelte b/frontend/src/routes/stream/screen.svelte new file mode 100644 index 0000000..abee44f --- /dev/null +++ b/frontend/src/routes/stream/screen.svelte @@ -0,0 +1,149 @@ + + +
+
+ +
+
+ + diff --git a/frontend/src/routes/view/+page.svelte b/frontend/src/routes/view/+page.svelte index 6e27427..a6c32da 100644 --- a/frontend/src/routes/view/+page.svelte +++ b/frontend/src/routes/view/+page.svelte @@ -10,6 +10,7 @@ let streamer = $derived(page.url.searchParams.get("streamer") || ""); let preview: HTMLVideoElement; + let isMuted = $state(false); $effect(() => { const wsProtocol = page.url.protocol === "https:" ? "wss:" : "ws:"; @@ -120,24 +121,80 @@ if (internalPc) internalPc.close(); }; }); + + function toggleMute() { + preview.muted = !preview.muted; + isMuted = preview.muted; + } - +
+
+ +
+ +
diff --git a/frontend/static/icons/camera.svg b/frontend/static/icons/camera.svg new file mode 100644 index 0000000..a736d09 --- /dev/null +++ b/frontend/static/icons/camera.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/static/icons/mute.svg b/frontend/static/icons/mute.svg new file mode 100644 index 0000000..cb38016 --- /dev/null +++ b/frontend/static/icons/mute.svg @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/frontend/static/icons/screenshare.svg b/frontend/static/icons/screenshare.svg new file mode 100644 index 0000000..c60c547 --- /dev/null +++ b/frontend/static/icons/screenshare.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/static/icons/unmute.svg b/frontend/static/icons/unmute.svg new file mode 100644 index 0000000..fceef94 --- /dev/null +++ b/frontend/static/icons/unmute.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/static/image/logo.png b/frontend/static/image/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8040567492307b8cfe511958275fa8a3941eb60d GIT binary patch literal 36497 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 S^>6?K5O})!xvX Date: Sun, 19 Apr 2026 17:24:30 -0300 Subject: [PATCH 4/9] feat: major refactor --- frontend/src/lib/WebRTCManager.ts | 150 +++++++++++ .../src/{routes/stream => lib}/camera.svelte | 19 +- frontend/src/lib/global.css | 1 + frontend/src/lib/interfaces/RemoteMedia.ts | 3 + frontend/src/lib/interfaces/Role.ts | 5 + frontend/src/lib/player.svelte | 19 ++ .../src/{routes/stream => lib}/screen.svelte | 4 +- frontend/src/lib/viewer.svelte | 0 frontend/src/lib/websocketManager.ts | 104 ++++++++ frontend/src/routes/controller/+page.svelte | 28 ++- frontend/src/routes/stream/+page.svelte | 232 ++++++------------ frontend/src/routes/view/+page.svelte | 220 ++++++++--------- 12 files changed, 499 insertions(+), 286 deletions(-) create mode 100644 frontend/src/lib/WebRTCManager.ts rename frontend/src/{routes/stream => lib}/camera.svelte (94%) create mode 100644 frontend/src/lib/interfaces/RemoteMedia.ts create mode 100644 frontend/src/lib/interfaces/Role.ts create mode 100644 frontend/src/lib/player.svelte rename frontend/src/{routes/stream => lib}/screen.svelte (97%) delete mode 100644 frontend/src/lib/viewer.svelte create mode 100644 frontend/src/lib/websocketManager.ts diff --git a/frontend/src/lib/WebRTCManager.ts b/frontend/src/lib/WebRTCManager.ts new file mode 100644 index 0000000..119a5d8 --- /dev/null +++ b/frontend/src/lib/WebRTCManager.ts @@ -0,0 +1,150 @@ +const ICE_CONFIG = { + iceServers: [ + { urls: "stun:stun.l.google.com:19302" }, + { urls: "stun:stun1.l.google.com:19302" }, + ], +}; + +interface WRTCManagerCallbacks { + onRemoteTrack?: (track: MediaStreamTrack, streams: readonly MediaStream[]) => void; + onICECandidate?: (connectionId: string, candidate: RTCIceCandidateInit) => void; +} + +export class WRTCManager { + private connections: Record = {}; + private candidateQueues: Record = {}; + private Callbacks: WRTCManagerCallbacks; + + constructor(callbacks: WRTCManagerCallbacks) { + this.Callbacks = callbacks; + } + + public finish() { + for (const connectionId in this.connections) { + this.closePeerConnection(connectionId); + } + } + + public createPeerConnection(connectionId: string) { + if (this.connections[connectionId]) { + this.connections[connectionId].close(); + } + + const pc = new RTCPeerConnection(ICE_CONFIG); + console.log("Creating PeerConnection for ID:", connectionId); + this.connections[connectionId] = pc; + this.candidateQueues[connectionId] = []; + + pc.addEventListener('connectionstatechange', () => this.onConnectionState(connectionId, pc.connectionState)); + pc.addEventListener('track', ({ track, streams }) => this.Callbacks.onRemoteTrack?.(track, streams)); + pc.addEventListener('icecandidate', ({ candidate }) => { + if (candidate) this.Callbacks.onICECandidate?.(connectionId, candidate.toJSON()); + }); + + return pc; + } + + private async onConnectionState(connectionId: string, state: RTCPeerConnectionState) { + switch (state) { + case "closed": + case "failed": + this.closePeerConnection(connectionId); + break; + case "connected": + this.flushCandidateQueue(connectionId); + break; + default: + break; + } + } + + public closePeerConnection(connectionId: string) { + const pc = this.connections[connectionId]; + if (!pc) return; + pc.close(); + delete this.connections[connectionId]; + delete this.candidateQueues[connectionId]; + } + + public async addTrack(connectionId: string, track: MediaStreamTrack) { + const pc = this.connections[connectionId]; + if (!pc) throw new Error("[AddTrack] PeerConnection not found for ID: " + connectionId); + pc.addTrack(track); + } + + public async setStreamBandwidth( + connectionId: string, + maxBitrate: number, + maxFramerate: number, + ) { + const peerConnection = this.connections[connectionId]; + if (!peerConnection) throw new Error("[SetStreamBandwidth] PeerConnection not found for ID: " + connectionId); + + for (const sender of peerConnection.getSenders()) { + if (sender.track?.kind !== "video") continue; + const params = sender.getParameters(); + if (!params.encodings?.length) params.encodings = [{}]; + params.encodings[0].maxBitrate = maxBitrate; + params.encodings[0].maxFramerate = maxFramerate; + params.encodings[0].networkPriority = "high"; + params.encodings[0].active = true; + params.encodings[0].priority = "high"; + params.encodings[0].scaleResolutionDownBy = 1.0; + await sender.setParameters(params).catch(() => { }); + } + } + + public getConnections() { + return Object.keys(this.connections); + } + + public getPeerConnection(connectionId: string) { + return this.connections[connectionId]; + } + + public async addICECandidate(connectionId: string, candidate: RTCIceCandidateInit) { + const pc = this.connections[connectionId]; + console.log(pc.signalingState); + console.log(pc.connectionState); + if (!pc || !pc.remoteDescription) { + this.candidateQueues[connectionId]?.push(candidate); + return; + } + await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch((err) => { + console.warn(`[AddICECandidate] Failed to add ICE candidate for ID: ${connectionId}`, err); + }); + } + + public async flushCandidateQueue(connectionId: string) { + const pc = this.connections[connectionId]; + if (!pc || !pc.remoteDescription) return; + const queue = this.candidateQueues[connectionId] || []; + for await (const candidate of queue) { + await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch(() => { }); + } + this.candidateQueues[connectionId] = []; + } + + public async setRemoteDescription(connectionId: string, description: RTCSessionDescriptionInit) { + const pc = this.connections[connectionId]; + if (!pc || (pc.currentLocalDescription !== null && pc.signalingState === "stable")) throw new Error("[SetRemoteDescription] PeerConnection not found or is already connected for ID: " + connectionId); + await pc.setRemoteDescription(new RTCSessionDescription(description)); + this.flushCandidateQueue(connectionId); + } + + public async createStreamOffer(connectionId: string) { + const pc = this.connections[connectionId]; + if (!pc) throw new Error("[CreateStreamOffer] PeerConnection not found for ID: " + connectionId); + const offer = await pc.createOffer(); + await pc.setLocalDescription(offer); + return offer; + } + + public async createStreamAnswer(connectionId: string) { + const pc = this.connections[connectionId]; + if (!pc) throw new Error("[CreateStreamAnswer] PeerConnection not found for ID: " + connectionId); + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + return answer; + } +} \ No newline at end of file diff --git a/frontend/src/routes/stream/camera.svelte b/frontend/src/lib/camera.svelte similarity index 94% rename from frontend/src/routes/stream/camera.svelte rename to frontend/src/lib/camera.svelte index 62772d0..3d2287b 100644 --- a/frontend/src/routes/stream/camera.svelte +++ b/frontend/src/lib/camera.svelte @@ -36,7 +36,10 @@ zoom: number; rotation: number; }) => void; - setStreamingBandwidth: (maxBitrate: number, maxFramerate: number) => void; + setStreamingBandwidth: ( + maxBitrate: number, + maxFramerate: number, + ) => Promise; } let props: Props = $props(); @@ -86,21 +89,25 @@ if (localStream) { const vTrack = localStream.getVideoTracks()[0]; if (vTrack) { + vTrack.contentHint = "detail"; await vTrack .applyConstraints({ - width: { exact: q.width }, - height: { exact: q.height }, - frameRate: { exact: q.frameRate }, + width: { exact: q.width, min: q.width, max: q.width }, + height: { exact: q.height, min: q.height, max: q.height }, + frameRate: { + exact: q.frameRate, + min: q.frameRate, + max: q.frameRate, + }, }) .catch(() => {}); } } - props.setStreamingBandwidth(q.bitrate, q.frameRate); + await props.setStreamingBandwidth(q.bitrate, q.frameRate); } function handleTouchStart(e: TouchEvent) { - console.log(e.touches.length); if (e.touches.length === 2) { initialPinchDistance = Math.hypot( e.touches[0].clientX - e.touches[1].clientX, diff --git a/frontend/src/lib/global.css b/frontend/src/lib/global.css index 0d48a8c..993f869 100644 --- a/frontend/src/lib/global.css +++ b/frontend/src/lib/global.css @@ -18,4 +18,5 @@ body { display: flex; justify-content: center; align-items: center; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } \ No newline at end of file diff --git a/frontend/src/lib/interfaces/RemoteMedia.ts b/frontend/src/lib/interfaces/RemoteMedia.ts new file mode 100644 index 0000000..0e2e46d --- /dev/null +++ b/frontend/src/lib/interfaces/RemoteMedia.ts @@ -0,0 +1,3 @@ +export interface RemoteMedia { + +} \ No newline at end of file diff --git a/frontend/src/lib/interfaces/Role.ts b/frontend/src/lib/interfaces/Role.ts new file mode 100644 index 0000000..1e557b3 --- /dev/null +++ b/frontend/src/lib/interfaces/Role.ts @@ -0,0 +1,5 @@ +export enum Role { + Streamer = 'streamer', + Viewer = 'viewer', + Controller = 'controller', +}; \ No newline at end of file diff --git a/frontend/src/lib/player.svelte b/frontend/src/lib/player.svelte new file mode 100644 index 0000000..d91e15e --- /dev/null +++ b/frontend/src/lib/player.svelte @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/frontend/src/routes/stream/screen.svelte b/frontend/src/lib/screen.svelte similarity index 97% rename from frontend/src/routes/stream/screen.svelte rename to frontend/src/lib/screen.svelte index abee44f..c026d02 100644 --- a/frontend/src/routes/stream/screen.svelte +++ b/frontend/src/lib/screen.svelte @@ -23,7 +23,7 @@ zoom: number; rotation: number; }) => void; - setStreamingBandwidth: (maxBitrate: number, maxFramerate: number) => void; + setStreamingBandwidth: (maxBitrate: number, maxFramerate: number) => Promise; } let props: Props = $props(); @@ -54,7 +54,7 @@ } } - props.setStreamingBandwidth(q.bitrate, q.frameRate); + await props.setStreamingBandwidth(q.bitrate, q.frameRate); } export async function recalculateVideoDimensions(retryAttempt = 0) { diff --git a/frontend/src/lib/viewer.svelte b/frontend/src/lib/viewer.svelte deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/lib/websocketManager.ts b/frontend/src/lib/websocketManager.ts new file mode 100644 index 0000000..db0afb8 --- /dev/null +++ b/frontend/src/lib/websocketManager.ts @@ -0,0 +1,104 @@ +import type { Role } from "./interfaces/Role"; + +export class WebsocketManager { + private hostProtocol?: string; + private hostname?: string; + private role: Role; + private socket?: WebSocket; + private commandParser: (command: string, params: string) => void; + private extraQueries?: [string, string][]; + private retryCount = 0; + private isRetrying = false; + private isExiting = false; + private messagePool: string[] = []; + + constructor(role: Role, commandParser: (command: string, params: string) => void) { + this.role = role; + this.commandParser = commandParser; + } + + async connect(host: { protocol: string, hostname: string, extraQueries?: [string, string][] }) { + this.hostProtocol = host.protocol; + this.hostname = host.hostname; + this.extraQueries = host.extraQueries; + + const wsProtocol = this.hostProtocol === "https:" ? "wss:" : "ws:"; + this.socket = new WebSocket( + `${wsProtocol}//${this.hostname}/ws?role=${this.role}` + (this.extraQueries ? "&" + this.extraQueries.map(([k, v]) => `${k}=${v}`).join("&") : ""), + ); + + this.socket.addEventListener('message', (event) => { this.onMessage(event.data) }); + this.socket.addEventListener('error', this.onError.bind(this)); + this.socket.addEventListener('close', this.onClose.bind(this)); + this.socket.addEventListener('open', () => { + this.onOpen(); + return Promise.resolve(); + }); + } + + public finish() { + this.isExiting = true; + if (this.socket) { + this.socket.close(); + this.socket = undefined; + } + } + + private onOpen() { + console.log('Connection established'); + this.retryCount = 0; + // Send any queued messages + while (this.messagePool.length > 0) { + const message = this.messagePool.shift(); + if (message) { + this.send(message); + } + } + } + + private onMessage(message: ArrayBuffer) { + const commandStr: string = message.toString(); + const parts = commandStr.split(":"); + const command = parts.shift(); + const params = parts.join(":"); + + if (command) { + this.commandParser(command, params); + } else { + console.warn('Received invalid command:', commandStr); + } + } + + private onError() { + console.log('Connection error'); + } + + private retryConnection() { + if (this.retryCount < 10 && !this.isRetrying && !this.isExiting) { + this.isRetrying = true; + console.log(`Attempting to reconnect... (attempt ${this.retryCount + 1})`); + setTimeout(async () => { + this.isRetrying = false; + await this.connect({ + protocol: this.hostProtocol!, + hostname: this.hostname!, + extraQueries: this.extraQueries, + }); + }, Math.min(1000 * (2 ** this.retryCount), 30000)); // cap at 30 seconds + this.retryCount++; + } + } + + private onClose() { + console.log('Connection closed'); + this.retryConnection(); + } + + public send(message: string) { + if (this.socket && this.socket.readyState === WebSocket.OPEN) { + this.socket.send(message); + } else { + this.messagePool.push(message); + } + } +} \ No newline at end of file diff --git a/frontend/src/routes/controller/+page.svelte b/frontend/src/routes/controller/+page.svelte index c563633..55a3627 100644 --- a/frontend/src/routes/controller/+page.svelte +++ b/frontend/src/routes/controller/+page.svelte @@ -6,7 +6,19 @@
-
+
+

Logs

+

+ > MobileCam V1.0.0 +
+
+ Transmissores: 0
+ Receptores: 0 +
+
+ Controller_id: 0 +

+
@@ -52,6 +64,20 @@ color: var(--accent-color); } + .logsBoxTitle { + font-size: 1.25rem; + padding: 0.5rem 1rem; + background: hsla(from var(--accent-color) h s l / 0.3); + width: 100%; + height: fit-content; + } + + .logText { + padding-inline: .5rem; + font-family: monospace; + font-size: 1rem; + } + .logo { width: 100%; object-fit: contain; diff --git a/frontend/src/routes/stream/+page.svelte b/frontend/src/routes/stream/+page.svelte index b5bd4f7..f90f849 100644 --- a/frontend/src/routes/stream/+page.svelte +++ b/frontend/src/routes/stream/+page.svelte @@ -1,9 +1,13 @@ -{#if !ws} +{#if !isSocketOpen}
+ {/if} + {#if loading} +
+ {:else if error} +

{error}

+ {:else if result} +
{@render result[0](result[1])}
+ {/if} +
+ +{/if} + + diff --git a/frontend/src/lib/camera.svelte b/frontend/src/lib/camera.svelte index 3d2287b..0a66692 100644 --- a/frontend/src/lib/camera.svelte +++ b/frontend/src/lib/camera.svelte @@ -1,19 +1,46 @@
-
- + { + applyZoom(newValue); + }} />
@@ -260,25 +273,23 @@ justify-content: center; align-items: center; } + .overlay { + position: absolute; + width: 7rem; + height: 100%; + top: 0; + right: 0; + padding: 1rem; + z-index: 999; + } + .videoPreview { - width: 100vw; - max-width: calc(90dvh * (var(--width) / var(--height)) - 1rem); - aspect-ratio: calc(var(--width) / var(--height)); - height: auto; background: black; position: relative; pointer-events: none; overflow: hidden; - } - - #preview { width: 100%; height: 100%; - object-fit: contain; - pointer-events: all; - position: absolute; - top: 0; - left: 0; } .hoz-rulers, @@ -313,51 +324,4 @@ height: 1px; } } - - .zoomSlider { - position: absolute; - right: 0; - top: 0; - z-index: 999; - height: 100%; - padding: 1rem; - } - - .inputRange { - --slider-bg-color: hsla(from var(--accent-color) h calc(s/3) l); - --deselected-bg-color: #444; - - appearance: none; - width: 3rem; - height: 100%; - border: 1px solid #333333; - background: linear-gradient( - 0deg, - var(--slider-bg-color) 0%, - var(--deselected-bg-color) 0% - ); - writing-mode: vertical-rl; - direction: rtl; - cursor: pointer; - } - - /* Thumb: for Chrome, Safari, Edge */ - .inputRange::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 3rem; - height: 0.5rem; - background: var(--accent-color); - box-shadow: none; - } - - /* Thumb: for Firefox */ - .inputRange::-moz-range-thumb { - border: none; - border-radius: 0; - width: 3rem; - height: 0.5rem; - background: var(--accent-color); - box-shadow: none; - } diff --git a/frontend/src/lib/consts.ts b/frontend/src/lib/consts.ts new file mode 100644 index 0000000..f445beb --- /dev/null +++ b/frontend/src/lib/consts.ts @@ -0,0 +1,6 @@ +export const ICE_CONFIG = { + iceServers: [ + { urls: "stun:stun.l.google.com:19302" }, + { urls: "stun:stun1.l.google.com:19302" }, + ], +}; \ No newline at end of file diff --git a/frontend/src/lib/global.css b/frontend/src/lib/global.css index 993f869..1dd6528 100644 --- a/frontend/src/lib/global.css +++ b/frontend/src/lib/global.css @@ -2,6 +2,7 @@ --background: #191919; --accent-color: #54ff98; --black: #000; + --white: #f0f0f0; } * { @@ -19,4 +20,4 @@ body { justify-content: center; align-items: center; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; -} \ No newline at end of file +} diff --git a/frontend/src/lib/interfaces/Quality.ts b/frontend/src/lib/interfaces/Quality.ts deleted file mode 100644 index fb94c75..0000000 --- a/frontend/src/lib/interfaces/Quality.ts +++ /dev/null @@ -1 +0,0 @@ -export type Quality = "high" | "medium" | "low"; \ No newline at end of file diff --git a/frontend/src/lib/interfaces/session.ts b/frontend/src/lib/interfaces/session.ts new file mode 100644 index 0000000..3adc9a7 --- /dev/null +++ b/frontend/src/lib/interfaces/session.ts @@ -0,0 +1,6 @@ +import type { SessionType } from "$lib/protos/common"; + +export interface Session { + id: number, + type: SessionType, +} \ No newline at end of file diff --git a/frontend/src/lib/player.svelte b/frontend/src/lib/player.svelte index d91e15e..21edaf9 100644 --- a/frontend/src/lib/player.svelte +++ b/frontend/src/lib/player.svelte @@ -1,19 +1,153 @@ - \ No newline at end of file +
+ {#if props.showOverlay} +
+ +
+ {/if} +
+ +
+
+ + diff --git a/frontend/src/lib/protos/common.ts b/frontend/src/lib/protos/common.ts new file mode 100644 index 0000000..8094e58 --- /dev/null +++ b/frontend/src/lib/protos/common.ts @@ -0,0 +1,696 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.11.6 +// protoc v7.34.1 +// source: common.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = "common"; + +/** Represents the preset quality of the audio and video stream. */ +export enum Quality { + /** QUALITY_UNSPECIFIED - The default value. */ + QUALITY_UNSPECIFIED = 0, + /** QUALITY_LOW - The lowest quality, with the lowest bitrate and resolution. */ + QUALITY_LOW = 1, + /** QUALITY_MEDIUM - The medium quality, with a moderate bitrate and resolution. */ + QUALITY_MEDIUM = 2, + /** QUALITY_HIGH - The highest quality, with the highest bitrate and resolution. */ + QUALITY_HIGH = 3, + UNRECOGNIZED = -1, +} + +export function qualityFromJSON(object: any): Quality { + switch (object) { + case 0: + case "QUALITY_UNSPECIFIED": + return Quality.QUALITY_UNSPECIFIED; + case 1: + case "QUALITY_LOW": + return Quality.QUALITY_LOW; + case 2: + case "QUALITY_MEDIUM": + return Quality.QUALITY_MEDIUM; + case 3: + case "QUALITY_HIGH": + return Quality.QUALITY_HIGH; + case -1: + case "UNRECOGNIZED": + default: + return Quality.UNRECOGNIZED; + } +} + +export function qualityToJSON(object: Quality): string { + switch (object) { + case Quality.QUALITY_UNSPECIFIED: + return "QUALITY_UNSPECIFIED"; + case Quality.QUALITY_LOW: + return "QUALITY_LOW"; + case Quality.QUALITY_MEDIUM: + return "QUALITY_MEDIUM"; + case Quality.QUALITY_HIGH: + return "QUALITY_HIGH"; + case Quality.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** Represents the type of session connected to the server. */ +export enum SessionType { + /** SESSION_TYPE_UNKNOWN - The default value. */ + SESSION_TYPE_UNKNOWN = 0, + /** SESSION_TYPE_CONTROLLER - A session for a controller. */ + SESSION_TYPE_CONTROLLER = 1, + /** SESSION_TYPE_STREAMER - A session for a streamer. */ + SESSION_TYPE_STREAMER = 2, + /** SESSION_TYPE_VIEWER - A session for a viewer. */ + SESSION_TYPE_VIEWER = 3, + UNRECOGNIZED = -1, +} + +export function sessionTypeFromJSON(object: any): SessionType { + switch (object) { + case 0: + case "SESSION_TYPE_UNKNOWN": + return SessionType.SESSION_TYPE_UNKNOWN; + case 1: + case "SESSION_TYPE_CONTROLLER": + return SessionType.SESSION_TYPE_CONTROLLER; + case 2: + case "SESSION_TYPE_STREAMER": + return SessionType.SESSION_TYPE_STREAMER; + case 3: + case "SESSION_TYPE_VIEWER": + return SessionType.SESSION_TYPE_VIEWER; + case -1: + case "UNRECOGNIZED": + default: + return SessionType.UNRECOGNIZED; + } +} + +export function sessionTypeToJSON(object: SessionType): string { + switch (object) { + case SessionType.SESSION_TYPE_UNKNOWN: + return "SESSION_TYPE_UNKNOWN"; + case SessionType.SESSION_TYPE_CONTROLLER: + return "SESSION_TYPE_CONTROLLER"; + case SessionType.SESSION_TYPE_STREAMER: + return "SESSION_TYPE_STREAMER"; + case SessionType.SESSION_TYPE_VIEWER: + return "SESSION_TYPE_VIEWER"; + case SessionType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** Requests to answer an WebRTC offer from the streamer to establish a WebRTC connection. */ +export interface RequestRtcAnswer { + /** The ID of the streamer to request the WebRTC answer from. */ + streamerId: number; + /** The offer created by the streamer in response to the viewer or controller's request. */ + offer: string; + /** The video transform from the streamer to apply to the stream. */ + videoTransform?: VideoTransform | undefined; +} + +/** Response to the viewer or controller's request for an WebRTC offer, containing the answer of the WebRTC offer. */ +export interface RtcAnswerResponse { + /** The ID of the streamer that created the offer. */ + streamerId: number; + /** The answer created by the streamer in response to the viewer or controller's request. */ + answer: string; +} + +/** Represents an ICE candidate from the streamer or viewer. */ +export interface IceCandidate { + /** The ID of the session that the candidate is for or from. */ + session: + | Session + | undefined; + /** The candidate string. */ + candidate: string; +} + +/** Represents the video transform to apply to the stream. */ +export interface VideoTransform { + /** The zoom level to apply to the stream. A value of 1.0 means no zoom, while a value greater than 1.0 means zoom in and a value less than 1.0 means zoom out. */ + zoom: number; + /** The rotation to apply to the stream in degrees. A value of 0 means no rotation, while a value of 90 means rotate 90 degrees clockwise, and a value of -90 means rotate 90 degrees counterclockwise. */ + rotation: number; +} + +/** / Represents the video transform to apply to the stream update request. */ +export interface UpdateVideoTransform { + /** The ID of the streamer to update the video transform for. */ + streamerId: number; + /** The new video transform to apply to the stream. */ + videoTransform: VideoTransform | undefined; +} + +/** Represents a session ID and type, which can be used to identify a session in the server. */ +export interface Session { + /** The ID of the session. */ + id: number; + /** The type of the session. */ + type: SessionType; +} + +function createBaseRequestRtcAnswer(): RequestRtcAnswer { + return { streamerId: 0, offer: "", videoTransform: undefined }; +} + +export const RequestRtcAnswer: MessageFns = { + encode(message: RequestRtcAnswer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.offer !== "") { + writer.uint32(18).string(message.offer); + } + if (message.videoTransform !== undefined) { + VideoTransform.encode(message.videoTransform, writer.uint32(26).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RequestRtcAnswer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRequestRtcAnswer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.offer = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.videoTransform = VideoTransform.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RequestRtcAnswer { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + offer: isSet(object.offer) ? globalThis.String(object.offer) : "", + videoTransform: isSet(object.videoTransform) + ? VideoTransform.fromJSON(object.videoTransform) + : isSet(object.video_transform) + ? VideoTransform.fromJSON(object.video_transform) + : undefined, + }; + }, + + toJSON(message: RequestRtcAnswer): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.offer !== "") { + obj.offer = message.offer; + } + if (message.videoTransform !== undefined) { + obj.videoTransform = VideoTransform.toJSON(message.videoTransform); + } + return obj; + }, + + create, I>>(base?: I): RequestRtcAnswer { + return RequestRtcAnswer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RequestRtcAnswer { + const message = createBaseRequestRtcAnswer(); + message.streamerId = object.streamerId ?? 0; + message.offer = object.offer ?? ""; + message.videoTransform = (object.videoTransform !== undefined && object.videoTransform !== null) + ? VideoTransform.fromPartial(object.videoTransform) + : undefined; + return message; + }, +}; + +function createBaseRtcAnswerResponse(): RtcAnswerResponse { + return { streamerId: 0, answer: "" }; +} + +export const RtcAnswerResponse: MessageFns = { + encode(message: RtcAnswerResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.answer !== "") { + writer.uint32(18).string(message.answer); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RtcAnswerResponse { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRtcAnswerResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.answer = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RtcAnswerResponse { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + answer: isSet(object.answer) ? globalThis.String(object.answer) : "", + }; + }, + + toJSON(message: RtcAnswerResponse): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.answer !== "") { + obj.answer = message.answer; + } + return obj; + }, + + create, I>>(base?: I): RtcAnswerResponse { + return RtcAnswerResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RtcAnswerResponse { + const message = createBaseRtcAnswerResponse(); + message.streamerId = object.streamerId ?? 0; + message.answer = object.answer ?? ""; + return message; + }, +}; + +function createBaseIceCandidate(): IceCandidate { + return { session: undefined, candidate: "" }; +} + +export const IceCandidate: MessageFns = { + encode(message: IceCandidate, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.session !== undefined) { + Session.encode(message.session, writer.uint32(10).fork()).join(); + } + if (message.candidate !== "") { + writer.uint32(18).string(message.candidate); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): IceCandidate { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseIceCandidate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.session = Session.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.candidate = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): IceCandidate { + return { + session: isSet(object.session) ? Session.fromJSON(object.session) : undefined, + candidate: isSet(object.candidate) ? globalThis.String(object.candidate) : "", + }; + }, + + toJSON(message: IceCandidate): unknown { + const obj: any = {}; + if (message.session !== undefined) { + obj.session = Session.toJSON(message.session); + } + if (message.candidate !== "") { + obj.candidate = message.candidate; + } + return obj; + }, + + create, I>>(base?: I): IceCandidate { + return IceCandidate.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): IceCandidate { + const message = createBaseIceCandidate(); + message.session = (object.session !== undefined && object.session !== null) + ? Session.fromPartial(object.session) + : undefined; + message.candidate = object.candidate ?? ""; + return message; + }, +}; + +function createBaseVideoTransform(): VideoTransform { + return { zoom: 0, rotation: 0 }; +} + +export const VideoTransform: MessageFns = { + encode(message: VideoTransform, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.zoom !== 0) { + writer.uint32(13).float(message.zoom); + } + if (message.rotation !== 0) { + writer.uint32(16).int32(message.rotation); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): VideoTransform { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTransform(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 13) { + break; + } + + message.zoom = reader.float(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.rotation = reader.int32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): VideoTransform { + return { + zoom: isSet(object.zoom) ? globalThis.Number(object.zoom) : 0, + rotation: isSet(object.rotation) ? globalThis.Number(object.rotation) : 0, + }; + }, + + toJSON(message: VideoTransform): unknown { + const obj: any = {}; + if (message.zoom !== 0) { + obj.zoom = message.zoom; + } + if (message.rotation !== 0) { + obj.rotation = Math.round(message.rotation); + } + return obj; + }, + + create, I>>(base?: I): VideoTransform { + return VideoTransform.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): VideoTransform { + const message = createBaseVideoTransform(); + message.zoom = object.zoom ?? 0; + message.rotation = object.rotation ?? 0; + return message; + }, +}; + +function createBaseUpdateVideoTransform(): UpdateVideoTransform { + return { streamerId: 0, videoTransform: undefined }; +} + +export const UpdateVideoTransform: MessageFns = { + encode(message: UpdateVideoTransform, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.videoTransform !== undefined) { + VideoTransform.encode(message.videoTransform, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): UpdateVideoTransform { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateVideoTransform(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.videoTransform = VideoTransform.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): UpdateVideoTransform { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + videoTransform: isSet(object.videoTransform) + ? VideoTransform.fromJSON(object.videoTransform) + : isSet(object.video_transform) + ? VideoTransform.fromJSON(object.video_transform) + : undefined, + }; + }, + + toJSON(message: UpdateVideoTransform): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.videoTransform !== undefined) { + obj.videoTransform = VideoTransform.toJSON(message.videoTransform); + } + return obj; + }, + + create, I>>(base?: I): UpdateVideoTransform { + return UpdateVideoTransform.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UpdateVideoTransform { + const message = createBaseUpdateVideoTransform(); + message.streamerId = object.streamerId ?? 0; + message.videoTransform = (object.videoTransform !== undefined && object.videoTransform !== null) + ? VideoTransform.fromPartial(object.videoTransform) + : undefined; + return message; + }, +}; + +function createBaseSession(): Session { + return { id: 0, type: 0 }; +} + +export const Session: MessageFns = { + encode(message: Session, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.id !== 0) { + writer.uint32(8).uint64(message.id); + } + if (message.type !== 0) { + writer.uint32(16).int32(message.type); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): Session { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSession(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.id = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.type = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): Session { + return { + id: isSet(object.id) ? globalThis.Number(object.id) : 0, + type: isSet(object.type) ? sessionTypeFromJSON(object.type) : 0, + }; + }, + + toJSON(message: Session): unknown { + const obj: any = {}; + if (message.id !== 0) { + obj.id = Math.round(message.id); + } + if (message.type !== 0) { + obj.type = sessionTypeToJSON(message.type); + } + return obj; + }, + + create, I>>(base?: I): Session { + return Session.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Session { + const message = createBaseSession(); + message.id = object.id ?? 0; + message.type = object.type ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(int64: { toString(): string }): number { + const num = globalThis.Number(int64.toString()); + if (num > globalThis.Number.MAX_SAFE_INTEGER) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + if (num < globalThis.Number.MIN_SAFE_INTEGER) { + throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER"); + } + return num; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/frontend/src/lib/protos/controller.ts b/frontend/src/lib/protos/controller.ts new file mode 100644 index 0000000..45fb172 --- /dev/null +++ b/frontend/src/lib/protos/controller.ts @@ -0,0 +1,1985 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.11.6 +// protoc v7.34.1 +// source: controller.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; +import { + IceCandidate, + Quality, + qualityFromJSON, + qualityToJSON, + RequestRtcAnswer, + RtcAnswerResponse, + Session, + UpdateVideoTransform, +} from "./common"; + +export const protobufPackage = "controller"; + +/** The controller is ready to receive commands from the server. */ +export interface Ready { + /** The ID of the controller that is ready. */ + id: number; + /** The list of streamers connected to the server. */ + streamers: StreamerSession[]; + /** The number of viewers connected to the server. */ + viewerCount: number; +} + +/** Contains the different commands that the controller can send to the server. */ +export interface ClientToServer { + /** Responds to the streamer WebRTC offer. */ + rtcAnswerResponse?: + | RtcAnswerResponse + | undefined; + /** Controller is requesting a change in the quality of the stream from the streamer. */ + requestChangeQuality?: + | RequestChangeQuality + | undefined; + /** Controller is requesting the list of streamers connected to the server. */ + listStreamers?: + | ListStreamers + | undefined; + /** Controller is requesting the list of viewers connected to a specific streamer or not watching any streamer. */ + listViewers?: + | ListViewers + | undefined; + /** Controller is requesting the list of all viewers connected to the server. */ + listAllViewers?: + | ListAllViewers + | undefined; + /** Controller is sending an ICE candidate to the streamer to establish a WebRTC connection. */ + iceCandidate?: + | IceCandidate + | undefined; + /** Controller is requesting the streamer to update the zoom level applied to the stream. */ + requestZoom?: + | RequestZoom + | undefined; + /** / Controller is requesting a viewer to update which streamer they are watching. */ + updateWatching?: UpdateWatching | undefined; +} + +/** Contains the different commands that the server can send to the controller. */ +export interface ServerToClient { + /** The streamer is requesting the controller to answer the RTC offer created by the streamer to establish a WebRTC connection. */ + requestRtcAnswer?: + | RequestRtcAnswer + | undefined; + /** The streamer will send a request to update the video transform applied to the stream when the streamer requests it. */ + updateVideoTransform?: + | UpdateVideoTransform + | undefined; + /** Sent by the streamer to the controller when the streamer updates its battery level. */ + batteryLevel?: + | BatteryLevel + | undefined; + /** The streamer will send an ICE candidate to the controller to establish a WebRTC connection. */ + iceCandidate?: + | IceCandidate + | undefined; + /** The streamer will send an update to the controller when the streamer updates its zoom level. */ + updateZoom?: + | UpdateZoom + | undefined; + /** The server will send the list of streamers connected to the server. */ + listStreamersResponse?: + | ListStreamersResponse + | undefined; + /** The server will send the list of viewers connected to the server in response to the controller's request. */ + listViewersResponse?: + | ListViewersResponse + | undefined; + /** The server will send a notification to the controller when a new session, either a streamer or viewer, has connected to the server, when a session has disconnected from the server, or when a viewer has changed which streamer they are watching. */ + newSession?: + | NewSession + | undefined; + /** The server will send a notification to the controller when a session, either a streamer or viewer, has disconnected from the server. */ + dropSession?: + | DropSession + | undefined; + /** The server will send a notification to the controller when a viewer has changed which streamer they are watching. */ + changedWatching?: ChangedWatching | undefined; +} + +/** Requests a change in the quality of the stream from the streamer. */ +export interface RequestChangeQuality { + /** The ID of the streamer to change the quality for. */ + streamerId: number; + /** The new quality to set for the stream. */ + quality: Quality; +} + +/** Requests the list of streamers connected to the server. */ +export interface ListStreamers { +} + +/** Requests the list of viewers connected to the server based on if they are watching a specific streamer or not. */ +export interface ListViewers { + /** The ID of the streamer to list the viewers for. If not provided, the server will return the list of viewers that are not watching any streamer. */ + streamerId?: number | undefined; +} + +/** Requests the list of all viewers connected to the server. */ +export interface ListAllViewers { +} + +/** Response to a request for the list of streamers connected to the server. */ +export interface ListStreamersResponse { + /** The list of streamers connected to the server. */ + streamers: StreamerSession[]; +} + +/** Response to a request for the list of viewers connected to the server. */ +export interface ListViewersResponse { + /** The list of viewers connected to the server. */ + viewers: ViewerSession[]; +} + +/** Represents a new session that has connected to the server, either a streamer or a viewer. */ +export interface NewSession { + /** A new streamer has connected to the server. */ + streamerSession?: + | StreamerSession + | undefined; + /** A new viewer has connected to the server. */ + viewerSession?: ViewerSession | undefined; +} + +/** Represents a session that has disconnected from the server, either a streamer or a viewer. */ +export interface DropSession { + /** The session that has disconnected from the server, either a streamer or a viewer. */ + session: Session | undefined; +} + +/** Represents a viewer that has changed which streamer they are watching. */ +export interface ChangedWatching { + /** The ID of the viewer that has changed which streamer they are watching. */ + viewerId: number; + /** The ID of the streamer that the viewer is now watching. If not provided, the viewer is not watching any streamer. */ + streamerId?: number | undefined; +} + +/** Represents a streamer session connected to the server. */ +export interface StreamerSession { + /** The ID of the streamer. */ + id: number; + /** The name of the streamer. */ + name?: + | string + | undefined; + /** The zoom level of the streamer. */ + zoom?: + | number + | undefined; + /** The battery level of the streamer, represented as a percentage from 0 to 100. */ + batteryLevel?: number | undefined; +} + +/** Represents a viewer session connected to the server. */ +export interface ViewerSession { + /** The ID of the viewer. */ + id: number; + /** The name of the viewer. */ + name?: + | string + | undefined; + /** The ID of the streamer the viewer is connected to. */ + streamerId?: number | undefined; +} + +/** The streamer is updating its battery level. */ +export interface BatteryLevel { + /** The ID of the streamer that has updated its battery level. */ + streamerId: number; + /** The new battery level, represented as a percentage from 0 to 100. */ + batteryLevel: number; +} + +/** Requests the streamer to update the zoom level applied to the stream. */ +export interface RequestZoom { + /** The ID of the streamer to request the zoom level for. */ + streamerId: number; + /** The new zoom level to set for the stream. */ + zoom: number; +} + +/** Represents a streamer that has updated its zoom level. */ +export interface UpdateZoom { + /** The ID of the streamer that has updated its zoom level. */ + streamerId: number; + /** The new zoom level that the streamer has updated to. */ + zoom: number; +} + +/** Update sent by the controller to the server when a viewer should update which streamer they are watching. */ +export interface UpdateWatching { + /** The ID of the viewer that has updated which streamer they are watching. */ + viewerId: number; + /** The ID of the streamer that the viewer is now watching. If not provided, the viewer is not watching any streamer. */ + streamerId?: number | undefined; +} + +function createBaseReady(): Ready { + return { id: 0, streamers: [], viewerCount: 0 }; +} + +export const Ready: MessageFns = { + encode(message: Ready, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.id !== 0) { + writer.uint32(8).uint64(message.id); + } + for (const v of message.streamers) { + StreamerSession.encode(v!, writer.uint32(18).fork()).join(); + } + if (message.viewerCount !== 0) { + writer.uint32(24).uint32(message.viewerCount); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): Ready { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseReady(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.id = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.streamers.push(StreamerSession.decode(reader, reader.uint32())); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.viewerCount = reader.uint32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): Ready { + return { + id: isSet(object.id) ? globalThis.Number(object.id) : 0, + streamers: globalThis.Array.isArray(object?.streamers) + ? object.streamers.map((e: any) => StreamerSession.fromJSON(e)) + : [], + viewerCount: isSet(object.viewerCount) + ? globalThis.Number(object.viewerCount) + : isSet(object.viewer_count) + ? globalThis.Number(object.viewer_count) + : 0, + }; + }, + + toJSON(message: Ready): unknown { + const obj: any = {}; + if (message.id !== 0) { + obj.id = Math.round(message.id); + } + if (message.streamers?.length) { + obj.streamers = message.streamers.map((e) => StreamerSession.toJSON(e)); + } + if (message.viewerCount !== 0) { + obj.viewerCount = Math.round(message.viewerCount); + } + return obj; + }, + + create, I>>(base?: I): Ready { + return Ready.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Ready { + const message = createBaseReady(); + message.id = object.id ?? 0; + message.streamers = object.streamers?.map((e) => StreamerSession.fromPartial(e)) || []; + message.viewerCount = object.viewerCount ?? 0; + return message; + }, +}; + +function createBaseClientToServer(): ClientToServer { + return { + rtcAnswerResponse: undefined, + requestChangeQuality: undefined, + listStreamers: undefined, + listViewers: undefined, + listAllViewers: undefined, + iceCandidate: undefined, + requestZoom: undefined, + updateWatching: undefined, + }; +} + +export const ClientToServer: MessageFns = { + encode(message: ClientToServer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.rtcAnswerResponse !== undefined) { + RtcAnswerResponse.encode(message.rtcAnswerResponse, writer.uint32(10).fork()).join(); + } + if (message.requestChangeQuality !== undefined) { + RequestChangeQuality.encode(message.requestChangeQuality, writer.uint32(18).fork()).join(); + } + if (message.listStreamers !== undefined) { + ListStreamers.encode(message.listStreamers, writer.uint32(26).fork()).join(); + } + if (message.listViewers !== undefined) { + ListViewers.encode(message.listViewers, writer.uint32(34).fork()).join(); + } + if (message.listAllViewers !== undefined) { + ListAllViewers.encode(message.listAllViewers, writer.uint32(42).fork()).join(); + } + if (message.iceCandidate !== undefined) { + IceCandidate.encode(message.iceCandidate, writer.uint32(50).fork()).join(); + } + if (message.requestZoom !== undefined) { + RequestZoom.encode(message.requestZoom, writer.uint32(58).fork()).join(); + } + if (message.updateWatching !== undefined) { + UpdateWatching.encode(message.updateWatching, writer.uint32(66).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ClientToServer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientToServer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.rtcAnswerResponse = RtcAnswerResponse.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.requestChangeQuality = RequestChangeQuality.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.listStreamers = ListStreamers.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.listViewers = ListViewers.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.listAllViewers = ListAllViewers.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.iceCandidate = IceCandidate.decode(reader, reader.uint32()); + continue; + } + case 7: { + if (tag !== 58) { + break; + } + + message.requestZoom = RequestZoom.decode(reader, reader.uint32()); + continue; + } + case 8: { + if (tag !== 66) { + break; + } + + message.updateWatching = UpdateWatching.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ClientToServer { + return { + rtcAnswerResponse: isSet(object.rtcAnswerResponse) + ? RtcAnswerResponse.fromJSON(object.rtcAnswerResponse) + : isSet(object.rtc_answer_response) + ? RtcAnswerResponse.fromJSON(object.rtc_answer_response) + : undefined, + requestChangeQuality: isSet(object.requestChangeQuality) + ? RequestChangeQuality.fromJSON(object.requestChangeQuality) + : isSet(object.request_change_quality) + ? RequestChangeQuality.fromJSON(object.request_change_quality) + : undefined, + listStreamers: isSet(object.listStreamers) + ? ListStreamers.fromJSON(object.listStreamers) + : isSet(object.list_streamers) + ? ListStreamers.fromJSON(object.list_streamers) + : undefined, + listViewers: isSet(object.listViewers) + ? ListViewers.fromJSON(object.listViewers) + : isSet(object.list_viewers) + ? ListViewers.fromJSON(object.list_viewers) + : undefined, + listAllViewers: isSet(object.listAllViewers) + ? ListAllViewers.fromJSON(object.listAllViewers) + : isSet(object.list_all_viewers) + ? ListAllViewers.fromJSON(object.list_all_viewers) + : undefined, + iceCandidate: isSet(object.iceCandidate) + ? IceCandidate.fromJSON(object.iceCandidate) + : isSet(object.ice_candidate) + ? IceCandidate.fromJSON(object.ice_candidate) + : undefined, + requestZoom: isSet(object.requestZoom) + ? RequestZoom.fromJSON(object.requestZoom) + : isSet(object.request_zoom) + ? RequestZoom.fromJSON(object.request_zoom) + : undefined, + updateWatching: isSet(object.updateWatching) + ? UpdateWatching.fromJSON(object.updateWatching) + : isSet(object.update_watching) + ? UpdateWatching.fromJSON(object.update_watching) + : undefined, + }; + }, + + toJSON(message: ClientToServer): unknown { + const obj: any = {}; + if (message.rtcAnswerResponse !== undefined) { + obj.rtcAnswerResponse = RtcAnswerResponse.toJSON(message.rtcAnswerResponse); + } + if (message.requestChangeQuality !== undefined) { + obj.requestChangeQuality = RequestChangeQuality.toJSON(message.requestChangeQuality); + } + if (message.listStreamers !== undefined) { + obj.listStreamers = ListStreamers.toJSON(message.listStreamers); + } + if (message.listViewers !== undefined) { + obj.listViewers = ListViewers.toJSON(message.listViewers); + } + if (message.listAllViewers !== undefined) { + obj.listAllViewers = ListAllViewers.toJSON(message.listAllViewers); + } + if (message.iceCandidate !== undefined) { + obj.iceCandidate = IceCandidate.toJSON(message.iceCandidate); + } + if (message.requestZoom !== undefined) { + obj.requestZoom = RequestZoom.toJSON(message.requestZoom); + } + if (message.updateWatching !== undefined) { + obj.updateWatching = UpdateWatching.toJSON(message.updateWatching); + } + return obj; + }, + + create, I>>(base?: I): ClientToServer { + return ClientToServer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ClientToServer { + const message = createBaseClientToServer(); + message.rtcAnswerResponse = (object.rtcAnswerResponse !== undefined && object.rtcAnswerResponse !== null) + ? RtcAnswerResponse.fromPartial(object.rtcAnswerResponse) + : undefined; + message.requestChangeQuality = (object.requestChangeQuality !== undefined && object.requestChangeQuality !== null) + ? RequestChangeQuality.fromPartial(object.requestChangeQuality) + : undefined; + message.listStreamers = (object.listStreamers !== undefined && object.listStreamers !== null) + ? ListStreamers.fromPartial(object.listStreamers) + : undefined; + message.listViewers = (object.listViewers !== undefined && object.listViewers !== null) + ? ListViewers.fromPartial(object.listViewers) + : undefined; + message.listAllViewers = (object.listAllViewers !== undefined && object.listAllViewers !== null) + ? ListAllViewers.fromPartial(object.listAllViewers) + : undefined; + message.iceCandidate = (object.iceCandidate !== undefined && object.iceCandidate !== null) + ? IceCandidate.fromPartial(object.iceCandidate) + : undefined; + message.requestZoom = (object.requestZoom !== undefined && object.requestZoom !== null) + ? RequestZoom.fromPartial(object.requestZoom) + : undefined; + message.updateWatching = (object.updateWatching !== undefined && object.updateWatching !== null) + ? UpdateWatching.fromPartial(object.updateWatching) + : undefined; + return message; + }, +}; + +function createBaseServerToClient(): ServerToClient { + return { + requestRtcAnswer: undefined, + updateVideoTransform: undefined, + batteryLevel: undefined, + iceCandidate: undefined, + updateZoom: undefined, + listStreamersResponse: undefined, + listViewersResponse: undefined, + newSession: undefined, + dropSession: undefined, + changedWatching: undefined, + }; +} + +export const ServerToClient: MessageFns = { + encode(message: ServerToClient, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.requestRtcAnswer !== undefined) { + RequestRtcAnswer.encode(message.requestRtcAnswer, writer.uint32(10).fork()).join(); + } + if (message.updateVideoTransform !== undefined) { + UpdateVideoTransform.encode(message.updateVideoTransform, writer.uint32(18).fork()).join(); + } + if (message.batteryLevel !== undefined) { + BatteryLevel.encode(message.batteryLevel, writer.uint32(26).fork()).join(); + } + if (message.iceCandidate !== undefined) { + IceCandidate.encode(message.iceCandidate, writer.uint32(34).fork()).join(); + } + if (message.updateZoom !== undefined) { + UpdateZoom.encode(message.updateZoom, writer.uint32(42).fork()).join(); + } + if (message.listStreamersResponse !== undefined) { + ListStreamersResponse.encode(message.listStreamersResponse, writer.uint32(50).fork()).join(); + } + if (message.listViewersResponse !== undefined) { + ListViewersResponse.encode(message.listViewersResponse, writer.uint32(58).fork()).join(); + } + if (message.newSession !== undefined) { + NewSession.encode(message.newSession, writer.uint32(66).fork()).join(); + } + if (message.dropSession !== undefined) { + DropSession.encode(message.dropSession, writer.uint32(74).fork()).join(); + } + if (message.changedWatching !== undefined) { + ChangedWatching.encode(message.changedWatching, writer.uint32(82).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerToClient { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerToClient(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.requestRtcAnswer = RequestRtcAnswer.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.updateVideoTransform = UpdateVideoTransform.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.batteryLevel = BatteryLevel.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.iceCandidate = IceCandidate.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.updateZoom = UpdateZoom.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.listStreamersResponse = ListStreamersResponse.decode(reader, reader.uint32()); + continue; + } + case 7: { + if (tag !== 58) { + break; + } + + message.listViewersResponse = ListViewersResponse.decode(reader, reader.uint32()); + continue; + } + case 8: { + if (tag !== 66) { + break; + } + + message.newSession = NewSession.decode(reader, reader.uint32()); + continue; + } + case 9: { + if (tag !== 74) { + break; + } + + message.dropSession = DropSession.decode(reader, reader.uint32()); + continue; + } + case 10: { + if (tag !== 82) { + break; + } + + message.changedWatching = ChangedWatching.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerToClient { + return { + requestRtcAnswer: isSet(object.requestRtcAnswer) + ? RequestRtcAnswer.fromJSON(object.requestRtcAnswer) + : isSet(object.request_rtc_answer) + ? RequestRtcAnswer.fromJSON(object.request_rtc_answer) + : undefined, + updateVideoTransform: isSet(object.updateVideoTransform) + ? UpdateVideoTransform.fromJSON(object.updateVideoTransform) + : isSet(object.update_video_transform) + ? UpdateVideoTransform.fromJSON(object.update_video_transform) + : undefined, + batteryLevel: isSet(object.batteryLevel) + ? BatteryLevel.fromJSON(object.batteryLevel) + : isSet(object.battery_level) + ? BatteryLevel.fromJSON(object.battery_level) + : undefined, + iceCandidate: isSet(object.iceCandidate) + ? IceCandidate.fromJSON(object.iceCandidate) + : isSet(object.ice_candidate) + ? IceCandidate.fromJSON(object.ice_candidate) + : undefined, + updateZoom: isSet(object.updateZoom) + ? UpdateZoom.fromJSON(object.updateZoom) + : isSet(object.update_zoom) + ? UpdateZoom.fromJSON(object.update_zoom) + : undefined, + listStreamersResponse: isSet(object.listStreamersResponse) + ? ListStreamersResponse.fromJSON(object.listStreamersResponse) + : isSet(object.list_streamers_response) + ? ListStreamersResponse.fromJSON(object.list_streamers_response) + : undefined, + listViewersResponse: isSet(object.listViewersResponse) + ? ListViewersResponse.fromJSON(object.listViewersResponse) + : isSet(object.list_viewers_response) + ? ListViewersResponse.fromJSON(object.list_viewers_response) + : undefined, + newSession: isSet(object.newSession) + ? NewSession.fromJSON(object.newSession) + : isSet(object.new_session) + ? NewSession.fromJSON(object.new_session) + : undefined, + dropSession: isSet(object.dropSession) + ? DropSession.fromJSON(object.dropSession) + : isSet(object.drop_session) + ? DropSession.fromJSON(object.drop_session) + : undefined, + changedWatching: isSet(object.changedWatching) + ? ChangedWatching.fromJSON(object.changedWatching) + : isSet(object.changed_watching) + ? ChangedWatching.fromJSON(object.changed_watching) + : undefined, + }; + }, + + toJSON(message: ServerToClient): unknown { + const obj: any = {}; + if (message.requestRtcAnswer !== undefined) { + obj.requestRtcAnswer = RequestRtcAnswer.toJSON(message.requestRtcAnswer); + } + if (message.updateVideoTransform !== undefined) { + obj.updateVideoTransform = UpdateVideoTransform.toJSON(message.updateVideoTransform); + } + if (message.batteryLevel !== undefined) { + obj.batteryLevel = BatteryLevel.toJSON(message.batteryLevel); + } + if (message.iceCandidate !== undefined) { + obj.iceCandidate = IceCandidate.toJSON(message.iceCandidate); + } + if (message.updateZoom !== undefined) { + obj.updateZoom = UpdateZoom.toJSON(message.updateZoom); + } + if (message.listStreamersResponse !== undefined) { + obj.listStreamersResponse = ListStreamersResponse.toJSON(message.listStreamersResponse); + } + if (message.listViewersResponse !== undefined) { + obj.listViewersResponse = ListViewersResponse.toJSON(message.listViewersResponse); + } + if (message.newSession !== undefined) { + obj.newSession = NewSession.toJSON(message.newSession); + } + if (message.dropSession !== undefined) { + obj.dropSession = DropSession.toJSON(message.dropSession); + } + if (message.changedWatching !== undefined) { + obj.changedWatching = ChangedWatching.toJSON(message.changedWatching); + } + return obj; + }, + + create, I>>(base?: I): ServerToClient { + return ServerToClient.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerToClient { + const message = createBaseServerToClient(); + message.requestRtcAnswer = (object.requestRtcAnswer !== undefined && object.requestRtcAnswer !== null) + ? RequestRtcAnswer.fromPartial(object.requestRtcAnswer) + : undefined; + message.updateVideoTransform = (object.updateVideoTransform !== undefined && object.updateVideoTransform !== null) + ? UpdateVideoTransform.fromPartial(object.updateVideoTransform) + : undefined; + message.batteryLevel = (object.batteryLevel !== undefined && object.batteryLevel !== null) + ? BatteryLevel.fromPartial(object.batteryLevel) + : undefined; + message.iceCandidate = (object.iceCandidate !== undefined && object.iceCandidate !== null) + ? IceCandidate.fromPartial(object.iceCandidate) + : undefined; + message.updateZoom = (object.updateZoom !== undefined && object.updateZoom !== null) + ? UpdateZoom.fromPartial(object.updateZoom) + : undefined; + message.listStreamersResponse = + (object.listStreamersResponse !== undefined && object.listStreamersResponse !== null) + ? ListStreamersResponse.fromPartial(object.listStreamersResponse) + : undefined; + message.listViewersResponse = (object.listViewersResponse !== undefined && object.listViewersResponse !== null) + ? ListViewersResponse.fromPartial(object.listViewersResponse) + : undefined; + message.newSession = (object.newSession !== undefined && object.newSession !== null) + ? NewSession.fromPartial(object.newSession) + : undefined; + message.dropSession = (object.dropSession !== undefined && object.dropSession !== null) + ? DropSession.fromPartial(object.dropSession) + : undefined; + message.changedWatching = (object.changedWatching !== undefined && object.changedWatching !== null) + ? ChangedWatching.fromPartial(object.changedWatching) + : undefined; + return message; + }, +}; + +function createBaseRequestChangeQuality(): RequestChangeQuality { + return { streamerId: 0, quality: 0 }; +} + +export const RequestChangeQuality: MessageFns = { + encode(message: RequestChangeQuality, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.quality !== 0) { + writer.uint32(16).int32(message.quality); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RequestChangeQuality { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRequestChangeQuality(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.quality = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RequestChangeQuality { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + quality: isSet(object.quality) ? qualityFromJSON(object.quality) : 0, + }; + }, + + toJSON(message: RequestChangeQuality): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.quality !== 0) { + obj.quality = qualityToJSON(message.quality); + } + return obj; + }, + + create, I>>(base?: I): RequestChangeQuality { + return RequestChangeQuality.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RequestChangeQuality { + const message = createBaseRequestChangeQuality(); + message.streamerId = object.streamerId ?? 0; + message.quality = object.quality ?? 0; + return message; + }, +}; + +function createBaseListStreamers(): ListStreamers { + return {}; +} + +export const ListStreamers: MessageFns = { + encode(_: ListStreamers, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListStreamers { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListStreamers(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): ListStreamers { + return {}; + }, + + toJSON(_: ListStreamers): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): ListStreamers { + return ListStreamers.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): ListStreamers { + const message = createBaseListStreamers(); + return message; + }, +}; + +function createBaseListViewers(): ListViewers { + return { streamerId: undefined }; +} + +export const ListViewers: MessageFns = { + encode(message: ListViewers, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== undefined) { + writer.uint32(8).uint64(message.streamerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListViewers { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListViewers(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListViewers { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : undefined, + }; + }, + + toJSON(message: ListViewers): unknown { + const obj: any = {}; + if (message.streamerId !== undefined) { + obj.streamerId = Math.round(message.streamerId); + } + return obj; + }, + + create, I>>(base?: I): ListViewers { + return ListViewers.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ListViewers { + const message = createBaseListViewers(); + message.streamerId = object.streamerId ?? undefined; + return message; + }, +}; + +function createBaseListAllViewers(): ListAllViewers { + return {}; +} + +export const ListAllViewers: MessageFns = { + encode(_: ListAllViewers, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListAllViewers { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListAllViewers(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): ListAllViewers { + return {}; + }, + + toJSON(_: ListAllViewers): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): ListAllViewers { + return ListAllViewers.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): ListAllViewers { + const message = createBaseListAllViewers(); + return message; + }, +}; + +function createBaseListStreamersResponse(): ListStreamersResponse { + return { streamers: [] }; +} + +export const ListStreamersResponse: MessageFns = { + encode(message: ListStreamersResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + for (const v of message.streamers) { + StreamerSession.encode(v!, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListStreamersResponse { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListStreamersResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.streamers.push(StreamerSession.decode(reader, reader.uint32())); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListStreamersResponse { + return { + streamers: globalThis.Array.isArray(object?.streamers) + ? object.streamers.map((e: any) => StreamerSession.fromJSON(e)) + : [], + }; + }, + + toJSON(message: ListStreamersResponse): unknown { + const obj: any = {}; + if (message.streamers?.length) { + obj.streamers = message.streamers.map((e) => StreamerSession.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): ListStreamersResponse { + return ListStreamersResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ListStreamersResponse { + const message = createBaseListStreamersResponse(); + message.streamers = object.streamers?.map((e) => StreamerSession.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseListViewersResponse(): ListViewersResponse { + return { viewers: [] }; +} + +export const ListViewersResponse: MessageFns = { + encode(message: ListViewersResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + for (const v of message.viewers) { + ViewerSession.encode(v!, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ListViewersResponse { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListViewersResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.viewers.push(ViewerSession.decode(reader, reader.uint32())); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListViewersResponse { + return { + viewers: globalThis.Array.isArray(object?.viewers) + ? object.viewers.map((e: any) => ViewerSession.fromJSON(e)) + : [], + }; + }, + + toJSON(message: ListViewersResponse): unknown { + const obj: any = {}; + if (message.viewers?.length) { + obj.viewers = message.viewers.map((e) => ViewerSession.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): ListViewersResponse { + return ListViewersResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ListViewersResponse { + const message = createBaseListViewersResponse(); + message.viewers = object.viewers?.map((e) => ViewerSession.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseNewSession(): NewSession { + return { streamerSession: undefined, viewerSession: undefined }; +} + +export const NewSession: MessageFns = { + encode(message: NewSession, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerSession !== undefined) { + StreamerSession.encode(message.streamerSession, writer.uint32(10).fork()).join(); + } + if (message.viewerSession !== undefined) { + ViewerSession.encode(message.viewerSession, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): NewSession { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNewSession(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.streamerSession = StreamerSession.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.viewerSession = ViewerSession.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): NewSession { + return { + streamerSession: isSet(object.streamerSession) + ? StreamerSession.fromJSON(object.streamerSession) + : isSet(object.streamer_session) + ? StreamerSession.fromJSON(object.streamer_session) + : undefined, + viewerSession: isSet(object.viewerSession) + ? ViewerSession.fromJSON(object.viewerSession) + : isSet(object.viewer_session) + ? ViewerSession.fromJSON(object.viewer_session) + : undefined, + }; + }, + + toJSON(message: NewSession): unknown { + const obj: any = {}; + if (message.streamerSession !== undefined) { + obj.streamerSession = StreamerSession.toJSON(message.streamerSession); + } + if (message.viewerSession !== undefined) { + obj.viewerSession = ViewerSession.toJSON(message.viewerSession); + } + return obj; + }, + + create, I>>(base?: I): NewSession { + return NewSession.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): NewSession { + const message = createBaseNewSession(); + message.streamerSession = (object.streamerSession !== undefined && object.streamerSession !== null) + ? StreamerSession.fromPartial(object.streamerSession) + : undefined; + message.viewerSession = (object.viewerSession !== undefined && object.viewerSession !== null) + ? ViewerSession.fromPartial(object.viewerSession) + : undefined; + return message; + }, +}; + +function createBaseDropSession(): DropSession { + return { session: undefined }; +} + +export const DropSession: MessageFns = { + encode(message: DropSession, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.session !== undefined) { + Session.encode(message.session, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): DropSession { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDropSession(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.session = Session.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): DropSession { + return { session: isSet(object.session) ? Session.fromJSON(object.session) : undefined }; + }, + + toJSON(message: DropSession): unknown { + const obj: any = {}; + if (message.session !== undefined) { + obj.session = Session.toJSON(message.session); + } + return obj; + }, + + create, I>>(base?: I): DropSession { + return DropSession.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): DropSession { + const message = createBaseDropSession(); + message.session = (object.session !== undefined && object.session !== null) + ? Session.fromPartial(object.session) + : undefined; + return message; + }, +}; + +function createBaseChangedWatching(): ChangedWatching { + return { viewerId: 0, streamerId: undefined }; +} + +export const ChangedWatching: MessageFns = { + encode(message: ChangedWatching, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.viewerId !== 0) { + writer.uint32(8).uint64(message.viewerId); + } + if (message.streamerId !== undefined) { + writer.uint32(16).uint64(message.streamerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ChangedWatching { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseChangedWatching(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.viewerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ChangedWatching { + return { + viewerId: isSet(object.viewerId) + ? globalThis.Number(object.viewerId) + : isSet(object.viewer_id) + ? globalThis.Number(object.viewer_id) + : 0, + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : undefined, + }; + }, + + toJSON(message: ChangedWatching): unknown { + const obj: any = {}; + if (message.viewerId !== 0) { + obj.viewerId = Math.round(message.viewerId); + } + if (message.streamerId !== undefined) { + obj.streamerId = Math.round(message.streamerId); + } + return obj; + }, + + create, I>>(base?: I): ChangedWatching { + return ChangedWatching.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ChangedWatching { + const message = createBaseChangedWatching(); + message.viewerId = object.viewerId ?? 0; + message.streamerId = object.streamerId ?? undefined; + return message; + }, +}; + +function createBaseStreamerSession(): StreamerSession { + return { id: 0, name: undefined, zoom: undefined, batteryLevel: undefined }; +} + +export const StreamerSession: MessageFns = { + encode(message: StreamerSession, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.id !== 0) { + writer.uint32(8).uint64(message.id); + } + if (message.name !== undefined) { + writer.uint32(18).string(message.name); + } + if (message.zoom !== undefined) { + writer.uint32(29).float(message.zoom); + } + if (message.batteryLevel !== undefined) { + writer.uint32(32).uint32(message.batteryLevel); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): StreamerSession { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamerSession(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.id = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.name = reader.string(); + continue; + } + case 3: { + if (tag !== 29) { + break; + } + + message.zoom = reader.float(); + continue; + } + case 4: { + if (tag !== 32) { + break; + } + + message.batteryLevel = reader.uint32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): StreamerSession { + return { + id: isSet(object.id) ? globalThis.Number(object.id) : 0, + name: isSet(object.name) ? globalThis.String(object.name) : undefined, + zoom: isSet(object.zoom) ? globalThis.Number(object.zoom) : undefined, + batteryLevel: isSet(object.batteryLevel) + ? globalThis.Number(object.batteryLevel) + : isSet(object.battery_level) + ? globalThis.Number(object.battery_level) + : undefined, + }; + }, + + toJSON(message: StreamerSession): unknown { + const obj: any = {}; + if (message.id !== 0) { + obj.id = Math.round(message.id); + } + if (message.name !== undefined) { + obj.name = message.name; + } + if (message.zoom !== undefined) { + obj.zoom = message.zoom; + } + if (message.batteryLevel !== undefined) { + obj.batteryLevel = Math.round(message.batteryLevel); + } + return obj; + }, + + create, I>>(base?: I): StreamerSession { + return StreamerSession.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): StreamerSession { + const message = createBaseStreamerSession(); + message.id = object.id ?? 0; + message.name = object.name ?? undefined; + message.zoom = object.zoom ?? undefined; + message.batteryLevel = object.batteryLevel ?? undefined; + return message; + }, +}; + +function createBaseViewerSession(): ViewerSession { + return { id: 0, name: undefined, streamerId: undefined }; +} + +export const ViewerSession: MessageFns = { + encode(message: ViewerSession, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.id !== 0) { + writer.uint32(8).uint64(message.id); + } + if (message.name !== undefined) { + writer.uint32(18).string(message.name); + } + if (message.streamerId !== undefined) { + writer.uint32(24).uint64(message.streamerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ViewerSession { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseViewerSession(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.id = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.name = reader.string(); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ViewerSession { + return { + id: isSet(object.id) ? globalThis.Number(object.id) : 0, + name: isSet(object.name) ? globalThis.String(object.name) : undefined, + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : undefined, + }; + }, + + toJSON(message: ViewerSession): unknown { + const obj: any = {}; + if (message.id !== 0) { + obj.id = Math.round(message.id); + } + if (message.name !== undefined) { + obj.name = message.name; + } + if (message.streamerId !== undefined) { + obj.streamerId = Math.round(message.streamerId); + } + return obj; + }, + + create, I>>(base?: I): ViewerSession { + return ViewerSession.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ViewerSession { + const message = createBaseViewerSession(); + message.id = object.id ?? 0; + message.name = object.name ?? undefined; + message.streamerId = object.streamerId ?? undefined; + return message; + }, +}; + +function createBaseBatteryLevel(): BatteryLevel { + return { streamerId: 0, batteryLevel: 0 }; +} + +export const BatteryLevel: MessageFns = { + encode(message: BatteryLevel, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.batteryLevel !== 0) { + writer.uint32(16).uint32(message.batteryLevel); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): BatteryLevel { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBatteryLevel(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.batteryLevel = reader.uint32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): BatteryLevel { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + batteryLevel: isSet(object.batteryLevel) + ? globalThis.Number(object.batteryLevel) + : isSet(object.battery_level) + ? globalThis.Number(object.battery_level) + : 0, + }; + }, + + toJSON(message: BatteryLevel): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.batteryLevel !== 0) { + obj.batteryLevel = Math.round(message.batteryLevel); + } + return obj; + }, + + create, I>>(base?: I): BatteryLevel { + return BatteryLevel.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): BatteryLevel { + const message = createBaseBatteryLevel(); + message.streamerId = object.streamerId ?? 0; + message.batteryLevel = object.batteryLevel ?? 0; + return message; + }, +}; + +function createBaseRequestZoom(): RequestZoom { + return { streamerId: 0, zoom: 0 }; +} + +export const RequestZoom: MessageFns = { + encode(message: RequestZoom, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.zoom !== 0) { + writer.uint32(21).float(message.zoom); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RequestZoom { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRequestZoom(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 21) { + break; + } + + message.zoom = reader.float(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RequestZoom { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + zoom: isSet(object.zoom) ? globalThis.Number(object.zoom) : 0, + }; + }, + + toJSON(message: RequestZoom): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.zoom !== 0) { + obj.zoom = message.zoom; + } + return obj; + }, + + create, I>>(base?: I): RequestZoom { + return RequestZoom.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RequestZoom { + const message = createBaseRequestZoom(); + message.streamerId = object.streamerId ?? 0; + message.zoom = object.zoom ?? 0; + return message; + }, +}; + +function createBaseUpdateZoom(): UpdateZoom { + return { streamerId: 0, zoom: 0 }; +} + +export const UpdateZoom: MessageFns = { + encode(message: UpdateZoom, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.streamerId !== 0) { + writer.uint32(8).uint64(message.streamerId); + } + if (message.zoom !== 0) { + writer.uint32(21).float(message.zoom); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): UpdateZoom { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateZoom(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 21) { + break; + } + + message.zoom = reader.float(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): UpdateZoom { + return { + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : 0, + zoom: isSet(object.zoom) ? globalThis.Number(object.zoom) : 0, + }; + }, + + toJSON(message: UpdateZoom): unknown { + const obj: any = {}; + if (message.streamerId !== 0) { + obj.streamerId = Math.round(message.streamerId); + } + if (message.zoom !== 0) { + obj.zoom = message.zoom; + } + return obj; + }, + + create, I>>(base?: I): UpdateZoom { + return UpdateZoom.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UpdateZoom { + const message = createBaseUpdateZoom(); + message.streamerId = object.streamerId ?? 0; + message.zoom = object.zoom ?? 0; + return message; + }, +}; + +function createBaseUpdateWatching(): UpdateWatching { + return { viewerId: 0, streamerId: undefined }; +} + +export const UpdateWatching: MessageFns = { + encode(message: UpdateWatching, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.viewerId !== 0) { + writer.uint32(8).uint64(message.viewerId); + } + if (message.streamerId !== undefined) { + writer.uint32(16).uint64(message.streamerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): UpdateWatching { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateWatching(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.viewerId = longToNumber(reader.uint64()); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.streamerId = longToNumber(reader.uint64()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): UpdateWatching { + return { + viewerId: isSet(object.viewerId) + ? globalThis.Number(object.viewerId) + : isSet(object.viewer_id) + ? globalThis.Number(object.viewer_id) + : 0, + streamerId: isSet(object.streamerId) + ? globalThis.Number(object.streamerId) + : isSet(object.streamer_id) + ? globalThis.Number(object.streamer_id) + : undefined, + }; + }, + + toJSON(message: UpdateWatching): unknown { + const obj: any = {}; + if (message.viewerId !== 0) { + obj.viewerId = Math.round(message.viewerId); + } + if (message.streamerId !== undefined) { + obj.streamerId = Math.round(message.streamerId); + } + return obj; + }, + + create, I>>(base?: I): UpdateWatching { + return UpdateWatching.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UpdateWatching { + const message = createBaseUpdateWatching(); + message.viewerId = object.viewerId ?? 0; + message.streamerId = object.streamerId ?? undefined; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(int64: { toString(): string }): number { + const num = globalThis.Number(int64.toString()); + if (num > globalThis.Number.MAX_SAFE_INTEGER) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + if (num < globalThis.Number.MIN_SAFE_INTEGER) { + throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER"); + } + return num; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/frontend/src/lib/protos/streamer.ts b/frontend/src/lib/protos/streamer.ts new file mode 100644 index 0000000..37bbf4f --- /dev/null +++ b/frontend/src/lib/protos/streamer.ts @@ -0,0 +1,1006 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.11.6 +// protoc v7.34.1 +// source: streamer.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; +import { IceCandidate, Quality, qualityFromJSON, qualityToJSON, Session, VideoTransform } from "./common"; + +export const protobufPackage = "streamer"; + +/** Contains the different commands that the viewer or controller can send to the streamer. */ +export interface ClientToServer { + /** Responds to the streamer WebRTC offer. */ + rtcOfferResponse?: + | RtcOfferResponse + | undefined; + /** Requests the viewer or controller to update the video transform applied to the stream. */ + requestVideoTransform?: + | RequestVideoTransform + | undefined; + /** Streamer is updating their battery level. */ + updateBatteryLevel?: + | UpdateBatteryLevel + | undefined; + /** Sent the ICE candidate to the viewer or controller. */ + iceCandidate?: + | IceCandidate + | undefined; + /** The streamer has updated the zoom level applied to the stream. */ + requestZoom?: Zoom | undefined; +} + +/** Contains the different commands that the server can send to the streamer. */ +export interface ServerToClient { + /** The viewer or controller is requesting an WebRTC offer from the streamer to establish a WebRTC connection. */ + requestRtcOffer?: + | RequestRtcOffer + | undefined; + /** The viewer or controller is responding to the streamer's WebRTC offer with an answer to establish a WebRTC connection. */ + rtcAnswer?: + | RtcAnswer + | undefined; + /** The streamer will send an ICE candidate to the viewer or controller to establish a WebRTC connection. */ + iceCandidate?: + | IceCandidate + | undefined; + /** The viewer or controller is requesting to disconnect from the streamer. */ + disconnectPeer?: + | DisconnectPeer + | undefined; + /** The controller is requesting the streamer to update the zoom level applied to the stream. */ + changeZoom?: + | Zoom + | undefined; + /** The controller is requesting the streamer to change the quality of the stream. */ + changeQuality?: ChangeQuality | undefined; +} + +/** Requests the streamer to create an RTC offer for the viewer or controller to establish a WebRTC connection. */ +export interface RequestRtcOffer { + /** Who is requesting the RTC offer. */ + session: Session | undefined; +} + +/** / Response to the viewer or controller's request for an RTC offer, containing the SDP of the RTC offer. */ +export interface RtcOfferResponse { + /** The session that requested the RTC offer. */ + session: + | Session + | undefined; + /** The offer created by the streamer in response to the viewer or controller's request. */ + offer: string; +} + +/** / Answer from the viewer or controller to the streamer in response to the RTC offer created by the streamer. */ +export interface RtcAnswer { + /** The session that responded to the RTC offer. */ + session: + | Session + | undefined; + /** The answer created by the viewer or controller in response to the RTC offer created by the streamer. */ + answer: string; +} + +/** Requests the streamer to disconnect the WebRTC connection with the viewer or controller. */ +export interface DisconnectPeer { + /** The session that needs to have disconnected from the streamer. */ + session: Session | undefined; +} + +/** Requests all viewers and controllers to update the video transform applied to the stream. */ +export interface RequestVideoTransform { + /** The new video transform the streamer is requesting to apply to the stream. */ + videoTransform: VideoTransform | undefined; +} + +/** Sent by the streamer to the controller when the streamer updates its battery level. */ +export interface UpdateBatteryLevel { + /** The new battery level of the streamer's device as a percentage from 0 to 100. */ + batteryLevel: number; +} + +/** Sent to the streamer to update the quality of the stream. */ +export interface ChangeQuality { + /** The new quality the streamer is requesting to apply to the stream. */ + quality: Quality; +} + +/** A request and event for updating the zoom level applied to the stream. */ +export interface Zoom { + /** The new zoom level the streamer is requesting or reporting. A value of 1.0 means no zoom, while a value greater than 1.0 means zoom in and a value less than 1.0 means zoom out. */ + zoom: number; +} + +function createBaseClientToServer(): ClientToServer { + return { + rtcOfferResponse: undefined, + requestVideoTransform: undefined, + updateBatteryLevel: undefined, + iceCandidate: undefined, + requestZoom: undefined, + }; +} + +export const ClientToServer: MessageFns = { + encode(message: ClientToServer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.rtcOfferResponse !== undefined) { + RtcOfferResponse.encode(message.rtcOfferResponse, writer.uint32(10).fork()).join(); + } + if (message.requestVideoTransform !== undefined) { + RequestVideoTransform.encode(message.requestVideoTransform, writer.uint32(18).fork()).join(); + } + if (message.updateBatteryLevel !== undefined) { + UpdateBatteryLevel.encode(message.updateBatteryLevel, writer.uint32(26).fork()).join(); + } + if (message.iceCandidate !== undefined) { + IceCandidate.encode(message.iceCandidate, writer.uint32(34).fork()).join(); + } + if (message.requestZoom !== undefined) { + Zoom.encode(message.requestZoom, writer.uint32(42).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ClientToServer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientToServer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.rtcOfferResponse = RtcOfferResponse.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.requestVideoTransform = RequestVideoTransform.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.updateBatteryLevel = UpdateBatteryLevel.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.iceCandidate = IceCandidate.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.requestZoom = Zoom.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ClientToServer { + return { + rtcOfferResponse: isSet(object.rtcOfferResponse) + ? RtcOfferResponse.fromJSON(object.rtcOfferResponse) + : isSet(object.rtc_offer_response) + ? RtcOfferResponse.fromJSON(object.rtc_offer_response) + : undefined, + requestVideoTransform: isSet(object.requestVideoTransform) + ? RequestVideoTransform.fromJSON(object.requestVideoTransform) + : isSet(object.request_video_transform) + ? RequestVideoTransform.fromJSON(object.request_video_transform) + : undefined, + updateBatteryLevel: isSet(object.updateBatteryLevel) + ? UpdateBatteryLevel.fromJSON(object.updateBatteryLevel) + : isSet(object.update_battery_level) + ? UpdateBatteryLevel.fromJSON(object.update_battery_level) + : undefined, + iceCandidate: isSet(object.iceCandidate) + ? IceCandidate.fromJSON(object.iceCandidate) + : isSet(object.ice_candidate) + ? IceCandidate.fromJSON(object.ice_candidate) + : undefined, + requestZoom: isSet(object.requestZoom) + ? Zoom.fromJSON(object.requestZoom) + : isSet(object.request_zoom) + ? Zoom.fromJSON(object.request_zoom) + : undefined, + }; + }, + + toJSON(message: ClientToServer): unknown { + const obj: any = {}; + if (message.rtcOfferResponse !== undefined) { + obj.rtcOfferResponse = RtcOfferResponse.toJSON(message.rtcOfferResponse); + } + if (message.requestVideoTransform !== undefined) { + obj.requestVideoTransform = RequestVideoTransform.toJSON(message.requestVideoTransform); + } + if (message.updateBatteryLevel !== undefined) { + obj.updateBatteryLevel = UpdateBatteryLevel.toJSON(message.updateBatteryLevel); + } + if (message.iceCandidate !== undefined) { + obj.iceCandidate = IceCandidate.toJSON(message.iceCandidate); + } + if (message.requestZoom !== undefined) { + obj.requestZoom = Zoom.toJSON(message.requestZoom); + } + return obj; + }, + + create, I>>(base?: I): ClientToServer { + return ClientToServer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ClientToServer { + const message = createBaseClientToServer(); + message.rtcOfferResponse = (object.rtcOfferResponse !== undefined && object.rtcOfferResponse !== null) + ? RtcOfferResponse.fromPartial(object.rtcOfferResponse) + : undefined; + message.requestVideoTransform = + (object.requestVideoTransform !== undefined && object.requestVideoTransform !== null) + ? RequestVideoTransform.fromPartial(object.requestVideoTransform) + : undefined; + message.updateBatteryLevel = (object.updateBatteryLevel !== undefined && object.updateBatteryLevel !== null) + ? UpdateBatteryLevel.fromPartial(object.updateBatteryLevel) + : undefined; + message.iceCandidate = (object.iceCandidate !== undefined && object.iceCandidate !== null) + ? IceCandidate.fromPartial(object.iceCandidate) + : undefined; + message.requestZoom = (object.requestZoom !== undefined && object.requestZoom !== null) + ? Zoom.fromPartial(object.requestZoom) + : undefined; + return message; + }, +}; + +function createBaseServerToClient(): ServerToClient { + return { + requestRtcOffer: undefined, + rtcAnswer: undefined, + iceCandidate: undefined, + disconnectPeer: undefined, + changeZoom: undefined, + changeQuality: undefined, + }; +} + +export const ServerToClient: MessageFns = { + encode(message: ServerToClient, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.requestRtcOffer !== undefined) { + RequestRtcOffer.encode(message.requestRtcOffer, writer.uint32(10).fork()).join(); + } + if (message.rtcAnswer !== undefined) { + RtcAnswer.encode(message.rtcAnswer, writer.uint32(18).fork()).join(); + } + if (message.iceCandidate !== undefined) { + IceCandidate.encode(message.iceCandidate, writer.uint32(26).fork()).join(); + } + if (message.disconnectPeer !== undefined) { + DisconnectPeer.encode(message.disconnectPeer, writer.uint32(34).fork()).join(); + } + if (message.changeZoom !== undefined) { + Zoom.encode(message.changeZoom, writer.uint32(42).fork()).join(); + } + if (message.changeQuality !== undefined) { + ChangeQuality.encode(message.changeQuality, writer.uint32(50).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerToClient { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerToClient(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.requestRtcOffer = RequestRtcOffer.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.rtcAnswer = RtcAnswer.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.iceCandidate = IceCandidate.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.disconnectPeer = DisconnectPeer.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.changeZoom = Zoom.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.changeQuality = ChangeQuality.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerToClient { + return { + requestRtcOffer: isSet(object.requestRtcOffer) + ? RequestRtcOffer.fromJSON(object.requestRtcOffer) + : isSet(object.request_rtc_offer) + ? RequestRtcOffer.fromJSON(object.request_rtc_offer) + : undefined, + rtcAnswer: isSet(object.rtcAnswer) + ? RtcAnswer.fromJSON(object.rtcAnswer) + : isSet(object.rtc_answer) + ? RtcAnswer.fromJSON(object.rtc_answer) + : undefined, + iceCandidate: isSet(object.iceCandidate) + ? IceCandidate.fromJSON(object.iceCandidate) + : isSet(object.ice_candidate) + ? IceCandidate.fromJSON(object.ice_candidate) + : undefined, + disconnectPeer: isSet(object.disconnectPeer) + ? DisconnectPeer.fromJSON(object.disconnectPeer) + : isSet(object.disconnect_peer) + ? DisconnectPeer.fromJSON(object.disconnect_peer) + : undefined, + changeZoom: isSet(object.changeZoom) + ? Zoom.fromJSON(object.changeZoom) + : isSet(object.change_zoom) + ? Zoom.fromJSON(object.change_zoom) + : undefined, + changeQuality: isSet(object.changeQuality) + ? ChangeQuality.fromJSON(object.changeQuality) + : isSet(object.change_quality) + ? ChangeQuality.fromJSON(object.change_quality) + : undefined, + }; + }, + + toJSON(message: ServerToClient): unknown { + const obj: any = {}; + if (message.requestRtcOffer !== undefined) { + obj.requestRtcOffer = RequestRtcOffer.toJSON(message.requestRtcOffer); + } + if (message.rtcAnswer !== undefined) { + obj.rtcAnswer = RtcAnswer.toJSON(message.rtcAnswer); + } + if (message.iceCandidate !== undefined) { + obj.iceCandidate = IceCandidate.toJSON(message.iceCandidate); + } + if (message.disconnectPeer !== undefined) { + obj.disconnectPeer = DisconnectPeer.toJSON(message.disconnectPeer); + } + if (message.changeZoom !== undefined) { + obj.changeZoom = Zoom.toJSON(message.changeZoom); + } + if (message.changeQuality !== undefined) { + obj.changeQuality = ChangeQuality.toJSON(message.changeQuality); + } + return obj; + }, + + create, I>>(base?: I): ServerToClient { + return ServerToClient.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerToClient { + const message = createBaseServerToClient(); + message.requestRtcOffer = (object.requestRtcOffer !== undefined && object.requestRtcOffer !== null) + ? RequestRtcOffer.fromPartial(object.requestRtcOffer) + : undefined; + message.rtcAnswer = (object.rtcAnswer !== undefined && object.rtcAnswer !== null) + ? RtcAnswer.fromPartial(object.rtcAnswer) + : undefined; + message.iceCandidate = (object.iceCandidate !== undefined && object.iceCandidate !== null) + ? IceCandidate.fromPartial(object.iceCandidate) + : undefined; + message.disconnectPeer = (object.disconnectPeer !== undefined && object.disconnectPeer !== null) + ? DisconnectPeer.fromPartial(object.disconnectPeer) + : undefined; + message.changeZoom = (object.changeZoom !== undefined && object.changeZoom !== null) + ? Zoom.fromPartial(object.changeZoom) + : undefined; + message.changeQuality = (object.changeQuality !== undefined && object.changeQuality !== null) + ? ChangeQuality.fromPartial(object.changeQuality) + : undefined; + return message; + }, +}; + +function createBaseRequestRtcOffer(): RequestRtcOffer { + return { session: undefined }; +} + +export const RequestRtcOffer: MessageFns = { + encode(message: RequestRtcOffer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.session !== undefined) { + Session.encode(message.session, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RequestRtcOffer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRequestRtcOffer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.session = Session.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RequestRtcOffer { + return { session: isSet(object.session) ? Session.fromJSON(object.session) : undefined }; + }, + + toJSON(message: RequestRtcOffer): unknown { + const obj: any = {}; + if (message.session !== undefined) { + obj.session = Session.toJSON(message.session); + } + return obj; + }, + + create, I>>(base?: I): RequestRtcOffer { + return RequestRtcOffer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RequestRtcOffer { + const message = createBaseRequestRtcOffer(); + message.session = (object.session !== undefined && object.session !== null) + ? Session.fromPartial(object.session) + : undefined; + return message; + }, +}; + +function createBaseRtcOfferResponse(): RtcOfferResponse { + return { session: undefined, offer: "" }; +} + +export const RtcOfferResponse: MessageFns = { + encode(message: RtcOfferResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.session !== undefined) { + Session.encode(message.session, writer.uint32(10).fork()).join(); + } + if (message.offer !== "") { + writer.uint32(18).string(message.offer); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RtcOfferResponse { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRtcOfferResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.session = Session.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.offer = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RtcOfferResponse { + return { + session: isSet(object.session) ? Session.fromJSON(object.session) : undefined, + offer: isSet(object.offer) ? globalThis.String(object.offer) : "", + }; + }, + + toJSON(message: RtcOfferResponse): unknown { + const obj: any = {}; + if (message.session !== undefined) { + obj.session = Session.toJSON(message.session); + } + if (message.offer !== "") { + obj.offer = message.offer; + } + return obj; + }, + + create, I>>(base?: I): RtcOfferResponse { + return RtcOfferResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RtcOfferResponse { + const message = createBaseRtcOfferResponse(); + message.session = (object.session !== undefined && object.session !== null) + ? Session.fromPartial(object.session) + : undefined; + message.offer = object.offer ?? ""; + return message; + }, +}; + +function createBaseRtcAnswer(): RtcAnswer { + return { session: undefined, answer: "" }; +} + +export const RtcAnswer: MessageFns = { + encode(message: RtcAnswer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.session !== undefined) { + Session.encode(message.session, writer.uint32(10).fork()).join(); + } + if (message.answer !== "") { + writer.uint32(18).string(message.answer); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RtcAnswer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRtcAnswer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.session = Session.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.answer = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RtcAnswer { + return { + session: isSet(object.session) ? Session.fromJSON(object.session) : undefined, + answer: isSet(object.answer) ? globalThis.String(object.answer) : "", + }; + }, + + toJSON(message: RtcAnswer): unknown { + const obj: any = {}; + if (message.session !== undefined) { + obj.session = Session.toJSON(message.session); + } + if (message.answer !== "") { + obj.answer = message.answer; + } + return obj; + }, + + create, I>>(base?: I): RtcAnswer { + return RtcAnswer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RtcAnswer { + const message = createBaseRtcAnswer(); + message.session = (object.session !== undefined && object.session !== null) + ? Session.fromPartial(object.session) + : undefined; + message.answer = object.answer ?? ""; + return message; + }, +}; + +function createBaseDisconnectPeer(): DisconnectPeer { + return { session: undefined }; +} + +export const DisconnectPeer: MessageFns = { + encode(message: DisconnectPeer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.session !== undefined) { + Session.encode(message.session, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): DisconnectPeer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDisconnectPeer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.session = Session.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): DisconnectPeer { + return { session: isSet(object.session) ? Session.fromJSON(object.session) : undefined }; + }, + + toJSON(message: DisconnectPeer): unknown { + const obj: any = {}; + if (message.session !== undefined) { + obj.session = Session.toJSON(message.session); + } + return obj; + }, + + create, I>>(base?: I): DisconnectPeer { + return DisconnectPeer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): DisconnectPeer { + const message = createBaseDisconnectPeer(); + message.session = (object.session !== undefined && object.session !== null) + ? Session.fromPartial(object.session) + : undefined; + return message; + }, +}; + +function createBaseRequestVideoTransform(): RequestVideoTransform { + return { videoTransform: undefined }; +} + +export const RequestVideoTransform: MessageFns = { + encode(message: RequestVideoTransform, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.videoTransform !== undefined) { + VideoTransform.encode(message.videoTransform, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RequestVideoTransform { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRequestVideoTransform(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.videoTransform = VideoTransform.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): RequestVideoTransform { + return { + videoTransform: isSet(object.videoTransform) + ? VideoTransform.fromJSON(object.videoTransform) + : isSet(object.video_transform) + ? VideoTransform.fromJSON(object.video_transform) + : undefined, + }; + }, + + toJSON(message: RequestVideoTransform): unknown { + const obj: any = {}; + if (message.videoTransform !== undefined) { + obj.videoTransform = VideoTransform.toJSON(message.videoTransform); + } + return obj; + }, + + create, I>>(base?: I): RequestVideoTransform { + return RequestVideoTransform.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): RequestVideoTransform { + const message = createBaseRequestVideoTransform(); + message.videoTransform = (object.videoTransform !== undefined && object.videoTransform !== null) + ? VideoTransform.fromPartial(object.videoTransform) + : undefined; + return message; + }, +}; + +function createBaseUpdateBatteryLevel(): UpdateBatteryLevel { + return { batteryLevel: 0 }; +} + +export const UpdateBatteryLevel: MessageFns = { + encode(message: UpdateBatteryLevel, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.batteryLevel !== 0) { + writer.uint32(8).uint32(message.batteryLevel); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): UpdateBatteryLevel { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateBatteryLevel(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.batteryLevel = reader.uint32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): UpdateBatteryLevel { + return { + batteryLevel: isSet(object.batteryLevel) + ? globalThis.Number(object.batteryLevel) + : isSet(object.battery_level) + ? globalThis.Number(object.battery_level) + : 0, + }; + }, + + toJSON(message: UpdateBatteryLevel): unknown { + const obj: any = {}; + if (message.batteryLevel !== 0) { + obj.batteryLevel = Math.round(message.batteryLevel); + } + return obj; + }, + + create, I>>(base?: I): UpdateBatteryLevel { + return UpdateBatteryLevel.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UpdateBatteryLevel { + const message = createBaseUpdateBatteryLevel(); + message.batteryLevel = object.batteryLevel ?? 0; + return message; + }, +}; + +function createBaseChangeQuality(): ChangeQuality { + return { quality: 0 }; +} + +export const ChangeQuality: MessageFns = { + encode(message: ChangeQuality, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.quality !== 0) { + writer.uint32(8).int32(message.quality); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ChangeQuality { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseChangeQuality(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.quality = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ChangeQuality { + return { quality: isSet(object.quality) ? qualityFromJSON(object.quality) : 0 }; + }, + + toJSON(message: ChangeQuality): unknown { + const obj: any = {}; + if (message.quality !== 0) { + obj.quality = qualityToJSON(message.quality); + } + return obj; + }, + + create, I>>(base?: I): ChangeQuality { + return ChangeQuality.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ChangeQuality { + const message = createBaseChangeQuality(); + message.quality = object.quality ?? 0; + return message; + }, +}; + +function createBaseZoom(): Zoom { + return { zoom: 0 }; +} + +export const Zoom: MessageFns = { + encode(message: Zoom, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.zoom !== 0) { + writer.uint32(13).float(message.zoom); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): Zoom { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseZoom(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 13) { + break; + } + + message.zoom = reader.float(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): Zoom { + return { zoom: isSet(object.zoom) ? globalThis.Number(object.zoom) : 0 }; + }, + + toJSON(message: Zoom): unknown { + const obj: any = {}; + if (message.zoom !== 0) { + obj.zoom = message.zoom; + } + return obj; + }, + + create, I>>(base?: I): Zoom { + return Zoom.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Zoom { + const message = createBaseZoom(); + message.zoom = object.zoom ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/frontend/src/lib/protos/viewer.ts b/frontend/src/lib/protos/viewer.ts new file mode 100644 index 0000000..b3d8be4 --- /dev/null +++ b/frontend/src/lib/protos/viewer.ts @@ -0,0 +1,336 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.11.6 +// protoc v7.34.1 +// source: viewer.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; +import { IceCandidate, RequestRtcAnswer, RtcAnswerResponse, UpdateVideoTransform } from "./common"; + +export const protobufPackage = "viewer"; + +/** Contains the different commands that the viewer can send to the server. */ +export interface ClientToServer { + /** Responds to the streamer WebRTC offer. */ + rtcAnswerResponse?: + | RtcAnswerResponse + | undefined; + /** The viewer is sending an ICE candidate to the streamer to establish a WebRTC connection. */ + iceCandidate?: IceCandidate | undefined; +} + +/** Contains the different commands that the server can send to the viewer. */ +export interface ServerToClient { + /** The streamer is requesting the viewer to answer the RTC offer created by the streamer to establish a WebRTC connection. */ + requestRtcAnswer?: + | RequestRtcAnswer + | undefined; + /** The streamer will send a request to update the video transform applied to the stream when the streamer requests it. */ + updateVideoTransform?: + | UpdateVideoTransform + | undefined; + /** The streamer will send an ICE candidate to the viewer to establish a WebRTC connection. */ + iceCandidate?: + | IceCandidate + | undefined; + /** The server will send a notification to the viewer when the streamer they are watching has disconnected from the server. */ + disconnectStreamer?: DisconnectStreamer | undefined; +} + +/** A command sent when streamer disconnect or controller requests a viewer to disconnect from a streamer. */ +export interface DisconnectStreamer { +} + +function createBaseClientToServer(): ClientToServer { + return { rtcAnswerResponse: undefined, iceCandidate: undefined }; +} + +export const ClientToServer: MessageFns = { + encode(message: ClientToServer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.rtcAnswerResponse !== undefined) { + RtcAnswerResponse.encode(message.rtcAnswerResponse, writer.uint32(10).fork()).join(); + } + if (message.iceCandidate !== undefined) { + IceCandidate.encode(message.iceCandidate, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ClientToServer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientToServer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.rtcAnswerResponse = RtcAnswerResponse.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.iceCandidate = IceCandidate.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ClientToServer { + return { + rtcAnswerResponse: isSet(object.rtcAnswerResponse) + ? RtcAnswerResponse.fromJSON(object.rtcAnswerResponse) + : isSet(object.rtc_answer_response) + ? RtcAnswerResponse.fromJSON(object.rtc_answer_response) + : undefined, + iceCandidate: isSet(object.iceCandidate) + ? IceCandidate.fromJSON(object.iceCandidate) + : isSet(object.ice_candidate) + ? IceCandidate.fromJSON(object.ice_candidate) + : undefined, + }; + }, + + toJSON(message: ClientToServer): unknown { + const obj: any = {}; + if (message.rtcAnswerResponse !== undefined) { + obj.rtcAnswerResponse = RtcAnswerResponse.toJSON(message.rtcAnswerResponse); + } + if (message.iceCandidate !== undefined) { + obj.iceCandidate = IceCandidate.toJSON(message.iceCandidate); + } + return obj; + }, + + create, I>>(base?: I): ClientToServer { + return ClientToServer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ClientToServer { + const message = createBaseClientToServer(); + message.rtcAnswerResponse = (object.rtcAnswerResponse !== undefined && object.rtcAnswerResponse !== null) + ? RtcAnswerResponse.fromPartial(object.rtcAnswerResponse) + : undefined; + message.iceCandidate = (object.iceCandidate !== undefined && object.iceCandidate !== null) + ? IceCandidate.fromPartial(object.iceCandidate) + : undefined; + return message; + }, +}; + +function createBaseServerToClient(): ServerToClient { + return { + requestRtcAnswer: undefined, + updateVideoTransform: undefined, + iceCandidate: undefined, + disconnectStreamer: undefined, + }; +} + +export const ServerToClient: MessageFns = { + encode(message: ServerToClient, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.requestRtcAnswer !== undefined) { + RequestRtcAnswer.encode(message.requestRtcAnswer, writer.uint32(10).fork()).join(); + } + if (message.updateVideoTransform !== undefined) { + UpdateVideoTransform.encode(message.updateVideoTransform, writer.uint32(18).fork()).join(); + } + if (message.iceCandidate !== undefined) { + IceCandidate.encode(message.iceCandidate, writer.uint32(26).fork()).join(); + } + if (message.disconnectStreamer !== undefined) { + DisconnectStreamer.encode(message.disconnectStreamer, writer.uint32(34).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerToClient { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerToClient(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.requestRtcAnswer = RequestRtcAnswer.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.updateVideoTransform = UpdateVideoTransform.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.iceCandidate = IceCandidate.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.disconnectStreamer = DisconnectStreamer.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerToClient { + return { + requestRtcAnswer: isSet(object.requestRtcAnswer) + ? RequestRtcAnswer.fromJSON(object.requestRtcAnswer) + : isSet(object.request_rtc_answer) + ? RequestRtcAnswer.fromJSON(object.request_rtc_answer) + : undefined, + updateVideoTransform: isSet(object.updateVideoTransform) + ? UpdateVideoTransform.fromJSON(object.updateVideoTransform) + : isSet(object.update_video_transform) + ? UpdateVideoTransform.fromJSON(object.update_video_transform) + : undefined, + iceCandidate: isSet(object.iceCandidate) + ? IceCandidate.fromJSON(object.iceCandidate) + : isSet(object.ice_candidate) + ? IceCandidate.fromJSON(object.ice_candidate) + : undefined, + disconnectStreamer: isSet(object.disconnectStreamer) + ? DisconnectStreamer.fromJSON(object.disconnectStreamer) + : isSet(object.disconnect_streamer) + ? DisconnectStreamer.fromJSON(object.disconnect_streamer) + : undefined, + }; + }, + + toJSON(message: ServerToClient): unknown { + const obj: any = {}; + if (message.requestRtcAnswer !== undefined) { + obj.requestRtcAnswer = RequestRtcAnswer.toJSON(message.requestRtcAnswer); + } + if (message.updateVideoTransform !== undefined) { + obj.updateVideoTransform = UpdateVideoTransform.toJSON(message.updateVideoTransform); + } + if (message.iceCandidate !== undefined) { + obj.iceCandidate = IceCandidate.toJSON(message.iceCandidate); + } + if (message.disconnectStreamer !== undefined) { + obj.disconnectStreamer = DisconnectStreamer.toJSON(message.disconnectStreamer); + } + return obj; + }, + + create, I>>(base?: I): ServerToClient { + return ServerToClient.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerToClient { + const message = createBaseServerToClient(); + message.requestRtcAnswer = (object.requestRtcAnswer !== undefined && object.requestRtcAnswer !== null) + ? RequestRtcAnswer.fromPartial(object.requestRtcAnswer) + : undefined; + message.updateVideoTransform = (object.updateVideoTransform !== undefined && object.updateVideoTransform !== null) + ? UpdateVideoTransform.fromPartial(object.updateVideoTransform) + : undefined; + message.iceCandidate = (object.iceCandidate !== undefined && object.iceCandidate !== null) + ? IceCandidate.fromPartial(object.iceCandidate) + : undefined; + message.disconnectStreamer = (object.disconnectStreamer !== undefined && object.disconnectStreamer !== null) + ? DisconnectStreamer.fromPartial(object.disconnectStreamer) + : undefined; + return message; + }, +}; + +function createBaseDisconnectStreamer(): DisconnectStreamer { + return {}; +} + +export const DisconnectStreamer: MessageFns = { + encode(_: DisconnectStreamer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): DisconnectStreamer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDisconnectStreamer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): DisconnectStreamer { + return {}; + }, + + toJSON(_: DisconnectStreamer): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): DisconnectStreamer { + return DisconnectStreamer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): DisconnectStreamer { + const message = createBaseDisconnectStreamer(); + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/frontend/src/lib/screen.svelte b/frontend/src/lib/screen.svelte index c026d02..f2adc04 100644 --- a/frontend/src/lib/screen.svelte +++ b/frontend/src/lib/screen.svelte @@ -1,14 +1,30 @@ + +{#snippet viewerAddScreen(props: unknown[])} +
+

+ Adicionar Receptor a {streamers[ + (props[0] as { streamerId: number }).streamerId + ].name} +

+ + +
+{/snippet} +
+
-
+
+

Transmissores

+
+ {#each Object.keys(streamers) as streamerId, index (index)} +
+
+ + {#if currentStreamerBoxSelected === Number(streamerId)} +
+

Receptores:

+ {#each Object.entries(viewers).filter((item) => item[1].streamerId === currentStreamerBoxSelected) as receiver, recIndex (recIndex)} +
+ {receiver[1].name} + +
+ {/each} + +
+ {/if} +
+ {#if currentStreamerBoxSelected === Number(streamerId)} +
+
+ Qualidade + +
+
+
+ { + const msgData = ClientToServer.create(); + msgData.requestZoom = { + streamerId: Number(streamerId), + zoom: value, + }; + socket.send(ClientToServer.encode(msgData).finish()); + }} + /> +
+
+
+ {/if} +
+ {/each} +
+
-
+
+ +

Logs

> MobileCam V1.0.0

- Transmissores: 0
- Receptores: 0 + Transmissores: {Object.keys(streamers).length}
+ Receptores: {viewerCount}

- Controller_id: 0 + Controller_id: {controllerId ?? "Conectando..."}

@@ -28,7 +527,7 @@ width: 100%; height: 100%; display: grid; - grid-template-columns: 5rem 3fr 1fr; + grid-template-columns: 5rem 5fr 2fr; } .sidebar { @@ -42,6 +541,7 @@ flex-direction: column; gap: 1rem; padding: 1rem; + padding-left: 0.5rem; } .preview { @@ -73,7 +573,7 @@ } .logText { - padding-inline: .5rem; + padding-inline: 0.5rem; font-family: monospace; font-size: 1rem; } @@ -82,4 +582,269 @@ width: 100%; object-fit: contain; } + + .pages { + padding: 1rem; + padding-right: 0; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + overflow: hidden; + } + + .page { + display: flex; + flex-direction: column; + gap: 1rem; + width: 100%; + height: 100%; + overflow: hidden; + } + + .homepage { + display: none; + } + + .pageTitle { + color: var(--accent-color); + font-size: 2rem; + font-weight: bolder; + width: 100%; + } + + .streamersList { + display: flex; + flex-direction: column; + gap: 1rem; + width: 100%; + height: 100%; + padding-right: 0.5rem; + overflow-y: scroll; + scroll-behavior: smooth; + scrollbar-gutter: stable; + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-button { + display: none; + } + + &::-webkit-scrollbar-thumb { + background: var(--accent-color); + border-radius: 100vw; + } + + &::-webkit-scrollbar-thumb:hover { + background: hsla(from var(--accent-color) h s calc(l - 25)); + } + + &::-webkit-scrollbar-track { + background: hsla(from var(--accent-color) h s l / 0.3); + border-radius: 100vw; + } + } + + @supports (-moz-transform-style: preserve-3d) { + .streamersList { + scrollbar-width: thin; + scrollbar-color: var(--accent-color) + hsla(from var(--accent-color) h s l / 0.3); + } + } + + .streamer { + width: 100%; + height: fit-content; + border-radius: 1rem; + background: hsla(from var(--accent-color) h s l / 0.3); + border: 1px solid var(--accent-color); + overflow: hidden; + flex-shrink: 0; + + &.openStreamer { + display: grid; + grid-template-columns: 1fr 7rem; + gap: 0.5rem; + } + } + + .streamerLeft { + display: grid; + grid-template-rows: fit-content 1fr; + } + + .streamerHeader { + width: 100%; + height: 100%; + padding: 1rem 0.5rem; + background: transparent; + border: 0; + display: flex; + flex-direction: row; + justify-content: start; + align-items: center; + cursor: pointer; + .openStreamer & { + padding-right: 0; + } + } + + .streamerName { + width: 100%; + height: fit-content; + overflow-wrap: anywhere; + overflow: hidden; + text-overflow: ellipsis; + color: var(--accent-color); + font-size: 1.75rem; + font-weight: bolder; + text-align: left; + } + + .streamerReceivers { + width: 100%; + height: 100%; + background: var(--black); + padding: 0.5rem; + color: var(--white); + display: flex; + flex-direction: column; + gap: 1em; + border-top-right-radius: 1rem; + } + + .receiversTitle { + font-size: 1.25rem; + font-weight: normal; + color: var(--accent-color); + } + + .receiver { + display: flex; + flex-direction: row; + justify-content: start; + align-items: center; + padding: 0.5rem 0.25rem 0.5rem 0.5rem; + border-radius: 0.5rem; + background: hsla(from var(--accent-color) h s l / 0.3); + border: 1px solid var(--accent-color); + } + + .receiverName { + width: 100%; + font-weight: normal; + font-size: 1.25rem; + color: var(--accent-color); + } + + .removeViewerButton { + border: 0; + background: transparent; + height: 100%; + aspect-ratio: 1; + color: var(--accent-color); + background: hsla(from var(--accent-color) h s l / 0.3); + border-radius: 0.25rem; + font-size: 1.25rem; + font-weight: bolder; + cursor: pointer; + } + + .addViewerButton { + width: 50%; + margin-right: auto; + padding: 0.25rem; + border: 1px solid var(--accent-color); + background: hsla(from var(--accent-color) h s l / 0.3); + color: var(--accent-color); + font-size: 1.25rem; + font-weight: bolder; + border-radius: 0.25rem; + cursor: pointer; + } + + .AVSContainer { + display: flex; + flex-direction: column; + gap: 1rem; + place-content: start; + } + + .AVSTitle { + font-size: 1.5rem; + font-weight: bolder; + color: var(--accent-color); + word-break: keep-all; + white-space: pre; + } + + .AVSOptions { + background: var(--black); + border: 1px solid var(--accent-color); + color: var(--accent-color); + padding: 0.5rem; + border-radius: 0.5rem; + cursor: pointer; + font-size: 1.25rem; + } + + .AVSConfirmButton { + width: fit-content; + padding: 0.5rem 1rem; + border: 1px solid var(--accent-color); + background: hsla(from var(--accent-color) h s l / 0.3); + color: var(--accent-color); + font-size: 1.25rem; + font-weight: bolder; + border-radius: 0.5rem; + cursor: pointer; + } + + .qualityModifier { + display: flex; + flex-direction: column; + gap: 0.5rem; + justify-content: start; + } + + .selectQualityTitle { + color: var(--accent-color); + font-size: 1rem; + font-weight: bolder; + } + + .selectQuality { + background: var(--black); + border: 1px solid var(--accent-color); + color: var(--accent-color); + padding: 0.5rem; + cursor: pointer; + font-size: 1rem; + } + + .streamerRight { + padding-block: 0.5rem; + padding-right: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + justify-content: start; + } + + .zoomSliderContainer { + position: relative; + width: 100%; + height: 100%; + } + + .zoomSliderWrapper { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } diff --git a/frontend/src/routes/stream/+page.svelte b/frontend/src/routes/stream/+page.svelte index f90f849..7e49d45 100644 --- a/frontend/src/routes/stream/+page.svelte +++ b/frontend/src/routes/stream/+page.svelte @@ -1,6 +1,5 @@ diff --git a/frontend/src/routes/view/+page.svelte b/frontend/src/routes/view/+page.svelte index dde0e98..9ca079b 100644 --- a/frontend/src/routes/view/+page.svelte +++ b/frontend/src/routes/view/+page.svelte @@ -1,190 +1,95 @@ -
-
- -
-
- -
-
- - + diff --git a/frontend/static/image/logo.png b/frontend/static/image/logo.png index 8040567492307b8cfe511958275fa8a3941eb60d..99262002ecece165f3d8d66e84b74c2a557f90dd 100644 GIT binary patch literal 36567 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
H5CVDL9Gqf>J*V)Mo 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 S^>6?K5O})!xvX Date: Tue, 21 Apr 2026 03:00:28 -0300 Subject: [PATCH 9/9] feat: finished implementing frontend with new protocol --- frontend/package-lock.json | 309 +++++++++- frontend/package.json | 4 +- frontend/src/lib/WebRTCManager.ts | 97 +-- frontend/src/lib/camera.svelte | 13 +- frontend/src/lib/player.svelte | 66 +- frontend/src/lib/protos/common.ts | 4 +- frontend/src/lib/protos/controller.ts | 454 +++++++++++++- frontend/src/lib/protos/streamer.ts | 94 ++- frontend/src/lib/protos/viewer.ts | 187 +++++- frontend/src/lib/qrcode.svelte | 39 ++ frontend/src/routes/+layout.svelte | 11 + frontend/src/routes/controller/+page.svelte | 583 ++++++++++++++---- frontend/src/routes/stream/+page.svelte | 21 +- frontend/src/routes/view/+page.svelte | 45 +- frontend/static/icons/battery-full.svg | 4 + frontend/static/icons/battery.svg | 4 + frontend/static/icons/broadcaster.svg | 2 + frontend/static/icons/collapsable-open.svg | 4 + frontend/static/icons/home.svg | 2 + frontend/static/icons/quality.svg | 5 + frontend/static/icons/zoom.svg | 4 + frontend/svelte.config.js | 14 +- protos/controller.proto | 8 + src/main.rs | 7 +- src/routes/logo.rs | 9 +- src/routes/mod.rs | 1 + src/routes/version.rs | 9 + src/server/controller.rs | 28 + src/static/index.html | 34 - .../static/image => src/static}/logo.png | Bin 30 files changed, 1802 insertions(+), 260 deletions(-) create mode 100644 frontend/src/lib/qrcode.svelte create mode 100644 frontend/static/icons/battery-full.svg create mode 100644 frontend/static/icons/battery.svg create mode 100644 frontend/static/icons/broadcaster.svg create mode 100644 frontend/static/icons/collapsable-open.svg create mode 100644 frontend/static/icons/home.svg create mode 100644 frontend/static/icons/quality.svg create mode 100644 frontend/static/icons/zoom.svg create mode 100644 src/routes/version.rs delete mode 100644 src/static/index.html rename {frontend/static/image => src/static}/logo.png (100%) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0623163..dc7b309 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.0.1", "dependencies": { + "qrcode": "^1.5.4", "ts-proto": "^2.11.6" }, "devDependencies": { @@ -17,6 +18,7 @@ "@sveltejs/kit": "^2.57.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", "@types/node": "^22", + "@types/qrcode": "^1.5.6", "eslint": "^10.2.0", "eslint-plugin-svelte": "^3.17.0", "globals": "^17.4.0", @@ -756,6 +758,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -1033,6 +1045,30 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/aria-query": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", @@ -1076,6 +1112,15 @@ "node": "18 || 20 || >=22" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/case-anything": { "version": "2.1.13", "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", @@ -1104,6 +1149,17 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -1114,6 +1170,24 @@ "node": ">=6" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -1170,6 +1244,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1204,6 +1287,12 @@ "dev": true, "license": "MIT" }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dprint-node": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz", @@ -1225,6 +1314,12 @@ "node": ">=0.10" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1567,6 +1662,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1623,6 +1727,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2149,11 +2262,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2189,6 +2310,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/postcss": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", @@ -2346,6 +2476,23 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -2360,6 +2507,21 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/rolldown": { "version": "1.0.0-rc.15", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", @@ -2420,6 +2582,12 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-cookie-parser": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", @@ -2475,6 +2643,32 @@ "node": ">=0.10.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/svelte": { "version": "5.55.4", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.4.tgz", @@ -2876,6 +3070,12 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -2886,6 +3086,113 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 153ee01..c49c888 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "@sveltejs/kit": "^2.57.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", "@types/node": "^22", + "@types/qrcode": "^1.5.6", "eslint": "^10.2.0", "eslint-plugin-svelte": "^3.17.0", "globals": "^17.4.0", @@ -30,6 +31,7 @@ "vite": "^8.0.7" }, "dependencies": { + "qrcode": "^1.5.4", "ts-proto": "^2.11.6" } -} \ No newline at end of file +} diff --git a/frontend/src/lib/WebRTCManager.ts b/frontend/src/lib/WebRTCManager.ts index 44f9396..636ad9c 100644 --- a/frontend/src/lib/WebRTCManager.ts +++ b/frontend/src/lib/WebRTCManager.ts @@ -7,8 +7,8 @@ interface WRTCManagerCallbacks { } export class WRTCManager { - private connections: Record = {}; - private candidateQueues: Record = {}; + private connections: Record = {}; + private candidateQueues: Record = {}; private Callbacks: WRTCManagerCallbacks; constructor(callbacks: WRTCManagerCallbacks) { @@ -17,18 +17,28 @@ export class WRTCManager { public finish() { for (const connectionId in this.connections) { - this.closePeerConnection(Number(connectionId)); + this.closePeerConnection(WRTCManager.keyToSession(connectionId)); } } + public static sessionToKey(session: Session): string { + return `${session.id}:${session.type}`; + } + + public static keyToSession(key: string): Session { + const parts = key.split(':'); + return { id: Number(parts[0]), type: Number(parts[1]) }; + } + public createPeerConnection(connection: Session) { - if (this.connections[connection.id]) { - this.connections[connection.id].close(); + const key = WRTCManager.sessionToKey(connection); + if (this.connections[key]) { + this.connections[key].close(); } const pc = new RTCPeerConnection(ICE_CONFIG); - this.connections[connection.id] = pc; - this.candidateQueues[connection.id] = []; + this.connections[key] = pc; + this.candidateQueues[key] = []; pc.addEventListener('connectionstatechange', () => this.onConnectionState(connection, pc.connectionState)); pc.addEventListener('track', ({ track, streams }) => this.Callbacks.onRemoteTrack?.(track, streams)); @@ -43,37 +53,40 @@ export class WRTCManager { switch (state) { case "closed": case "failed": - this.closePeerConnection(connection.id); + this.closePeerConnection(connection); break; case "connected": - this.flushCandidateQueue(connection.id); + this.flushCandidateQueue(connection); break; default: break; } } - public closePeerConnection(connectionId: number) { - const pc = this.connections[connectionId]; + public closePeerConnection(session: Session) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; if (!pc) return; pc.close(); - delete this.connections[connectionId]; - delete this.candidateQueues[connectionId]; + delete this.connections[key]; + delete this.candidateQueues[key]; } - public async addTrack(connectionId: number, track: MediaStreamTrack) { - const pc = this.connections[connectionId]; - if (!pc) throw new Error("[AddTrack] PeerConnection not found for ID: " + connectionId); + public async addTrack(session: Session, track: MediaStreamTrack) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; + if (!pc) throw new Error("[AddTrack] PeerConnection not found for Session: " + key); pc.addTrack(track); } public async setStreamBandwidth( - connectionId: number, + session: Session, maxBitrate: number, maxFramerate: number, ) { - const peerConnection = this.connections[connectionId]; - if (!peerConnection) throw new Error("[SetStreamBandwidth] PeerConnection not found for ID: " + connectionId); + const key = WRTCManager.sessionToKey(session); + const peerConnection = this.connections[key]; + if (!peerConnection) throw new Error("[SetStreamBandwidth] PeerConnection not found for ID: " + key); for (const sender of peerConnection.getSenders()) { if (sender.track?.kind !== "video") continue; @@ -93,49 +106,55 @@ export class WRTCManager { return Object.keys(this.connections); } - public getPeerConnection(connectionId: number) { - return this.connections[connectionId]; + public getPeerConnection(session: Session) { + return this.connections[WRTCManager.sessionToKey(session)]; } - public async addICECandidate(connectionId: number, candidate: RTCIceCandidateInit) { - const pc = this.connections[connectionId]; + public async addICECandidate(session: Session, candidate: RTCIceCandidateInit) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; if (!pc || !pc.remoteDescription) { - this.candidateQueues[connectionId]?.push(candidate); + this.candidateQueues[key]?.push(candidate); return; } await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch((err) => { - console.warn(`[AddICECandidate] Failed to add ICE candidate for ID: ${connectionId}`, err); + console.warn(`[AddICECandidate] Failed to add ICE candidate for session: ${key}`, err); }); } - public async flushCandidateQueue(connectionId: number) { - const pc = this.connections[connectionId]; + public async flushCandidateQueue(session: Session) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; if (!pc || !pc.remoteDescription) return; - const queue = this.candidateQueues[connectionId] || []; + const queue = this.candidateQueues[key] || []; for await (const candidate of queue) { await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch(() => { }); } - this.candidateQueues[connectionId] = []; + this.candidateQueues[key] = []; } - public async setRemoteDescription(connectionId: number, description: RTCSessionDescriptionInit) { - const pc = this.connections[connectionId]; - if (!pc || (pc.currentLocalDescription !== null && pc.signalingState === "stable")) throw new Error("[SetRemoteDescription] PeerConnection not found or is already connected for ID: " + connectionId); + public async setRemoteDescription(session: Session, description: RTCSessionDescriptionInit) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; + if (!pc || (pc.currentLocalDescription !== null && pc.signalingState === "stable")) throw new Error("[SetRemoteDescription] PeerConnection not found or is already connected for ID: " + key); + console.log(description); await pc.setRemoteDescription(new RTCSessionDescription(description)); - this.flushCandidateQueue(connectionId); + this.flushCandidateQueue(session); } - public async createStreamOffer(connectionId: number) { - const pc = this.connections[connectionId]; - if (!pc) throw new Error("[CreateStreamOffer] PeerConnection not found for ID: " + connectionId); + public async createStreamOffer(session: Session) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; + if (!pc) throw new Error("[CreateStreamOffer] PeerConnection not found for ID: " + key); const offer = await pc.createOffer(); await pc.setLocalDescription(offer); return offer; } - public async createStreamAnswer(connectionId: number) { - const pc = this.connections[connectionId]; - if (!pc) throw new Error("[CreateStreamAnswer] PeerConnection not found for ID: " + connectionId); + public async createStreamAnswer(session: Session) { + const key = WRTCManager.sessionToKey(session); + const pc = this.connections[key]; + if (!pc) throw new Error("[CreateStreamAnswer] PeerConnection not found for ID: " + key); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); return answer; diff --git a/frontend/src/lib/camera.svelte b/frontend/src/lib/camera.svelte index 0a66692..df79ffa 100644 --- a/frontend/src/lib/camera.svelte +++ b/frontend/src/lib/camera.svelte @@ -38,7 +38,7 @@ let currentQuality: Quality = Quality.QUALITY_HIGH; - let zoomValue = $state(1); + let zoomValue = $state(0); let maxZoom = $state(5); let minZoom = $state(1); let nativeZoomSupported = false; @@ -46,7 +46,7 @@ let initialPinchDistance = 0; let pinchStartZoom = 1; let previewStyleTransform = $state({ - zoom: 1, + zoom: 0, rotation: 0, }); @@ -164,12 +164,7 @@ if (!localStream) return; const settings = localStream.getVideoTracks()[0].getSettings(); const videoRatio = settings.aspectRatio ?? 16 / 9; - if (!nativeZoomSupported) - preview.setAspectRatio( - previewStyleTransform.rotation % 180 === 0 - ? 1 / videoRatio - : videoRatio, - ); + if (!nativeZoomSupported) preview.setAspectRatio(videoRatio); }, 500); } @@ -213,7 +208,6 @@ handleVideoResize(); if (nativeZoomSupported) { preview.setRotation(0); - preview.setIsNative(true); return; } const angle = screen.orientation.type.includes("landscape-primary") @@ -226,7 +220,6 @@ if (previewStyleTransform.rotation === angle) return; previewStyleTransform.rotation = angle; preview.setRotation(angle); - preview.setIsNative(false); props.onTransform({ ...previewStyleTransform, isNative: false }); }); } catch (err) { diff --git a/frontend/src/lib/player.svelte b/frontend/src/lib/player.svelte index 21edaf9..6bcad8a 100644 --- a/frontend/src/lib/player.svelte +++ b/frontend/src/lib/player.svelte @@ -5,14 +5,17 @@ rotation: number; zoom: number; aspectRatio: number; - isNative: boolean; - }>({ rotation: 0, zoom: 1, aspectRatio: 16 / 9, isNative: true }); + }>({ rotation: 0, zoom: 0, aspectRatio: 16 / 9 }); - const props: { showOverlay: boolean } = $props(); + const props: { + showOverlay: boolean; + onMuteChange?: (mute: boolean) => void; + } = $props(); - function toggleMute() { - preview.muted = !preview.muted; + export function toggleMute(force?: boolean) { + preview.muted = force != null ? force : !preview.muted; isMuted = preview.muted; + if (props.onMuteChange) props.onMuteChange(isMuted); } export async function play() { @@ -25,6 +28,11 @@ export function setStream(src: MediaProvider) { preview.srcObject = src; + viewFallbackTransform = { + rotation: 0, + zoom: 0, + aspectRatio: 16 / 9, + }; } export function setZoom(value: number) { @@ -39,19 +47,19 @@ viewFallbackTransform.aspectRatio = value; } - export function setIsNative(bool: boolean) { - viewFallbackTransform.isNative = bool; - } - export function getVideo() { return preview; }
+
+ +

Aguardando Imagem...

+
{#if props.showOverlay}
-
@@ -59,14 +67,9 @@