diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cac9f40f..0007a505 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/deno - key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.lock', '**/deno.json', '**/deno.jsonc') }} + key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.lock', '**/package.json') }} restore-keys: | ${{ runner.os }}-deno- @@ -40,7 +40,7 @@ jobs: path: | ~/.bun/install/cache packages/web/node_modules - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} restore-keys: | ${{ runner.os }}-bun- @@ -60,10 +60,7 @@ jobs: for pkg_dir in ${{ steps.changed_packages.outputs.all_changed_and_modified_files }}; do echo "🔍 Inspecting $pkg_dir..." - if [[ -f "$pkg_dir/deno.json" ]]; then - echo "🔧 Building with Deno: $pkg_dir" - deno task build "$pkg_dir" - elif [[ -f "$pkg_dir/bun.lockb" ]]; then + if [[ -f "$pkg_dir/deno.lock" ]]; then echo "🔧 Building with Bun: $pkg_dir" (cd "$pkg_dir" && bun install && bun run build) else diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ddf52599..2a4a8730 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/deno - key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.lock', '**/deno.json', '**/deno.jsonc') }} + key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.lock') }} restore-keys: | ${{ runner.os }}-deno- @@ -80,7 +80,7 @@ jobs: run: | set -euo pipefail - excluded=("packages/web" "packages/transport-deno") + excluded=("packages/web" "packages/transport-deno" "packages/transport-node") for pkg_dir in ${{ steps.changed_packages.outputs.all_changed_and_modified_files }}; do echo "🔍 Inspecting $pkg_dir" @@ -90,12 +90,12 @@ jobs: continue fi - if [[ -f "$pkg_dir/deno.json" ]]; then - echo "🦕 Building with Deno: $pkg_dir" - deno task build "$pkg_dir" + if [[ -f "$pkg_dir/jsr.json" ]]; then + echo "🦕 Publishing to NPM: $pkg_dir" + bun run build:npm $pkg_dir echo "📦 Publishing to JSR" (cd "$pkg_dir" && deno publish --allow-dirty) - elif [[ -f "$pkg_dir/bun.lockb" ]]; then + elif [[ -f "$pkg_dir/bun.lock" ]]; then echo "🥖 Building with Bun: $pkg_dir" (cd "$pkg_dir" && bun install && bun run build) else diff --git a/README.md b/README.md index d591a06d..58ec703a 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,8 @@ This monorepo leverages the following technologies: ### Prerequisites -You'll need to have [Bun](https://bun.sh/) installed to work with this -monorepo. Follow the installation instructions on their home page. +You'll need to have [Bun](https://bun.sh/) installed to work with this monorepo. +Follow the installation instructions on their home page. ### Development Setup @@ -80,22 +80,12 @@ monorepo. Follow the installation instructions on their home page. To start the development server for the web client: ```bash -bun run --filter web dev +cd ./packages/web && bun run dev ``` This will typically run the web client on http://localhost:3000 and requires a Chromium browser -## Meshtastic JS Packages - -While the js packages are primarily libraries, you can run their tests or -specific development scripts if defined within their package.json files. For -example, to run tests for a specific package: - -```bash -bun run --filter core test -``` - ### Feedback If you encounter any issues with nightly builds, please report them in our diff --git a/packages/web/biome.json b/biome.json similarity index 66% rename from packages/web/biome.json rename to biome.json index 91d7a3e8..c11b9714 100644 --- a/packages/web/biome.json +++ b/biome.json @@ -1,7 +1,6 @@ { - "$schema": "https://biomejs.dev/schemas/2.0.5/schema.json", "files": { - "includes": ["**", "!node_modules/**", "!**/*.css", "!dist/**", "!build/**", "!coverage/**", "!**/*.d.ts"], + "includes": ["**/*.ts", "**/*.tsx", "!**/*.test.ts", "!**/*.test.tsx", "!npm_modules/**", "!dist/**", "!npm/**"], "ignoreUnknown": false }, "formatter": { @@ -14,6 +13,7 @@ }, "linter": { "enabled": true, + "includes": ["**", "!test/**"], "rules": { "recommended": true, "suspicious": { @@ -21,13 +21,9 @@ "noDebugger": "error" }, "style": { - "useConst": "error", "useBlockStatements": "error", "useSingleVarDeclarator": "off" }, - "complexity": { - "noForEach": "off" - }, "correctness": { "noUnusedVariables": "error", "noUnusedImports": "error" @@ -40,10 +36,9 @@ "semicolons": "always" } }, - "json": { - "formatter": { - "indentStyle": "space", - "indentWidth": 2 - } - } +"json": { + "formatter": { + "enabled": false + } + } } diff --git a/bun.lock b/bun.lock index b05e7d7f..0732f678 100644 --- a/bun.lock +++ b/bun.lock @@ -3,12 +3,51 @@ "workspaces": { "": { "name": "meshtastic-web", + "dependencies": { + "@bufbuild/protobuf": "^2.6.1", + "ste-simple-events": "^3.0.11", + "tslog": "^4.9.3", + }, "devDependencies": { - "@biomejs/biome": "^1.8.3", - "bun": "^1.1.18", + "@types/node": "^22.16.4", + "bun": "^1.2.18", "typescript": "^5.8.3", }, }, + "packages/core": { + "name": "@meshtastic/core", + "version": "2.6.5", + "dependencies": { + "@meshtastic/protobufs": "npm:@jsr/meshtastic__protobufs", + "crc": "npm:crc@^4.3.2", + }, + }, + "packages/transport-deno": { + "name": "@meshtastic/transport-deno", + "version": "0.1.1", + }, + "packages/transport-http": { + "name": "@meshtastic/transport-http", + "version": "0.2.1", + }, + "packages/transport-node": { + "name": "@meshtastic/transport-node", + "version": "0.0.1", + }, + "packages/transport-web-bluetooth": { + "name": "@meshtastic/transport-web-bluetooth", + "version": "0.1.2", + "devDependencies": { + "@types/web-bluetooth": "npm:@types/web-bluetooth@^0.0.20", + }, + }, + "packages/transport-web-serial": { + "name": "@meshtastic/transport-web-serial", + "version": "0.2.1", + "dependencies": { + "@types/w3c-web-serial": "npm:@types/w3c-web-serial@^1.0.7", + }, + }, "packages/web": { "name": "meshtastic-web", "version": "2.7.0-0", @@ -167,25 +206,25 @@ "@babel/types": ["@babel/types@7.28.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ=="], - "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], + "@biomejs/biome": ["@biomejs/biome@2.0.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.6", "@biomejs/cli-darwin-x64": "2.0.6", "@biomejs/cli-linux-arm64": "2.0.6", "@biomejs/cli-linux-arm64-musl": "2.0.6", "@biomejs/cli-linux-x64": "2.0.6", "@biomejs/cli-linux-x64-musl": "2.0.6", "@biomejs/cli-win32-arm64": "2.0.6", "@biomejs/cli-win32-x64": "2.0.6" }, "bin": { "biome": "bin/biome" } }, "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.6", "", { "os": "win32", "cpu": "x64" }, "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw=="], - "@bufbuild/protobuf": ["@bufbuild/protobuf@2.6.0", "", {}, "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg=="], + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.6.1", "", {}, "sha512-DaG6XlyKpz08bmHY5SGX2gfIllaqtDJ/KwVoxsmP22COOLYwDBe7yD3DZGwXem/Xq7QOc9cuR7R3MpAv5CFfDw=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.6", "", { "os": "aix", "cpu": "ppc64" }, "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw=="], @@ -281,13 +320,19 @@ "@maplibre/maplibre-gl-style-spec": ["@maplibre/maplibre-gl-style-spec@23.3.0", "", { "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", "quickselect": "^3.0.0", "rw": "^1.3.3", "tinyqueue": "^3.0.0" }, "bin": { "gl-style-format": "dist/gl-style-format.mjs", "gl-style-migrate": "dist/gl-style-migrate.mjs", "gl-style-validate": "dist/gl-style-validate.mjs" } }, "sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA=="], - "@meshtastic/core": ["@jsr/meshtastic__core@2.6.4", "https://npm.jsr.io/~/11/@jsr/meshtastic__core/2.6.4.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.2", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-1Kz5DK6peFxluHOJR38vFwfgeJzMXTz+3p6TvibjILVhSQC2U1nu8aJbn6w5zhRqS+j79OmtrRvdzL6VNsTkkQ=="], + "@meshtastic/core": ["@meshtastic/core@workspace:packages/core"], - "@meshtastic/transport-http": ["@jsr/meshtastic__transport-http@0.2.1", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-http/0.2.1.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0" } }, "sha512-lmQKr3aIINKvtGROU4HchmSVqbZSbkIHqajowRRC8IAjsnR0zNTyxz210QyY4pFUF9hpcW3GRjwq5h/VO2JuGg=="], + "@meshtastic/protobufs": ["@jsr/meshtastic__protobufs@2.7.0", "https://npm.jsr.io/~/11/@jsr/meshtastic__protobufs/2.7.0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3" } }, "sha512-ndZhUyB/ADSyjJI+iSeSOoIKqNGZ2+ERVjfY0qnh4jgF740tFTwefC5mzZhOqDLbreGFYS79+429NtH5Ujdzdg=="], - "@meshtastic/transport-web-bluetooth": ["@jsr/meshtastic__transport-web-bluetooth@0.1.2", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-bluetooth/0.1.2.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.4" } }, "sha512-Z+5pv9RXNgY0/crKExOH3pZ6LT0HIXFmnBL7NX5AO2knOFRn+4lmxQEhhmiTTlkUfqyEfAvbjuY5u4mq9TPTdQ=="], + "@meshtastic/transport-deno": ["@meshtastic/transport-deno@workspace:packages/transport-deno"], - "@meshtastic/transport-web-serial": ["@jsr/meshtastic__transport-web-serial@0.2.1", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-serial/0.2.1.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0" } }, "sha512-yumjEGLkAuJYOC3aWKvZzbQqi/LnqaKfNpVCY7Ki7oLtAshNiZrBLiwiFhN7+ZR9FfMdJThyBMqREBDRRWTO1Q=="], + "@meshtastic/transport-http": ["@meshtastic/transport-http@workspace:packages/transport-http"], + + "@meshtastic/transport-node": ["@meshtastic/transport-node@workspace:packages/transport-node"], + + "@meshtastic/transport-web-bluetooth": ["@meshtastic/transport-web-bluetooth@workspace:packages/transport-web-bluetooth"], + + "@meshtastic/transport-web-serial": ["@meshtastic/transport-web-serial@workspace:packages/transport-web-serial"], "@noble/curves": ["@noble/curves@1.9.2", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g=="], @@ -781,7 +826,7 @@ "@types/mapbox__vector-tile": ["@types/mapbox__vector-tile@1.3.4", "", { "dependencies": { "@types/geojson": "*", "@types/mapbox__point-geometry": "*", "@types/pbf": "*" } }, "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg=="], - "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], + "@types/node": ["@types/node@22.16.4", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g=="], "@types/pbf": ["@types/pbf@3.0.5", "", {}, "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA=="], @@ -795,7 +840,7 @@ "@types/w3c-web-serial": ["@types/w3c-web-serial@1.0.8", "", {}, "sha512-QQOT+bxQJhRGXoZDZGLs3ksLud1dMNnMiSQtBA0w8KXvLpXX4oM4TZb6J0GgJ8UbCaHo5s9/4VQT8uXy9JER2A=="], - "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], + "@types/web-bluetooth": ["@types/web-bluetooth@0.0.20", "", {}, "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="], "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], @@ -1335,7 +1380,7 @@ "typewise-core": ["typewise-core@1.2.0", "", {}, "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg=="], - "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "union-value": ["union-value@1.0.1", "", { "dependencies": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", "set-value": "^2.0.1" } }, "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg=="], @@ -1393,6 +1438,12 @@ "zustand": ["zustand@5.0.6", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A=="], + "@jsr/meshtastic__core/@bufbuild/protobuf": ["@bufbuild/protobuf@2.6.0", "", {}, "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg=="], + + "@jsr/meshtastic__protobufs/@bufbuild/protobuf": ["@bufbuild/protobuf@2.6.0", "", {}, "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg=="], + + "@meshtastic/protobufs/@bufbuild/protobuf": ["@bufbuild/protobuf@2.6.0", "", {}, "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.4", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg=="], @@ -1645,7 +1696,15 @@ "lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "meshtastic-web/@biomejs/biome": ["@biomejs/biome@2.0.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.6", "@biomejs/cli-darwin-x64": "2.0.6", "@biomejs/cli-linux-arm64": "2.0.6", "@biomejs/cli-linux-arm64-musl": "2.0.6", "@biomejs/cli-linux-x64": "2.0.6", "@biomejs/cli-linux-x64-musl": "2.0.6", "@biomejs/cli-win32-arm64": "2.0.6", "@biomejs/cli-win32-x64": "2.0.6" }, "bin": { "biome": "bin/biome" } }, "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA=="], + "meshtastic-web/@meshtastic/core": ["@jsr/meshtastic__core@2.6.4", "https://npm.jsr.io/~/11/@jsr/meshtastic__core/2.6.4.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.2", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-1Kz5DK6peFxluHOJR38vFwfgeJzMXTz+3p6TvibjILVhSQC2U1nu8aJbn6w5zhRqS+j79OmtrRvdzL6VNsTkkQ=="], + + "meshtastic-web/@meshtastic/transport-http": ["@jsr/meshtastic__transport-http@0.2.1", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-http/0.2.1.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0" } }, "sha512-lmQKr3aIINKvtGROU4HchmSVqbZSbkIHqajowRRC8IAjsnR0zNTyxz210QyY4pFUF9hpcW3GRjwq5h/VO2JuGg=="], + + "meshtastic-web/@meshtastic/transport-web-bluetooth": ["@jsr/meshtastic__transport-web-bluetooth@0.1.2", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-bluetooth/0.1.2.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.4" } }, "sha512-Z+5pv9RXNgY0/crKExOH3pZ6LT0HIXFmnBL7NX5AO2knOFRn+4lmxQEhhmiTTlkUfqyEfAvbjuY5u4mq9TPTdQ=="], + + "meshtastic-web/@meshtastic/transport-web-serial": ["@jsr/meshtastic__transport-web-serial@0.2.1", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-serial/0.2.1.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0" } }, "sha512-yumjEGLkAuJYOC3aWKvZzbQqi/LnqaKfNpVCY7Ki7oLtAshNiZrBLiwiFhN7+ZR9FfMdJThyBMqREBDRRWTO1Q=="], + + "meshtastic-web/@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], "peek-stream/through2": ["through2@2.0.5", "", { "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="], @@ -1689,23 +1748,9 @@ "geojson-polygon-self-intersections/rbush/quickselect": ["quickselect@1.1.1", "", {}, "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ=="], - "happy-dom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "meshtastic-web/@meshtastic/core/@bufbuild/protobuf": ["@bufbuild/protobuf@2.6.0", "", {}, "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg=="], - "meshtastic-web/@biomejs/biome/@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA=="], - - "meshtastic-web/@biomejs/biome/@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.6", "", { "os": "win32", "cpu": "x64" }, "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw=="], + "meshtastic-web/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], "peek-stream/through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], diff --git a/deno.lock b/deno.lock new file mode 100644 index 00000000..b05a9c31 --- /dev/null +++ b/deno.lock @@ -0,0 +1,5472 @@ +{ + "version": "5", + "specifiers": { + "jsr:@david/code-block-writer@^13.0.3": "13.0.3", + "jsr:@std/fmt@1": "1.0.8", + "jsr:@std/fs@1": "1.0.19", + "jsr:@std/internal@^1.0.9": "1.0.9", + "jsr:@std/path@1": "1.1.1", + "jsr:@std/path@^1.1.1": "1.1.1", + "jsr:@ts-morph/bootstrap@0.27": "0.27.0", + "jsr:@ts-morph/common@0.27": "0.27.0", + "npm:@biomejs/biome@2.0.6": "2.0.6", + "npm:@bufbuild/protobuf@^2.6.0": "2.6.1", + "npm:@bufbuild/protobuf@^2.6.1": "2.6.1", + "npm:@hookform/resolvers@^5.1.1": "5.1.1_react-hook-form@7.60.0__react@19.1.0_react@19.1.0", + "npm:@jsr/meshtastic__core@2.6.4": "2.6.4", + "npm:@jsr/meshtastic__protobufs@*": "2.7.0", + "npm:@jsr/meshtastic__transport-http@*": "0.2.1", + "npm:@jsr/meshtastic__transport-web-bluetooth@*": "0.1.2", + "npm:@jsr/meshtastic__transport-web-serial@*": "0.2.1", + "npm:@noble/curves@^1.9.2": "1.9.4", + "npm:@radix-ui/react-accordion@^1.2.11": "1.2.11_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-checkbox@^1.3.2": "1.3.2_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-dialog@^1.1.14": "1.1.14_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-dropdown-menu@^2.1.15": "2.1.15_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-label@^2.1.7": "2.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-menubar@^1.1.15": "1.1.15_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-popover@^1.1.14": "1.1.14_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-scroll-area@^1.2.9": "1.2.9_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-select@^2.2.5": "2.2.5_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-separator@^1.1.7": "1.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-slider@^1.3.5": "1.3.5_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-switch@^1.2.5": "1.2.5_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-tabs@^1.1.12": "1.1.12_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-toast@^1.2.14": "1.2.14_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-toggle-group@^1.1.10": "1.1.10_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-tooltip@^1.2.7": "1.2.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@tailwindcss/vite@^4.1.11": "4.1.11_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@types+node@22.16.4", + "npm:@tanstack/react-router-devtools@^1.127.9": "1.128.0_@tanstack+react-router@1.128.0__react@19.1.0__react-dom@19.1.0___react@19.1.0_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@tanstack/react-router@^1.127.9": "1.128.0_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@tanstack/router-cli@^1.127.8": "1.128.0", + "npm:@tanstack/router-devtools@^1.127.9": "1.128.0_@tanstack+react-router@1.128.0__react@19.1.0__react-dom@19.1.0___react@19.1.0_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@tanstack/router-plugin@^1.127.9": "1.128.0_@tanstack+react-router@1.128.0__react@19.1.0__react-dom@19.1.0___react@19.1.0_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@babel+core@7.28.0_react@19.1.0_react-dom@19.1.0__react@19.1.0_@types+node@22.16.4", + "npm:@testing-library/jest-dom@^6.6.3": "6.6.3", + "npm:@testing-library/react@^16.3.0": "16.3.0_@testing-library+dom@10.4.0_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@testing-library/user-event@^14.6.1": "14.6.1_@testing-library+dom@10.4.0", + "npm:@turf/turf@^7.2.0": "7.2.0", + "npm:@types/chrome@0.1": "0.1.1", + "npm:@types/js-cookie@^3.0.6": "3.0.6", + "npm:@types/node@^22.16.4": "22.16.4", + "npm:@types/node@^24.0.14": "24.0.14", + "npm:@types/react-dom@^19.1.6": "19.1.6_@types+react@19.1.8", + "npm:@types/react@^19.1.8": "19.1.8", + "npm:@types/serviceworker@^0.0.142": "0.0.142", + "npm:@types/w3c-web-serial@^1.0.7": "1.0.8", + "npm:@types/w3c-web-serial@^1.0.8": "1.0.8", + "npm:@types/web-bluetooth@^0.0.20": "0.0.20", + "npm:@types/web-bluetooth@^0.0.21": "0.0.21", + "npm:@vitejs/plugin-react@^4.6.0": "4.6.0_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@babel+core@7.28.0_@types+node@22.16.4", + "npm:autoprefixer@^10.4.21": "10.4.21_postcss@8.5.6", + "npm:base64-js@^1.5.1": "1.5.1", + "npm:bun@^1.2.18": "1.2.18", + "npm:class-variance-authority@~0.7.1": "0.7.1", + "npm:clsx@^2.1.1": "2.1.1", + "npm:cmdk@^1.1.1": "1.1.1_react@19.1.0_react-dom@19.1.0__react@19.1.0_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8", + "npm:crc@^4.3.2": "4.3.2", + "npm:crypto-random-string@5": "5.0.0", + "npm:gzipper@^8.2.1": "8.2.1", + "npm:happy-dom@^18.0.1": "18.0.1", + "npm:i18next-browser-languagedetector@^8.2.0": "8.2.0", + "npm:i18next-http-backend@^3.0.2": "3.0.2", + "npm:i18next@^25.3.2": "25.3.2_typescript@5.8.3", + "npm:idb-keyval@^6.2.2": "6.2.2", + "npm:immer@^10.1.1": "10.1.1", + "npm:js-cookie@^3.0.5": "3.0.5", + "npm:lucide-react@0.525": "0.525.0_react@19.1.0", + "npm:maplibre-gl@5.6.1": "5.6.1", + "npm:react-dom@^19.1.0": "19.1.0_react@19.1.0", + "npm:react-error-boundary@6": "6.0.0_react@19.1.0", + "npm:react-hook-form@^7.60.0": "7.60.0_react@19.1.0", + "npm:react-i18next@^15.6.0": "15.6.0_i18next@25.3.2__typescript@5.8.3_react@19.1.0_typescript@5.8.3", + "npm:react-map-gl@8.0.4": "8.0.4_maplibre-gl@5.6.1_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:react-qrcode-logo@3": "3.0.0_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:react@^19.1.0": "19.1.0", + "npm:rfc4648@^1.5.4": "1.5.4", + "npm:simple-git-hooks@^2.13.0": "2.13.0", + "npm:ste-simple-events@^3.0.11": "3.0.11", + "npm:tailwind-merge@^3.3.1": "3.3.1", + "npm:tailwindcss-animate@^1.0.7": "1.0.7_tailwindcss@4.1.11", + "npm:tailwindcss@^4.1.11": "4.1.11", + "npm:tar@^7.4.3": "7.4.3", + "npm:testing-library@^0.0.2": "0.0.2_@angular+common@6.1.10__@angular+core@6.1.10___rxjs@6.6.7___zone.js@0.8.29__rxjs@6.6.7_@angular+core@6.1.10__rxjs@6.6.7__zone.js@0.8.29", + "npm:tslog@^4.9.3": "4.9.3", + "npm:typescript@^5.8.3": "5.8.3", + "npm:vite@^7.0.4": "7.0.5_@types+node@22.16.4_picomatch@4.0.3", + "npm:vitest@^3.2.4": "3.2.4_@types+node@22.16.4_happy-dom@18.0.1_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3", + "npm:zod@^4.0.5": "4.0.5", + "npm:zustand@5.0.6": "5.0.6_@types+react@19.1.8_immer@10.1.1_react@19.1.0" + }, + "jsr": { + "@david/code-block-writer@13.0.3": { + "integrity": "f98c77d320f5957899a61bfb7a9bead7c6d83ad1515daee92dbacc861e13bb7f" + }, + "@deno/dnt@0.42.3": { + "integrity": "62a917a0492f3c8af002dce90605bb0d41f7d29debc06aca40dba72ab65d8ae3", + "dependencies": [ + "jsr:@david/code-block-writer", + "jsr:@std/fmt", + "jsr:@std/fs", + "jsr:@std/path@1", + "jsr:@ts-morph/bootstrap" + ] + }, + "@std/fmt@1.0.8": { + "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" + }, + "@std/fs@1.0.19": { + "integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06", + "dependencies": [ + "jsr:@std/internal", + "jsr:@std/path@^1.1.1" + ] + }, + "@std/internal@1.0.9": { + "integrity": "bdfb97f83e4db7a13e8faab26fb1958d1b80cc64366501af78a0aee151696eb8" + }, + "@std/path@1.1.1": { + "integrity": "fe00026bd3a7e6a27f73709b83c607798be40e20c81dde655ce34052fd82ec76", + "dependencies": [ + "jsr:@std/internal" + ] + }, + "@ts-morph/bootstrap@0.27.0": { + "integrity": "b8d7bc8f7942ce853dde4161b28f9aa96769cef3d8eebafb379a81800b9e2448", + "dependencies": [ + "jsr:@ts-morph/common" + ] + }, + "@ts-morph/common@0.27.0": { + "integrity": "c7b73592d78ce8479b356fd4f3d6ec3c460d77753a8680ff196effea7a939052", + "dependencies": [ + "jsr:@std/fs", + "jsr:@std/path@1" + ] + } + }, + "npm": { + "@adobe/css-tools@4.4.3": { + "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==" + }, + "@ampproject/remapping@2.3.0": { + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": [ + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping" + ] + }, + "@angular/common@6.1.10_@angular+core@6.1.10__rxjs@6.6.7__zone.js@0.8.29_rxjs@6.6.7": { + "integrity": "sha512-73xxTSYJNKfiJ7C1Ajg+sz5l8y+blb/vNgHYg7O3yem5zLBnfPpidJ1UGg4W4d2Y+jwUVJbZKh8SKJarqAJVUQ==", + "dependencies": [ + "@angular/core", + "rxjs", + "tslib@1.14.1" + ] + }, + "@angular/core@6.1.10_rxjs@6.6.7_zone.js@0.8.29": { + "integrity": "sha512-61l3rIQTVdT45eOf6/fBJIeVmV10mcrxqS4N/1OWkuDT29YSJTZSxGcv8QjAyyutuhcqWWpO6gVRkN07rWmkPg==", + "dependencies": [ + "rxjs", + "tslib@1.14.1", + "zone.js" + ] + }, + "@babel/code-frame@7.27.1": { + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dependencies": [ + "@babel/helper-validator-identifier", + "js-tokens@4.0.0", + "picocolors" + ] + }, + "@babel/compat-data@7.28.0": { + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==" + }, + "@babel/core@7.28.0": { + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dependencies": [ + "@ampproject/remapping", + "@babel/code-frame", + "@babel/generator", + "@babel/helper-compilation-targets", + "@babel/helper-module-transforms", + "@babel/helpers", + "@babel/parser", + "@babel/template", + "@babel/traverse", + "@babel/types", + "convert-source-map", + "debug", + "gensync", + "json5", + "semver" + ] + }, + "@babel/generator@7.28.0": { + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dependencies": [ + "@babel/parser", + "@babel/types", + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping", + "jsesc" + ] + }, + "@babel/helper-annotate-as-pure@7.27.3": { + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dependencies": [ + "@babel/types" + ] + }, + "@babel/helper-compilation-targets@7.27.2": { + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dependencies": [ + "@babel/compat-data", + "@babel/helper-validator-option", + "browserslist", + "lru-cache", + "semver" + ] + }, + "@babel/helper-create-class-features-plugin@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "dependencies": [ + "@babel/core", + "@babel/helper-annotate-as-pure", + "@babel/helper-member-expression-to-functions", + "@babel/helper-optimise-call-expression", + "@babel/helper-replace-supers", + "@babel/helper-skip-transparent-expression-wrappers", + "@babel/traverse", + "semver" + ] + }, + "@babel/helper-globals@7.28.0": { + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" + }, + "@babel/helper-member-expression-to-functions@7.27.1": { + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dependencies": [ + "@babel/traverse", + "@babel/types" + ] + }, + "@babel/helper-module-imports@7.27.1": { + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dependencies": [ + "@babel/traverse", + "@babel/types" + ] + }, + "@babel/helper-module-transforms@7.27.3_@babel+core@7.28.0": { + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dependencies": [ + "@babel/core", + "@babel/helper-module-imports", + "@babel/helper-validator-identifier", + "@babel/traverse" + ] + }, + "@babel/helper-optimise-call-expression@7.27.1": { + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dependencies": [ + "@babel/types" + ] + }, + "@babel/helper-plugin-utils@7.27.1": { + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==" + }, + "@babel/helper-replace-supers@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dependencies": [ + "@babel/core", + "@babel/helper-member-expression-to-functions", + "@babel/helper-optimise-call-expression", + "@babel/traverse" + ] + }, + "@babel/helper-skip-transparent-expression-wrappers@7.27.1": { + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dependencies": [ + "@babel/traverse", + "@babel/types" + ] + }, + "@babel/helper-string-parser@7.27.1": { + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" + }, + "@babel/helper-validator-identifier@7.27.1": { + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" + }, + "@babel/helper-validator-option@7.27.1": { + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" + }, + "@babel/helpers@7.27.6": { + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dependencies": [ + "@babel/template", + "@babel/types" + ] + }, + "@babel/parser@7.28.0": { + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dependencies": [ + "@babel/types" + ], + "bin": true + }, + "@babel/plugin-syntax-jsx@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-syntax-typescript@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-modules-commonjs@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dependencies": [ + "@babel/core", + "@babel/helper-module-transforms", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-react-jsx-self@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-react-jsx-source@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-typescript@7.28.0_@babel+core@7.28.0": { + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dependencies": [ + "@babel/core", + "@babel/helper-annotate-as-pure", + "@babel/helper-create-class-features-plugin", + "@babel/helper-plugin-utils", + "@babel/helper-skip-transparent-expression-wrappers", + "@babel/plugin-syntax-typescript" + ] + }, + "@babel/preset-typescript@7.27.1_@babel+core@7.28.0": { + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils", + "@babel/helper-validator-option", + "@babel/plugin-syntax-jsx", + "@babel/plugin-transform-modules-commonjs", + "@babel/plugin-transform-typescript" + ] + }, + "@babel/runtime@7.27.6": { + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==" + }, + "@babel/template@7.27.2": { + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dependencies": [ + "@babel/code-frame", + "@babel/parser", + "@babel/types" + ] + }, + "@babel/traverse@7.28.0": { + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dependencies": [ + "@babel/code-frame", + "@babel/generator", + "@babel/helper-globals", + "@babel/parser", + "@babel/template", + "@babel/types", + "debug" + ] + }, + "@babel/types@7.28.1": { + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "dependencies": [ + "@babel/helper-string-parser", + "@babel/helper-validator-identifier" + ] + }, + "@biomejs/biome@2.0.6": { + "integrity": "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==", + "optionalDependencies": [ + "@biomejs/cli-darwin-arm64", + "@biomejs/cli-darwin-x64", + "@biomejs/cli-linux-arm64", + "@biomejs/cli-linux-arm64-musl", + "@biomejs/cli-linux-x64", + "@biomejs/cli-linux-x64-musl", + "@biomejs/cli-win32-arm64", + "@biomejs/cli-win32-x64" + ], + "bin": true + }, + "@biomejs/cli-darwin-arm64@2.0.6": { + "integrity": "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@biomejs/cli-darwin-x64@2.0.6": { + "integrity": "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@biomejs/cli-linux-arm64-musl@2.0.6": { + "integrity": "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@biomejs/cli-linux-arm64@2.0.6": { + "integrity": "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@biomejs/cli-linux-x64-musl@2.0.6": { + "integrity": "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@biomejs/cli-linux-x64@2.0.6": { + "integrity": "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@biomejs/cli-win32-arm64@2.0.6": { + "integrity": "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@biomejs/cli-win32-x64@2.0.6": { + "integrity": "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@bufbuild/protobuf@2.6.1": { + "integrity": "sha512-DaG6XlyKpz08bmHY5SGX2gfIllaqtDJ/KwVoxsmP22COOLYwDBe7yD3DZGwXem/Xq7QOc9cuR7R3MpAv5CFfDw==" + }, + "@emnapi/core@1.4.4": { + "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "dependencies": [ + "@emnapi/wasi-threads", + "tslib@2.8.1" + ] + }, + "@emnapi/runtime@1.4.4": { + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "dependencies": [ + "tslib@2.8.1" + ] + }, + "@emnapi/wasi-threads@1.0.3": { + "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "dependencies": [ + "tslib@2.8.1" + ] + }, + "@esbuild/aix-ppc64@0.25.6": { + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/android-arm64@0.25.6": { + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm@0.25.6": { + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-x64@0.25.6": { + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/darwin-arm64@0.25.6": { + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-x64@0.25.6": { + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-arm64@0.25.6": { + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-x64@0.25.6": { + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/linux-arm64@0.25.6": { + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm@0.25.6": { + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-ia32@0.25.6": { + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-loong64@0.25.6": { + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-mips64el@0.25.6": { + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-ppc64@0.25.6": { + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-riscv64@0.25.6": { + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-s390x@0.25.6": { + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-x64@0.25.6": { + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-arm64@0.25.6": { + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-x64@0.25.6": { + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-arm64@0.25.6": { + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-x64@0.25.6": { + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openharmony-arm64@0.25.6": { + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/sunos-x64@0.25.6": { + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/win32-arm64@0.25.6": { + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-ia32@0.25.6": { + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-x64@0.25.6": { + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@floating-ui/core@1.7.2": { + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "dependencies": [ + "@floating-ui/utils" + ] + }, + "@floating-ui/dom@1.7.2": { + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "dependencies": [ + "@floating-ui/core", + "@floating-ui/utils" + ] + }, + "@floating-ui/react-dom@2.1.4_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==", + "dependencies": [ + "@floating-ui/dom", + "react", + "react-dom" + ] + }, + "@floating-ui/utils@0.2.10": { + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" + }, + "@gfx/zopfli@1.0.15": { + "integrity": "sha512-7mBgpi7UD82fsff5ThQKet0uBTl4BYerQuc+/qA1ELTwWEiIedRTcD3JgiUu9wwZ2kytW8JOb165rSdAt8PfcQ==", + "dependencies": [ + "base64-js" + ] + }, + "@hookform/resolvers@5.1.1_react-hook-form@7.60.0__react@19.1.0_react@19.1.0": { + "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==", + "dependencies": [ + "@standard-schema/utils", + "react-hook-form" + ] + }, + "@isaacs/fs-minipass@4.0.1": { + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dependencies": [ + "minipass" + ] + }, + "@jridgewell/gen-mapping@0.3.12": { + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dependencies": [ + "@jridgewell/sourcemap-codec", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/sourcemap-codec@1.5.4": { + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" + }, + "@jridgewell/trace-mapping@0.3.29": { + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dependencies": [ + "@jridgewell/resolve-uri", + "@jridgewell/sourcemap-codec" + ] + }, + "@jsr/meshtastic__core@2.6.4": { + "integrity": "sha512-1Kz5DK6peFxluHOJR38vFwfgeJzMXTz+3p6TvibjILVhSQC2U1nu8aJbn6w5zhRqS+j79OmtrRvdzL6VNsTkkQ==", + "dependencies": [ + "@bufbuild/protobuf", + "@jsr/meshtastic__protobufs", + "crc", + "ste-simple-events", + "tslog" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/meshtastic__core/2.6.4.tgz" + }, + "@jsr/meshtastic__protobufs@2.7.0": { + "integrity": "sha512-ndZhUyB/ADSyjJI+iSeSOoIKqNGZ2+ERVjfY0qnh4jgF740tFTwefC5mzZhOqDLbreGFYS79+429NtH5Ujdzdg==", + "dependencies": [ + "@bufbuild/protobuf" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/meshtastic__protobufs/2.7.0.tgz" + }, + "@jsr/meshtastic__transport-http@0.2.1": { + "integrity": "sha512-lmQKr3aIINKvtGROU4HchmSVqbZSbkIHqajowRRC8IAjsnR0zNTyxz210QyY4pFUF9hpcW3GRjwq5h/VO2JuGg==", + "dependencies": [ + "@jsr/meshtastic__core" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-http/0.2.1.tgz" + }, + "@jsr/meshtastic__transport-web-bluetooth@0.1.2": { + "integrity": "sha512-Z+5pv9RXNgY0/crKExOH3pZ6LT0HIXFmnBL7NX5AO2knOFRn+4lmxQEhhmiTTlkUfqyEfAvbjuY5u4mq9TPTdQ==", + "dependencies": [ + "@jsr/meshtastic__core" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-bluetooth/0.1.2.tgz" + }, + "@jsr/meshtastic__transport-web-serial@0.2.1": { + "integrity": "sha512-yumjEGLkAuJYOC3aWKvZzbQqi/LnqaKfNpVCY7Ki7oLtAshNiZrBLiwiFhN7+ZR9FfMdJThyBMqREBDRRWTO1Q==", + "dependencies": [ + "@jsr/meshtastic__core" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-serial/0.2.1.tgz" + }, + "@mapbox/geojson-rewind@0.5.2": { + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "dependencies": [ + "get-stream", + "minimist" + ], + "bin": true + }, + "@mapbox/jsonlint-lines-primitives@2.0.2": { + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==" + }, + "@mapbox/point-geometry@0.1.0": { + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "@mapbox/tiny-sdf@2.0.6": { + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "@mapbox/unitbezier@0.0.1": { + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "@mapbox/vector-tile@1.3.1": { + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "dependencies": [ + "@mapbox/point-geometry" + ] + }, + "@mapbox/whoots-js@3.1.0": { + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==" + }, + "@maplibre/maplibre-gl-style-spec@19.3.3": { + "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", + "dependencies": [ + "@mapbox/jsonlint-lines-primitives", + "@mapbox/unitbezier", + "json-stringify-pretty-compact@3.0.0", + "minimist", + "rw", + "sort-object" + ], + "bin": true + }, + "@maplibre/maplibre-gl-style-spec@23.3.0": { + "integrity": "sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==", + "dependencies": [ + "@mapbox/jsonlint-lines-primitives", + "@mapbox/unitbezier", + "json-stringify-pretty-compact@4.0.0", + "minimist", + "quickselect@3.0.0", + "rw", + "tinyqueue@3.0.0" + ], + "bin": true + }, + "@napi-rs/wasm-runtime@0.2.12": { + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dependencies": [ + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util@0.10.0" + ] + }, + "@noble/curves@1.9.4": { + "integrity": "sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==", + "dependencies": [ + "@noble/hashes" + ] + }, + "@noble/hashes@1.8.0": { + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==" + }, + "@oven/bun-darwin-aarch64@1.2.18": { + "integrity": "sha512-GNxVh9VUOQ6S0aDp4Qe80MGadGbh8BS6p3jEHXIboRoTrb/80oR0csMjGUpdwGa2hX1zTvpPBwOFXvVP9UaB0Q==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@oven/bun-darwin-x64-baseline@1.2.18": { + "integrity": "sha512-LT/MF4DySLjskZf4mUgVXhpDBCuGXI7+uHJTiAjinddglh7ENbrSRuM01cjlJ/dxivvekq5+w6k9gdYpHUibuw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@oven/bun-darwin-x64@1.2.18": { + "integrity": "sha512-/oxsG7eIkvw3rxt3V9gqY23i0ajk8m1cG/FedRj8b15GW2TgA+F9F6FQNLqxc/59SBkcrbTLoqk5EtAQwuwi/w==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@oven/bun-linux-aarch64-musl@1.2.18": { + "integrity": "sha512-hk58uY6LSvDn2WDB8o/WAVCOZERYZPShUujI8rCwcDXkQRI4pbm5B5RJP5wEF0fClRI+WXxyyoBFsTKb7lbgyQ==", + "os": ["linux"], + "cpu": ["aarch64"] + }, + "@oven/bun-linux-aarch64@1.2.18": { + "integrity": "sha512-0uTiUZJFS69LbYPCw963BAdP4wvUXEozbNf7vrB/3rT82x+fPZKF3C+4nfFScm+6UYusjH468vG7/g9x38jBIg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@oven/bun-linux-x64-baseline@1.2.18": { + "integrity": "sha512-ERnR7gZz/YYpo/ZhRKXvY9qtsJNQnTrp5HayExfvD1achoHcYEvf3TarajRLVC7gDi7BxlaOPZyJjgdo5g0tUg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oven/bun-linux-x64-musl-baseline@1.2.18": { + "integrity": "sha512-u4sqExX5gdcMRdwzL16qP/xJlnxVR+fF43GGQJNopOTXDrsK33BXw3aUObHRtVkqRiK3cyubJUgTtz2ykQ4Dng==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oven/bun-linux-x64-musl@1.2.18": { + "integrity": "sha512-Oqj8yDkObDWMlxzbhOefb+B75tgKEP4uGEFcBHXjVxSEL0lB7B7LYTvTpeDm8QPldhLs1xAN4FtzZlPUn6qI+Q==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oven/bun-linux-x64@1.2.18": { + "integrity": "sha512-okHdy9+Yov5BvI19FynnvsmQUP477SNJRv33TIHxs9cpj/ClgaYXMihS+yH0LCzYDFIeojfABiIHdBVUFmxqtQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oven/bun-windows-x64-baseline@1.2.18": { + "integrity": "sha512-n5XF3N0Kr53z4NnVWfTqS72U2rSHJlFafO70SOSzgiu26ylKTGOC9BBsvEQhKld4nKAsbp8YjpOViomrtC6bCQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@oven/bun-windows-x64@1.2.18": { + "integrity": "sha512-jklsKWT9zfh8wXewKPfO7Uq8vo72esaQoGzCTTt0NKY+juXvyKaiMHEfT7v4o7cmrql3QPeVtsbp9uNAiuotgw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@radix-ui/number@1.1.1": { + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==" + }, + "@radix-ui/primitive@1.1.2": { + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" + }, + "@radix-ui/react-accordion@1.2.11_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-collapsible", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-id", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-arrow@1.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "dependencies": [ + "@radix-ui/react-primitive", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-checkbox@1.3.2_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-previous", + "@radix-ui/react-use-size", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-collapsible@1.1.11_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-id", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-layout-effect", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-collection@1.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "dependencies": [ + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-primitive", + "@radix-ui/react-slot", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-compose-refs@1.1.2_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-context@1.1.2_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-dialog@1.1.14_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-dismissable-layer", + "@radix-ui/react-focus-guards", + "@radix-ui/react-focus-scope", + "@radix-ui/react-id", + "@radix-ui/react-portal", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-slot", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "aria-hidden", + "react", + "react-dom", + "react-remove-scroll" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-direction@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-dismissable-layer@1.1.10_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-escape-keydown", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-dropdown-menu@2.1.15_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-id", + "@radix-ui/react-menu", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-focus-guards@1.1.2_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-focus-scope@1.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "dependencies": [ + "@radix-ui/react-compose-refs", + "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-id@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "dependencies": [ + "@radix-ui/react-use-layout-effect", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-label@2.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "dependencies": [ + "@radix-ui/react-primitive", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-menu@2.1.15_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-dismissable-layer", + "@radix-ui/react-focus-guards", + "@radix-ui/react-focus-scope", + "@radix-ui/react-id", + "@radix-ui/react-popper", + "@radix-ui/react-portal", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-roving-focus", + "@radix-ui/react-slot", + "@radix-ui/react-use-callback-ref", + "@types/react", + "@types/react-dom", + "aria-hidden", + "react", + "react-dom", + "react-remove-scroll" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-menubar@1.1.15_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-id", + "@radix-ui/react-menu", + "@radix-ui/react-primitive", + "@radix-ui/react-roving-focus", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-popover@1.1.14_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-dismissable-layer", + "@radix-ui/react-focus-guards", + "@radix-ui/react-focus-scope", + "@radix-ui/react-id", + "@radix-ui/react-popper", + "@radix-ui/react-portal", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-slot", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "aria-hidden", + "react", + "react-dom", + "react-remove-scroll" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-popper@1.2.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", + "dependencies": [ + "@floating-ui/react-dom", + "@radix-ui/react-arrow", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-layout-effect", + "@radix-ui/react-use-rect", + "@radix-ui/react-use-size", + "@radix-ui/rect", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-portal@1.1.9_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "dependencies": [ + "@radix-ui/react-primitive", + "@radix-ui/react-use-layout-effect", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-presence@1.1.4_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "dependencies": [ + "@radix-ui/react-compose-refs", + "@radix-ui/react-use-layout-effect", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-primitive@2.1.3_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "dependencies": [ + "@radix-ui/react-slot", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-roving-focus@1.1.10_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-id", + "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-scroll-area@1.2.9_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", + "dependencies": [ + "@radix-ui/number", + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-layout-effect", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-select@2.2.5_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", + "dependencies": [ + "@radix-ui/number", + "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-dismissable-layer", + "@radix-ui/react-focus-guards", + "@radix-ui/react-focus-scope", + "@radix-ui/react-id", + "@radix-ui/react-popper", + "@radix-ui/react-portal", + "@radix-ui/react-primitive", + "@radix-ui/react-slot", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-layout-effect", + "@radix-ui/react-use-previous", + "@radix-ui/react-visually-hidden", + "@types/react", + "@types/react-dom", + "aria-hidden", + "react", + "react-dom", + "react-remove-scroll" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-separator@1.1.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "dependencies": [ + "@radix-ui/react-primitive", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-slider@1.3.5_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==", + "dependencies": [ + "@radix-ui/number", + "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-layout-effect", + "@radix-ui/react-use-previous", + "@radix-ui/react-use-size", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-slot@1.2.3_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "dependencies": [ + "@radix-ui/react-compose-refs", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-switch@1.2.5_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-previous", + "@radix-ui/react-use-size", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-tabs@1.1.12_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-id", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-roving-focus", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-toast@1.2.14_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-dismissable-layer", + "@radix-ui/react-portal", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-layout-effect", + "@radix-ui/react-visually-hidden", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-toggle-group@1.1.10_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-context", + "@radix-ui/react-direction", + "@radix-ui/react-primitive", + "@radix-ui/react-roving-focus", + "@radix-ui/react-toggle", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-toggle@1.1.9_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-primitive", + "@radix-ui/react-use-controllable-state", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-tooltip@1.2.7_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", + "dependencies": [ + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-dismissable-layer", + "@radix-ui/react-id", + "@radix-ui/react-popper", + "@radix-ui/react-portal", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-slot", + "@radix-ui/react-use-controllable-state", + "@radix-ui/react-visually-hidden", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/react-use-callback-ref@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-controllable-state@1.2.2_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "dependencies": [ + "@radix-ui/react-use-effect-event", + "@radix-ui/react-use-layout-effect", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-effect-event@0.0.2_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "dependencies": [ + "@radix-ui/react-use-layout-effect", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-escape-keydown@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "dependencies": [ + "@radix-ui/react-use-callback-ref", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-layout-effect@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-previous@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-rect@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "dependencies": [ + "@radix-ui/rect", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-use-size@1.1.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "dependencies": [ + "@radix-ui/react-use-layout-effect", + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-visually-hidden@1.2.3_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "dependencies": [ + "@radix-ui/react-primitive", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@radix-ui/rect@1.1.1": { + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==" + }, + "@rolldown/pluginutils@1.0.0-beta.19": { + "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==" + }, + "@rollup/rollup-android-arm-eabi@4.45.1": { + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "os": ["android"], + "cpu": ["arm"] + }, + "@rollup/rollup-android-arm64@4.45.1": { + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-arm64@4.45.1": { + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-x64@4.45.1": { + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@rollup/rollup-freebsd-arm64@4.45.1": { + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@rollup/rollup-freebsd-x64@4.45.1": { + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-arm-gnueabihf@4.45.1": { + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm-musleabihf@4.45.1": { + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm64-gnu@4.45.1": { + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-arm64-musl@4.45.1": { + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-loongarch64-gnu@4.45.1": { + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-powerpc64le-gnu@4.45.1": { + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-riscv64-gnu@4.45.1": { + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-riscv64-musl@4.45.1": { + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-s390x-gnu@4.45.1": { + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@rollup/rollup-linux-x64-gnu@4.45.1": { + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-x64-musl@4.45.1": { + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-win32-arm64-msvc@4.45.1": { + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-ia32-msvc@4.45.1": { + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@rollup/rollup-win32-x64-msvc@4.45.1": { + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@standard-schema/utils@0.3.0": { + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==" + }, + "@tailwindcss/node@4.1.11": { + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "dependencies": [ + "@ampproject/remapping", + "enhanced-resolve", + "jiti", + "lightningcss", + "magic-string", + "source-map-js", + "tailwindcss" + ] + }, + "@tailwindcss/oxide-android-arm64@4.1.11": { + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-arm64@4.1.11": { + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-x64@4.1.11": { + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-freebsd-x64@4.1.11": { + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11": { + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@tailwindcss/oxide-linux-arm64-gnu@4.1.11": { + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-arm64-musl@4.1.11": { + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-x64-gnu@4.1.11": { + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-x64-musl@4.1.11": { + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-wasm32-wasi@4.1.11": { + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "dependencies": [ + "@emnapi/core", + "@emnapi/runtime", + "@emnapi/wasi-threads", + "@napi-rs/wasm-runtime", + "@tybys/wasm-util@0.9.0", + "tslib@2.8.1" + ], + "cpu": ["wasm32"] + }, + "@tailwindcss/oxide-win32-arm64-msvc@4.1.11": { + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-win32-x64-msvc@4.1.11": { + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide@4.1.11": { + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "dependencies": [ + "detect-libc", + "tar" + ], + "optionalDependencies": [ + "@tailwindcss/oxide-android-arm64", + "@tailwindcss/oxide-darwin-arm64", + "@tailwindcss/oxide-darwin-x64", + "@tailwindcss/oxide-freebsd-x64", + "@tailwindcss/oxide-linux-arm-gnueabihf", + "@tailwindcss/oxide-linux-arm64-gnu", + "@tailwindcss/oxide-linux-arm64-musl", + "@tailwindcss/oxide-linux-x64-gnu", + "@tailwindcss/oxide-linux-x64-musl", + "@tailwindcss/oxide-wasm32-wasi", + "@tailwindcss/oxide-win32-arm64-msvc", + "@tailwindcss/oxide-win32-x64-msvc" + ], + "scripts": true + }, + "@tailwindcss/vite@4.1.11_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@types+node@22.16.4": { + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "dependencies": [ + "@tailwindcss/node", + "@tailwindcss/oxide", + "tailwindcss", + "vite" + ] + }, + "@tanstack/history@1.121.34": { + "integrity": "sha512-YL8dGi5ZU+xvtav2boRlw4zrRghkY6hvdcmHhA0RGSJ/CBgzv+cbADW9eYJLx74XMZvIQ1pp6VMbrpXnnM5gHA==" + }, + "@tanstack/react-router-devtools@1.128.0_@tanstack+react-router@1.128.0__react@19.1.0__react-dom@19.1.0___react@19.1.0_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-SJoFzCTvKdwPk8u1r5sTUFQqdEvt1lk0Y80DTzBz1Yc+PcUSTRMMfC6aGVFDSbWJt8QnQc65jDIlu6oTcnLhOQ==", + "dependencies": [ + "@tanstack/react-router", + "@tanstack/router-devtools-core", + "react", + "react-dom" + ] + }, + "@tanstack/react-router@1.128.0_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-fYB/eeO7wPRS5Xh8uts2Qesp/unvSUz1deV76d8qY4SCNTLXcSrH5w0thxviGcHqRNuWnNCz7OEp8oowK4i7bw==", + "dependencies": [ + "@tanstack/history", + "@tanstack/react-store", + "@tanstack/router-core", + "isbot", + "react", + "react-dom", + "tiny-invariant", + "tiny-warning" + ] + }, + "@tanstack/react-store@0.7.3_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-3Dnqtbw9P2P0gw8uUM8WP2fFfg8XMDSZCTsywRPZe/XqqYW8PGkXKZTvP0AHkE4mpqP9Y43GpOg9vwO44azu6Q==", + "dependencies": [ + "@tanstack/store", + "react", + "react-dom", + "use-sync-external-store" + ] + }, + "@tanstack/router-cli@1.128.0": { + "integrity": "sha512-slgBRYyKPu106VX4ssPxsGNZ/QmkMQdAnXl13gQV5ABVBu/uABL2uklPD4o6yMRII00CsPN368nKMtRq58SoBg==", + "dependencies": [ + "@tanstack/router-generator", + "chokidar", + "yargs" + ], + "bin": true + }, + "@tanstack/router-core@1.128.0_seroval@1.3.2": { + "integrity": "sha512-XLDHPzDCjsjA8Q8NxiACsOiITv1ESWAL/oiXOrygdcZR+quistNixhy38z7XLJBojs0iCxRCV92qck5tAhjLoA==", + "dependencies": [ + "@tanstack/history", + "@tanstack/store", + "cookie-es", + "seroval", + "seroval-plugins", + "tiny-invariant", + "tiny-warning" + ] + }, + "@tanstack/router-devtools-core@1.128.0_@tanstack+router-core@1.128.0__seroval@1.3.2_solid-js@1.9.7__seroval@1.3.2_tiny-invariant@1.3.3": { + "integrity": "sha512-KaHe3X1aB8D7cAi5lvKLKRhjy6f44x/jQV2yt6rurKQiGv10yzsKXoUDPZNOOaS9LUVkwUUXv7zuDwd/kv2Wow==", + "dependencies": [ + "@tanstack/router-core", + "clsx", + "goober", + "solid-js", + "tiny-invariant" + ] + }, + "@tanstack/router-devtools@1.128.0_@tanstack+react-router@1.128.0__react@19.1.0__react-dom@19.1.0___react@19.1.0_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-CkV3Iclrp/1hFkasdIXrD5f4IJWY2r1aJxUGAyt2N4zVF4CswOFElQyISGbXqRfBcxAQVxSy5RYBbsT6YZz0vg==", + "dependencies": [ + "@tanstack/react-router", + "@tanstack/react-router-devtools", + "clsx", + "goober", + "react", + "react-dom" + ] + }, + "@tanstack/router-generator@1.128.0": { + "integrity": "sha512-7ibImI7bv4feWgWKZZF69CrZPFzuYbtfFtRPCmoFNY5aCKUIYya4HNNlldOkJLNXiUyWQ31kuoqvNhEdqwhGwQ==", + "dependencies": [ + "@tanstack/router-core", + "@tanstack/router-utils", + "@tanstack/virtual-file-routes", + "prettier", + "recast", + "source-map@0.7.4", + "tsx", + "zod@3.25.76" + ] + }, + "@tanstack/router-plugin@1.128.0_@tanstack+react-router@1.128.0__react@19.1.0__react-dom@19.1.0___react@19.1.0_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@babel+core@7.28.0_react@19.1.0_react-dom@19.1.0__react@19.1.0_@types+node@22.16.4": { + "integrity": "sha512-HebeNn2AE8Ew4yhLc2kin5gTovSO20Y5bJBnJ6BJN3bX3sO4K1A4HzDof3oaW5bb4wqlESeVlZarD9zO9ANQ3Q==", + "dependencies": [ + "@babel/core", + "@babel/plugin-syntax-jsx", + "@babel/plugin-syntax-typescript", + "@babel/template", + "@babel/traverse", + "@babel/types", + "@tanstack/react-router", + "@tanstack/router-core", + "@tanstack/router-generator", + "@tanstack/router-utils", + "@tanstack/virtual-file-routes", + "babel-dead-code-elimination", + "chokidar", + "unplugin", + "vite", + "zod@3.25.76" + ], + "optionalPeers": [ + "@tanstack/react-router", + "vite" + ] + }, + "@tanstack/router-utils@1.121.21_@babel+core@7.28.0": { + "integrity": "sha512-u7ubq1xPBtNiU7Fm+EOWlVWdgFLzuKOa1thhqdscVn8R4dNMUd1VoOjZ6AKmLw201VaUhFtlX+u0pjzI6szX7A==", + "dependencies": [ + "@babel/core", + "@babel/generator", + "@babel/parser", + "@babel/preset-typescript", + "ansis", + "diff" + ] + }, + "@tanstack/store@0.7.2": { + "integrity": "sha512-RP80Z30BYiPX2Pyo0Nyw4s1SJFH2jyM6f9i3HfX4pA+gm5jsnYryscdq2aIQLnL4TaGuQMO+zXmN9nh1Qck+Pg==" + }, + "@tanstack/virtual-file-routes@1.121.21": { + "integrity": "sha512-3nuYsTyaq6ZN7jRZ9z6Gj3GXZqBOqOT0yzd/WZ33ZFfv4yVNIvsa5Lw+M1j3sgyEAxKMqGu/FaNi7FCjr3yOdw==" + }, + "@testing-library/dom@10.4.0": { + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dependencies": [ + "@babel/code-frame", + "@babel/runtime", + "@types/aria-query", + "aria-query@5.3.0", + "chalk@4.1.2", + "dom-accessibility-api@0.5.16", + "lz-string", + "pretty-format" + ] + }, + "@testing-library/jest-dom@6.6.3": { + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "dependencies": [ + "@adobe/css-tools", + "aria-query@5.3.2", + "chalk@3.0.0", + "css.escape", + "dom-accessibility-api@0.6.3", + "lodash", + "redent" + ] + }, + "@testing-library/react@16.3.0_@testing-library+dom@10.4.0_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "dependencies": [ + "@babel/runtime", + "@testing-library/dom", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@testing-library/user-event@14.6.1_@testing-library+dom@10.4.0": { + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dependencies": [ + "@testing-library/dom" + ] + }, + "@turf/along@7.2.0": { + "integrity": "sha512-Cf+d2LozABdb0TJoIcJwFKB+qisJY4nMUW9z6PAuZ9UCH7AR//hy2Z06vwYCKFZKP4a7DRPkOMBadQABCyoYuw==", + "dependencies": [ + "@turf/bearing", + "@turf/destination", + "@turf/distance", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/angle@7.2.0": { + "integrity": "sha512-b28rs1NO8Dt/MXadFhnpqH7GnEWRsl+xF5JeFtg9+eM/+l/zGrdliPYMZtAj12xn33w22J1X4TRprAI0rruvVQ==", + "dependencies": [ + "@turf/bearing", + "@turf/helpers", + "@turf/invariant", + "@turf/rhumb-bearing", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/area@7.2.0": { + "integrity": "sha512-zuTTdQ4eoTI9nSSjerIy4QwgvxqwJVciQJ8tOPuMHbXJ9N/dNjI7bU8tasjhxas/Cx3NE9NxVHtNpYHL0FSzoA==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/bbox-clip@7.2.0": { + "integrity": "sha512-q6RXTpqeUQAYLAieUL1n3J6ukRGsNVDOqcYtfzaJbPW+0VsAf+1cI16sN700t0sekbeU1DH/RRVAHhpf8+36wA==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/bbox-polygon@7.2.0": { + "integrity": "sha512-Aj4G1GAAy26fmOqMjUk0Z+Lcax5VQ9g1xYDbHLQWXvfTsaueBT+RzdH6XPnZ/seEEnZkio2IxE8V5af/osupgA==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/bbox@7.2.0": { + "integrity": "sha512-wzHEjCXlYZiDludDbXkpBSmv8Zu6tPGLmJ1sXQ6qDwpLE1Ew3mcWqt8AaxfTP5QwDNQa3sf2vvgTEzNbPQkCiA==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/bearing@7.2.0": { + "integrity": "sha512-Jm0Xt3GgHjRrWvBtAGvgfnADLm+4exud2pRlmCYx8zfiKuNXQFkrcTZcOiJOgTfG20Agq28iSh15uta47jSIbg==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/bezier-spline@7.2.0": { + "integrity": "sha512-7BPkc3ufYB9KLvcaTpTsnpXzh9DZoENxCS0Ms9XUwuRXw45TpevwUpOsa3atO76iKQ5puHntqFO4zs8IUxBaaA==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-clockwise@7.2.0": { + "integrity": "sha512-0fJeFSARxy6ealGBM4Gmgpa1o8msQF87p2Dx5V6uSqzT8VPDegX1NSWl4b7QgXczYa9qv7IAABttdWP0K7Q7eQ==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-concave@7.2.0": { + "integrity": "sha512-v3dTN04dfO6VqctQj1a+pjDHb6+/Ev90oAR2QjJuAntY4ubhhr7vKeJdk/w+tWNSMKULnYwfe65Du3EOu3/TeA==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-contains@7.2.0": { + "integrity": "sha512-dgRQm4uVO5XuLee4PLVH7CFQZKdefUBMIXTPITm2oRIDmPLJKHDOFKQTNkGJ73mDKKBR2lmt6eVH3br6OYrEYg==", + "dependencies": [ + "@turf/bbox", + "@turf/boolean-point-in-polygon", + "@turf/boolean-point-on-line", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-crosses@7.2.0": { + "integrity": "sha512-9GyM4UUWFKQOoNhHVSfJBf5XbPy8Fxfz9djjJNAnm/IOl8NmFUSwFPAjKlpiMcr6yuaAoc9R/1KokS9/eLqPvA==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/helpers", + "@turf/invariant", + "@turf/line-intersect", + "@turf/polygon-to-line", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-disjoint@7.2.0": { + "integrity": "sha512-xdz+pYKkLMuqkNeJ6EF/3OdAiJdiHhcHCV0ykX33NIuALKIEpKik0+NdxxNsZsivOW6keKwr61SI+gcVtHYcnQ==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/helpers", + "@turf/line-intersect", + "@turf/meta", + "@turf/polygon-to-line", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-equal@7.2.0": { + "integrity": "sha512-TmjKYLsxXqEmdDtFq3QgX4aSogiISp3/doeEtDOs3NNSR8susOtBEZkmvwO6DLW+g/rgoQJIBR6iVoWiRqkBxw==", + "dependencies": [ + "@turf/clean-coords", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "geojson-equality-ts", + "tslib@2.8.1" + ] + }, + "@turf/boolean-intersects@7.2.0": { + "integrity": "sha512-GLRyLQgK3F14drkK5Qi9Mv7Z9VT1bgQUd9a3DB3DACTZWDSwfh8YZUFn/HBwRkK8dDdgNEXaavggQHcPi1k9ow==", + "dependencies": [ + "@turf/boolean-disjoint", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-overlap@7.2.0": { + "integrity": "sha512-ieM5qIE4anO+gUHIOvEN7CjyowF+kQ6v20/oNYJCp63TVS6eGMkwgd+I4uMzBXfVW66nVHIXjODdUelU+Xyctw==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@turf/line-intersect", + "@turf/line-overlap", + "@turf/meta", + "@types/geojson", + "geojson-equality-ts", + "tslib@2.8.1" + ] + }, + "@turf/boolean-parallel@7.2.0": { + "integrity": "sha512-iOtuzzff8nmwv05ROkSvyeGLMrfdGkIi+3hyQ+DH4IVyV37vQbqR5oOJ0Nt3Qq1Tjrq9fvF8G3OMdAv3W2kY9w==", + "dependencies": [ + "@turf/clean-coords", + "@turf/helpers", + "@turf/line-segment", + "@turf/rhumb-bearing", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-point-in-polygon@7.2.0": { + "integrity": "sha512-lvEOjxeXIp+wPXgl9kJA97dqzMfNexjqHou+XHVcfxQgolctoJiRYmcVCWGpiZ9CBf/CJha1KmD1qQoRIsjLaA==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "point-in-polygon-hao", + "tslib@2.8.1" + ] + }, + "@turf/boolean-point-on-line@7.2.0": { + "integrity": "sha512-H/bXX8+2VYeSyH8JWrOsu8OGmeA9KVZfM7M6U5/fSqGsRHXo9MyYJ94k39A9kcKSwI0aWiMXVD2UFmiWy8423Q==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-touches@7.2.0": { + "integrity": "sha512-8qb1CO+cwFATGRGFgTRjzL9aibfsbI91pdiRl7KIEkVdeN/H9k8FDrUA1neY7Yq48IaciuwqjbbojQ16FD9b0w==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/boolean-point-on-line", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/boolean-valid@7.2.0": { + "integrity": "sha512-xb7gdHN8VV6ivPJh6rPpgxmAEGReiRxqY+QZoEZVGpW2dXcmU1BdY6FA6G/cwvggXAXxJBREoANtEDgp/0ySbA==", + "dependencies": [ + "@turf/bbox", + "@turf/boolean-crosses", + "@turf/boolean-disjoint", + "@turf/boolean-overlap", + "@turf/boolean-point-in-polygon", + "@turf/boolean-point-on-line", + "@turf/helpers", + "@turf/invariant", + "@turf/line-intersect", + "@types/geojson", + "geojson-polygon-self-intersections", + "tslib@2.8.1" + ] + }, + "@turf/boolean-within@7.2.0": { + "integrity": "sha512-zB3AiF59zQZ27Dp1iyhp9mVAKOFHat8RDH45TZhLY8EaqdEPdmLGvwMFCKfLryQcUDQvmzP8xWbtUR82QM5C4g==", + "dependencies": [ + "@turf/bbox", + "@turf/boolean-point-in-polygon", + "@turf/boolean-point-on-line", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/buffer@7.2.0": { + "integrity": "sha512-QH1FTr5Mk4z1kpQNztMD8XBOZfpOXPOtlsxaSAj2kDIf5+LquA6HtJjZrjUngnGtzG5+XwcfyRL4ImvLnFjm5Q==", + "dependencies": [ + "@turf/bbox", + "@turf/center", + "@turf/helpers", + "@turf/jsts", + "@turf/meta", + "@turf/projection", + "@types/geojson", + "d3-geo" + ] + }, + "@turf/center-mean@7.2.0": { + "integrity": "sha512-NaW6IowAooTJ35O198Jw3U4diZ6UZCCeJY+4E+WMLpks3FCxMDSHEfO2QjyOXQMGWZnVxVelqI5x9DdniDbQ+A==", + "dependencies": [ + "@turf/bbox", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/center-median@7.2.0": { + "integrity": "sha512-/CgVyHNG4zAoZpvkl7qBCe4w7giWNVtLyTU5PoIfg1vWM4VpYw+N7kcBBH46bbzvVBn0vhmZr586r543EwdC/A==", + "dependencies": [ + "@turf/center-mean", + "@turf/centroid", + "@turf/distance", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/center-of-mass@7.2.0": { + "integrity": "sha512-ij3pmG61WQPHGTQvOziPOdIgwTMegkYTwIc71Gl7xn4C0vWH6KLDSshCphds9xdWSXt2GbHpUs3tr4XGntHkEQ==", + "dependencies": [ + "@turf/centroid", + "@turf/convex", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/center@7.2.0": { + "integrity": "sha512-UTNp9abQ2kuyRg5gCIGDNwwEQeF3NbpYsd1Q0KW9lwWuzbLVNn0sOwbxjpNF4J2HtMOs5YVOcqNvYyuoa2XrXw==", + "dependencies": [ + "@turf/bbox", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/centroid@7.2.0": { + "integrity": "sha512-yJqDSw25T7P48au5KjvYqbDVZ7qVnipziVfZ9aSo7P2/jTE7d4BP21w0/XLi3T/9bry/t9PR1GDDDQljN4KfDw==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/circle@7.2.0": { + "integrity": "sha512-1AbqBYtXhstrHmnW6jhLwsv7TtmT0mW58Hvl1uZXEDM1NCVXIR50yDipIeQPjrCuJ/Zdg/91gU8+4GuDCAxBGA==", + "dependencies": [ + "@turf/destination", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/clean-coords@7.2.0": { + "integrity": "sha512-+5+J1+D7wW7O/RDXn46IfCHuX1gIV1pIAQNSA7lcDbr3HQITZj334C4mOGZLEcGbsiXtlHWZiBtm785Vg8i+QQ==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/clone@7.2.0": { + "integrity": "sha512-JlGUT+/5qoU5jqZmf6NMFIoLDY3O7jKd53Up+zbpJ2vzUp6QdwdNzwrsCeONhynWM13F0MVtPXH4AtdkrgFk4g==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/clusters-dbscan@7.2.0": { + "integrity": "sha512-VWVUuDreev56g3/BMlnq/81yzczqaz+NVTypN5CigGgP67e+u/CnijphiuhKjtjDd/MzGjXgEWBJc26Y6LYKAw==", + "dependencies": [ + "@turf/clone", + "@turf/distance", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "rbush@3.0.1", + "tslib@2.8.1" + ] + }, + "@turf/clusters-kmeans@7.2.0": { + "integrity": "sha512-BxQdK8jc8Mwm9yoClCYkktm4W004uiQGqb/i/6Y7a8xqgJITWDgTu/cy//wOxAWPk4xfe6MThjnqkszWW8JdyQ==", + "dependencies": [ + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "skmeans", + "tslib@2.8.1" + ] + }, + "@turf/clusters@7.2.0": { + "integrity": "sha512-sKOrIKHHtXAuTKNm2USnEct+6/MrgyzMW42deZ2YG2RRKWGaaxHMFU2Yw71Yk4DqStOqTIBQpIOdrRuSOwbuQw==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/collect@7.2.0": { + "integrity": "sha512-zRVGDlYS8Bx/Zz4vnEUyRg4dmqHhkDbW/nIUIJh657YqaMj1SFi4Iv2i9NbcurlUBDJFkpuOhCvvEvAdskJ8UA==", + "dependencies": [ + "@turf/bbox", + "@turf/boolean-point-in-polygon", + "@turf/helpers", + "@types/geojson", + "rbush@3.0.1", + "tslib@2.8.1" + ] + }, + "@turf/combine@7.2.0": { + "integrity": "sha512-VEjm3IvnbMt3IgeRIhCDhhQDbLqCU1/5uN1+j1u6fyA095pCizPThGp4f/COSzC3t1s/iiV+fHuDsB6DihHffQ==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/concave@7.2.0": { + "integrity": "sha512-cpaDDlumK762kdadexw5ZAB6g/h2pJdihZ+e65lbQVe3WukJHAANnIEeKsdFCuIyNKrwTz2gWu5ws+OpjP48Yw==", + "dependencies": [ + "@turf/clone", + "@turf/distance", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/tin", + "@types/geojson", + "topojson-client", + "topojson-server", + "tslib@2.8.1" + ] + }, + "@turf/convex@7.2.0": { + "integrity": "sha512-HsgHm+zHRE8yPCE/jBUtWFyaaBmpXcSlyHd5/xsMhSZRImFzRzBibaONWQo7xbKZMISC3Nc6BtUjDi/jEVbqyA==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "concaveman", + "tslib@2.8.1" + ] + }, + "@turf/destination@7.2.0": { + "integrity": "sha512-8DUxtOO0Fvrh1xclIUj3d9C5WS20D21F5E+j+X9Q+ju6fcM4huOqTg5ckV1DN2Pg8caABEc5HEZJnGch/5YnYQ==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/difference@7.2.0": { + "integrity": "sha512-NHKD1v3s8RX+9lOpvHJg6xRuJOKiY3qxHhz5/FmE0VgGqnCkE7OObqWZ5SsXG+Ckh0aafs5qKhmDdDV/gGi6JA==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "polyclip-ts", + "tslib@2.8.1" + ] + }, + "@turf/dissolve@7.2.0": { + "integrity": "sha512-gPG5TE3mAYuZqBut8tPYCKwi4hhx5Cq0ALoQMB9X0hrVtFIKrihrsj98XQM/5pL/UIpAxQfwisQvy6XaOFaoPA==", + "dependencies": [ + "@turf/flatten", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "polyclip-ts", + "tslib@2.8.1" + ] + }, + "@turf/distance-weight@7.2.0": { + "integrity": "sha512-NeoyV0fXDH+7nIoNtLjAoH9XL0AS1pmTIyDxEE6LryoDTsqjnuR0YQxIkLCCWDqECoqaOmmBqpeWONjX5BwWCg==", + "dependencies": [ + "@turf/centroid", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/distance@7.2.0": { + "integrity": "sha512-HBjjXIgEcD/wJYjv7/6OZj5yoky2oUvTtVeIAqO3lL80XRvoYmVg6vkOIu6NswkerwLDDNT9kl7+BFLJoHbh6Q==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/ellipse@7.2.0": { + "integrity": "sha512-/Y75S5hE2+xjnTw4dXpQ5r/Y2HPM4xrwkPRCCQRpuuboKdEvm42azYmh7isPnMnBTVcmGb9UmGKj0HHAbiwt1g==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@turf/rhumb-destination", + "@turf/transform-rotate", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/envelope@7.2.0": { + "integrity": "sha512-xOMtDeNKHwUuDfzQeoSNmdabsP0/IgVDeyzitDe/8j9wTeW+MrKzVbGz7627PT3h6gsO+2nUv5asfKtUbmTyHA==", + "dependencies": [ + "@turf/bbox", + "@turf/bbox-polygon", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/explode@7.2.0": { + "integrity": "sha512-jyMXg93J1OI7/65SsLE1k9dfQD3JbcPNMi4/O3QR2Qb3BAs2039oFaSjtW+YqhMqVC4V3ZeKebMcJ8h9sK1n+A==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/flatten@7.2.0": { + "integrity": "sha512-q38Qsqr4l7mxp780zSdn0gp/WLBX+sa+gV6qIbDQ1HKCrrPK8QQJmNx7gk1xxEXVot6tq/WyAPysCQdX+kLmMA==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/flip@7.2.0": { + "integrity": "sha512-X0TQ0U/UYh4tyXdLO5itP1sO2HOvfrZC0fYSWmTfLDM14jEPkEK8PblofznfBygL+pIFtOS2is8FuVcp5XxYpQ==", + "dependencies": [ + "@turf/clone", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/geojson-rbush@7.2.0": { + "integrity": "sha512-ST8fLv+EwxVkDgsmhHggM0sPk2SfOHTZJkdgMXVFT7gB9o4lF8qk4y4lwvCCGIfFQAp2yv/PN5EaGMEKutk6xw==", + "dependencies": [ + "@turf/bbox", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "rbush@3.0.1" + ] + }, + "@turf/great-circle@7.2.0": { + "integrity": "sha512-n30OiADyOKHhor0aXNgYfXQYXO3UtsOKmhQsY1D89/Oh1nCIXG/1ZPlLL9ZoaRXXBTUBjh99a+K8029NQbGDhw==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson" + ] + }, + "@turf/helpers@7.2.0": { + "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", + "dependencies": [ + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/hex-grid@7.2.0": { + "integrity": "sha512-Yo2yUGxrTCQfmcVsSjDt0G3Veg8YD26WRd7etVPD9eirNNgXrIyZkbYA7zVV/qLeRWVmYIKRXg1USWl7ORQOGA==", + "dependencies": [ + "@turf/distance", + "@turf/helpers", + "@turf/intersect", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/interpolate@7.2.0": { + "integrity": "sha512-Ifgjm1SEo6XujuSAU6lpRMvoJ1SYTreil1Rf5WsaXj16BQJCedht/4FtWCTNhSWTwEz2motQ1WNrjTCuPG94xA==", + "dependencies": [ + "@turf/bbox", + "@turf/centroid", + "@turf/clone", + "@turf/distance", + "@turf/helpers", + "@turf/hex-grid", + "@turf/invariant", + "@turf/meta", + "@turf/point-grid", + "@turf/square-grid", + "@turf/triangle-grid", + "@types/geojson" + ] + }, + "@turf/intersect@7.2.0": { + "integrity": "sha512-81GMzKS9pKqLPa61qSlFxLFeAC8XbwyCQ9Qv4z6o5skWk1qmMUbEHeMqaGUTEzk+q2XyhZ0sju1FV4iLevQ/aw==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "polyclip-ts", + "tslib@2.8.1" + ] + }, + "@turf/invariant@7.2.0": { + "integrity": "sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/isobands@7.2.0": { + "integrity": "sha512-lYoHeRieFzpBp29Jh19QcDIb0E+dzo/K5uwZuNga4wxr6heNU0AfkD4ByAHYIXHtvmp4m/JpSKq/2N6h/zvBkg==", + "dependencies": [ + "@turf/area", + "@turf/bbox", + "@turf/boolean-point-in-polygon", + "@turf/explode", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "marchingsquares", + "tslib@2.8.1" + ] + }, + "@turf/isolines@7.2.0": { + "integrity": "sha512-4ZXKxvA/JKkxAXixXhN3UVza5FABsdYgOWXyYm3L5ryTPJVOYTVSSd9A+CAVlv9dZc3YdlsqMqLTXNOOre/kwg==", + "dependencies": [ + "@turf/bbox", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "marchingsquares", + "tslib@2.8.1" + ] + }, + "@turf/jsts@2.7.2": { + "integrity": "sha512-zAezGlwWHPyU0zxwcX2wQY3RkRpwuoBmhhNE9HY9kWhFDkCxZ3aWK5URKwa/SWKJbj9aztO+8vtdiBA28KVJFg==", + "dependencies": [ + "jsts" + ] + }, + "@turf/kinks@7.2.0": { + "integrity": "sha512-BtxDxGewJR0Q5WR9HKBSxZhirFX+GEH1rD7/EvgDsHS8e1Y5/vNQQUmXdURjdPa4StzaUBsWRU5T3A356gLbPA==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/length@7.2.0": { + "integrity": "sha512-LBmYN+iCgVtWNLsckVnpQIJENqIIPO63mogazMp23lrDGfWXu07zZQ9ZinJVO5xYurXNhc/QI2xxoqt2Xw90Ig==", + "dependencies": [ + "@turf/distance", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/line-arc@7.2.0": { + "integrity": "sha512-kfWzA5oYrTpslTg5fN50G04zSypiYQzjZv3FLjbZkk6kta5fo4JkERKjTeA8x4XNojb+pfmjMBB0yIh2w2dDRw==", + "dependencies": [ + "@turf/circle", + "@turf/destination", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/line-chunk@7.2.0": { + "integrity": "sha512-1ODyL5gETtWSL85MPI0lgp/78vl95M39gpeBxePXyDIqx8geDP9kXfAzctuKdxBoR4JmOVM3NT7Fz7h+IEkC+g==", + "dependencies": [ + "@turf/helpers", + "@turf/length", + "@turf/line-slice-along", + "@turf/meta", + "@types/geojson" + ] + }, + "@turf/line-intersect@7.2.0": { + "integrity": "sha512-GhCJVEkc8EmggNi85EuVLoXF5T5jNVxmhIetwppiVyJzMrwkYAkZSYB3IBFYGUUB9qiNFnTwungVSsBV/S8ZiA==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "sweepline-intersections", + "tslib@2.8.1" + ] + }, + "@turf/line-offset@7.2.0": { + "integrity": "sha512-1+OkYueDCbnEWzbfBh3taVr+3SyM2bal5jfnSEuDiLA6jnlScgr8tn3INo+zwrUkPFZPPAejL1swVyO5TjUahw==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson" + ] + }, + "@turf/line-overlap@7.2.0": { + "integrity": "sha512-NNn7/jg53+N10q2Kyt66bEDqN3101iW/1zA5FW7J6UbKApDFkByh+18YZq1of71kS6oUYplP86WkDp16LFpqqw==", + "dependencies": [ + "@turf/boolean-point-on-line", + "@turf/geojson-rbush", + "@turf/helpers", + "@turf/invariant", + "@turf/line-segment", + "@turf/meta", + "@turf/nearest-point-on-line", + "@types/geojson", + "fast-deep-equal", + "tslib@2.8.1" + ] + }, + "@turf/line-segment@7.2.0": { + "integrity": "sha512-E162rmTF9XjVN4rINJCd15AdQGCBlNqeWN3V0YI1vOUpZFNT2ii4SqEMCcH2d+5EheHLL8BWVwZoOsvHZbvaWA==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/line-slice-along@7.2.0": { + "integrity": "sha512-4/gPgP0j5Rp+1prbhXqn7kIH/uZTmSgiubUnn67F8nb9zE+MhbRglhSlRYEZxAVkB7VrGwjyolCwvrROhjHp2A==", + "dependencies": [ + "@turf/bearing", + "@turf/destination", + "@turf/distance", + "@turf/helpers", + "@types/geojson" + ] + }, + "@turf/line-slice@7.2.0": { + "integrity": "sha512-bHotzZIaU1GPV3RMwttYpDrmcvb3X2i1g/WUttPZWtKrEo2VVAkoYdeZ2aFwtogERYS4quFdJ/TDzAtquBC8WQ==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@turf/nearest-point-on-line", + "@types/geojson" + ] + }, + "@turf/line-split@7.2.0": { + "integrity": "sha512-yJTZR+c8CwoKqdW/aIs+iLbuFwAa3Yan+EOADFQuXXIUGps3bJUXx/38rmowNoZbHyP1np1+OtrotyHu5uBsfQ==", + "dependencies": [ + "@turf/bbox", + "@turf/geojson-rbush", + "@turf/helpers", + "@turf/invariant", + "@turf/line-intersect", + "@turf/line-segment", + "@turf/meta", + "@turf/nearest-point-on-line", + "@turf/square", + "@turf/truncate", + "@types/geojson" + ] + }, + "@turf/line-to-polygon@7.2.0": { + "integrity": "sha512-iKpJqc7EYc5NvlD4KaqrKKO6mXR7YWO/YwtW60E2FnsF/blnsy9OfAOcilYHgH3S/V/TT0VedC7DW7Kgjy2EIA==", + "dependencies": [ + "@turf/bbox", + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/mask@7.2.0": { + "integrity": "sha512-ulJ6dQqXC0wrjIoqFViXuMUdIPX5Q6GPViZ3kGfeVijvlLM7kTFBsZiPQwALSr5nTQg4Ppf3FD0Jmg8IErPrgA==", + "dependencies": [ + "@turf/clone", + "@turf/helpers", + "@types/geojson", + "polyclip-ts", + "tslib@2.8.1" + ] + }, + "@turf/meta@7.2.0": { + "integrity": "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==", + "dependencies": [ + "@turf/helpers", + "@types/geojson" + ] + }, + "@turf/midpoint@7.2.0": { + "integrity": "sha512-AMn5S9aSrbXdE+Q4Rj+T5nLdpfpn+mfzqIaEKkYI021HC0vb22HyhQHsQbSeX+AWcS4CjD1hFsYVcgKI+5qCfw==", + "dependencies": [ + "@turf/bearing", + "@turf/destination", + "@turf/distance", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/moran-index@7.2.0": { + "integrity": "sha512-Aexh1EmXVPJhApr9grrd120vbalIthcIsQ3OAN2Tqwf+eExHXArJEJqGBo9IZiQbIpFJeftt/OvUvlI8BeO1bA==", + "dependencies": [ + "@turf/distance-weight", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/nearest-neighbor-analysis@7.2.0": { + "integrity": "sha512-LmP/crXb7gilgsL0wL9hsygqc537W/a1W5r9XBKJT4SKdqjoXX5APJatJfd3nwXbRIqwDH0cDA9/YyFjBPlKnA==", + "dependencies": [ + "@turf/area", + "@turf/bbox", + "@turf/bbox-polygon", + "@turf/centroid", + "@turf/distance", + "@turf/helpers", + "@turf/meta", + "@turf/nearest-point", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/nearest-point-on-line@7.2.0": { + "integrity": "sha512-UOhAeoDPVewBQV+PWg1YTMQcYpJsIqfW5+EuZ5vJl60XwUa0+kqB/eVfSLNXmHENjKKIlEt9Oy9HIDF4VeWmXA==", + "dependencies": [ + "@turf/distance", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/nearest-point-to-line@7.2.0": { + "integrity": "sha512-EorU7Qj30A7nAjh++KF/eTPDlzwuuV4neBz7tmSTB21HKuXZAR0upJsx6M2X1CSyGEgNsbFB0ivNKIvymRTKBw==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/point-to-line-distance", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/nearest-point@7.2.0": { + "integrity": "sha512-0wmsqXZ8CGw4QKeZmS+NdjYTqCMC+HXZvM3XAQIU6k6laNLqjad2oS4nDrtcRs/nWDvcj1CR+Io7OiQ6sbpn5Q==", + "dependencies": [ + "@turf/clone", + "@turf/distance", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/planepoint@7.2.0": { + "integrity": "sha512-8Vno01tvi5gThUEKBQ46CmlEKDAwVpkl7stOPFvJYlA1oywjAL4PsmgwjXgleZuFtXQUPBNgv5a42Pf438XP4g==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/point-grid@7.2.0": { + "integrity": "sha512-ai7lwBV2FREPW3XiUNohT4opC1hd6+F56qZe20xYhCTkTD9diWjXHiNudQPSmVAUjgMzQGasblQQqvOdL+bJ3Q==", + "dependencies": [ + "@turf/boolean-within", + "@turf/distance", + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/point-on-feature@7.2.0": { + "integrity": "sha512-ksoYoLO9WtJ/qI8VI9ltF+2ZjLWrAjZNsCsu8F7nyGeCh4I8opjf4qVLytFG44XA2qI5yc6iXDpyv0sshvP82Q==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/center", + "@turf/explode", + "@turf/helpers", + "@turf/nearest-point", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/point-to-line-distance@7.2.0": { + "integrity": "sha512-fB9Rdnb5w5+t76Gho2dYDkGe20eRrFk8CXi4v1+l1PC8YyLXO+x+l3TrtT8HzL/dVaZeepO6WUIsIw3ditTOPg==", + "dependencies": [ + "@turf/bearing", + "@turf/distance", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/nearest-point-on-line", + "@turf/projection", + "@turf/rhumb-bearing", + "@turf/rhumb-distance", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/point-to-polygon-distance@7.2.0": { + "integrity": "sha512-w+WYuINgTiFjoZemQwOaQSje/8Kq+uqJOynvx7+gleQPHyWQ3VtTodtV4LwzVzXz8Sf7Mngx1Jcp2SNai5CJYA==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/point-to-line-distance", + "@turf/polygon-to-line", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/points-within-polygon@7.2.0": { + "integrity": "sha512-jRKp8/mWNMzA+hKlQhxci97H5nOio9tp14R2SzpvkOt+cswxl+NqTEi1hDd2XetA7tjU0TSoNjEgVY8FfA0S6w==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/polygon-smooth@7.2.0": { + "integrity": "sha512-KCp9wF2IEynvGXVhySR8oQ2razKP0zwg99K+fuClP21pSKCFjAPaihPEYq6e8uI/1J7ibjL5++6EMl+LrUTrLg==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/polygon-tangents@7.2.0": { + "integrity": "sha512-AHUUPmOjiQDrtP/ODXukHBlUG0C/9I1je7zz50OTfl2ZDOdEqFJQC3RyNELwq07grTXZvg5TS5wYx/Y7nsm47g==", + "dependencies": [ + "@turf/bbox", + "@turf/boolean-within", + "@turf/explode", + "@turf/helpers", + "@turf/invariant", + "@turf/nearest-point", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/polygon-to-line@7.2.0": { + "integrity": "sha512-9jeTN3LiJ933I5sd4K0kwkcivOYXXm1emk0dHorwXeSFSHF+nlYesEW3Hd889wb9lZd7/SVLMUeX/h39mX+vCA==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/polygonize@7.2.0": { + "integrity": "sha512-U9v+lBhUPDv+nsg/VcScdiqCB59afO6CHDGrwIl2+5i6Ve+/KQKjpTV/R+NqoC1iMXAEq3brY6HY8Ukp/pUWng==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/envelope", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/projection@7.2.0": { + "integrity": "sha512-/qke5vJScv8Mu7a+fU3RSChBRijE6EVuFHU3RYihMuYm04Vw8dBMIs0enEpoq0ke/IjSbleIrGQNZIMRX9EwZQ==", + "dependencies": [ + "@turf/clone", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/quadrat-analysis@7.2.0": { + "integrity": "sha512-fDQh3+ldYNxUqS6QYlvJ7GZLlCeDZR6tD3ikdYtOsSemwW1n/4gm2xcgWJqy3Y0uszBwxc13IGGY7NGEjHA+0w==", + "dependencies": [ + "@turf/area", + "@turf/bbox", + "@turf/bbox-polygon", + "@turf/centroid", + "@turf/helpers", + "@turf/invariant", + "@turf/point-grid", + "@turf/random", + "@turf/square-grid", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/random@7.2.0": { + "integrity": "sha512-fNXs5mOeXsrirliw84S8UCNkpm4RMNbefPNsuCTfZEXhcr1MuHMzq4JWKb4FweMdN1Yx2l/xcytkO0s71cJ50w==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/rectangle-grid@7.2.0": { + "integrity": "sha512-f0o5ifvy0Ml/nHDJzMNcuSk4h11aa3BfvQNnYQhLpuTQu03j/ICZNlzKTLxwjcUqvxADUifty7Z9CX5W6zky4A==", + "dependencies": [ + "@turf/boolean-intersects", + "@turf/distance", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/rewind@7.2.0": { + "integrity": "sha512-SZpRAZiZsE22+HVz6pEID+ST25vOdpAMGk5NO1JeqzhpMALIkIGnkG+xnun2CfYHz7wv8/Z0ADiAvei9rkcQYA==", + "dependencies": [ + "@turf/boolean-clockwise", + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/rhumb-bearing@7.2.0": { + "integrity": "sha512-jbdexlrR8X2ZauUciHx3tRwG+BXoMXke4B8p8/IgDlAfIrVdzAxSQN89FMzIKnjJ/kdLjo9bFGvb92bu31Etug==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/rhumb-destination@7.2.0": { + "integrity": "sha512-U9OLgLAHlH4Wfx3fBZf3jvnkDjdTcfRan5eI7VPV1+fQWkOteATpzkiRjCvSYK575GljVwWBjkKca8LziGWitQ==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/rhumb-distance@7.2.0": { + "integrity": "sha512-NsijTPON1yOc9tirRPEQQuJ5aQi7pREsqchQquaYKbHNWsexZjcDi4wnw2kM3Si4XjmgynT+2f7aXH7FHarHzw==", + "dependencies": [ + "@turf/helpers", + "@turf/invariant", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/sample@7.2.0": { + "integrity": "sha512-f+ZbcbQJ9glQ/F26re8LadxO0ORafy298EJZe6XtbctRTJrNus6UNAsl8+GYXFqMnXM22tbTAznnJX3ZiWNorA==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/sector@7.2.0": { + "integrity": "sha512-zL06MjbbMG4DdpiNz+Q9Ax8jsCekt3R76uxeWShulAGkyDB5smdBOUDoRwxn05UX7l4kKv4Ucq2imQXhxKFd1w==", + "dependencies": [ + "@turf/circle", + "@turf/helpers", + "@turf/invariant", + "@turf/line-arc", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/shortest-path@7.2.0": { + "integrity": "sha512-6fpx8feZ2jMSaeRaFdqFShGWkNb+veUOeyLFSHA/aRD9n/e9F2pWZoRbQWKbKTpcKFJ2FnDEqCZnh/GrcAsqWA==", + "dependencies": [ + "@turf/bbox", + "@turf/bbox-polygon", + "@turf/boolean-point-in-polygon", + "@turf/clean-coords", + "@turf/distance", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/transform-scale", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/simplify@7.2.0": { + "integrity": "sha512-9YHIfSc8BXQfi5IvEMbCeQYqNch0UawIGwbboJaoV8rodhtk6kKV2wrpXdGqk/6Thg6/RWvChJFKVVTjVrULyQ==", + "dependencies": [ + "@turf/clean-coords", + "@turf/clone", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/square-grid@7.2.0": { + "integrity": "sha512-EmzGXa90hz+tiCOs9wX+Lak6pH0Vghb7QuX6KZej+pmWi3Yz7vdvQLmy/wuN048+wSkD5c8WUo/kTeNDe7GnmA==", + "dependencies": [ + "@turf/helpers", + "@turf/rectangle-grid", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/square@7.2.0": { + "integrity": "sha512-9pMoAGFvqzCDOlO9IRSSBCGXKbl8EwMx6xRRBMKdZgpS0mZgfm9xiptMmx/t1m4qqHIlb/N+3MUF7iMBx6upcA==", + "dependencies": [ + "@turf/distance", + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/standard-deviational-ellipse@7.2.0": { + "integrity": "sha512-+uC0pR2nRjm90JvMXe/2xOCZsYV2II1ZZ2zmWcBWv6bcFXBspcxk2QfCC3k0bj6jDapELzoQgnn3cG5lbdQV2w==", + "dependencies": [ + "@turf/center-mean", + "@turf/ellipse", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/points-within-polygon", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/tag@7.2.0": { + "integrity": "sha512-TAFvsbp5TCBqXue8ui+CtcLsPZ6NPC88L8Ad6Hb/R6VAi21qe0U42WJHQYXzWmtThoTNwxi+oKSeFbRDsr0FIA==", + "dependencies": [ + "@turf/boolean-point-in-polygon", + "@turf/clone", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/tesselate@7.2.0": { + "integrity": "sha512-zHGcG85aOJJu1seCm+CYTJ3UempX4Xtyt669vFG6Hbr/Hc7ii6STQ2ysFr7lJwFtU9uyYhphVrrgwIqwglvI/Q==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "earcut@2.2.4", + "tslib@2.8.1" + ] + }, + "@turf/tin@7.2.0": { + "integrity": "sha512-y24Vt3oeE6ZXvyLJamP0Ke02rPlDGE9gF7OFADnR0mT+2uectb0UTIBC3kKzON80TEAlA3GXpKFkCW5Fo/O/Kg==", + "dependencies": [ + "@turf/helpers", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/transform-rotate@7.2.0": { + "integrity": "sha512-EMCj0Zqy3cF9d3mGRqDlYnX2ZBXe3LgT+piDR0EuF5c5sjuKErcFcaBIsn/lg1gp4xCNZFinkZ3dsFfgGHf6fw==", + "dependencies": [ + "@turf/centroid", + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/rhumb-bearing", + "@turf/rhumb-destination", + "@turf/rhumb-distance", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/transform-scale@7.2.0": { + "integrity": "sha512-HYB+pw938eeI8s1/zSWFy6hq+t38fuUaBb0jJsZB1K9zQ1WjEYpPvKF/0//80zNPlyxLv3cOkeBucso3hzI07A==", + "dependencies": [ + "@turf/bbox", + "@turf/center", + "@turf/centroid", + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/rhumb-bearing", + "@turf/rhumb-destination", + "@turf/rhumb-distance", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/transform-translate@7.2.0": { + "integrity": "sha512-zAglR8MKCqkzDTjGMIQgbg/f+Q3XcKVzr9cELw5l9CrS1a0VTSDtBZLDm0kWx0ankwtam7ZmI2jXyuQWT8Gbug==", + "dependencies": [ + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@turf/meta", + "@turf/rhumb-destination", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/triangle-grid@7.2.0": { + "integrity": "sha512-4gcAqWKh9hg6PC5nNSb9VWyLgl821cwf9yR9yEzQhEFfwYL/pZONBWCO1cwVF23vSYMSMm+/TwqxH4emxaArfw==", + "dependencies": [ + "@turf/distance", + "@turf/helpers", + "@turf/intersect", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/truncate@7.2.0": { + "integrity": "sha512-jyFzxYbPugK4XjV5V/k6Xr3taBjjvo210IbPHJXw0Zh7Y6sF+hGxeRVtSuZ9VP/6oRyqAOHKUrze+OOkPqBgUg==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/turf@7.2.0": { + "integrity": "sha512-G1kKBu4hYgoNoRJgnpJohNuS7bLnoWHZ2G/4wUMym5xOSiYah6carzdTEsMoTsauyi7ilByWHx5UHwbjjCVcBw==", + "dependencies": [ + "@turf/along", + "@turf/angle", + "@turf/area", + "@turf/bbox", + "@turf/bbox-clip", + "@turf/bbox-polygon", + "@turf/bearing", + "@turf/bezier-spline", + "@turf/boolean-clockwise", + "@turf/boolean-concave", + "@turf/boolean-contains", + "@turf/boolean-crosses", + "@turf/boolean-disjoint", + "@turf/boolean-equal", + "@turf/boolean-intersects", + "@turf/boolean-overlap", + "@turf/boolean-parallel", + "@turf/boolean-point-in-polygon", + "@turf/boolean-point-on-line", + "@turf/boolean-touches", + "@turf/boolean-valid", + "@turf/boolean-within", + "@turf/buffer", + "@turf/center", + "@turf/center-mean", + "@turf/center-median", + "@turf/center-of-mass", + "@turf/centroid", + "@turf/circle", + "@turf/clean-coords", + "@turf/clone", + "@turf/clusters", + "@turf/clusters-dbscan", + "@turf/clusters-kmeans", + "@turf/collect", + "@turf/combine", + "@turf/concave", + "@turf/convex", + "@turf/destination", + "@turf/difference", + "@turf/dissolve", + "@turf/distance", + "@turf/distance-weight", + "@turf/ellipse", + "@turf/envelope", + "@turf/explode", + "@turf/flatten", + "@turf/flip", + "@turf/geojson-rbush", + "@turf/great-circle", + "@turf/helpers", + "@turf/hex-grid", + "@turf/interpolate", + "@turf/intersect", + "@turf/invariant", + "@turf/isobands", + "@turf/isolines", + "@turf/kinks", + "@turf/length", + "@turf/line-arc", + "@turf/line-chunk", + "@turf/line-intersect", + "@turf/line-offset", + "@turf/line-overlap", + "@turf/line-segment", + "@turf/line-slice", + "@turf/line-slice-along", + "@turf/line-split", + "@turf/line-to-polygon", + "@turf/mask", + "@turf/meta", + "@turf/midpoint", + "@turf/moran-index", + "@turf/nearest-neighbor-analysis", + "@turf/nearest-point", + "@turf/nearest-point-on-line", + "@turf/nearest-point-to-line", + "@turf/planepoint", + "@turf/point-grid", + "@turf/point-on-feature", + "@turf/point-to-line-distance", + "@turf/point-to-polygon-distance", + "@turf/points-within-polygon", + "@turf/polygon-smooth", + "@turf/polygon-tangents", + "@turf/polygon-to-line", + "@turf/polygonize", + "@turf/projection", + "@turf/quadrat-analysis", + "@turf/random", + "@turf/rectangle-grid", + "@turf/rewind", + "@turf/rhumb-bearing", + "@turf/rhumb-destination", + "@turf/rhumb-distance", + "@turf/sample", + "@turf/sector", + "@turf/shortest-path", + "@turf/simplify", + "@turf/square", + "@turf/square-grid", + "@turf/standard-deviational-ellipse", + "@turf/tag", + "@turf/tesselate", + "@turf/tin", + "@turf/transform-rotate", + "@turf/transform-scale", + "@turf/transform-translate", + "@turf/triangle-grid", + "@turf/truncate", + "@turf/union", + "@turf/unkink-polygon", + "@turf/voronoi", + "@types/geojson", + "tslib@2.8.1" + ] + }, + "@turf/union@7.2.0": { + "integrity": "sha512-Xex/cfKSmH0RZRWSJl4RLlhSmEALVewywiEXcu0aIxNbuZGTcpNoI0h4oLFrE/fUd0iBGFg/EGLXRL3zTfpg6g==", + "dependencies": [ + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "polyclip-ts", + "tslib@2.8.1" + ] + }, + "@turf/unkink-polygon@7.2.0": { + "integrity": "sha512-dFPfzlIgkEr15z6oXVxTSWshWi51HeITGVFtl1GAKGMtiXJx1uMqnfRsvljqEjaQu/4AzG1QAp3b+EkSklQSiQ==", + "dependencies": [ + "@turf/area", + "@turf/boolean-point-in-polygon", + "@turf/helpers", + "@turf/meta", + "@types/geojson", + "rbush@3.0.1", + "tslib@2.8.1" + ] + }, + "@turf/voronoi@7.2.0": { + "integrity": "sha512-3K6N0LtJsWTXxPb/5N2qD9e8f4q8+tjTbGV3lE3v8x06iCnNlnuJnqM5NZNPpvgvCatecBkhClO3/3RndE61Fw==", + "dependencies": [ + "@turf/clone", + "@turf/helpers", + "@turf/invariant", + "@types/d3-voronoi", + "@types/geojson", + "d3-voronoi", + "tslib@2.8.1" + ] + }, + "@tybys/wasm-util@0.10.0": { + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dependencies": [ + "tslib@2.8.1" + ] + }, + "@tybys/wasm-util@0.9.0": { + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dependencies": [ + "tslib@2.8.1" + ] + }, + "@types/aria-query@5.0.4": { + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==" + }, + "@types/babel__core@7.20.5": { + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dependencies": [ + "@babel/parser", + "@babel/types", + "@types/babel__generator", + "@types/babel__template", + "@types/babel__traverse" + ] + }, + "@types/babel__generator@7.27.0": { + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dependencies": [ + "@babel/types" + ] + }, + "@types/babel__template@7.4.4": { + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": [ + "@babel/parser", + "@babel/types" + ] + }, + "@types/babel__traverse@7.20.7": { + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dependencies": [ + "@babel/types" + ] + }, + "@types/chai@5.2.2": { + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dependencies": [ + "@types/deep-eql" + ] + }, + "@types/chrome@0.1.1": { + "integrity": "sha512-MLtFW++/n+OPQIaf5hA6pmURd3Zn+OxuvASyf2mYh8B8pHDpbhHjwlVHMw3H/aJC9Z7Z3itO0AFaZeegrGk0yA==", + "dependencies": [ + "@types/filesystem", + "@types/har-format" + ] + }, + "@types/d3-voronoi@1.1.12": { + "integrity": "sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw==" + }, + "@types/deep-eql@4.0.2": { + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==" + }, + "@types/estree@1.0.8": { + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "@types/filesystem@0.0.36": { + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dependencies": [ + "@types/filewriter" + ] + }, + "@types/filewriter@0.0.33": { + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==" + }, + "@types/geojson-vt@3.2.5": { + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "dependencies": [ + "@types/geojson" + ] + }, + "@types/geojson@7946.0.16": { + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" + }, + "@types/har-format@1.2.16": { + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==" + }, + "@types/js-cookie@3.0.6": { + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==" + }, + "@types/mapbox__point-geometry@0.1.4": { + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" + }, + "@types/mapbox__vector-tile@1.3.4": { + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "dependencies": [ + "@types/geojson", + "@types/mapbox__point-geometry", + "@types/pbf" + ] + }, + "@types/node@20.19.8": { + "integrity": "sha512-HzbgCY53T6bfu4tT7Aq3TvViJyHjLjPNaAS3HOuMc9pw97KHsUtXNX4L+wu59g1WnjsZSko35MbEqnO58rihhw==", + "dependencies": [ + "undici-types@6.21.0" + ] + }, + "@types/node@22.16.4": { + "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", + "dependencies": [ + "undici-types@6.21.0" + ] + }, + "@types/node@24.0.14": { + "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "dependencies": [ + "undici-types@7.8.0" + ] + }, + "@types/pbf@3.0.5": { + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" + }, + "@types/react-dom@19.1.6_@types+react@19.1.8": { + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "dependencies": [ + "@types/react" + ] + }, + "@types/react@19.1.8": { + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "dependencies": [ + "csstype" + ] + }, + "@types/serviceworker@0.0.142": { + "integrity": "sha512-OdziWMTLuM+orvUfsA66/5PDS6Q/EQrhhS/48F5UB9bZuNvvVcifgiu+uhJtrxM1HUb7jndVaVlG3emCCHTuSw==" + }, + "@types/supercluster@7.1.3": { + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "dependencies": [ + "@types/geojson" + ] + }, + "@types/w3c-web-serial@1.0.8": { + "integrity": "sha512-QQOT+bxQJhRGXoZDZGLs3ksLud1dMNnMiSQtBA0w8KXvLpXX4oM4TZb6J0GgJ8UbCaHo5s9/4VQT8uXy9JER2A==" + }, + "@types/web-bluetooth@0.0.20": { + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==" + }, + "@types/web-bluetooth@0.0.21": { + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==" + }, + "@types/whatwg-mimetype@3.0.2": { + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==" + }, + "@vis.gl/react-mapbox@8.0.4_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-NFk0vsWcNzSs0YCsVdt2100Zli9QWR+pje8DacpLkkGEAXFaJsFtI1oKD0Hatiate4/iAIW39SQHhgfhbeEPfQ==", + "dependencies": [ + "react", + "react-dom" + ] + }, + "@vis.gl/react-maplibre@8.0.4_maplibre-gl@5.6.1_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-HwZyfLjEu+y1mUFvwDAkVxinGm8fEegaWN+O8np/WZ2Sqe5Lv6OXFpV6GWz9LOEvBYMbGuGk1FQdejo+4HCJ5w==", + "dependencies": [ + "@maplibre/maplibre-gl-style-spec@19.3.3", + "maplibre-gl", + "react", + "react-dom" + ], + "optionalPeers": [ + "maplibre-gl" + ] + }, + "@vitejs/plugin-react@4.6.0_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@babel+core@7.28.0_@types+node@22.16.4": { + "integrity": "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==", + "dependencies": [ + "@babel/core", + "@babel/plugin-transform-react-jsx-self", + "@babel/plugin-transform-react-jsx-source", + "@rolldown/pluginutils", + "@types/babel__core", + "react-refresh", + "vite" + ] + }, + "@vitest/expect@3.2.4": { + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dependencies": [ + "@types/chai", + "@vitest/spy", + "@vitest/utils", + "chai", + "tinyrainbow" + ] + }, + "@vitest/mocker@3.2.4_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3_@types+node@22.16.4": { + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dependencies": [ + "@vitest/spy", + "estree-walker", + "magic-string", + "vite" + ], + "optionalPeers": [ + "vite" + ] + }, + "@vitest/pretty-format@3.2.4": { + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dependencies": [ + "tinyrainbow" + ] + }, + "@vitest/runner@3.2.4": { + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dependencies": [ + "@vitest/utils", + "pathe", + "strip-literal" + ] + }, + "@vitest/snapshot@3.2.4": { + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dependencies": [ + "@vitest/pretty-format", + "magic-string", + "pathe" + ] + }, + "@vitest/spy@3.2.4": { + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dependencies": [ + "tinyspy" + ] + }, + "@vitest/utils@3.2.4": { + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dependencies": [ + "@vitest/pretty-format", + "loupe", + "tinyrainbow" + ] + }, + "acorn@8.15.0": { + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": true + }, + "ansi-regex@5.0.1": { + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles@4.3.0": { + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": [ + "color-convert" + ] + }, + "ansi-styles@5.2.0": { + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "ansis@4.1.0": { + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==" + }, + "anymatch@3.1.3": { + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": [ + "normalize-path", + "picomatch@2.3.1" + ] + }, + "aria-hidden@1.2.6": { + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "dependencies": [ + "tslib@2.8.1" + ] + }, + "aria-query@5.3.0": { + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dependencies": [ + "dequal" + ] + }, + "aria-query@5.3.2": { + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==" + }, + "arr-union@3.1.0": { + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" + }, + "assertion-error@2.0.1": { + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==" + }, + "assign-symbols@1.0.0": { + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==" + }, + "ast-types@0.16.1": { + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dependencies": [ + "tslib@2.8.1" + ] + }, + "autoprefixer@10.4.21_postcss@8.5.6": { + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dependencies": [ + "browserslist", + "caniuse-lite", + "fraction.js", + "normalize-range", + "picocolors", + "postcss", + "postcss-value-parser" + ], + "bin": true + }, + "babel-dead-code-elimination@1.0.10": { + "integrity": "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==", + "dependencies": [ + "@babel/core", + "@babel/parser", + "@babel/traverse", + "@babel/types" + ] + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js@9.3.1": { + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==" + }, + "binary-extensions@2.3.0": { + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": [ + "fill-range" + ] + }, + "browserslist@4.25.1": { + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dependencies": [ + "caniuse-lite", + "electron-to-chromium", + "node-releases", + "update-browserslist-db" + ], + "bin": true + }, + "buffer-from@1.1.2": { + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "bun@1.2.18": { + "integrity": "sha512-OR+EpNckoJN4tHMVZPaTPxDj2RgpJgJwLruTIFYbO3bQMguLd0YrmkWKYqsiihcLgm2ehIjF/H1RLfZiRa7+qQ==", + "optionalDependencies": [ + "@oven/bun-darwin-aarch64", + "@oven/bun-darwin-x64", + "@oven/bun-darwin-x64-baseline", + "@oven/bun-linux-aarch64", + "@oven/bun-linux-aarch64-musl", + "@oven/bun-linux-x64", + "@oven/bun-linux-x64-baseline", + "@oven/bun-linux-x64-musl", + "@oven/bun-linux-x64-musl-baseline", + "@oven/bun-windows-x64", + "@oven/bun-windows-x64-baseline" + ], + "os": ["darwin", "linux", "win32"], + "cpu": ["arm64", "x64", "aarch64"], + "scripts": true, + "bin": true + }, + "bytewise-core@1.2.3": { + "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==", + "dependencies": [ + "typewise-core" + ] + }, + "bytewise@1.1.0": { + "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==", + "dependencies": [ + "bytewise-core", + "typewise" + ] + }, + "cac@6.7.14": { + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" + }, + "caniuse-lite@1.0.30001727": { + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==" + }, + "chai@5.2.1": { + "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "dependencies": [ + "assertion-error", + "check-error", + "deep-eql", + "loupe", + "pathval" + ] + }, + "chalk@3.0.0": { + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": [ + "ansi-styles@4.3.0", + "supports-color" + ] + }, + "chalk@4.1.2": { + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": [ + "ansi-styles@4.3.0", + "supports-color" + ] + }, + "check-error@2.1.1": { + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==" + }, + "chokidar@3.6.0": { + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": [ + "anymatch", + "braces", + "glob-parent", + "is-binary-path", + "is-glob", + "normalize-path", + "readdirp" + ], + "optionalDependencies": [ + "fsevents" + ] + }, + "chownr@3.0.0": { + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==" + }, + "class-variance-authority@0.7.1": { + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dependencies": [ + "clsx" + ] + }, + "cliui@8.0.1": { + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": [ + "string-width", + "strip-ansi", + "wrap-ansi" + ] + }, + "clsx@2.1.1": { + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" + }, + "cmdk@1.1.1_react@19.1.0_react-dom@19.1.0__react@19.1.0_@types+react@19.1.8_@types+react-dom@19.1.6__@types+react@19.1.8": { + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "dependencies": [ + "@radix-ui/react-compose-refs", + "@radix-ui/react-dialog", + "@radix-ui/react-id", + "@radix-ui/react-primitive", + "react", + "react-dom" + ] + }, + "color-convert@2.0.1": { + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": [ + "color-name" + ] + }, + "color-name@1.1.4": { + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "commander@12.1.0": { + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==" + }, + "commander@2.20.3": { + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "concaveman@1.2.1": { + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "dependencies": [ + "point-in-polygon", + "rbush@3.0.1", + "robust-predicates@2.0.4", + "tinyqueue@2.0.3" + ] + }, + "convert-source-map@2.0.0": { + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "cookie-es@1.2.2": { + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==" + }, + "core-util-is@1.0.3": { + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "crc@4.3.2": { + "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==" + }, + "cross-fetch@4.0.0": { + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": [ + "node-fetch" + ] + }, + "crypto-random-string@5.0.0": { + "integrity": "sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==", + "dependencies": [ + "type-fest" + ] + }, + "css.escape@1.5.1": { + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "csstype@3.1.3": { + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "d3-array@1.2.4": { + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-geo@1.7.1": { + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "dependencies": [ + "d3-array" + ] + }, + "d3-voronoi@1.1.2": { + "integrity": "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw==" + }, + "debug@4.4.1": { + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": [ + "ms" + ] + }, + "deep-eql@5.0.2": { + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==" + }, + "dequal@2.0.3": { + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "detect-libc@2.0.4": { + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==" + }, + "detect-node-es@1.1.0": { + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "diff@8.0.2": { + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==" + }, + "dom-accessibility-api@0.5.16": { + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" + }, + "dom-accessibility-api@0.6.3": { + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==" + }, + "duplex-maker@1.0.0": { + "integrity": "sha512-KoHuzggxg7f+vvjqOHfXxaQYI1POzBm+ah0eec7YDssZmbt6QFBI8d1nl5GQwAgR2f+VQCPvyvZtmWWqWuFtlA==" + }, + "duplexify@3.7.1": { + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dependencies": [ + "end-of-stream", + "inherits", + "readable-stream@2.3.8", + "stream-shift" + ] + }, + "earcut@2.2.4": { + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "earcut@3.0.2": { + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==" + }, + "electron-to-chromium@1.5.187": { + "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==" + }, + "emoji-regex@8.0.0": { + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "end-of-stream@1.4.5": { + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": [ + "once" + ] + }, + "enhanced-resolve@5.18.2": { + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "dependencies": [ + "graceful-fs", + "tapable" + ] + }, + "es-module-lexer@1.7.0": { + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==" + }, + "esbuild@0.25.6": { + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "optionalDependencies": [ + "@esbuild/aix-ppc64", + "@esbuild/android-arm", + "@esbuild/android-arm64", + "@esbuild/android-x64", + "@esbuild/darwin-arm64", + "@esbuild/darwin-x64", + "@esbuild/freebsd-arm64", + "@esbuild/freebsd-x64", + "@esbuild/linux-arm", + "@esbuild/linux-arm64", + "@esbuild/linux-ia32", + "@esbuild/linux-loong64", + "@esbuild/linux-mips64el", + "@esbuild/linux-ppc64", + "@esbuild/linux-riscv64", + "@esbuild/linux-s390x", + "@esbuild/linux-x64", + "@esbuild/netbsd-arm64", + "@esbuild/netbsd-x64", + "@esbuild/openbsd-arm64", + "@esbuild/openbsd-x64", + "@esbuild/openharmony-arm64", + "@esbuild/sunos-x64", + "@esbuild/win32-arm64", + "@esbuild/win32-ia32", + "@esbuild/win32-x64" + ], + "scripts": true, + "bin": true + }, + "escalade@3.2.0": { + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" + }, + "esprima@4.0.1": { + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": true + }, + "estree-walker@3.0.3": { + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": [ + "@types/estree" + ] + }, + "expect-type@1.2.2": { + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==" + }, + "extend-shallow@2.0.1": { + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": [ + "is-extendable@0.1.1" + ] + }, + "extend-shallow@3.0.2": { + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": [ + "assign-symbols", + "is-extendable@1.0.1" + ] + }, + "fast-deep-equal@3.1.3": { + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fdir@6.4.6_picomatch@4.0.3": { + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dependencies": [ + "picomatch@4.0.3" + ], + "optionalPeers": [ + "picomatch@4.0.3" + ] + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": [ + "to-regex-range" + ] + }, + "fraction.js@4.3.7": { + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "gensync@1.0.0-beta.2": { + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "geojson-equality-ts@1.0.2": { + "integrity": "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==", + "dependencies": [ + "@types/geojson" + ] + }, + "geojson-polygon-self-intersections@1.2.1": { + "integrity": "sha512-/QM1b5u2d172qQVO//9CGRa49jEmclKEsYOQmWP9ooEjj63tBM51m2805xsbxkzlEELQ2REgTf700gUhhlegxA==", + "dependencies": [ + "rbush@2.0.2" + ] + }, + "geojson-vt@4.0.2": { + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==" + }, + "get-caller-file@2.0.5": { + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-nonce@1.0.1": { + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==" + }, + "get-stream@6.0.1": { + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-tsconfig@4.10.1": { + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dependencies": [ + "resolve-pkg-maps" + ] + }, + "get-value@2.0.6": { + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==" + }, + "gl-matrix@3.4.3": { + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": [ + "is-glob" + ] + }, + "global-prefix@4.0.0": { + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "dependencies": [ + "ini", + "kind-of", + "which" + ] + }, + "goober@2.1.16_csstype@3.1.3": { + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "dependencies": [ + "csstype" + ] + }, + "graceful-fs@4.2.11": { + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "gzipper@8.2.1": { + "integrity": "sha512-Vp2vDpwU4xKtWxTaLPfNTR4euqHJamB6aKCfSEbSd/CrgqihwNxrjihJcWJG1+3Ku1ROsfF6fPXRoytTFLhFlw==", + "dependencies": [ + "@gfx/zopfli", + "commander@12.1.0", + "simple-zstd" + ], + "bin": true + }, + "happy-dom@18.0.1": { + "integrity": "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==", + "dependencies": [ + "@types/node@20.19.8", + "@types/whatwg-mimetype", + "whatwg-mimetype" + ] + }, + "has-flag@4.0.0": { + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "html-parse-stringify@3.0.1": { + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": [ + "void-elements" + ] + }, + "i18next-browser-languagedetector@8.2.0": { + "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==", + "dependencies": [ + "@babel/runtime" + ] + }, + "i18next-http-backend@3.0.2": { + "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", + "dependencies": [ + "cross-fetch" + ] + }, + "i18next@25.3.2_typescript@5.8.3": { + "integrity": "sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==", + "dependencies": [ + "@babel/runtime", + "typescript" + ], + "optionalPeers": [ + "typescript" + ] + }, + "idb-keyval@6.2.2": { + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==" + }, + "ieee754@1.2.1": { + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "immer@10.1.1": { + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==" + }, + "indent-string@4.0.0": { + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "inherits@2.0.4": { + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini@4.1.3": { + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==" + }, + "is-binary-path@2.1.0": { + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": [ + "binary-extensions" + ] + }, + "is-extendable@0.1.1": { + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" + }, + "is-extendable@1.0.1": { + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": [ + "is-plain-object" + ] + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-fullwidth-code-point@3.0.0": { + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object@2.0.4": { + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": [ + "isobject" + ] + }, + "is-zst@1.0.0": { + "integrity": "sha512-ZA5lvshKAl8z30dX7saXLpVhpsq3d2EHK9uf7qtUjnOtdw4XBpAoWb2RvZ5kyoaebdoidnGI0g2hn9Z7ObPbww==" + }, + "isarray@1.0.0": { + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isbot@5.1.28": { + "integrity": "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw==" + }, + "isexe@3.1.1": { + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, + "isobject@3.0.1": { + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "jiti@2.4.2": { + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "bin": true + }, + "js-cookie@3.0.5": { + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" + }, + "js-tokens@4.0.0": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-tokens@9.0.1": { + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==" + }, + "jsesc@3.1.0": { + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": true + }, + "json-stringify-pretty-compact@3.0.0": { + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" + }, + "json-stringify-pretty-compact@4.0.0": { + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==" + }, + "json5@2.2.3": { + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": true + }, + "jsts@2.7.1": { + "integrity": "sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==" + }, + "kdbush@4.0.2": { + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" + }, + "kind-of@6.0.3": { + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "lightningcss-darwin-arm64@1.30.1": { + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-x64@1.30.1": { + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "lightningcss-freebsd-x64@1.30.1": { + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "lightningcss-linux-arm-gnueabihf@1.30.1": { + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "os": ["linux"], + "cpu": ["arm"] + }, + "lightningcss-linux-arm64-gnu@1.30.1": { + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-arm64-musl@1.30.1": { + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-x64-gnu@1.30.1": { + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-linux-x64-musl@1.30.1": { + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-win32-arm64-msvc@1.30.1": { + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "lightningcss-win32-x64-msvc@1.30.1": { + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "os": ["win32"], + "cpu": ["x64"] + }, + "lightningcss@1.30.1": { + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dependencies": [ + "detect-libc" + ], + "optionalDependencies": [ + "lightningcss-darwin-arm64", + "lightningcss-darwin-x64", + "lightningcss-freebsd-x64", + "lightningcss-linux-arm-gnueabihf", + "lightningcss-linux-arm64-gnu", + "lightningcss-linux-arm64-musl", + "lightningcss-linux-x64-gnu", + "lightningcss-linux-x64-musl", + "lightningcss-win32-arm64-msvc", + "lightningcss-win32-x64-msvc" + ] + }, + "lodash.isequal@4.5.0": { + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": true + }, + "lodash@4.17.21": { + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "loupe@3.1.4": { + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==" + }, + "lru-cache@5.1.1": { + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": [ + "yallist@3.1.1" + ] + }, + "lucide-react@0.525.0_react@19.1.0": { + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "dependencies": [ + "react" + ] + }, + "lz-string@1.5.0": { + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "bin": true + }, + "magic-string@0.30.17": { + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": [ + "@jridgewell/sourcemap-codec" + ] + }, + "maplibre-gl@5.6.1": { + "integrity": "sha512-TTSfoTaF7RqKUR9wR5qDxCHH2J1XfZ1E85luiLOx0h8r50T/LnwAwwfV0WVNh9o8dA7rwt57Ucivf1emyeukXg==", + "dependencies": [ + "@mapbox/geojson-rewind", + "@mapbox/jsonlint-lines-primitives", + "@mapbox/point-geometry", + "@mapbox/tiny-sdf", + "@mapbox/unitbezier", + "@mapbox/vector-tile", + "@mapbox/whoots-js", + "@maplibre/maplibre-gl-style-spec@23.3.0", + "@types/geojson", + "@types/geojson-vt", + "@types/mapbox__point-geometry", + "@types/mapbox__vector-tile", + "@types/pbf", + "@types/supercluster", + "earcut@3.0.2", + "geojson-vt", + "gl-matrix", + "global-prefix", + "kdbush", + "murmurhash-js", + "pbf", + "potpack", + "quickselect@3.0.0", + "supercluster", + "tinyqueue@3.0.0", + "vt-pbf" + ] + }, + "marchingsquares@1.3.3": { + "integrity": "sha512-gz6nNQoVK7Lkh2pZulrT4qd4347S/toG9RXH2pyzhLgkL5mLkBoqgv4EvAGXcV0ikDW72n/OQb3Xe8bGagQZCg==" + }, + "min-indent@1.0.1": { + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, + "minimist@1.2.8": { + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "minipass@7.1.2": { + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + }, + "minizlib@3.0.2": { + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dependencies": [ + "minipass" + ] + }, + "mkdirp@3.0.1": { + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": true + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "murmurhash-js@1.0.0": { + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, + "nanoid@3.3.11": { + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "bin": true + }, + "node-fetch@2.7.0": { + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": [ + "whatwg-url" + ] + }, + "node-releases@2.0.19": { + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range@0.1.2": { + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, + "once@1.4.0": { + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": [ + "wrappy" + ] + }, + "pathe@2.0.3": { + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, + "pathval@2.0.1": { + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==" + }, + "pbf@3.3.0": { + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "dependencies": [ + "ieee754", + "resolve-protobuf-schema" + ], + "bin": true + }, + "peek-stream@1.1.3": { + "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", + "dependencies": [ + "buffer-from", + "duplexify", + "through2@2.0.5" + ] + }, + "picocolors@1.1.1": { + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch@2.3.1": { + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "picomatch@4.0.3": { + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==" + }, + "point-in-polygon-hao@1.2.4": { + "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", + "dependencies": [ + "robust-predicates@3.0.2" + ] + }, + "point-in-polygon@1.1.0": { + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, + "polyclip-ts@0.16.8": { + "integrity": "sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ==", + "dependencies": [ + "bignumber.js", + "splaytree-ts" + ] + }, + "postcss-value-parser@4.2.0": { + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "postcss@8.5.6": { + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dependencies": [ + "nanoid", + "picocolors", + "source-map-js" + ] + }, + "potpack@2.1.0": { + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==" + }, + "prettier@3.6.2": { + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "bin": true + }, + "pretty-format@27.5.1": { + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": [ + "ansi-regex", + "ansi-styles@5.2.0", + "react-is" + ] + }, + "process-nextick-args@2.0.1": { + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "process-streams@1.0.3": { + "integrity": "sha512-xkIaM5vYnyekB88WyET78YEqXiaJRy0xcvIdE22n+myhvBT7LlLmX6iAtq7jDvVH8CUx2rqQsd32JdRyJMV3NA==", + "dependencies": [ + "duplex-maker" + ] + }, + "protocol-buffers-schema@3.6.0": { + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, + "qrcode-generator@1.5.2": { + "integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==" + }, + "quickselect@1.1.1": { + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, + "quickselect@2.0.0": { + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "quickselect@3.0.0": { + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==" + }, + "rbush@2.0.2": { + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "dependencies": [ + "quickselect@1.1.1" + ] + }, + "rbush@3.0.1": { + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": [ + "quickselect@2.0.0" + ] + }, + "react-dom@19.1.0_react@19.1.0": { + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "dependencies": [ + "react", + "scheduler" + ] + }, + "react-error-boundary@6.0.0_react@19.1.0": { + "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==", + "dependencies": [ + "@babel/runtime", + "react" + ] + }, + "react-hook-form@7.60.0_react@19.1.0": { + "integrity": "sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==", + "dependencies": [ + "react" + ] + }, + "react-i18next@15.6.0_i18next@25.3.2__typescript@5.8.3_react@19.1.0_typescript@5.8.3": { + "integrity": "sha512-W135dB0rDfiFmbMipC17nOhGdttO5mzH8BivY+2ybsQBbXvxWIwl3cmeH3T9d+YPBSJu/ouyJKFJTtkK7rJofw==", + "dependencies": [ + "@babel/runtime", + "html-parse-stringify", + "i18next", + "react", + "typescript" + ], + "optionalPeers": [ + "typescript" + ] + }, + "react-is@17.0.2": { + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "react-map-gl@8.0.4_maplibre-gl@5.6.1_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-SHdpvFIvswsZBg6BCPcwY/nbKuCo3sJM1Cj7Sd+gA3gFRFOixD+KtZ2XSuUWq2WySL2emYEXEgrLZoXsV4Ut4Q==", + "dependencies": [ + "@vis.gl/react-mapbox", + "@vis.gl/react-maplibre", + "maplibre-gl", + "react", + "react-dom" + ], + "optionalPeers": [ + "maplibre-gl" + ] + }, + "react-qrcode-logo@3.0.0_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-2+vZ3GNBdUpYxIKyt6SFZsDGXa0xniyUQ0wPI4O0hJTzRjttPIx1pPnH9IWQmp/4nDMoN47IBhi3Breu1KudYw==", + "dependencies": [ + "lodash.isequal", + "qrcode-generator", + "react", + "react-dom" + ] + }, + "react-refresh@0.17.0": { + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==" + }, + "react-remove-scroll-bar@2.3.8_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "dependencies": [ + "@types/react", + "react", + "react-style-singleton", + "tslib@2.8.1" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "react-remove-scroll@2.7.1_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "dependencies": [ + "@types/react", + "react", + "react-remove-scroll-bar", + "react-style-singleton", + "tslib@2.8.1", + "use-callback-ref", + "use-sidecar" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "react-style-singleton@2.2.3_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "dependencies": [ + "@types/react", + "get-nonce", + "react", + "tslib@2.8.1" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "react@19.1.0": { + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==" + }, + "readable-stream@2.3.8": { + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": [ + "core-util-is", + "inherits", + "isarray", + "process-nextick-args", + "safe-buffer@5.1.2", + "string_decoder@1.1.1", + "util-deprecate" + ] + }, + "readable-stream@3.6.2": { + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": [ + "inherits", + "string_decoder@1.3.0", + "util-deprecate" + ] + }, + "readdirp@3.6.0": { + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": [ + "picomatch@2.3.1" + ] + }, + "recast@0.23.11": { + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "dependencies": [ + "ast-types", + "esprima", + "source-map@0.6.1", + "tiny-invariant", + "tslib@2.8.1" + ] + }, + "redent@3.0.0": { + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dependencies": [ + "indent-string", + "strip-indent" + ] + }, + "require-directory@2.1.1": { + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" + }, + "resolve-protobuf-schema@2.1.0": { + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": [ + "protocol-buffers-schema" + ] + }, + "rfc4648@1.5.4": { + "integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==" + }, + "robust-predicates@2.0.4": { + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, + "robust-predicates@3.0.2": { + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "rollup@4.45.1": { + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dependencies": [ + "@types/estree" + ], + "optionalDependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-freebsd-arm64", + "@rollup/rollup-freebsd-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-loongarch64-gnu", + "@rollup/rollup-linux-powerpc64le-gnu", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-riscv64-musl", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-msvc", + "fsevents" + ], + "bin": true + }, + "rw@1.3.3": { + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "rxjs@6.6.7": { + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": [ + "tslib@1.14.1" + ] + }, + "safe-buffer@5.1.2": { + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "scheduler@0.26.0": { + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + }, + "semver@6.3.1": { + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": true + }, + "seroval-plugins@1.3.2_seroval@1.3.2": { + "integrity": "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==", + "dependencies": [ + "seroval" + ] + }, + "seroval@1.3.2": { + "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==" + }, + "set-value@2.0.1": { + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": [ + "extend-shallow@2.0.1", + "is-extendable@0.1.1", + "is-plain-object", + "split-string" + ] + }, + "siginfo@2.0.0": { + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==" + }, + "simple-git-hooks@2.13.0": { + "integrity": "sha512-N+goiLxlkHJlyaYEglFypzVNMaNplPAk5syu0+OPp/Bk6dwVoXF6FfOw2vO0Dp+JHsBaI+w6cm8TnFl2Hw6tDA==", + "scripts": true, + "bin": true + }, + "simple-zstd@1.4.2": { + "integrity": "sha512-kGYEvT33M5XfyQvvW4wxl3eKcWbdbCc1V7OZzuElnaXft0qbVzoIIXHXiCm3JCUki+MZKKmvjl8p2VGLJc5Y/A==", + "dependencies": [ + "is-zst", + "peek-stream", + "process-streams", + "through2@4.0.2" + ] + }, + "skmeans@0.9.7": { + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" + }, + "solid-js@1.9.7_seroval@1.3.2": { + "integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==", + "dependencies": [ + "csstype", + "seroval", + "seroval-plugins" + ] + }, + "sort-asc@0.2.0": { + "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==" + }, + "sort-desc@0.2.0": { + "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==" + }, + "sort-object@3.0.3": { + "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==", + "dependencies": [ + "bytewise", + "get-value", + "is-extendable@0.1.1", + "sort-asc", + "sort-desc", + "union-value" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "source-map@0.6.1": { + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map@0.7.4": { + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + }, + "splaytree-ts@1.0.2": { + "integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==" + }, + "split-string@3.1.0": { + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": [ + "extend-shallow@3.0.2" + ] + }, + "stackback@0.0.2": { + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" + }, + "std-env@3.9.0": { + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==" + }, + "ste-core@3.0.11": { + "integrity": "sha512-ivkRENMh0mdGoPlZ4xVcEaC8rXQfTEfvonRw5m8VDKV7kgcbZbaNd1TnKl08wXbcLdT7okSc63HNP8cVhy95zg==" + }, + "ste-simple-events@3.0.11": { + "integrity": "sha512-PDoQajqiTtJLNDWfJCihzACiTVZyFsXi6hNAVNelNJoNmqj+BaWuhJ/NHaAHxzfSRoMbL+hFgfPqFmxiHhAQSQ==", + "dependencies": [ + "ste-core" + ] + }, + "stream-shift@1.0.3": { + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" + }, + "string-width@4.2.3": { + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": [ + "emoji-regex", + "is-fullwidth-code-point", + "strip-ansi" + ] + }, + "string_decoder@1.1.1": { + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": [ + "safe-buffer@5.1.2" + ] + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer@5.2.1" + ] + }, + "strip-ansi@6.0.1": { + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": [ + "ansi-regex" + ] + }, + "strip-indent@3.0.0": { + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dependencies": [ + "min-indent" + ] + }, + "strip-literal@3.0.0": { + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dependencies": [ + "js-tokens@9.0.1" + ] + }, + "supercluster@8.0.1": { + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "dependencies": [ + "kdbush" + ] + }, + "supports-color@7.2.0": { + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": [ + "has-flag" + ] + }, + "sweepline-intersections@1.5.0": { + "integrity": "sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==", + "dependencies": [ + "tinyqueue@2.0.3" + ] + }, + "tailwind-merge@3.3.1": { + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==" + }, + "tailwindcss-animate@1.0.7_tailwindcss@4.1.11": { + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dependencies": [ + "tailwindcss" + ] + }, + "tailwindcss@4.1.11": { + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==" + }, + "tapable@2.2.2": { + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==" + }, + "tar@7.4.3": { + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dependencies": [ + "@isaacs/fs-minipass", + "chownr", + "minipass", + "minizlib", + "mkdirp", + "yallist@5.0.0" + ] + }, + "testing-library@0.0.2_@angular+common@6.1.10__@angular+core@6.1.10___rxjs@6.6.7___zone.js@0.8.29__rxjs@6.6.7_@angular+core@6.1.10__rxjs@6.6.7__zone.js@0.8.29": { + "integrity": "sha512-KCbqCCllbgiCXOgmh9MdsgdJ05pmimXGuggtC78pzpxpq/40A3bS+NJoqwCIqZbNnMr6KIZ2mlMZoZCkWVnaWw==", + "dependencies": [ + "@angular/common", + "@angular/core", + "tslib@1.14.1" + ] + }, + "through2@2.0.5": { + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": [ + "readable-stream@2.3.8", + "xtend" + ] + }, + "through2@4.0.2": { + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dependencies": [ + "readable-stream@3.6.2" + ] + }, + "tiny-invariant@1.3.3": { + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "tiny-warning@1.0.3": { + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "tinybench@2.9.0": { + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==" + }, + "tinyexec@0.3.2": { + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==" + }, + "tinyglobby@0.2.14_picomatch@4.0.3": { + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dependencies": [ + "fdir", + "picomatch@4.0.3" + ] + }, + "tinypool@1.1.1": { + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==" + }, + "tinyqueue@2.0.3": { + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "tinyqueue@3.0.0": { + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==" + }, + "tinyrainbow@2.0.0": { + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==" + }, + "tinyspy@4.0.3": { + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==" + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number" + ] + }, + "topojson-client@3.1.0": { + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "dependencies": [ + "commander@2.20.3" + ], + "bin": true + }, + "topojson-server@3.0.1": { + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "dependencies": [ + "commander@2.20.3" + ], + "bin": true + }, + "tr46@0.0.3": { + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tslib@1.14.1": { + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "tslog@4.9.3": { + "integrity": "sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==" + }, + "tsx@4.20.3": { + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dependencies": [ + "esbuild", + "get-tsconfig" + ], + "optionalDependencies": [ + "fsevents" + ], + "bin": true + }, + "type-fest@2.19.0": { + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "typescript@5.8.3": { + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "bin": true + }, + "typewise-core@1.2.0": { + "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==" + }, + "typewise@1.0.3": { + "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==", + "dependencies": [ + "typewise-core" + ] + }, + "undici-types@6.21.0": { + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "undici-types@7.8.0": { + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" + }, + "union-value@1.0.1": { + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": [ + "arr-union", + "get-value", + "is-extendable@0.1.1", + "set-value" + ] + }, + "unplugin@2.3.5": { + "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==", + "dependencies": [ + "acorn", + "picomatch@4.0.3", + "webpack-virtual-modules" + ] + }, + "update-browserslist-db@1.1.3_browserslist@4.25.1": { + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dependencies": [ + "browserslist", + "escalade", + "picocolors" + ], + "bin": true + }, + "use-callback-ref@1.3.3_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "dependencies": [ + "@types/react", + "react", + "tslib@2.8.1" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "use-sidecar@1.1.3_@types+react@19.1.8_react@19.1.0": { + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "dependencies": [ + "@types/react", + "detect-node-es", + "react", + "tslib@2.8.1" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "use-sync-external-store@1.5.0_react@19.1.0": { + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "dependencies": [ + "react" + ] + }, + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "vite-node@3.2.4_@types+node@22.16.4": { + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dependencies": [ + "cac", + "debug", + "es-module-lexer", + "pathe", + "vite" + ], + "bin": true + }, + "vite@7.0.5_@types+node@22.16.4_picomatch@4.0.3": { + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "dependencies": [ + "@types/node@22.16.4", + "esbuild", + "fdir", + "picomatch@4.0.3", + "postcss", + "rollup", + "tinyglobby" + ], + "optionalDependencies": [ + "fsevents" + ], + "optionalPeers": [ + "@types/node@22.16.4" + ], + "bin": true + }, + "vitest@3.2.4_@types+node@22.16.4_happy-dom@18.0.1_vite@7.0.5__@types+node@22.16.4__picomatch@4.0.3": { + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dependencies": [ + "@types/chai", + "@types/node@22.16.4", + "@vitest/expect", + "@vitest/mocker", + "@vitest/pretty-format", + "@vitest/runner", + "@vitest/snapshot", + "@vitest/spy", + "@vitest/utils", + "chai", + "debug", + "expect-type", + "happy-dom", + "magic-string", + "pathe", + "picomatch@4.0.3", + "std-env", + "tinybench", + "tinyexec", + "tinyglobby", + "tinypool", + "tinyrainbow", + "vite", + "vite-node", + "why-is-node-running" + ], + "optionalPeers": [ + "@types/node@22.16.4", + "happy-dom" + ], + "bin": true + }, + "void-elements@3.1.0": { + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" + }, + "vt-pbf@3.1.3": { + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": [ + "@mapbox/point-geometry", + "@mapbox/vector-tile", + "pbf" + ] + }, + "webidl-conversions@3.0.1": { + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "webpack-virtual-modules@0.6.2": { + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==" + }, + "whatwg-mimetype@3.0.0": { + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" + }, + "whatwg-url@5.0.0": { + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": [ + "tr46", + "webidl-conversions" + ] + }, + "which@4.0.0": { + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dependencies": [ + "isexe" + ], + "bin": true + }, + "why-is-node-running@2.3.0": { + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dependencies": [ + "siginfo", + "stackback" + ], + "bin": true + }, + "wrap-ansi@7.0.0": { + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": [ + "ansi-styles@4.3.0", + "string-width", + "strip-ansi" + ] + }, + "wrappy@1.0.2": { + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "xtend@4.0.2": { + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n@5.0.8": { + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist@3.1.1": { + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yallist@5.0.0": { + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==" + }, + "yargs-parser@21.1.1": { + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + }, + "yargs@17.7.2": { + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": [ + "cliui", + "escalade", + "get-caller-file", + "require-directory", + "string-width", + "y18n", + "yargs-parser" + ] + }, + "zod@3.25.76": { + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" + }, + "zod@4.0.5": { + "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==" + }, + "zone.js@0.8.29": { + "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" + }, + "zustand@5.0.6_@types+react@19.1.8_immer@10.1.1_react@19.1.0": { + "integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==", + "dependencies": [ + "@types/react", + "immer", + "react" + ], + "optionalPeers": [ + "@types/react", + "immer", + "react" + ] + } + }, + "remote": { + "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", + "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", + "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", + "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", + "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", + "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", + "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", + "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", + "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", + "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", + "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", + "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", + "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", + "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", + "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", + "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", + "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", + "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", + "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", + "https://deno.land/std@0.181.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.181.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.181.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", + "https://deno.land/std@0.181.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.181.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", + "https://deno.land/std@0.181.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", + "https://deno.land/std@0.181.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.181.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.181.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", + "https://deno.land/std@0.181.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.181.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.181.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", + "https://deno.land/std@0.181.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", + "https://deno.land/std@0.181.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.181.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", + "https://deno.land/std@0.182.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.182.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.182.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", + "https://deno.land/std@0.182.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", + "https://deno.land/std@0.182.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", + "https://deno.land/std@0.182.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", + "https://deno.land/std@0.182.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", + "https://deno.land/std@0.182.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.182.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.182.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", + "https://deno.land/std@0.182.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.182.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.182.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", + "https://deno.land/std@0.182.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", + "https://deno.land/std@0.182.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.182.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", + "https://deno.land/x/code_block_writer@12.0.0/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", + "https://deno.land/x/code_block_writer@12.0.0/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", + "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e", + "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195", + "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f", + "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343", + "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9", + "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9", + "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7", + "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd", + "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195", + "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe", + "https://deno.land/x/dir@1.5.1/data_local_dir/mod.ts": "91eb1c4bfadfbeda30171007bac6d85aadacd43224a5ed721bbe56bc64e9eb66", + "https://deno.land/x/dnt@0.37.0/lib/compiler.ts": "209ad2e1b294f93f87ec02ade9a0821f942d2e524104552d0aa8ff87021050a5", + "https://deno.land/x/dnt@0.37.0/lib/compiler_transforms.ts": "cbb1fd5948f5ced1aa5c5aed9e45134e2357ce1e7220924c1d7bded30dcd0dd0", + "https://deno.land/x/dnt@0.37.0/lib/mod.deps.ts": "30367fc68bcd2acf3b7020cf5cdd26f817f7ac9ac35c4bfb6c4551475f91bc3e", + "https://deno.land/x/dnt@0.37.0/lib/npm_ignore.ts": "b430caa1905b65ae89b119d84857b3ccc3cb783a53fc083d1970e442f791721d", + "https://deno.land/x/dnt@0.37.0/lib/package_json.ts": "61f35b06e374ed39ca776d29d67df4be7ee809d0bca29a8239687556c6d027c2", + "https://deno.land/x/dnt@0.37.0/lib/pkg/dnt_wasm.generated.js": "65514d733c044bb394e4765321e33b73c490b20f86563293b5665d7a7b185153", + "https://deno.land/x/dnt@0.37.0/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "a6b95adc943a68d513fe8ed9ec7d260ac466b7a4bced4e942f733e494bb9f1be", + "https://deno.land/x/dnt@0.37.0/lib/shims.ts": "df1bd4d9a196dca4b2d512b1564fff64ac6c945189a273d706391f87f210d7e6", + "https://deno.land/x/dnt@0.37.0/lib/test_runner/get_test_runner_code.ts": "4dc7a73a13b027341c0688df2b29a4ef102f287c126f134c33f69f0339b46968", + "https://deno.land/x/dnt@0.37.0/lib/test_runner/test_runner.ts": "4d0da0500ec427d5f390d9a8d42fb882fbeccc92c92d66b6f2e758606dbd40e6", + "https://deno.land/x/dnt@0.37.0/lib/transform.deps.ts": "e42f2bdef46d098453bdba19261a67cf90b583f5d868f7fe83113c1380d9b85c", + "https://deno.land/x/dnt@0.37.0/lib/types.ts": "b8e228b2fac44c2ae902fbb73b1689f6ab889915bd66486c8a85c0c24255f5fb", + "https://deno.land/x/dnt@0.37.0/lib/utils.ts": "878b7ac7003a10c16e6061aa49dbef9b42bd43174853ebffc9b67ea47eeb11d8", + "https://deno.land/x/dnt@0.37.0/mod.ts": "37d0c784371cf1750f30203a95de2555ba4c1aa89d826024f14c038f87e0f344", + "https://deno.land/x/dnt@0.37.0/transform.ts": "1b127c5f22699c8ab2545b98aeca38c4e5c21405b0f5342ea17e9c46280ed277", + "https://deno.land/x/ts_morph@18.0.0/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", + "https://deno.land/x/ts_morph@18.0.0/bootstrap/ts_morph_bootstrap.js": "6645ac03c5e6687dfa8c78109dc5df0250b811ecb3aea2d97c504c35e8401c06", + "https://deno.land/x/ts_morph@18.0.0/common/DenoRuntime.ts": "6a7180f0c6e90dcf23ccffc86aa8271c20b1c4f34c570588d08a45880b7e172d", + "https://deno.land/x/ts_morph@18.0.0/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed", + "https://deno.land/x/ts_morph@18.0.0/common/ts_morph_common.js": "845671ca951073400ce142f8acefa2d39ea9a51e29ca80928642f3f8cf2b7700", + "https://deno.land/x/ts_morph@18.0.0/common/typescript.js": "d5c598b6a2db2202d0428fca5fd79fc9a301a71880831a805d778797d2413c59", + "https://deno.land/x/wasmbuild@0.13.0/cache.ts": "89eea5f3ce6035a1164b3e655c95f21300498920575ade23161421f5b01967f4", + "https://deno.land/x/wasmbuild@0.13.0/loader.ts": "d98d195a715f823151cbc8baa3f32127337628379a02d9eb2a3c5902dbccfc02" + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@bufbuild/protobuf@^2.6.1", + "npm:@jsr/meshtastic__protobufs@*", + "npm:@types/node@^22.16.4", + "npm:bun@^1.2.18", + "npm:ste-simple-events@^3.0.11", + "npm:tslog@^4.9.3", + "npm:typescript@^5.8.3" + ] + }, + "members": { + "packages/core": { + "packageJson": { + "dependencies": [ + "npm:crc@^4.3.2" + ] + } + }, + "packages/transport-web-bluetooth": { + "packageJson": { + "dependencies": [ + "npm:@types/web-bluetooth@^0.0.20" + ] + } + }, + "packages/transport-web-serial": { + "packageJson": { + "dependencies": [ + "npm:@types/w3c-web-serial@^1.0.7" + ] + } + }, + "packages/web": { + "packageJson": { + "dependencies": [ + "npm:@biomejs/biome@2.0.6", + "npm:@bufbuild/protobuf@^2.6.0", + "npm:@hookform/resolvers@^5.1.1", + "npm:@jsr/meshtastic__core@2.6.4", + "npm:@jsr/meshtastic__transport-http@*", + "npm:@jsr/meshtastic__transport-web-bluetooth@*", + "npm:@jsr/meshtastic__transport-web-serial@*", + "npm:@noble/curves@^1.9.2", + "npm:@radix-ui/react-accordion@^1.2.11", + "npm:@radix-ui/react-checkbox@^1.3.2", + "npm:@radix-ui/react-dialog@^1.1.14", + "npm:@radix-ui/react-dropdown-menu@^2.1.15", + "npm:@radix-ui/react-label@^2.1.7", + "npm:@radix-ui/react-menubar@^1.1.15", + "npm:@radix-ui/react-popover@^1.1.14", + "npm:@radix-ui/react-scroll-area@^1.2.9", + "npm:@radix-ui/react-select@^2.2.5", + "npm:@radix-ui/react-separator@^1.1.7", + "npm:@radix-ui/react-slider@^1.3.5", + "npm:@radix-ui/react-switch@^1.2.5", + "npm:@radix-ui/react-tabs@^1.1.12", + "npm:@radix-ui/react-toast@^1.2.14", + "npm:@radix-ui/react-toggle-group@^1.1.10", + "npm:@radix-ui/react-tooltip@^1.2.7", + "npm:@tailwindcss/vite@^4.1.11", + "npm:@tanstack/react-router-devtools@^1.127.9", + "npm:@tanstack/react-router@^1.127.9", + "npm:@tanstack/router-cli@^1.127.8", + "npm:@tanstack/router-devtools@^1.127.9", + "npm:@tanstack/router-plugin@^1.127.9", + "npm:@testing-library/jest-dom@^6.6.3", + "npm:@testing-library/react@^16.3.0", + "npm:@testing-library/user-event@^14.6.1", + "npm:@turf/turf@^7.2.0", + "npm:@types/chrome@0.1", + "npm:@types/js-cookie@^3.0.6", + "npm:@types/node@^24.0.14", + "npm:@types/react-dom@^19.1.6", + "npm:@types/react@^19.1.8", + "npm:@types/serviceworker@^0.0.142", + "npm:@types/w3c-web-serial@^1.0.8", + "npm:@types/web-bluetooth@^0.0.21", + "npm:@vitejs/plugin-react@^4.6.0", + "npm:autoprefixer@^10.4.21", + "npm:base64-js@^1.5.1", + "npm:class-variance-authority@~0.7.1", + "npm:clsx@^2.1.1", + "npm:cmdk@^1.1.1", + "npm:crypto-random-string@5", + "npm:gzipper@^8.2.1", + "npm:happy-dom@^18.0.1", + "npm:i18next-browser-languagedetector@^8.2.0", + "npm:i18next-http-backend@^3.0.2", + "npm:i18next@^25.3.2", + "npm:idb-keyval@^6.2.2", + "npm:immer@^10.1.1", + "npm:js-cookie@^3.0.5", + "npm:lucide-react@0.525", + "npm:maplibre-gl@5.6.1", + "npm:react-dom@^19.1.0", + "npm:react-error-boundary@6", + "npm:react-hook-form@^7.60.0", + "npm:react-i18next@^15.6.0", + "npm:react-map-gl@8.0.4", + "npm:react-qrcode-logo@3", + "npm:react@^19.1.0", + "npm:rfc4648@^1.5.4", + "npm:simple-git-hooks@^2.13.0", + "npm:tailwind-merge@^3.3.1", + "npm:tailwindcss-animate@^1.0.7", + "npm:tailwindcss@^4.1.11", + "npm:tar@^7.4.3", + "npm:testing-library@^0.0.2", + "npm:typescript@^5.8.3", + "npm:vite@^7.0.4", + "npm:vitest@^3.2.4", + "npm:zod@^4.0.5", + "npm:zustand@5.0.6" + ] + } + } + } + } +} diff --git a/package.json b/package.json index 15fb47ca..04dcf6e4 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,28 @@ "url": "https://github.com/meshtastic/web/issues" }, "homepage": "https://meshtastic.org", - "workspaces": ["packages/web"], + "workspaces": ["packages/*"], + "simple-git-hooks": { + "pre-commit": "bun run check:fix" + }, "scripts": { + "lint": "biome lint", + "lint:fix": "biome lint --write", + "format": "biome format", + "format:fix": "biome format . --write", + "check": "biome check", + "check:fix": "biome check --write", "build:npm": "deno run -A scripts/build_npm_package.ts" }, + "dependencies": { + "@bufbuild/protobuf": "^2.6.1", + "@meshtastic/protobufs": "npm:@jsr/meshtastic__protobufs", + "ste-simple-events": "^3.0.11", + "tslog": "^4.9.3" + }, "devDependencies": { - "@biomejs/biome": "^1.8.3", - "bun": "^1.1.18", - "typescript": "^5.8.3" + "bun": "^1.2.18", + "typescript": "^5.8.3", + "@types/node": "^22.16.4" } } diff --git a/packages/core/deno.json b/packages/core/deno.json deleted file mode 100644 index b0a4c42a..00000000 --- a/packages/core/deno.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@meshtastic/core", - "version": "2.6.4", - "description": "Core functionalities for Meshtastic web applications.", - "exports": { - ".": "./mod.ts" - }, - "imports": { - "crc": "npm:crc@^4.3.2" - } -} diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 00000000..61ef861c --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,11 @@ +{ + "name": "@meshtastic/core", + "version": "2.6.5", + "description": "Core functionalities for Meshtastic web applications.", + "exports": { + ".": "./mod.ts" + }, + "dependencies": { + "crc": "npm:crc@^4.3.2" + } +} \ No newline at end of file diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index f7eee111..01053c3f 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -5,6 +5,6 @@ const broadcastNum = 0xffffffff; const minFwVer = 2.2; export const Constants = { - broadcastNum, - minFwVer, + broadcastNum, + minFwVer, }; diff --git a/packages/core/src/meshDevice.ts b/packages/core/src/meshDevice.ts index d624b7c8..7dbc5e07 100755 --- a/packages/core/src/meshDevice.ts +++ b/packages/core/src/meshDevice.ts @@ -10,1152 +10,1152 @@ import { EventSystem, Queue, Xmodem } from "./utils/mod.ts"; import { decodePacket } from "./utils/transform/decodePacket.ts"; export class MeshDevice { - public transport: Transport; - - /** Logs to the console and the logging event emitter */ - public log: Logger; - - /** Describes the current state of the device */ - protected deviceStatus: DeviceStatusEnum; - - /** Describes the current state of the device */ - protected isConfigured: boolean; - - /** Are there any settings that have yet to be applied? */ - protected pendingSettingsChanges: boolean; - - /** Device's node number */ - private myNodeInfo: Protobuf.Mesh.MyNodeInfo; - - /** Randomly generated number to ensure confiuration lockstep */ - public configId: number; - - /** - * Packert queue, to space out transmissions and routing handle errors and - * acks - */ - public queue: Queue; - - public events: EventSystem; - - public xModem: Xmodem; - - constructor(transport: Transport, configId?: number) { - this.log = new Logger({ - name: "iMeshDevice", - prettyLogTemplate: - "{{hh}}:{{MM}}:{{ss}}:{{ms}}\t{{logLevelName}}\t[{{name}}]\t", - }); - - this.transport = transport; - this.deviceStatus = DeviceStatusEnum.DeviceDisconnected; - this.isConfigured = false; - this.pendingSettingsChanges = false; - this.myNodeInfo = create(Protobuf.Mesh.MyNodeInfoSchema); - this.configId = configId ?? this.generateRandId(); - this.queue = new Queue(); - this.events = new EventSystem(); - this.xModem = new Xmodem(this.sendRaw.bind(this)); //TODO: try wihtout bind - - this.events.onDeviceStatus.subscribe((status) => { - this.deviceStatus = status; - if (status === DeviceStatusEnum.DeviceConfigured) { - this.isConfigured = true; - } else if (status === DeviceStatusEnum.DeviceConfiguring) { - this.isConfigured = false; - } - }); - - this.events.onMyNodeInfo.subscribe((myNodeInfo) => { - this.myNodeInfo = myNodeInfo; - }); - - this.events.onPendingSettingsChange.subscribe((state) => { - this.pendingSettingsChanges = state; - }); - - this.transport.fromDevice.pipeTo(decodePacket(this)); - } - - /** Abstract method that connects to the radio */ - // protected abstract connect( - // parameters: Types.ConnectionParameters, - // ): Promise; - - /** Abstract method that disconnects from the radio */ - // protected abstract disconnect(): void; - - /** Abstract method that pings the radio */ - // protected abstract ping(): Promise; - - /** - * Sends a text over the radio - */ - public async sendText( - text: string, - destination?: Destination, - wantAck?: boolean, - channel?: ChannelNumber, - replyId?: number, - emoji?: number, - ): Promise { - this.log.debug( - Emitter[Emitter.SendText], - `📤 Sending message to ${destination ?? "broadcast"} on channel ${ - channel?.toString() ?? 0 - }`, - ); - - const enc = new TextEncoder(); - - return await this.sendPacket( - enc.encode(text), - Protobuf.Portnums.PortNum.TEXT_MESSAGE_APP, - destination ?? "broadcast", - channel, - wantAck, - false, - true, - replyId, - emoji, - ); - } - - /** - * Sends a text over the radio - */ - public sendWaypoint( - waypointMessage: Protobuf.Mesh.Waypoint, - destination: Destination, - channel?: ChannelNumber, - ): Promise { - this.log.debug( - Emitter[Emitter.SendWaypoint], - `📤 Sending waypoint to ${destination} on channel ${ - channel?.toString() ?? 0 - }`, - ); - - waypointMessage.id = this.generateRandId(); - - return this.sendPacket( - toBinary(Protobuf.Mesh.WaypointSchema, waypointMessage), - Protobuf.Portnums.PortNum.WAYPOINT_APP, - destination, - channel, - true, - false, - ); - } - - /** - * Sends packet over the radio - */ - public async sendPacket( - byteData: Uint8Array, - portNum: Protobuf.Portnums.PortNum, - destination: Destination, - channel: ChannelNumber = ChannelNumber.Primary, - wantAck = true, - wantResponse = true, - echoResponse = false, - replyId?: number, - emoji?: number, - ): Promise { - this.log.trace( - Emitter[Emitter.SendPacket], - `📤 Sending ${Protobuf.Portnums.PortNum[portNum]} to ${destination}`, - ); - - const meshPacket = create(Protobuf.Mesh.MeshPacketSchema, { - payloadVariant: { - case: "decoded", - value: { - payload: byteData, - portnum: portNum, - wantResponse, - emoji, - replyId, - dest: 0, //change this! - requestId: 0, //change this! - source: 0, //change this! - }, - }, - from: this.myNodeInfo.myNodeNum, - to: - destination === "broadcast" - ? Constants.broadcastNum - : destination === "self" - ? this.myNodeInfo.myNodeNum - : destination, - id: this.generateRandId(), - wantAck: wantAck, - channel, - }); - - const toRadioMessage = create(Protobuf.Mesh.ToRadioSchema, { - payloadVariant: { - case: "packet", - value: meshPacket, - }, - }); - - if (echoResponse) { - meshPacket.rxTime = Math.trunc(new Date().getTime() / 1000); - this.handleMeshPacket(meshPacket); - } - return await this.sendRaw( - toBinary(Protobuf.Mesh.ToRadioSchema, toRadioMessage), - meshPacket.id, - ); - } - - /** - * Sends raw packet over the radio - */ - public async sendRaw( - toRadio: Uint8Array, - id: number = this.generateRandId(), - ): Promise { - if (toRadio.length > 512) { - throw new Error("Message longer than 512 bytes, it will not be sent!"); - } - this.queue.push({ - id, - data: toRadio, - }); - - await this.queue.processQueue(this.transport.toDevice); - - return this.queue.wait(id); - } - - /** - * Writes config to device - */ - public async setConfig(config: Protobuf.Config.Config): Promise { - this.log.debug( - Emitter[Emitter.SetConfig], - `⚙️ Setting config, Variant: ${config.payloadVariant.case ?? "Unknown"}`, - ); - - if (!this.pendingSettingsChanges) { - await this.beginEditSettings(); - } - - const configMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "setConfig", - value: config, - }, - }); - - return this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, configMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Writes module config to device - */ - public async setModuleConfig( - moduleConfig: Protobuf.ModuleConfig.ModuleConfig, - ): Promise { - this.log.debug(Emitter[Emitter.SetModuleConfig], "⚙️ Setting module config"); - - const moduleConfigMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "setModuleConfig", - value: moduleConfig, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, moduleConfigMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - // Write cannedMessages to device - public async setCannedMessages( - cannedMessages: Protobuf.CannedMessages.CannedMessageModuleConfig, - ): Promise { - this.log.debug( - Emitter[Emitter.SetCannedMessages], - "⚙️ Setting CannedMessages", - ); - - const cannedMessagesMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "setCannedMessageModuleMessages", - value: cannedMessages.messages, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, cannedMessagesMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Sets devices owner data - */ - public async setOwner(owner: Protobuf.Mesh.User): Promise { - this.log.debug(Emitter[Emitter.SetOwner], "👤 Setting owner"); - - const setOwnerMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "setOwner", - value: owner, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, setOwnerMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Sets devices ChannelSettings - */ - public async setChannel(channel: Protobuf.Channel.Channel): Promise { - this.log.debug( - Emitter[Emitter.SetChannel], - `📻 Setting Channel: ${channel.index}`, - ); - - const setChannelMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "setChannel", - value: channel, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, setChannelMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Triggers Device to enter DFU mode - */ - public async enterDfuMode(): Promise { - this.log.debug(Emitter[Emitter.EnterDfuMode], "🔌 Entering DFU mode"); - - const enterDfuModeRequest = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "enterDfuModeRequest", - value: true, - }, - }); - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, enterDfuModeRequest), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Sets static position of device - */ - public async setPosition( - positionMessage: Protobuf.Mesh.Position, - ): Promise { - return await this.sendPacket( - toBinary(Protobuf.Mesh.PositionSchema, positionMessage), - Protobuf.Portnums.PortNum.POSITION_APP, - "self", - ); - } - - /** - * Gets specified channel information from the radio - */ - public async getChannel(index: number): Promise { - this.log.debug( - Emitter[Emitter.GetChannel], - `📻 Requesting Channel: ${index}`, - ); - - const getChannelRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "getChannelRequest", - value: index + 1, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, getChannelRequestMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Gets devices config - */ - public async getConfig( - configType: Protobuf.Admin.AdminMessage_ConfigType, - ): Promise { - this.log.debug(Emitter[Emitter.GetConfig], "⚙️ Requesting config"); - - const getRadioRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "getConfigRequest", - value: configType, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, getRadioRequestMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Gets Module config - */ - public async getModuleConfig( - moduleConfigType: Protobuf.Admin.AdminMessage_ModuleConfigType, - ): Promise { - this.log.debug( - Emitter[Emitter.GetModuleConfig], - "⚙️ Requesting module config", - ); - - const getRadioRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "getModuleConfigRequest", - value: moduleConfigType, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, getRadioRequestMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** Gets devices Owner */ - public async getOwner(): Promise { - this.log.debug(Emitter[Emitter.GetOwner], "👤 Requesting owner"); - - const getOwnerRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "getOwnerRequest", - value: true, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, getOwnerRequestMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Gets devices metadata - */ - public async getMetadata(nodeNum: number): Promise { - this.log.debug( - Emitter[Emitter.GetMetadata], - `🏷️ Requesting metadata from ${nodeNum}`, - ); - - const getDeviceMetricsRequestMessage = create( - Protobuf.Admin.AdminMessageSchema, - { - payloadVariant: { - case: "getDeviceMetadataRequest", - value: true, - }, - }, - ); - - return await this.sendPacket( - toBinary( - Protobuf.Admin.AdminMessageSchema, - getDeviceMetricsRequestMessage, - ), - Protobuf.Portnums.PortNum.ADMIN_APP, - nodeNum, - ChannelNumber.Admin, - ); - } - - /** - * Clears specific channel with the designated index - */ - public async clearChannel(index: number): Promise { - this.log.debug( - Emitter[Emitter.ClearChannel], - `📻 Clearing Channel ${index}`, - ); - - const channel = create(Protobuf.Channel.ChannelSchema, { - index, - role: Protobuf.Channel.Channel_Role.DISABLED, - }); - const setChannelMessage = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "setChannel", - value: channel, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, setChannelMessage), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - private async beginEditSettings(): Promise { - this.events.onPendingSettingsChange.dispatch(true); - - const beginEditSettings = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "beginEditSettings", - value: true, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, beginEditSettings), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - public async commitEditSettings(): Promise { - this.events.onPendingSettingsChange.dispatch(false); - - const commitEditSettings = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "commitEditSettings", - value: true, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, commitEditSettings), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Resets the internal NodeDB of the radio, usefull for removing old nodes - * that no longer exist. - */ - public async resetNodes(): Promise { - this.log.debug(Emitter[Emitter.ResetNodes], "📻 Resetting NodeDB"); - - const resetNodes = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "nodedbReset", - value: 1, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, resetNodes), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Removes a node from the internal NodeDB of the radio by node number - */ - public async removeNodeByNum(nodeNum: number): Promise { - this.log.debug( - Emitter[Emitter.RemoveNodeByNum], - `📻 Removing Node ${nodeNum} from NodeDB`, - ); - - const removeNodeByNum = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "removeByNodenum", - value: nodeNum, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, removeNodeByNum), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** Shuts down the current node after the specified amount of time has elapsed. */ - public async shutdown(time: number): Promise { - this.log.debug( - Emitter[Emitter.Shutdown], - `🔌 Shutting down ${time > 2 ? "now" : `in ${time} seconds`}`, - ); - - const shutdown = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "shutdownSeconds", - value: time, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, shutdown), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** Reboots the current node after the specified amount of time has elapsed. */ - public async reboot(time: number): Promise { - this.log.debug( - Emitter[Emitter.Reboot], - `🔌 Rebooting node ${time > 0 ? "now" : `in ${time} seconds`}`, - ); - - const reboot = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "rebootSeconds", - value: time, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, reboot), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Reboots the current node into OTA mode after the specified amount of time has elapsed. - */ - public async rebootOta(time: number): Promise { - this.log.debug( - Emitter[Emitter.RebootOta], - `🔌 Rebooting into OTA mode ${time > 0 ? "now" : `in ${time} seconds`}`, - ); - - const rebootOta = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "rebootOtaSeconds", - value: time, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, rebootOta), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Factory resets the current device - */ - public async factoryResetDevice(): Promise { - this.log.debug(Emitter[Emitter.FactoryReset], "♻️ Factory resetting device"); - - const factoryReset = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "factoryResetDevice", - value: 1, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, factoryReset), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Factory resets the current config - */ - public async factoryResetConfig(): Promise { - this.log.debug(Emitter[Emitter.FactoryReset], "♻️ Factory resetting config"); - - const factoryReset = create(Protobuf.Admin.AdminMessageSchema, { - payloadVariant: { - case: "factoryResetConfig", - value: 1, - }, - }); - - return await this.sendPacket( - toBinary(Protobuf.Admin.AdminMessageSchema, factoryReset), - Protobuf.Portnums.PortNum.ADMIN_APP, - "self", - ); - } - - /** - * Triggers the device configure process - */ - public configure(): Promise { - this.log.debug( - Emitter[Emitter.Configure], - "⚙️ Requesting device configuration", - ); - this.updateDeviceStatus(DeviceStatusEnum.DeviceConfiguring); - - const toRadio = create(Protobuf.Mesh.ToRadioSchema, { - payloadVariant: { - case: "wantConfigId", - value: this.configId, - }, - }); - - return this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio)); - } - - /** - * Serial connection requires a heartbeat ping to stay connected, otherwise times out after 15 minutes - */ - public heartbeat(): Promise { - this.log.debug(Emitter[Emitter.Ping], "❤️ Send heartbeat ping to radio"); - - const toRadio = create(Protobuf.Mesh.ToRadioSchema, { - payloadVariant: { - case: "heartbeat", - value: {}, - }, - }); - - return this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio)); - } - - /** - * Sends a trace route packet to the designated node - */ - public async traceRoute(destination: number): Promise { - const routeDiscovery = create(Protobuf.Mesh.RouteDiscoverySchema, { - route: [], - }); - - return await this.sendPacket( - toBinary(Protobuf.Mesh.RouteDiscoverySchema, routeDiscovery), - Protobuf.Portnums.PortNum.TRACEROUTE_APP, - destination, - ); - } - - /** - * Requests position from the designated node - */ - public async requestPosition(destination: number): Promise { - return await this.sendPacket( - new Uint8Array(), - Protobuf.Portnums.PortNum.POSITION_APP, - destination, - ); - } - - /** - * Updates the device status eliminating duplicate status events - */ - public updateDeviceStatus(status: DeviceStatusEnum): void { - if (status !== this.deviceStatus) { - this.events.onDeviceStatus.dispatch(status); - } - } - - /** - * Generates random packet identifier - * - * @returns {number} Random packet ID - */ - private generateRandId(): number { - const seed = crypto.getRandomValues(new Uint32Array(1)); - if (!seed[0]) { - throw new Error("Cannot generate CSPRN"); - } - - return Math.floor(seed[0] * 2 ** -32 * 1e9); - } - - /** Completes all Events */ - public complete(): void { - this.queue.clear(); - } - - /** Disconnects from the device **/ - public async disconnect(): Promise { - this.log.debug(Emitter[Emitter.Disconnect], "🔌 Disconnecting from device"); - this.complete(); - await this.transport.toDevice.close(); - } - - /** - * Gets called when a MeshPacket is received from device - */ - public handleMeshPacket(meshPacket: Protobuf.Mesh.MeshPacket): void { - this.events.onMeshPacket.dispatch(meshPacket); - if (meshPacket.from !== this.myNodeInfo.myNodeNum) { - /** - * TODO: this shouldn't be called unless the device interracts with the - * mesh, currently it does. - */ - this.events.onMeshHeartbeat.dispatch(new Date()); - } - - switch (meshPacket.payloadVariant.case) { - case "decoded": { - this.handleDecodedPacket(meshPacket.payloadVariant.value, meshPacket); - break; - } - - case "encrypted": { - this.log.debug( - Emitter[Emitter.HandleMeshPacket], - "🔐 Device received encrypted data packet, ignoring.", - ); - break; - } - - default: - throw new Error(`Unhandled case ${meshPacket.payloadVariant.case}`); - } - } - - private handleDecodedPacket( - dataPacket: Protobuf.Mesh.Data, - meshPacket: Protobuf.Mesh.MeshPacket, - ) { - let adminMessage: Protobuf.Admin.AdminMessage | undefined = undefined; - let routingPacket: Protobuf.Mesh.Routing | undefined = undefined; - - const packetMetadata: Omit, "data"> = { - id: meshPacket.id, - rxTime: new Date(meshPacket.rxTime * 1000), - type: meshPacket.to === Constants.broadcastNum ? "broadcast" : "direct", - from: meshPacket.from, - to: meshPacket.to, - channel: meshPacket.channel, - }; - - this.log.trace( - Emitter[Emitter.HandleMeshPacket], - `📦 Received ${Protobuf.Portnums.PortNum[dataPacket.portnum]} packet`, - ); - - switch (dataPacket.portnum) { - case Protobuf.Portnums.PortNum.TEXT_MESSAGE_APP: { - this.events.onMessagePacket.dispatch({ - ...packetMetadata, - data: new TextDecoder().decode(dataPacket.payload), - }); - break; - } - - case Protobuf.Portnums.PortNum.REMOTE_HARDWARE_APP: { - this.events.onRemoteHardwarePacket.dispatch({ - ...packetMetadata, - data: fromBinary( - Protobuf.RemoteHardware.HardwareMessageSchema, - dataPacket.payload, - ), - }); - break; - } - - case Protobuf.Portnums.PortNum.POSITION_APP: { - this.events.onPositionPacket.dispatch({ - ...packetMetadata, - data: fromBinary(Protobuf.Mesh.PositionSchema, dataPacket.payload), - }); - break; - } - - case Protobuf.Portnums.PortNum.NODEINFO_APP: { - this.events.onUserPacket.dispatch({ - ...packetMetadata, - data: fromBinary(Protobuf.Mesh.UserSchema, dataPacket.payload), - }); - break; - } - - case Protobuf.Portnums.PortNum.ROUTING_APP: { - routingPacket = fromBinary( - Protobuf.Mesh.RoutingSchema, - dataPacket.payload, - ); - - this.events.onRoutingPacket.dispatch({ - ...packetMetadata, - data: routingPacket, - }); - switch (routingPacket.variant.case) { - case "errorReason": { - if ( - routingPacket.variant.value === Protobuf.Mesh.Routing_Error.NONE - ) { - this.queue.processAck(dataPacket.requestId); - } else { - this.queue.processError({ - id: dataPacket.requestId, - error: routingPacket.variant.value, - }); - } - - break; - } - case "routeReply": { - break; - } - case "routeRequest": { - break; - } - - default: { - throw new Error(`Unhandled case ${routingPacket.variant.case}`); - } - } - break; - } - - case Protobuf.Portnums.PortNum.ADMIN_APP: { - adminMessage = fromBinary( - Protobuf.Admin.AdminMessageSchema, - dataPacket.payload, - ); - switch (adminMessage.payloadVariant.case) { - case "getChannelResponse": { - this.events.onChannelPacket.dispatch( - adminMessage.payloadVariant.value, - ); - break; - } - case "getOwnerResponse": { - this.events.onUserPacket.dispatch({ - ...packetMetadata, - data: adminMessage.payloadVariant.value, - }); - break; - } - case "getConfigResponse": { - this.events.onConfigPacket.dispatch( - adminMessage.payloadVariant.value, - ); - break; - } - case "getModuleConfigResponse": { - this.events.onModuleConfigPacket.dispatch( - adminMessage.payloadVariant.value, - ); - break; - } - case "getDeviceMetadataResponse": { - this.log.debug( - Emitter[Emitter.GetMetadata], - `🏷️ Received metadata packet from ${dataPacket.source}`, - ); - - this.events.onDeviceMetadataPacket.dispatch({ - ...packetMetadata, - data: adminMessage.payloadVariant.value, - }); - break; - } - case "getCannedMessageModuleMessagesResponse": { - this.log.debug( - Emitter[Emitter.GetMetadata], - `🥫 Received CannedMessage Module Messages response packet`, - ); - - this.events.onCannedMessageModulePacket.dispatch({ - ...packetMetadata, - data: adminMessage.payloadVariant.value, - }); - break; - } - default: { - this.log.error( - Emitter[Emitter.HandleMeshPacket], - `⚠️ Received unhandled AdminMessage, type ${ - adminMessage.payloadVariant.case ?? "undefined" - }`, - dataPacket.payload, - ); - } - } - break; - } - - case Protobuf.Portnums.PortNum.WAYPOINT_APP: { - this.events.onWaypointPacket.dispatch({ - ...packetMetadata, - data: fromBinary(Protobuf.Mesh.WaypointSchema, dataPacket.payload), - }); - break; - } - - case Protobuf.Portnums.PortNum.AUDIO_APP: { - this.events.onAudioPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.DETECTION_SENSOR_APP: { - this.events.onDetectionSensorPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.REPLY_APP: { - this.events.onPingPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, //TODO: decode - }); - break; - } - - case Protobuf.Portnums.PortNum.IP_TUNNEL_APP: { - this.events.onIpTunnelPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.PAXCOUNTER_APP: { - this.events.onPaxcounterPacket.dispatch({ - ...packetMetadata, - data: fromBinary( - Protobuf.PaxCount.PaxcountSchema, - dataPacket.payload, - ), - }); - break; - } - - case Protobuf.Portnums.PortNum.SERIAL_APP: { - this.events.onSerialPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.STORE_FORWARD_APP: { - this.events.onStoreForwardPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.RANGE_TEST_APP: { - this.events.onRangeTestPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.TELEMETRY_APP: { - this.events.onTelemetryPacket.dispatch({ - ...packetMetadata, - data: fromBinary( - Protobuf.Telemetry.TelemetrySchema, - dataPacket.payload, - ), - }); - break; - } - - case Protobuf.Portnums.PortNum.ZPS_APP: { - this.events.onZpsPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.SIMULATOR_APP: { - this.events.onSimulatorPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.TRACEROUTE_APP: { - this.events.onTraceRoutePacket.dispatch({ - ...packetMetadata, - data: fromBinary( - Protobuf.Mesh.RouteDiscoverySchema, - dataPacket.payload, - ), - }); - break; - } - - case Protobuf.Portnums.PortNum.NEIGHBORINFO_APP: { - this.events.onNeighborInfoPacket.dispatch({ - ...packetMetadata, - data: fromBinary( - Protobuf.Mesh.NeighborInfoSchema, - dataPacket.payload, - ), - }); - break; - } - - case Protobuf.Portnums.PortNum.ATAK_PLUGIN: { - this.events.onAtakPluginPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.MAP_REPORT_APP: { - this.events.onMapReportPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.PRIVATE_APP: { - this.events.onPrivatePacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - case Protobuf.Portnums.PortNum.ATAK_FORWARDER: { - this.events.onAtakForwarderPacket.dispatch({ - ...packetMetadata, - data: dataPacket.payload, - }); - break; - } - - default: - throw new Error(`Unhandled case ${dataPacket.portnum}`); - } - } + public transport: Transport; + + /** Logs to the console and the logging event emitter */ + public log: Logger; + + /** Describes the current state of the device */ + protected deviceStatus: DeviceStatusEnum; + + /** Describes the current state of the device */ + protected isConfigured: boolean; + + /** Are there any settings that have yet to be applied? */ + protected pendingSettingsChanges: boolean; + + /** Device's node number */ + private myNodeInfo: Protobuf.Mesh.MyNodeInfo; + + /** Randomly generated number to ensure confiuration lockstep */ + public configId: number; + + /** + * Packert queue, to space out transmissions and routing handle errors and + * acks + */ + public queue: Queue; + + public events: EventSystem; + + public xModem: Xmodem; + + constructor(transport: Transport, configId?: number) { + this.log = new Logger({ + name: "iMeshDevice", + prettyLogTemplate: + "{{hh}}:{{MM}}:{{ss}}:{{ms}}\t{{logLevelName}}\t[{{name}}]\t", + }); + + this.transport = transport; + this.deviceStatus = DeviceStatusEnum.DeviceDisconnected; + this.isConfigured = false; + this.pendingSettingsChanges = false; + this.myNodeInfo = create(Protobuf.Mesh.MyNodeInfoSchema); + this.configId = configId ?? this.generateRandId(); + this.queue = new Queue(); + this.events = new EventSystem(); + this.xModem = new Xmodem(this.sendRaw.bind(this)); //TODO: try wihtout bind + + this.events.onDeviceStatus.subscribe((status) => { + this.deviceStatus = status; + if (status === DeviceStatusEnum.DeviceConfigured) { + this.isConfigured = true; + } else if (status === DeviceStatusEnum.DeviceConfiguring) { + this.isConfigured = false; + } + }); + + this.events.onMyNodeInfo.subscribe((myNodeInfo) => { + this.myNodeInfo = myNodeInfo; + }); + + this.events.onPendingSettingsChange.subscribe((state) => { + this.pendingSettingsChanges = state; + }); + + this.transport.fromDevice.pipeTo(decodePacket(this)); + } + + /** Abstract method that connects to the radio */ + // protected abstract connect( + // parameters: Types.ConnectionParameters, + // ): Promise; + + /** Abstract method that disconnects from the radio */ + // protected abstract disconnect(): void; + + /** Abstract method that pings the radio */ + // protected abstract ping(): Promise; + + /** + * Sends a text over the radio + */ + public async sendText( + text: string, + destination?: Destination, + wantAck?: boolean, + channel?: ChannelNumber, + replyId?: number, + emoji?: number, + ): Promise { + this.log.debug( + Emitter[Emitter.SendText], + `📤 Sending message to ${destination ?? "broadcast"} on channel ${ + channel?.toString() ?? 0 + }`, + ); + + const enc = new TextEncoder(); + + return await this.sendPacket( + enc.encode(text), + Protobuf.Portnums.PortNum.TEXT_MESSAGE_APP, + destination ?? "broadcast", + channel, + wantAck, + false, + true, + replyId, + emoji, + ); + } + + /** + * Sends a text over the radio + */ + public sendWaypoint( + waypointMessage: Protobuf.Mesh.Waypoint, + destination: Destination, + channel?: ChannelNumber, + ): Promise { + this.log.debug( + Emitter[Emitter.SendWaypoint], + `📤 Sending waypoint to ${destination} on channel ${ + channel?.toString() ?? 0 + }`, + ); + + waypointMessage.id = this.generateRandId(); + + return this.sendPacket( + toBinary(Protobuf.Mesh.WaypointSchema, waypointMessage), + Protobuf.Portnums.PortNum.WAYPOINT_APP, + destination, + channel, + true, + false, + ); + } + + /** + * Sends packet over the radio + */ + public async sendPacket( + byteData: Uint8Array, + portNum: Protobuf.Portnums.PortNum, + destination: Destination, + channel: ChannelNumber = ChannelNumber.Primary, + wantAck = true, + wantResponse = true, + echoResponse = false, + replyId?: number, + emoji?: number, + ): Promise { + this.log.trace( + Emitter[Emitter.SendPacket], + `📤 Sending ${Protobuf.Portnums.PortNum[portNum]} to ${destination}`, + ); + + const meshPacket = create(Protobuf.Mesh.MeshPacketSchema, { + payloadVariant: { + case: "decoded", + value: { + payload: byteData, + portnum: portNum, + wantResponse, + emoji, + replyId, + dest: 0, //change this! + requestId: 0, //change this! + source: 0, //change this! + }, + }, + from: this.myNodeInfo.myNodeNum, + to: + destination === "broadcast" + ? Constants.broadcastNum + : destination === "self" + ? this.myNodeInfo.myNodeNum + : destination, + id: this.generateRandId(), + wantAck: wantAck, + channel, + }); + + const toRadioMessage = create(Protobuf.Mesh.ToRadioSchema, { + payloadVariant: { + case: "packet", + value: meshPacket, + }, + }); + + if (echoResponse) { + meshPacket.rxTime = Math.trunc(new Date().getTime() / 1000); + this.handleMeshPacket(meshPacket); + } + return await this.sendRaw( + toBinary(Protobuf.Mesh.ToRadioSchema, toRadioMessage), + meshPacket.id, + ); + } + + /** + * Sends raw packet over the radio + */ + public async sendRaw( + toRadio: Uint8Array, + id: number = this.generateRandId(), + ): Promise { + if (toRadio.length > 512) { + throw new Error("Message longer than 512 bytes, it will not be sent!"); + } + this.queue.push({ + id, + data: toRadio, + }); + + await this.queue.processQueue(this.transport.toDevice); + + return this.queue.wait(id); + } + + /** + * Writes config to device + */ + public async setConfig(config: Protobuf.Config.Config): Promise { + this.log.debug( + Emitter[Emitter.SetConfig], + `⚙️ Setting config, Variant: ${config.payloadVariant.case ?? "Unknown"}`, + ); + + if (!this.pendingSettingsChanges) { + await this.beginEditSettings(); + } + + const configMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "setConfig", + value: config, + }, + }); + + return this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, configMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Writes module config to device + */ + public async setModuleConfig( + moduleConfig: Protobuf.ModuleConfig.ModuleConfig, + ): Promise { + this.log.debug(Emitter[Emitter.SetModuleConfig], "⚙️ Setting module config"); + + const moduleConfigMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "setModuleConfig", + value: moduleConfig, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, moduleConfigMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + // Write cannedMessages to device + public async setCannedMessages( + cannedMessages: Protobuf.CannedMessages.CannedMessageModuleConfig, + ): Promise { + this.log.debug( + Emitter[Emitter.SetCannedMessages], + "⚙️ Setting CannedMessages", + ); + + const cannedMessagesMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "setCannedMessageModuleMessages", + value: cannedMessages.messages, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, cannedMessagesMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Sets devices owner data + */ + public async setOwner(owner: Protobuf.Mesh.User): Promise { + this.log.debug(Emitter[Emitter.SetOwner], "👤 Setting owner"); + + const setOwnerMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "setOwner", + value: owner, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, setOwnerMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Sets devices ChannelSettings + */ + public async setChannel(channel: Protobuf.Channel.Channel): Promise { + this.log.debug( + Emitter[Emitter.SetChannel], + `📻 Setting Channel: ${channel.index}`, + ); + + const setChannelMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "setChannel", + value: channel, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, setChannelMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Triggers Device to enter DFU mode + */ + public async enterDfuMode(): Promise { + this.log.debug(Emitter[Emitter.EnterDfuMode], "🔌 Entering DFU mode"); + + const enterDfuModeRequest = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "enterDfuModeRequest", + value: true, + }, + }); + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, enterDfuModeRequest), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Sets static position of device + */ + public async setPosition( + positionMessage: Protobuf.Mesh.Position, + ): Promise { + return await this.sendPacket( + toBinary(Protobuf.Mesh.PositionSchema, positionMessage), + Protobuf.Portnums.PortNum.POSITION_APP, + "self", + ); + } + + /** + * Gets specified channel information from the radio + */ + public async getChannel(index: number): Promise { + this.log.debug( + Emitter[Emitter.GetChannel], + `📻 Requesting Channel: ${index}`, + ); + + const getChannelRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "getChannelRequest", + value: index + 1, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, getChannelRequestMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Gets devices config + */ + public async getConfig( + configType: Protobuf.Admin.AdminMessage_ConfigType, + ): Promise { + this.log.debug(Emitter[Emitter.GetConfig], "⚙️ Requesting config"); + + const getRadioRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "getConfigRequest", + value: configType, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, getRadioRequestMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Gets Module config + */ + public async getModuleConfig( + moduleConfigType: Protobuf.Admin.AdminMessage_ModuleConfigType, + ): Promise { + this.log.debug( + Emitter[Emitter.GetModuleConfig], + "⚙️ Requesting module config", + ); + + const getRadioRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "getModuleConfigRequest", + value: moduleConfigType, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, getRadioRequestMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** Gets devices Owner */ + public async getOwner(): Promise { + this.log.debug(Emitter[Emitter.GetOwner], "👤 Requesting owner"); + + const getOwnerRequestMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "getOwnerRequest", + value: true, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, getOwnerRequestMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Gets devices metadata + */ + public async getMetadata(nodeNum: number): Promise { + this.log.debug( + Emitter[Emitter.GetMetadata], + `🏷️ Requesting metadata from ${nodeNum}`, + ); + + const getDeviceMetricsRequestMessage = create( + Protobuf.Admin.AdminMessageSchema, + { + payloadVariant: { + case: "getDeviceMetadataRequest", + value: true, + }, + }, + ); + + return await this.sendPacket( + toBinary( + Protobuf.Admin.AdminMessageSchema, + getDeviceMetricsRequestMessage, + ), + Protobuf.Portnums.PortNum.ADMIN_APP, + nodeNum, + ChannelNumber.Admin, + ); + } + + /** + * Clears specific channel with the designated index + */ + public async clearChannel(index: number): Promise { + this.log.debug( + Emitter[Emitter.ClearChannel], + `📻 Clearing Channel ${index}`, + ); + + const channel = create(Protobuf.Channel.ChannelSchema, { + index, + role: Protobuf.Channel.Channel_Role.DISABLED, + }); + const setChannelMessage = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "setChannel", + value: channel, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, setChannelMessage), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + private async beginEditSettings(): Promise { + this.events.onPendingSettingsChange.dispatch(true); + + const beginEditSettings = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "beginEditSettings", + value: true, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, beginEditSettings), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + public async commitEditSettings(): Promise { + this.events.onPendingSettingsChange.dispatch(false); + + const commitEditSettings = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "commitEditSettings", + value: true, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, commitEditSettings), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Resets the internal NodeDB of the radio, usefull for removing old nodes + * that no longer exist. + */ + public async resetNodes(): Promise { + this.log.debug(Emitter[Emitter.ResetNodes], "📻 Resetting NodeDB"); + + const resetNodes = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "nodedbReset", + value: 1, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, resetNodes), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Removes a node from the internal NodeDB of the radio by node number + */ + public async removeNodeByNum(nodeNum: number): Promise { + this.log.debug( + Emitter[Emitter.RemoveNodeByNum], + `📻 Removing Node ${nodeNum} from NodeDB`, + ); + + const removeNodeByNum = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "removeByNodenum", + value: nodeNum, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, removeNodeByNum), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** Shuts down the current node after the specified amount of time has elapsed. */ + public async shutdown(time: number): Promise { + this.log.debug( + Emitter[Emitter.Shutdown], + `🔌 Shutting down ${time > 2 ? "now" : `in ${time} seconds`}`, + ); + + const shutdown = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "shutdownSeconds", + value: time, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, shutdown), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** Reboots the current node after the specified amount of time has elapsed. */ + public async reboot(time: number): Promise { + this.log.debug( + Emitter[Emitter.Reboot], + `🔌 Rebooting node ${time > 0 ? "now" : `in ${time} seconds`}`, + ); + + const reboot = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "rebootSeconds", + value: time, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, reboot), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Reboots the current node into OTA mode after the specified amount of time has elapsed. + */ + public async rebootOta(time: number): Promise { + this.log.debug( + Emitter[Emitter.RebootOta], + `🔌 Rebooting into OTA mode ${time > 0 ? "now" : `in ${time} seconds`}`, + ); + + const rebootOta = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "rebootOtaSeconds", + value: time, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, rebootOta), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Factory resets the current device + */ + public async factoryResetDevice(): Promise { + this.log.debug(Emitter[Emitter.FactoryReset], "♻️ Factory resetting device"); + + const factoryReset = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "factoryResetDevice", + value: 1, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, factoryReset), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Factory resets the current config + */ + public async factoryResetConfig(): Promise { + this.log.debug(Emitter[Emitter.FactoryReset], "♻️ Factory resetting config"); + + const factoryReset = create(Protobuf.Admin.AdminMessageSchema, { + payloadVariant: { + case: "factoryResetConfig", + value: 1, + }, + }); + + return await this.sendPacket( + toBinary(Protobuf.Admin.AdminMessageSchema, factoryReset), + Protobuf.Portnums.PortNum.ADMIN_APP, + "self", + ); + } + + /** + * Triggers the device configure process + */ + public configure(): Promise { + this.log.debug( + Emitter[Emitter.Configure], + "⚙️ Requesting device configuration", + ); + this.updateDeviceStatus(DeviceStatusEnum.DeviceConfiguring); + + const toRadio = create(Protobuf.Mesh.ToRadioSchema, { + payloadVariant: { + case: "wantConfigId", + value: this.configId, + }, + }); + + return this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio)); + } + + /** + * Serial connection requires a heartbeat ping to stay connected, otherwise times out after 15 minutes + */ + public heartbeat(): Promise { + this.log.debug(Emitter[Emitter.Ping], "❤️ Send heartbeat ping to radio"); + + const toRadio = create(Protobuf.Mesh.ToRadioSchema, { + payloadVariant: { + case: "heartbeat", + value: {}, + }, + }); + + return this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio)); + } + + /** + * Sends a trace route packet to the designated node + */ + public async traceRoute(destination: number): Promise { + const routeDiscovery = create(Protobuf.Mesh.RouteDiscoverySchema, { + route: [], + }); + + return await this.sendPacket( + toBinary(Protobuf.Mesh.RouteDiscoverySchema, routeDiscovery), + Protobuf.Portnums.PortNum.TRACEROUTE_APP, + destination, + ); + } + + /** + * Requests position from the designated node + */ + public async requestPosition(destination: number): Promise { + return await this.sendPacket( + new Uint8Array(), + Protobuf.Portnums.PortNum.POSITION_APP, + destination, + ); + } + + /** + * Updates the device status eliminating duplicate status events + */ + public updateDeviceStatus(status: DeviceStatusEnum): void { + if (status !== this.deviceStatus) { + this.events.onDeviceStatus.dispatch(status); + } + } + + /** + * Generates random packet identifier + * + * @returns {number} Random packet ID + */ + private generateRandId(): number { + const seed = crypto.getRandomValues(new Uint32Array(1)); + if (!seed[0]) { + throw new Error("Cannot generate CSPRN"); + } + + return Math.floor(seed[0] * 2 ** -32 * 1e9); + } + + /** Completes all Events */ + public complete(): void { + this.queue.clear(); + } + + /** Disconnects from the device **/ + public async disconnect(): Promise { + this.log.debug(Emitter[Emitter.Disconnect], "🔌 Disconnecting from device"); + this.complete(); + await this.transport.toDevice.close(); + } + + /** + * Gets called when a MeshPacket is received from device + */ + public handleMeshPacket(meshPacket: Protobuf.Mesh.MeshPacket): void { + this.events.onMeshPacket.dispatch(meshPacket); + if (meshPacket.from !== this.myNodeInfo.myNodeNum) { + /** + * TODO: this shouldn't be called unless the device interracts with the + * mesh, currently it does. + */ + this.events.onMeshHeartbeat.dispatch(new Date()); + } + + switch (meshPacket.payloadVariant.case) { + case "decoded": { + this.handleDecodedPacket(meshPacket.payloadVariant.value, meshPacket); + break; + } + + case "encrypted": { + this.log.debug( + Emitter[Emitter.HandleMeshPacket], + "🔐 Device received encrypted data packet, ignoring.", + ); + break; + } + + default: + throw new Error(`Unhandled case ${meshPacket.payloadVariant.case}`); + } + } + + private handleDecodedPacket( + dataPacket: Protobuf.Mesh.Data, + meshPacket: Protobuf.Mesh.MeshPacket, + ) { + let adminMessage: Protobuf.Admin.AdminMessage | undefined; + let routingPacket: Protobuf.Mesh.Routing | undefined; + + const packetMetadata: Omit, "data"> = { + id: meshPacket.id, + rxTime: new Date(meshPacket.rxTime * 1000), + type: meshPacket.to === Constants.broadcastNum ? "broadcast" : "direct", + from: meshPacket.from, + to: meshPacket.to, + channel: meshPacket.channel, + }; + + this.log.trace( + Emitter[Emitter.HandleMeshPacket], + `📦 Received ${Protobuf.Portnums.PortNum[dataPacket.portnum]} packet`, + ); + + switch (dataPacket.portnum) { + case Protobuf.Portnums.PortNum.TEXT_MESSAGE_APP: { + this.events.onMessagePacket.dispatch({ + ...packetMetadata, + data: new TextDecoder().decode(dataPacket.payload), + }); + break; + } + + case Protobuf.Portnums.PortNum.REMOTE_HARDWARE_APP: { + this.events.onRemoteHardwarePacket.dispatch({ + ...packetMetadata, + data: fromBinary( + Protobuf.RemoteHardware.HardwareMessageSchema, + dataPacket.payload, + ), + }); + break; + } + + case Protobuf.Portnums.PortNum.POSITION_APP: { + this.events.onPositionPacket.dispatch({ + ...packetMetadata, + data: fromBinary(Protobuf.Mesh.PositionSchema, dataPacket.payload), + }); + break; + } + + case Protobuf.Portnums.PortNum.NODEINFO_APP: { + this.events.onUserPacket.dispatch({ + ...packetMetadata, + data: fromBinary(Protobuf.Mesh.UserSchema, dataPacket.payload), + }); + break; + } + + case Protobuf.Portnums.PortNum.ROUTING_APP: { + routingPacket = fromBinary( + Protobuf.Mesh.RoutingSchema, + dataPacket.payload, + ); + + this.events.onRoutingPacket.dispatch({ + ...packetMetadata, + data: routingPacket, + }); + switch (routingPacket.variant.case) { + case "errorReason": { + if ( + routingPacket.variant.value === Protobuf.Mesh.Routing_Error.NONE + ) { + this.queue.processAck(dataPacket.requestId); + } else { + this.queue.processError({ + id: dataPacket.requestId, + error: routingPacket.variant.value, + }); + } + + break; + } + case "routeReply": { + break; + } + case "routeRequest": { + break; + } + + default: { + throw new Error(`Unhandled case ${routingPacket.variant.case}`); + } + } + break; + } + + case Protobuf.Portnums.PortNum.ADMIN_APP: { + adminMessage = fromBinary( + Protobuf.Admin.AdminMessageSchema, + dataPacket.payload, + ); + switch (adminMessage.payloadVariant.case) { + case "getChannelResponse": { + this.events.onChannelPacket.dispatch( + adminMessage.payloadVariant.value, + ); + break; + } + case "getOwnerResponse": { + this.events.onUserPacket.dispatch({ + ...packetMetadata, + data: adminMessage.payloadVariant.value, + }); + break; + } + case "getConfigResponse": { + this.events.onConfigPacket.dispatch( + adminMessage.payloadVariant.value, + ); + break; + } + case "getModuleConfigResponse": { + this.events.onModuleConfigPacket.dispatch( + adminMessage.payloadVariant.value, + ); + break; + } + case "getDeviceMetadataResponse": { + this.log.debug( + Emitter[Emitter.GetMetadata], + `🏷️ Received metadata packet from ${dataPacket.source}`, + ); + + this.events.onDeviceMetadataPacket.dispatch({ + ...packetMetadata, + data: adminMessage.payloadVariant.value, + }); + break; + } + case "getCannedMessageModuleMessagesResponse": { + this.log.debug( + Emitter[Emitter.GetMetadata], + `🥫 Received CannedMessage Module Messages response packet`, + ); + + this.events.onCannedMessageModulePacket.dispatch({ + ...packetMetadata, + data: adminMessage.payloadVariant.value, + }); + break; + } + default: { + this.log.error( + Emitter[Emitter.HandleMeshPacket], + `⚠️ Received unhandled AdminMessage, type ${ + adminMessage.payloadVariant.case ?? "undefined" + }`, + dataPacket.payload, + ); + } + } + break; + } + + case Protobuf.Portnums.PortNum.WAYPOINT_APP: { + this.events.onWaypointPacket.dispatch({ + ...packetMetadata, + data: fromBinary(Protobuf.Mesh.WaypointSchema, dataPacket.payload), + }); + break; + } + + case Protobuf.Portnums.PortNum.AUDIO_APP: { + this.events.onAudioPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.DETECTION_SENSOR_APP: { + this.events.onDetectionSensorPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.REPLY_APP: { + this.events.onPingPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, //TODO: decode + }); + break; + } + + case Protobuf.Portnums.PortNum.IP_TUNNEL_APP: { + this.events.onIpTunnelPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.PAXCOUNTER_APP: { + this.events.onPaxcounterPacket.dispatch({ + ...packetMetadata, + data: fromBinary( + Protobuf.PaxCount.PaxcountSchema, + dataPacket.payload, + ), + }); + break; + } + + case Protobuf.Portnums.PortNum.SERIAL_APP: { + this.events.onSerialPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.STORE_FORWARD_APP: { + this.events.onStoreForwardPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.RANGE_TEST_APP: { + this.events.onRangeTestPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.TELEMETRY_APP: { + this.events.onTelemetryPacket.dispatch({ + ...packetMetadata, + data: fromBinary( + Protobuf.Telemetry.TelemetrySchema, + dataPacket.payload, + ), + }); + break; + } + + case Protobuf.Portnums.PortNum.ZPS_APP: { + this.events.onZpsPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.SIMULATOR_APP: { + this.events.onSimulatorPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.TRACEROUTE_APP: { + this.events.onTraceRoutePacket.dispatch({ + ...packetMetadata, + data: fromBinary( + Protobuf.Mesh.RouteDiscoverySchema, + dataPacket.payload, + ), + }); + break; + } + + case Protobuf.Portnums.PortNum.NEIGHBORINFO_APP: { + this.events.onNeighborInfoPacket.dispatch({ + ...packetMetadata, + data: fromBinary( + Protobuf.Mesh.NeighborInfoSchema, + dataPacket.payload, + ), + }); + break; + } + + case Protobuf.Portnums.PortNum.ATAK_PLUGIN: { + this.events.onAtakPluginPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.MAP_REPORT_APP: { + this.events.onMapReportPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.PRIVATE_APP: { + this.events.onPrivatePacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + case Protobuf.Portnums.PortNum.ATAK_FORWARDER: { + this.events.onAtakForwarderPacket.dispatch({ + ...packetMetadata, + data: dataPacket.payload, + }); + break; + } + + default: + throw new Error(`Unhandled case ${dataPacket.portnum}`); + } + } } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 40d1dcb7..1130824d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,45 +1,45 @@ import type * as Protobuf from "@meshtastic/protobufs"; interface Packet { - type: "packet"; - data: Uint8Array; + type: "packet"; + data: Uint8Array; } interface DebugLog { - type: "debug"; - data: string; + type: "debug"; + data: string; } export type DeviceOutput = Packet | DebugLog; export interface Transport { - toDevice: WritableStream; - fromDevice: ReadableStream; + toDevice: WritableStream; + fromDevice: ReadableStream; } export interface QueueItem { - id: number; - data: Uint8Array; - sent: boolean; - added: Date; - promise: Promise; + id: number; + data: Uint8Array; + sent: boolean; + added: Date; + promise: Promise; } export interface HttpRetryConfig { - maxRetries: number; - initialDelayMs: number; - maxDelayMs: number; - backoffFactor: number; + maxRetries: number; + initialDelayMs: number; + maxDelayMs: number; + backoffFactor: number; } export enum DeviceStatusEnum { - DeviceRestarting = 1, - DeviceDisconnected = 2, - DeviceConnecting = 3, - DeviceReconnecting = 4, - DeviceConnected = 5, - DeviceConfiguring = 6, - DeviceConfigured = 7, + DeviceRestarting = 1, + DeviceDisconnected = 2, + DeviceConnecting = 3, + DeviceReconnecting = 4, + DeviceConnected = 5, + DeviceConfiguring = 6, + DeviceConfigured = 7, } export type LogEventPacket = LogEvent & { date: Date }; @@ -47,83 +47,83 @@ export type LogEventPacket = LogEvent & { date: Date }; export type PacketDestination = "broadcast" | "direct"; export interface PacketMetadata { - id: number; - rxTime: Date; - type: PacketDestination; - from: number; - to: number; - channel: ChannelNumber; - data: T; + id: number; + rxTime: Date; + type: PacketDestination; + from: number; + to: number; + channel: ChannelNumber; + data: T; } export enum EmitterScope { - MeshDevice = 1, - SerialConnection = 2, - NodeSerialConnection = 3, - BleConnection = 4, - HttpConnection = 5, + MeshDevice = 1, + SerialConnection = 2, + NodeSerialConnection = 3, + BleConnection = 4, + HttpConnection = 5, } export enum Emitter { - Constructor = 0, - SendText = 1, - SendWaypoint = 2, - SendPacket = 3, - SendRaw = 4, - SetConfig = 5, - SetModuleConfig = 6, - ConfirmSetConfig = 7, - SetOwner = 8, - SetChannel = 9, - ConfirmSetChannel = 10, - ClearChannel = 11, - GetChannel = 12, - GetAllChannels = 13, - GetConfig = 14, - GetModuleConfig = 15, - GetOwner = 16, - Configure = 17, - HandleFromRadio = 18, - HandleMeshPacket = 19, - Connect = 20, - Ping = 21, - ReadFromRadio = 22, - WriteToRadio = 23, - SetDebugMode = 24, - GetMetadata = 25, - ResetNodes = 26, - Shutdown = 27, - Reboot = 28, - RebootOta = 29, - FactoryReset = 30, - EnterDfuMode = 31, - RemoveNodeByNum = 32, - SetCannedMessages = 33, - Disconnect = 34, + Constructor = 0, + SendText = 1, + SendWaypoint = 2, + SendPacket = 3, + SendRaw = 4, + SetConfig = 5, + SetModuleConfig = 6, + ConfirmSetConfig = 7, + SetOwner = 8, + SetChannel = 9, + ConfirmSetChannel = 10, + ClearChannel = 11, + GetChannel = 12, + GetAllChannels = 13, + GetConfig = 14, + GetModuleConfig = 15, + GetOwner = 16, + Configure = 17, + HandleFromRadio = 18, + HandleMeshPacket = 19, + Connect = 20, + Ping = 21, + ReadFromRadio = 22, + WriteToRadio = 23, + SetDebugMode = 24, + GetMetadata = 25, + ResetNodes = 26, + Shutdown = 27, + Reboot = 28, + RebootOta = 29, + FactoryReset = 30, + EnterDfuMode = 31, + RemoveNodeByNum = 32, + SetCannedMessages = 33, + Disconnect = 34, } export interface LogEvent { - scope: EmitterScope; - emitter: Emitter; - message: string; - level: Protobuf.Mesh.LogRecord_Level; - packet?: Uint8Array; + scope: EmitterScope; + emitter: Emitter; + message: string; + level: Protobuf.Mesh.LogRecord_Level; + packet?: Uint8Array; } export enum ChannelNumber { - Primary = 0, - Channel1 = 1, - Channel2 = 2, - Channel3 = 3, - Channel4 = 4, - Channel5 = 5, - Channel6 = 6, - Admin = 7, + Primary = 0, + Channel1 = 1, + Channel2 = 2, + Channel3 = 3, + Channel4 = 4, + Channel5 = 5, + Channel6 = 6, + Admin = 7, } export type Destination = number | "self" | "broadcast"; export interface PacketError { - id: number; - error: Protobuf.Mesh.Routing_Error; + id: number; + error: Protobuf.Mesh.Routing_Error; } diff --git a/packages/core/src/utils/eventSystem.ts b/packages/core/src/utils/eventSystem.ts index 31ef2efc..f50ca035 100644 --- a/packages/core/src/utils/eventSystem.ts +++ b/packages/core/src/utils/eventSystem.ts @@ -4,378 +4,378 @@ import type { PacketMetadata } from "../types.ts"; import type * as Types from "../types.ts"; export class EventSystem { - /** - * Fires when a new FromRadio message has been received from the device - * - * @event onLogEvent - */ - public readonly onLogEvent: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new FromRadio message has been received from the device + * + * @event onLogEvent + */ + public readonly onLogEvent: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new FromRadio message has been received from the device - * - * @event onFromRadio - */ - public readonly onFromRadio: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new FromRadio message has been received from the device + * + * @event onFromRadio + */ + public readonly onFromRadio: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new FromRadio message containing a Data packet has been - * received from the device - * - * @event onMeshPacket - */ - public readonly onMeshPacket: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new FromRadio message containing a Data packet has been + * received from the device + * + * @event onMeshPacket + */ + public readonly onMeshPacket: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new MyNodeInfo message has been received from the device - * - * @event onMyNodeInfo - */ - public readonly onMyNodeInfo: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new MyNodeInfo message has been received from the device + * + * @event onMyNodeInfo + */ + public readonly onMyNodeInfo: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new MeshPacket message containing a NodeInfo packet has been - * received from device - * - * @event onNodeInfoPacket - */ - public readonly onNodeInfoPacket: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new MeshPacket message containing a NodeInfo packet has been + * received from device + * + * @event onNodeInfoPacket + */ + public readonly onNodeInfoPacket: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new Channel message is received - * - * @event onChannelPacket - */ - public readonly onChannelPacket: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new Channel message is received + * + * @event onChannelPacket + */ + public readonly onChannelPacket: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new Config message is received - * - * @event onConfigPacket - */ - public readonly onConfigPacket: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new Config message is received + * + * @event onConfigPacket + */ + public readonly onConfigPacket: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new ModuleConfig message is received - * - * @event onModuleConfigPacket - */ - public readonly onModuleConfigPacket: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new ModuleConfig message is received + * + * @event onModuleConfigPacket + */ + public readonly onModuleConfigPacket: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new MeshPacket message containing a ATAK packet has been - * received from device - * - * @event onAtakPacket - */ - public readonly onAtakPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a ATAK packet has been + * received from device + * + * @event onAtakPacket + */ + public readonly onAtakPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Text packet has been - * received from device - * - * @event onMessagePacket - */ - public readonly onMessagePacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Text packet has been + * received from device + * + * @event onMessagePacket + */ + public readonly onMessagePacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Remote Hardware packet has - * been received from device - * - * @event onRemoteHardwarePacket - */ - public readonly onRemoteHardwarePacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher< - PacketMetadata - >(); + /** + * Fires when a new MeshPacket message containing a Remote Hardware packet has + * been received from device + * + * @event onRemoteHardwarePacket + */ + public readonly onRemoteHardwarePacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher< + PacketMetadata + >(); - /** - * Fires when a new MeshPacket message containing a Position packet has been - * received from device - * - * @event onPositionPacket - */ - public readonly onPositionPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Position packet has been + * received from device + * + * @event onPositionPacket + */ + public readonly onPositionPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a User packet has been - * received from device - * - * @event onUserPacket - */ - public readonly onUserPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a User packet has been + * received from device + * + * @event onUserPacket + */ + public readonly onUserPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Routing packet has been - * received from device - * - * @event onRoutingPacket - */ - public readonly onRoutingPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Routing packet has been + * received from device + * + * @event onRoutingPacket + */ + public readonly onRoutingPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when the device receives a Metadata packet - * - * @event onDeviceMetadataPacket - */ - public readonly onDeviceMetadataPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when the device receives a Metadata packet + * + * @event onDeviceMetadataPacket + */ + public readonly onDeviceMetadataPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when the device receives a Canned Message Module message packet - * - * @event onCannedMessageModulePacket - */ - public readonly onCannedMessageModulePacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when the device receives a Canned Message Module message packet + * + * @event onCannedMessageModulePacket + */ + public readonly onCannedMessageModulePacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Waypoint packet has been - * received from device - * - * @event onWaypointPacket - */ - public readonly onWaypointPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Waypoint packet has been + * received from device + * + * @event onWaypointPacket + */ + public readonly onWaypointPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing an Audio packet has been - * received from device - * - * @event onAudioPacket - */ - public readonly onAudioPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing an Audio packet has been + * received from device + * + * @event onAudioPacket + */ + public readonly onAudioPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Detection Sensor packet has been - * received from device - * - * @event onDetectionSensorPacket - */ - public readonly onDetectionSensorPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Detection Sensor packet has been + * received from device + * + * @event onDetectionSensorPacket + */ + public readonly onDetectionSensorPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Ping packet has been - * received from device - * - * @event onPingPacket - */ - public readonly onPingPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Ping packet has been + * received from device + * + * @event onPingPacket + */ + public readonly onPingPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a IP Tunnel packet has been - * received from device - * - * @event onIpTunnelPacket - */ - public readonly onIpTunnelPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a IP Tunnel packet has been + * received from device + * + * @event onIpTunnelPacket + */ + public readonly onIpTunnelPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Paxcounter packet has been - * received from device - * - * @event onPaxcounterPacket - */ - public readonly onPaxcounterPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Paxcounter packet has been + * received from device + * + * @event onPaxcounterPacket + */ + public readonly onPaxcounterPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Serial packet has been - * received from device - * - * @event onSerialPacket - */ - public readonly onSerialPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Serial packet has been + * received from device + * + * @event onSerialPacket + */ + public readonly onSerialPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Store and Forward packet - * has been received from device - * - * @event onStoreForwardPacket - */ - public readonly onStoreForwardPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Store and Forward packet + * has been received from device + * + * @event onStoreForwardPacket + */ + public readonly onStoreForwardPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Store and Forward packet - * has been received from device - * - * @event onRangeTestPacket - */ - public readonly onRangeTestPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Store and Forward packet + * has been received from device + * + * @event onRangeTestPacket + */ + public readonly onRangeTestPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Telemetry packet has been - * received from device - * - * @event onTelemetryPacket - */ - public readonly onTelemetryPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Telemetry packet has been + * received from device + * + * @event onTelemetryPacket + */ + public readonly onTelemetryPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a ZPS packet has been - * received from device - * - * @event onZPSPacket - */ - public readonly onZpsPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a ZPS packet has been + * received from device + * + * @event onZPSPacket + */ + public readonly onZpsPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Simulator packet has been - * received from device - * - * @event onSimulatorPacket - */ - public readonly onSimulatorPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Simulator packet has been + * received from device + * + * @event onSimulatorPacket + */ + public readonly onSimulatorPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Trace Route packet has been - * received from device - * - * @event onTraceRoutePacket - */ - public readonly onTraceRoutePacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Trace Route packet has been + * received from device + * + * @event onTraceRoutePacket + */ + public readonly onTraceRoutePacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Neighbor Info packet has been - * received from device - * - * @event onNeighborInfoPacket - */ - public readonly onNeighborInfoPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Neighbor Info packet has been + * received from device + * + * @event onNeighborInfoPacket + */ + public readonly onNeighborInfoPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing an ATAK packet has been - * received from device - * - * @event onAtakPluginPacket - */ - public readonly onAtakPluginPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing an ATAK packet has been + * received from device + * + * @event onAtakPluginPacket + */ + public readonly onAtakPluginPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Map Report packet has been - * received from device - * - * @event onMapReportPacket - */ - public readonly onMapReportPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Map Report packet has been + * received from device + * + * @event onMapReportPacket + */ + public readonly onMapReportPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing a Private packet has been - * received from device - * - * @event onPrivatePacket - */ - public readonly onPrivatePacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing a Private packet has been + * received from device + * + * @event onPrivatePacket + */ + public readonly onPrivatePacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when a new MeshPacket message containing an ATAK Forwarder packet has been - * received from device - * - * @event onAtakForwarderPacket - */ - public readonly onAtakForwarderPacket: SimpleEventDispatcher< - PacketMetadata - > = new SimpleEventDispatcher>(); + /** + * Fires when a new MeshPacket message containing an ATAK Forwarder packet has been + * received from device + * + * @event onAtakForwarderPacket + */ + public readonly onAtakForwarderPacket: SimpleEventDispatcher< + PacketMetadata + > = new SimpleEventDispatcher>(); - /** - * Fires when the devices connection or configuration status changes - * - * @event onDeviceStatus - */ - public readonly onDeviceStatus: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when the devices connection or configuration status changes + * + * @event onDeviceStatus + */ + public readonly onDeviceStatus: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a new FromRadio message containing a LogRecord packet has been - * received from device - * - * @event onLogRecord - */ - public readonly onLogRecord: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a new FromRadio message containing a LogRecord packet has been + * received from device + * + * @event onLogRecord + */ + public readonly onLogRecord: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when the device receives a meshPacket, returns a timestamp - * - * @event onMeshHeartbeat - */ - public readonly onMeshHeartbeat: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when the device receives a meshPacket, returns a timestamp + * + * @event onMeshHeartbeat + */ + public readonly onMeshHeartbeat: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Outputs any debug log data (currently serial connections only) - * - * @event onDeviceDebugLog - */ - public readonly onDeviceDebugLog: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Outputs any debug log data (currently serial connections only) + * + * @event onDeviceDebugLog + */ + public readonly onDeviceDebugLog: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Outputs status of pending settings changes - * - * @event onpendingSettingsChange - */ - public readonly onPendingSettingsChange: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Outputs status of pending settings changes + * + * @event onpendingSettingsChange + */ + public readonly onPendingSettingsChange: SimpleEventDispatcher = + new SimpleEventDispatcher(); - /** - * Fires when a QueueStatus message is generated - * - * @event onQueueStatus - */ - public readonly onQueueStatus: SimpleEventDispatcher = - new SimpleEventDispatcher(); + /** + * Fires when a QueueStatus message is generated + * + * @event onQueueStatus + */ + public readonly onQueueStatus: SimpleEventDispatcher = + new SimpleEventDispatcher(); } diff --git a/packages/core/src/utils/queue.ts b/packages/core/src/utils/queue.ts index 1e24e82c..bd14d229 100644 --- a/packages/core/src/utils/queue.ts +++ b/packages/core/src/utils/queue.ts @@ -4,116 +4,116 @@ import { SimpleEventDispatcher } from "ste-simple-events"; import type { PacketError, QueueItem } from "../types.ts"; export class Queue { - private queue: QueueItem[] = []; - private lock = false; - private ackNotifier = new SimpleEventDispatcher(); - private errorNotifier = new SimpleEventDispatcher(); - private timeout: number; + private queue: QueueItem[] = []; + private lock = false; + private ackNotifier = new SimpleEventDispatcher(); + private errorNotifier = new SimpleEventDispatcher(); + private timeout: number; - constructor() { - this.timeout = 60000; - } + constructor() { + this.timeout = 60000; + } - public getState(): QueueItem[] { - return this.queue; - } + public getState(): QueueItem[] { + return this.queue; + } - public clear(): void { - this.queue = []; - } + public clear(): void { + this.queue = []; + } - public push(item: Omit): void { - const queueItem: QueueItem = { - ...item, - sent: false, - added: new Date(), - promise: new Promise((resolve, reject) => { - this.ackNotifier.subscribe((id) => { - if (item.id === id) { - this.remove(item.id); - resolve(id); - } - }); - this.errorNotifier.subscribe((e) => { - if (item.id === e.id) { - this.remove(item.id); - reject(e); - } - }); - setTimeout(() => { - if (this.queue.findIndex((qi) => qi.id === item.id) !== -1) { - this.remove(item.id); - const decoded = fromBinary(Protobuf.Mesh.ToRadioSchema, item.data); - console.warn( - `Packet ${item.id} of type ${decoded.payloadVariant.case} timed out`, - ); + public push(item: Omit): void { + const queueItem: QueueItem = { + ...item, + sent: false, + added: new Date(), + promise: new Promise((resolve, reject) => { + this.ackNotifier.subscribe((id) => { + if (item.id === id) { + this.remove(item.id); + resolve(id); + } + }); + this.errorNotifier.subscribe((e) => { + if (item.id === e.id) { + this.remove(item.id); + reject(e); + } + }); + setTimeout(() => { + if (this.queue.findIndex((qi) => qi.id === item.id) !== -1) { + this.remove(item.id); + const decoded = fromBinary(Protobuf.Mesh.ToRadioSchema, item.data); + console.warn( + `Packet ${item.id} of type ${decoded.payloadVariant.case} timed out`, + ); - reject({ - id: item.id, - error: Protobuf.Mesh.Routing_Error.TIMEOUT, - }); - } - }, this.timeout); - }), - }; - this.queue.push(queueItem); - } + reject({ + id: item.id, + error: Protobuf.Mesh.Routing_Error.TIMEOUT, + }); + } + }, this.timeout); + }), + }; + this.queue.push(queueItem); + } - public remove(id: number): void { - if (this.lock) { - setTimeout(() => this.remove(id), 100); - return; - } - this.queue = this.queue.filter((item) => item.id !== id); - } + public remove(id: number): void { + if (this.lock) { + setTimeout(() => this.remove(id), 100); + return; + } + this.queue = this.queue.filter((item) => item.id !== id); + } - public processAck(id: number): void { - this.ackNotifier.dispatch(id); - } + public processAck(id: number): void { + this.ackNotifier.dispatch(id); + } - public processError(e: PacketError): void { - console.error( - `Error received for packet ${e.id}: ${ - Protobuf.Mesh.Routing_Error[e.error] - }`, - ); - this.errorNotifier.dispatch(e); - } + public processError(e: PacketError): void { + console.error( + `Error received for packet ${e.id}: ${ + Protobuf.Mesh.Routing_Error[e.error] + }`, + ); + this.errorNotifier.dispatch(e); + } - public wait(id: number): Promise { - const queueItem = this.queue.find((qi) => qi.id === id); - if (!queueItem) { - throw new Error("Packet does not exist"); - } - return queueItem.promise; - } + public wait(id: number): Promise { + const queueItem = this.queue.find((qi) => qi.id === id); + if (!queueItem) { + throw new Error("Packet does not exist"); + } + return queueItem.promise; + } - public async processQueue( - outputStream: WritableStream, - ): Promise { - if (this.lock) { - return; - } + public async processQueue( + outputStream: WritableStream, + ): Promise { + if (this.lock) { + return; + } - this.lock = true; - const writer = outputStream.getWriter(); + this.lock = true; + const writer = outputStream.getWriter(); - try { - while (this.queue.filter((p) => !p.sent).length > 0) { - const item = this.queue.filter((p) => !p.sent)[0]; - if (item) { - await new Promise((resolve) => setTimeout(resolve, 200)); - try { - await writer.write(item.data); - item.sent = true; - } catch (error) { - console.error(`Error sending packet ${item.id}`, error); - } - } - } - } finally { - writer.releaseLock(); - this.lock = false; - } - } + try { + while (this.queue.filter((p) => !p.sent).length > 0) { + const item = this.queue.filter((p) => !p.sent)[0]; + if (item) { + await new Promise((resolve) => setTimeout(resolve, 200)); + try { + await writer.write(item.data); + item.sent = true; + } catch (error) { + console.error(`Error sending packet ${item.id}`, error); + } + } + } + } finally { + writer.releaseLock(); + this.lock = false; + } + } } diff --git a/packages/core/src/utils/transform/decodePacket.ts b/packages/core/src/utils/transform/decodePacket.ts index fa7d1611..00daf25e 100644 --- a/packages/core/src/utils/transform/decodePacket.ts +++ b/packages/core/src/utils/transform/decodePacket.ts @@ -4,219 +4,219 @@ import type { MeshDevice } from "../../../mod.ts"; import type { DeviceOutput } from "../../types.ts"; export const decodePacket = (device: MeshDevice) => - new WritableStream({ - write(chunk) { - switch (chunk.type) { - case "debug": { - break; - } - case "packet": { - const decodedMessage = fromBinary( - Protobuf.Mesh.FromRadioSchema, - chunk.data, - ); - device.events.onFromRadio.dispatch(decodedMessage); + new WritableStream({ + write(chunk) { + switch (chunk.type) { + case "debug": { + break; + } + case "packet": { + const decodedMessage = fromBinary( + Protobuf.Mesh.FromRadioSchema, + chunk.data, + ); + device.events.onFromRadio.dispatch(decodedMessage); - /** @todo Add map here when `all=true` gets fixed. */ - switch (decodedMessage.payloadVariant.case) { - case "packet": { - device.handleMeshPacket(decodedMessage.payloadVariant.value); - break; - } + /** @todo Add map here when `all=true` gets fixed. */ + switch (decodedMessage.payloadVariant.case) { + case "packet": { + device.handleMeshPacket(decodedMessage.payloadVariant.value); + break; + } - case "myInfo": { - device.events.onMyNodeInfo.dispatch( - decodedMessage.payloadVariant.value, - ); - device.log.info( - Types.Emitter[Types.Emitter.HandleFromRadio], - "📱 Received Node info for this device", - ); - break; - } + case "myInfo": { + device.events.onMyNodeInfo.dispatch( + decodedMessage.payloadVariant.value, + ); + device.log.info( + Types.Emitter[Types.Emitter.HandleFromRadio], + "📱 Received Node info for this device", + ); + break; + } - case "nodeInfo": { - device.log.info( - Types.Emitter[Types.Emitter.HandleFromRadio], - `📱 Received Node Info packet for node: ${decodedMessage.payloadVariant.value.num}`, - ); + case "nodeInfo": { + device.log.info( + Types.Emitter[Types.Emitter.HandleFromRadio], + `📱 Received Node Info packet for node: ${decodedMessage.payloadVariant.value.num}`, + ); - device.events.onNodeInfoPacket.dispatch( - decodedMessage.payloadVariant.value, - ); + device.events.onNodeInfoPacket.dispatch( + decodedMessage.payloadVariant.value, + ); - //TODO: HERE - if (decodedMessage.payloadVariant.value.position) { - device.events.onPositionPacket.dispatch({ - id: decodedMessage.id, - rxTime: new Date(), - from: decodedMessage.payloadVariant.value.num, - to: decodedMessage.payloadVariant.value.num, - type: "direct", - channel: Types.ChannelNumber.Primary, - data: decodedMessage.payloadVariant.value.position, - }); - } + //TODO: HERE + if (decodedMessage.payloadVariant.value.position) { + device.events.onPositionPacket.dispatch({ + id: decodedMessage.id, + rxTime: new Date(), + from: decodedMessage.payloadVariant.value.num, + to: decodedMessage.payloadVariant.value.num, + type: "direct", + channel: Types.ChannelNumber.Primary, + data: decodedMessage.payloadVariant.value.position, + }); + } - //TODO: HERE - if (decodedMessage.payloadVariant.value.user) { - device.events.onUserPacket.dispatch({ - id: decodedMessage.id, - rxTime: new Date(), - from: decodedMessage.payloadVariant.value.num, - to: decodedMessage.payloadVariant.value.num, - type: "direct", - channel: Types.ChannelNumber.Primary, - data: decodedMessage.payloadVariant.value.user, - }); - } - break; - } + //TODO: HERE + if (decodedMessage.payloadVariant.value.user) { + device.events.onUserPacket.dispatch({ + id: decodedMessage.id, + rxTime: new Date(), + from: decodedMessage.payloadVariant.value.num, + to: decodedMessage.payloadVariant.value.num, + type: "direct", + channel: Types.ChannelNumber.Primary, + data: decodedMessage.payloadVariant.value.user, + }); + } + break; + } - case "config": { - if (decodedMessage.payloadVariant.value.payloadVariant.case) { - device.log.trace( - Types.Emitter[Types.Emitter.HandleFromRadio], - `💾 Received Config packet of variant: ${decodedMessage.payloadVariant.value.payloadVariant.case}`, - ); - } else { - device.log.warn( - Types.Emitter[Types.Emitter.HandleFromRadio], - `⚠️ Received Config packet of variant: ${"UNK"}`, - ); - } + case "config": { + if (decodedMessage.payloadVariant.value.payloadVariant.case) { + device.log.trace( + Types.Emitter[Types.Emitter.HandleFromRadio], + `💾 Received Config packet of variant: ${decodedMessage.payloadVariant.value.payloadVariant.case}`, + ); + } else { + device.log.warn( + Types.Emitter[Types.Emitter.HandleFromRadio], + `⚠️ Received Config packet of variant: ${"UNK"}`, + ); + } - device.events.onConfigPacket.dispatch( - decodedMessage.payloadVariant.value, - ); - break; - } + device.events.onConfigPacket.dispatch( + decodedMessage.payloadVariant.value, + ); + break; + } - case "logRecord": { - device.log.trace( - Types.Emitter[Types.Emitter.HandleFromRadio], - "Received onLogRecord", - ); - device.events.onLogRecord.dispatch( - decodedMessage.payloadVariant.value, - ); - break; - } + case "logRecord": { + device.log.trace( + Types.Emitter[Types.Emitter.HandleFromRadio], + "Received onLogRecord", + ); + device.events.onLogRecord.dispatch( + decodedMessage.payloadVariant.value, + ); + break; + } - case "configCompleteId": { - if (decodedMessage.payloadVariant.value !== device.configId) { - device.log.error( - Types.Emitter[Types.Emitter.HandleFromRadio], - `❌ Invalid config id received from device, expected ${device.configId} but received ${decodedMessage.payloadVariant.value}`, - ); - } + case "configCompleteId": { + if (decodedMessage.payloadVariant.value !== device.configId) { + device.log.error( + Types.Emitter[Types.Emitter.HandleFromRadio], + `❌ Invalid config id received from device, expected ${device.configId} but received ${decodedMessage.payloadVariant.value}`, + ); + } - device.log.info( - Types.Emitter[Types.Emitter.HandleFromRadio], - `⚙️ Valid config id received from device: ${device.configId}`, - ); + device.log.info( + Types.Emitter[Types.Emitter.HandleFromRadio], + `⚙️ Valid config id received from device: ${device.configId}`, + ); - device.updateDeviceStatus( - Types.DeviceStatusEnum.DeviceConfigured, - ); - break; - } + device.updateDeviceStatus( + Types.DeviceStatusEnum.DeviceConfigured, + ); + break; + } - case "rebooted": { - device.configure().catch(() => { - // TODO: FIX, workaround for `wantConfigId` not getting acks. - }); - break; - } + case "rebooted": { + device.configure().catch(() => { + // TODO: FIX, workaround for `wantConfigId` not getting acks. + }); + break; + } - case "moduleConfig": { - if (decodedMessage.payloadVariant.value.payloadVariant.case) { - device.log.trace( - Types.Emitter[Types.Emitter.HandleFromRadio], - `💾 Received Module Config packet of variant: ${decodedMessage.payloadVariant.value.payloadVariant.case}`, - ); - } else { - device.log.warn( - Types.Emitter[Types.Emitter.HandleFromRadio], - "⚠️ Received Module Config packet of variant: UNK", - ); - } + case "moduleConfig": { + if (decodedMessage.payloadVariant.value.payloadVariant.case) { + device.log.trace( + Types.Emitter[Types.Emitter.HandleFromRadio], + `💾 Received Module Config packet of variant: ${decodedMessage.payloadVariant.value.payloadVariant.case}`, + ); + } else { + device.log.warn( + Types.Emitter[Types.Emitter.HandleFromRadio], + "⚠️ Received Module Config packet of variant: UNK", + ); + } - device.events.onModuleConfigPacket.dispatch( - decodedMessage.payloadVariant.value, - ); - break; - } + device.events.onModuleConfigPacket.dispatch( + decodedMessage.payloadVariant.value, + ); + break; + } - case "channel": { - device.log.trace( - Types.Emitter[Types.Emitter.HandleFromRadio], - `🔐 Received Channel: ${decodedMessage.payloadVariant.value.index}`, - ); + case "channel": { + device.log.trace( + Types.Emitter[Types.Emitter.HandleFromRadio], + `🔐 Received Channel: ${decodedMessage.payloadVariant.value.index}`, + ); - device.events.onChannelPacket.dispatch( - decodedMessage.payloadVariant.value, - ); - break; - } + device.events.onChannelPacket.dispatch( + decodedMessage.payloadVariant.value, + ); + break; + } - case "queueStatus": { - device.log.trace( - Types.Emitter[Types.Emitter.HandleFromRadio], - `🚧 Received Queue Status: ${decodedMessage.payloadVariant.value}`, - ); + case "queueStatus": { + device.log.trace( + Types.Emitter[Types.Emitter.HandleFromRadio], + `🚧 Received Queue Status: ${decodedMessage.payloadVariant.value}`, + ); - device.events.onQueueStatus.dispatch( - decodedMessage.payloadVariant.value, - ); - break; - } + device.events.onQueueStatus.dispatch( + decodedMessage.payloadVariant.value, + ); + break; + } - case "xmodemPacket": { - device.xModem.handlePacket(decodedMessage.payloadVariant.value); - break; - } + case "xmodemPacket": { + device.xModem.handlePacket(decodedMessage.payloadVariant.value); + break; + } - case "metadata": { - if ( - Number.parseFloat( - decodedMessage.payloadVariant.value.firmwareVersion, - ) < Constants.minFwVer - ) { - device.log.fatal( - Types.Emitter[Types.Emitter.HandleFromRadio], - `Device firmware outdated. Min supported: ${Constants.minFwVer} got : ${decodedMessage.payloadVariant.value.firmwareVersion}`, - ); - } - device.log.debug( - Types.Emitter[Types.Emitter.GetMetadata], - "🏷️ Received metadata packet", - ); + case "metadata": { + if ( + Number.parseFloat( + decodedMessage.payloadVariant.value.firmwareVersion, + ) < Constants.minFwVer + ) { + device.log.fatal( + Types.Emitter[Types.Emitter.HandleFromRadio], + `Device firmware outdated. Min supported: ${Constants.minFwVer} got : ${decodedMessage.payloadVariant.value.firmwareVersion}`, + ); + } + device.log.debug( + Types.Emitter[Types.Emitter.GetMetadata], + "🏷️ Received metadata packet", + ); - device.events.onDeviceMetadataPacket.dispatch({ - id: decodedMessage.id, - rxTime: new Date(), - from: 0, - to: 0, - type: "direct", - channel: Types.ChannelNumber.Primary, - data: decodedMessage.payloadVariant.value, - }); - break; - } + device.events.onDeviceMetadataPacket.dispatch({ + id: decodedMessage.id, + rxTime: new Date(), + from: 0, + to: 0, + type: "direct", + channel: Types.ChannelNumber.Primary, + data: decodedMessage.payloadVariant.value, + }); + break; + } - case "mqttClientProxyMessage": { - break; - } + case "mqttClientProxyMessage": { + break; + } - default: { - device.log.warn( - Types.Emitter[Types.Emitter.HandleFromRadio], - `⚠️ Unhandled payload variant: ${decodedMessage.payloadVariant.case}`, - ); - } - } - } - } - }, - }); + default: { + device.log.warn( + Types.Emitter[Types.Emitter.HandleFromRadio], + `⚠️ Unhandled payload variant: ${decodedMessage.payloadVariant.case}`, + ); + } + } + } + } + }, + }); diff --git a/packages/core/src/utils/transform/fromDevice.ts b/packages/core/src/utils/transform/fromDevice.ts index e9e6e0dc..832d7356 100644 --- a/packages/core/src/utils/transform/fromDevice.ts +++ b/packages/core/src/utils/transform/fromDevice.ts @@ -1,71 +1,71 @@ import type { DeviceOutput } from "../../types.ts"; export const fromDeviceStream: () => TransformStream = - ( - // onReleaseEvent: SimpleEventDispatcher, - ) => { - let byteBuffer = new Uint8Array([]); - const textDecoder = new TextDecoder(); - return new TransformStream({ - transform(chunk: Uint8Array, controller): void { - // onReleaseEvent.subscribe(() => { - // controller.terminate(); - // }); - byteBuffer = new Uint8Array([...byteBuffer, ...chunk]); - let processingExhausted = false; - while (byteBuffer.length !== 0 && !processingExhausted) { - const framingIndex = byteBuffer.findIndex((byte) => byte === 0x94); - const framingByte2 = byteBuffer[framingIndex + 1]; - if (framingByte2 === 0xc3) { - if (byteBuffer.subarray(0, framingIndex).length) { - controller.enqueue({ - type: "debug", - data: textDecoder.decode(byteBuffer.subarray(0, framingIndex)), - }); - byteBuffer = byteBuffer.subarray(framingIndex); - } + ( + // onReleaseEvent: SimpleEventDispatcher, + ) => { + let byteBuffer = new Uint8Array([]); + const textDecoder = new TextDecoder(); + return new TransformStream({ + transform(chunk: Uint8Array, controller): void { + // onReleaseEvent.subscribe(() => { + // controller.terminate(); + // }); + byteBuffer = new Uint8Array([...byteBuffer, ...chunk]); + let processingExhausted = false; + while (byteBuffer.length !== 0 && !processingExhausted) { + const framingIndex = byteBuffer.findIndex((byte) => byte === 0x94); + const framingByte2 = byteBuffer[framingIndex + 1]; + if (framingByte2 === 0xc3) { + if (byteBuffer.subarray(0, framingIndex).length) { + controller.enqueue({ + type: "debug", + data: textDecoder.decode(byteBuffer.subarray(0, framingIndex)), + }); + byteBuffer = byteBuffer.subarray(framingIndex); + } - const msb = byteBuffer[2]; - const lsb = byteBuffer[3]; + const msb = byteBuffer[2]; + const lsb = byteBuffer[3]; - if ( - msb !== undefined && - lsb !== undefined && - byteBuffer.length >= 4 + (msb << 8) + lsb - ) { - const packet = byteBuffer.subarray(4, 4 + (msb << 8) + lsb); + if ( + msb !== undefined && + lsb !== undefined && + byteBuffer.length >= 4 + (msb << 8) + lsb + ) { + const packet = byteBuffer.subarray(4, 4 + (msb << 8) + lsb); - const malformedDetectorIndex = packet.findIndex( - (byte) => byte === 0x94, - ); - if ( - malformedDetectorIndex !== -1 && - packet[malformedDetectorIndex + 1] === 0xc3 - ) { - console.warn( - `⚠️ Malformed packet found, discarding: ${byteBuffer - .subarray(0, malformedDetectorIndex - 1) - .toString()}`, - ); + const malformedDetectorIndex = packet.findIndex( + (byte) => byte === 0x94, + ); + if ( + malformedDetectorIndex !== -1 && + packet[malformedDetectorIndex + 1] === 0xc3 + ) { + console.warn( + `⚠️ Malformed packet found, discarding: ${byteBuffer + .subarray(0, malformedDetectorIndex - 1) + .toString()}`, + ); - byteBuffer = byteBuffer.subarray(malformedDetectorIndex); - } else { - byteBuffer = byteBuffer.subarray(3 + (msb << 8) + lsb + 1); + byteBuffer = byteBuffer.subarray(malformedDetectorIndex); + } else { + byteBuffer = byteBuffer.subarray(3 + (msb << 8) + lsb + 1); - controller.enqueue({ - type: "packet", - data: packet, - }); - } - } else { - /** Only partioal message in buffer, wait for the rest */ - processingExhausted = true; - } - } else { - /** Message not complete, only 1 byte in buffer */ - processingExhausted = true; - } - } - }, - }); - }; + controller.enqueue({ + type: "packet", + data: packet, + }); + } + } else { + /** Only partioal message in buffer, wait for the rest */ + processingExhausted = true; + } + } else { + /** Message not complete, only 1 byte in buffer */ + processingExhausted = true; + } + } + }, + }); + }; diff --git a/packages/core/src/utils/transform/toDevice.ts b/packages/core/src/utils/transform/toDevice.ts index a453a0ed..05350413 100644 --- a/packages/core/src/utils/transform/toDevice.ts +++ b/packages/core/src/utils/transform/toDevice.ts @@ -2,15 +2,15 @@ * Pads packets with appropriate framing information before writing to the output stream. */ export const toDeviceStream: TransformStream = - new TransformStream({ - transform(chunk: Uint8Array, controller): void { - const bufLen = chunk.length; - const header = new Uint8Array([ - 0x94, - 0xc3, - (bufLen >> 8) & 0xff, - bufLen & 0xff, - ]); - controller.enqueue(new Uint8Array([...header, ...chunk])); - }, - }); + new TransformStream({ + transform(chunk: Uint8Array, controller): void { + const bufLen = chunk.length; + const header = new Uint8Array([ + 0x94, + 0xc3, + (bufLen >> 8) & 0xff, + bufLen & 0xff, + ]); + controller.enqueue(new Uint8Array([...header, ...chunk])); + }, + }); diff --git a/packages/core/src/utils/xmodem.ts b/packages/core/src/utils/xmodem.ts index 16c63672..201de92f 100644 --- a/packages/core/src/utils/xmodem.ts +++ b/packages/core/src/utils/xmodem.ts @@ -6,130 +6,130 @@ import crc16ccitt from "crc/calculators/crc16ccitt"; type XmodemProps = (toRadio: Uint8Array, id?: number) => Promise; export class Xmodem { - private sendRaw: XmodemProps; - private rxBuffer: Uint8Array[]; - private txBuffer: Uint8Array[]; - private textEncoder: TextEncoder; - private counter: number; + private sendRaw: XmodemProps; + private rxBuffer: Uint8Array[]; + private txBuffer: Uint8Array[]; + private textEncoder: TextEncoder; + private counter: number; - constructor(sendRaw: XmodemProps) { - this.sendRaw = sendRaw; - this.rxBuffer = []; - this.txBuffer = []; - this.textEncoder = new TextEncoder(); - this.counter = 0; - } + constructor(sendRaw: XmodemProps) { + this.sendRaw = sendRaw; + this.rxBuffer = []; + this.txBuffer = []; + this.textEncoder = new TextEncoder(); + this.counter = 0; + } - async downloadFile(filename: string): Promise { - return await this.sendCommand( - Protobuf.Xmodem.XModem_Control.STX, - this.textEncoder.encode(filename), - 0, - ); - } + async downloadFile(filename: string): Promise { + return await this.sendCommand( + Protobuf.Xmodem.XModem_Control.STX, + this.textEncoder.encode(filename), + 0, + ); + } - async uploadFile(filename: string, data: Uint8Array): Promise { - for (let i = 0; i < data.length; i += 128) { - this.txBuffer.push(data.slice(i, i + 128)); - } + async uploadFile(filename: string, data: Uint8Array): Promise { + for (let i = 0; i < data.length; i += 128) { + this.txBuffer.push(data.slice(i, i + 128)); + } - return await this.sendCommand( - Protobuf.Xmodem.XModem_Control.SOH, - this.textEncoder.encode(filename), - 0, - ); - } + return await this.sendCommand( + Protobuf.Xmodem.XModem_Control.SOH, + this.textEncoder.encode(filename), + 0, + ); + } - async sendCommand( - command: Protobuf.Xmodem.XModem_Control, - buffer?: Uint8Array, - sequence?: number, - crc16?: number, - ): Promise { - const toRadio = create(Protobuf.Mesh.ToRadioSchema, { - payloadVariant: { - case: "xmodemPacket", - value: { - buffer, - control: command, - seq: sequence, - crc16: crc16, - }, - }, - }); - return await this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio)); - } + async sendCommand( + command: Protobuf.Xmodem.XModem_Control, + buffer?: Uint8Array, + sequence?: number, + crc16?: number, + ): Promise { + const toRadio = create(Protobuf.Mesh.ToRadioSchema, { + payloadVariant: { + case: "xmodemPacket", + value: { + buffer, + control: command, + seq: sequence, + crc16: crc16, + }, + }, + }); + return await this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio)); + } - async handlePacket(packet: Protobuf.Xmodem.XModem): Promise { - await new Promise((resolve) => setTimeout(resolve, 100)); + async handlePacket(packet: Protobuf.Xmodem.XModem): Promise { + await new Promise((resolve) => setTimeout(resolve, 100)); - switch (packet.control) { - case Protobuf.Xmodem.XModem_Control.NUL: { - // nothing - break; - } - case Protobuf.Xmodem.XModem_Control.SOH: { - this.counter = packet.seq; - if (this.validateCrc16(packet)) { - this.rxBuffer[this.counter] = packet.buffer; - return this.sendCommand(Protobuf.Xmodem.XModem_Control.ACK); - } - return await this.sendCommand( - Protobuf.Xmodem.XModem_Control.NAK, - undefined, - packet.seq, - ); - } - case Protobuf.Xmodem.XModem_Control.STX: { - break; - } - case Protobuf.Xmodem.XModem_Control.EOT: { - // end of transmission - break; - } - case Protobuf.Xmodem.XModem_Control.ACK: { - this.counter++; - if (this.txBuffer[this.counter - 1]) { - return this.sendCommand( - Protobuf.Xmodem.XModem_Control.SOH, - this.txBuffer[this.counter - 1], - this.counter, - crc16ccitt(this.txBuffer[this.counter - 1] ?? new Uint8Array()), - ); - } - if (this.counter === this.txBuffer.length + 1) { - return this.sendCommand(Protobuf.Xmodem.XModem_Control.EOT); - } - this.clear(); - break; - } - case Protobuf.Xmodem.XModem_Control.NAK: { - return this.sendCommand( - Protobuf.Xmodem.XModem_Control.SOH, - this.txBuffer[this.counter], - this.counter, - crc16ccitt(this.txBuffer[this.counter - 1] ?? new Uint8Array()), - ); - } - case Protobuf.Xmodem.XModem_Control.CAN: { - this.clear(); - break; - } - case Protobuf.Xmodem.XModem_Control.CTRLZ: { - break; - } - } + switch (packet.control) { + case Protobuf.Xmodem.XModem_Control.NUL: { + // nothing + break; + } + case Protobuf.Xmodem.XModem_Control.SOH: { + this.counter = packet.seq; + if (this.validateCrc16(packet)) { + this.rxBuffer[this.counter] = packet.buffer; + return this.sendCommand(Protobuf.Xmodem.XModem_Control.ACK); + } + return await this.sendCommand( + Protobuf.Xmodem.XModem_Control.NAK, + undefined, + packet.seq, + ); + } + case Protobuf.Xmodem.XModem_Control.STX: { + break; + } + case Protobuf.Xmodem.XModem_Control.EOT: { + // end of transmission + break; + } + case Protobuf.Xmodem.XModem_Control.ACK: { + this.counter++; + if (this.txBuffer[this.counter - 1]) { + return this.sendCommand( + Protobuf.Xmodem.XModem_Control.SOH, + this.txBuffer[this.counter - 1], + this.counter, + crc16ccitt(this.txBuffer[this.counter - 1] ?? new Uint8Array()), + ); + } + if (this.counter === this.txBuffer.length + 1) { + return this.sendCommand(Protobuf.Xmodem.XModem_Control.EOT); + } + this.clear(); + break; + } + case Protobuf.Xmodem.XModem_Control.NAK: { + return this.sendCommand( + Protobuf.Xmodem.XModem_Control.SOH, + this.txBuffer[this.counter], + this.counter, + crc16ccitt(this.txBuffer[this.counter - 1] ?? new Uint8Array()), + ); + } + case Protobuf.Xmodem.XModem_Control.CAN: { + this.clear(); + break; + } + case Protobuf.Xmodem.XModem_Control.CTRLZ: { + break; + } + } - return Promise.resolve(0); - } + return Promise.resolve(0); + } - validateCrc16(packet: Protobuf.Xmodem.XModem): boolean { - return crc16ccitt(packet.buffer) === packet.crc16; - } + validateCrc16(packet: Protobuf.Xmodem.XModem): boolean { + return crc16ccitt(packet.buffer) === packet.crc16; + } - clear() { - this.counter = 0; - this.rxBuffer = []; - this.txBuffer = []; - } + clear() { + this.counter = 0; + this.rxBuffer = []; + this.txBuffer = []; + } } diff --git a/packages/transport-deno/deno.json b/packages/transport-deno/package.json similarity index 100% rename from packages/transport-deno/deno.json rename to packages/transport-deno/package.json diff --git a/packages/transport-deno/src/transport.ts b/packages/transport-deno/src/transport.ts index c778cd30..c5ef2bb7 100644 --- a/packages/transport-deno/src/transport.ts +++ b/packages/transport-deno/src/transport.ts @@ -2,31 +2,31 @@ import { Utils } from "@meshtastic/core"; import type { Types } from "@meshtastic/core"; export class TransportDeno implements Types.Transport { - private _toDevice: WritableStream; - private _fromDevice: ReadableStream; + private _toDevice: WritableStream; + private _fromDevice: ReadableStream; - public static async create(hostname: string): Promise { - const connection = await Deno.connect({ - hostname, - port: 4403, - }); - return new TransportDeno(connection); - } + public static async create(hostname: string): Promise { + const connection = await Deno.connect({ + hostname, + port: 4403, + }); + return new TransportDeno(connection); + } - constructor(connection: Deno.Conn) { - Utils.toDeviceStream.readable.pipeTo(connection.writable); + constructor(connection: Deno.Conn) { + Utils.toDeviceStream.readable.pipeTo(connection.writable); - this._toDevice = Utils.toDeviceStream.writable; - this._fromDevice = connection.readable.pipeThrough( - Utils.fromDeviceStream(), - ); - } + this._toDevice = Utils.toDeviceStream.writable; + this._fromDevice = connection.readable.pipeThrough( + Utils.fromDeviceStream(), + ); + } - get toDevice(): WritableStream { - return this._toDevice; - } + get toDevice(): WritableStream { + return this._toDevice; + } - get fromDevice(): ReadableStream { - return this._fromDevice; - } + get fromDevice(): ReadableStream { + return this._fromDevice; + } } diff --git a/packages/transport-http/jsr.json b/packages/transport-http/jsr.json new file mode 100644 index 00000000..b590bbae --- /dev/null +++ b/packages/transport-http/jsr.json @@ -0,0 +1,5 @@ +{ + "name": "@meshtastic/transport-http", + "version": "0.2.2", + "exports": "./mod.ts" +} \ No newline at end of file diff --git a/packages/transport-http/deno.json b/packages/transport-http/package.json similarity index 57% rename from packages/transport-http/deno.json rename to packages/transport-http/package.json index ff0419e0..cc7d76f4 100644 --- a/packages/transport-http/deno.json +++ b/packages/transport-http/package.json @@ -1,8 +1,9 @@ { "name": "@meshtastic/transport-http", - "version": "0.2.1", + "version": "0.2.2", "description": "A transport layer for Meshtastic applications using HTTP.", - "exports": { - ".": "./mod.ts" + "exports": {".": "./mod.ts"}, + "tasks": { + "build": "deno build" } -} +} \ No newline at end of file diff --git a/packages/transport-http/src/transport.ts b/packages/transport-http/src/transport.ts index ff7825ee..03f578aa 100644 --- a/packages/transport-http/src/transport.ts +++ b/packages/transport-http/src/transport.ts @@ -1,89 +1,89 @@ import type { Types } from "@meshtastic/core"; export class TransportHTTP implements Types.Transport { - private _toDevice: WritableStream; - private _fromDevice: ReadableStream; - private url: string; - private receiveBatchRequests: boolean; - private fetchInterval: number; + private _toDevice: WritableStream; + private _fromDevice: ReadableStream; + private url: string; + private receiveBatchRequests: boolean; + private fetchInterval: number; - public static async create( - address: string, - tls?: boolean, - ): Promise { - const connectionUrl = `${tls ? "https" : "http"}://${address}`; - await fetch(`${connectionUrl}/json/report`); - await Promise.resolve(); - return new TransportHTTP(connectionUrl); - } + public static async create( + address: string, + tls?: boolean, + ): Promise { + const connectionUrl = `${tls ? "https" : "http"}://${address}`; + await fetch(`${connectionUrl}/json/report`); + await Promise.resolve(); + return new TransportHTTP(connectionUrl); + } - constructor(url: string) { - this.url = url; - this.receiveBatchRequests = false; - this.fetchInterval = 3000; + constructor(url: string) { + this.url = url; + this.receiveBatchRequests = false; + this.fetchInterval = 3000; - this._toDevice = new WritableStream({ - write: async (chunk) => { - await this.writeToRadio(chunk); - }, - }); + this._toDevice = new WritableStream({ + write: async (chunk) => { + await this.writeToRadio(chunk); + }, + }); - let controller: ReadableStreamDefaultController; + let controller: ReadableStreamDefaultController; - this._fromDevice = new ReadableStream({ - start: (ctrl) => { - controller = ctrl; - }, - }); + this._fromDevice = new ReadableStream({ + start: (ctrl) => { + controller = ctrl; + }, + }); - setInterval(async () => { - await this.readFromRadio(controller); - }, this.fetchInterval); - } + setInterval(async () => { + await this.readFromRadio(controller); + }, this.fetchInterval); + } - private async readFromRadio( - controller: ReadableStreamDefaultController, - ): Promise { - let readBuffer = new ArrayBuffer(1); - while (readBuffer.byteLength > 0) { - const response = await fetch( - `${this.url}/api/v1/fromradio?all=${ - this.receiveBatchRequests ? "true" : "false" - }`, - { - method: "GET", - headers: { - Accept: "application/x-protobuf", - }, - }, - ); + private async readFromRadio( + controller: ReadableStreamDefaultController, + ): Promise { + let readBuffer = new ArrayBuffer(1); + while (readBuffer.byteLength > 0) { + const response = await fetch( + `${this.url}/api/v1/fromradio?all=${ + this.receiveBatchRequests ? "true" : "false" + }`, + { + method: "GET", + headers: { + Accept: "application/x-protobuf", + }, + }, + ); - readBuffer = await response.arrayBuffer(); + readBuffer = await response.arrayBuffer(); - if (readBuffer.byteLength > 0) { - controller.enqueue({ - type: "packet", - data: new Uint8Array(readBuffer), - }); - } - } - } + if (readBuffer.byteLength > 0) { + controller.enqueue({ + type: "packet", + data: new Uint8Array(readBuffer), + }); + } + } + } - private async writeToRadio(data: Uint8Array): Promise { - await fetch(`${this.url}/api/v1/toradio`, { - method: "PUT", - headers: { - "Content-Type": "application/x-protobuf", - }, - body: data, - }); - } + private async writeToRadio(data: Uint8Array): Promise { + await fetch(`${this.url}/api/v1/toradio`, { + method: "PUT", + headers: { + "Content-Type": "application/x-protobuf", + }, + body: data, + }); + } - get toDevice(): WritableStream { - return this._toDevice; - } + get toDevice(): WritableStream { + return this._toDevice; + } - get fromDevice(): ReadableStream { - return this._fromDevice; - } + get fromDevice(): ReadableStream { + return this._fromDevice; + } } diff --git a/packages/transport-node/deno.json b/packages/transport-node/package.json similarity index 100% rename from packages/transport-node/deno.json rename to packages/transport-node/package.json diff --git a/packages/transport-node/src/transport.ts b/packages/transport-node/src/transport.ts index 29393f1e..6d4f2722 100644 --- a/packages/transport-node/src/transport.ts +++ b/packages/transport-node/src/transport.ts @@ -4,73 +4,73 @@ import { Utils } from "@meshtastic/core"; import type { Types } from "@meshtastic/core"; export class TransportNode implements Types.Transport { - private readonly _toDevice: WritableStream; - private readonly _fromDevice: ReadableStream; + private readonly _toDevice: WritableStream; + private readonly _fromDevice: ReadableStream; - /** - * Creates and connects a new TransportNode instance. - * @param hostname - The IP address or hostname of the Meshtastic device. - * @param port - The port number for the TCP connection (defaults to 4403). - * @returns A promise that resolves with a connected TransportNode instance. - */ - public static create(hostname: string, port = 4403): Promise { - return new Promise((resolve, reject) => { - const socket = new Socket(); + /** + * Creates and connects a new TransportNode instance. + * @param hostname - The IP address or hostname of the Meshtastic device. + * @param port - The port number for the TCP connection (defaults to 4403). + * @returns A promise that resolves with a connected TransportNode instance. + */ + public static create(hostname: string, port = 4403): Promise { + return new Promise((resolve, reject) => { + const socket = new Socket(); - const onError = (err: Error) => { - socket.destroy(); - reject(err); - }; + const onError = (err: Error) => { + socket.destroy(); + reject(err); + }; - socket.once("error", onError); + socket.once("error", onError); - socket.connect(port, hostname, () => { - socket.removeListener("error", onError); - resolve(new TransportNode(socket)); - }); - }); - } + socket.connect(port, hostname, () => { + socket.removeListener("error", onError); + resolve(new TransportNode(socket)); + }); + }); + } - /** - * Constructs a new TransportNode. - * @param connection - An active Node.js net.Socket connection. - */ - constructor(connection: Socket) { - connection.on("error", (err) => { - console.error("Socket connection error:", err); - }); + /** + * Constructs a new TransportNode. + * @param connection - An active Node.js net.Socket connection. + */ + constructor(connection: Socket) { + connection.on("error", (err) => { + console.error("Socket connection error:", err); + }); - const fromDeviceSource = Readable.toWeb( - connection, - ) as ReadableStream; - this._fromDevice = fromDeviceSource.pipeThrough(Utils.fromDeviceStream()); + const fromDeviceSource = Readable.toWeb( + connection, + ) as ReadableStream; + this._fromDevice = fromDeviceSource.pipeThrough(Utils.fromDeviceStream()); // Stream for data going FROM the application TO the Meshtastic device. const toDeviceTransform = Utils.toDeviceStream; this._toDevice = toDeviceTransform.writable; - // The readable end of the transform is then piped to the Node.js socket. - // A similar assertion is needed here because `Writable.toWeb` also returns - // a generically typed stream (`WritableStream`). - toDeviceTransform.readable - .pipeTo(Writable.toWeb(connection) as WritableStream) - .catch((err) => { - console.error("Error piping data to socket:", err); - connection.destroy(err as Error); - }); - } + // The readable end of the transform is then piped to the Node.js socket. + // A similar assertion is needed here because `Writable.toWeb` also returns + // a generically typed stream (`WritableStream`). + toDeviceTransform.readable + .pipeTo(Writable.toWeb(connection) as WritableStream) + .catch((err) => { + console.error("Error piping data to socket:", err); + connection.destroy(err as Error); + }); + } - /** - * The WritableStream to send data to the Meshtastic device. - */ - public get toDevice(): WritableStream { - return this._toDevice; - } + /** + * The WritableStream to send data to the Meshtastic device. + */ + public get toDevice(): WritableStream { + return this._toDevice; + } - /** - * The ReadableStream to receive data from the Meshtastic device. - */ - public get fromDevice(): ReadableStream { - return this._fromDevice; - } + /** + * The ReadableStream to receive data from the Meshtastic device. + */ + public get fromDevice(): ReadableStream { + return this._fromDevice; + } } diff --git a/packages/transport-web-bluetooth/jsr.json b/packages/transport-web-bluetooth/jsr.json new file mode 100644 index 00000000..de6aa307 --- /dev/null +++ b/packages/transport-web-bluetooth/jsr.json @@ -0,0 +1,5 @@ +{ + "name": "@meshtastic/transport-web-bluetooth", + "version": "0.1.3", + "exports": "./mod.ts" +} \ No newline at end of file diff --git a/packages/transport-web-bluetooth/deno.json b/packages/transport-web-bluetooth/package.json similarity index 66% rename from packages/transport-web-bluetooth/deno.json rename to packages/transport-web-bluetooth/package.json index eb61116a..c301f583 100644 --- a/packages/transport-web-bluetooth/deno.json +++ b/packages/transport-web-bluetooth/package.json @@ -1,14 +1,14 @@ { - "name": "@meshtastic/transport-web-bluetooth", - "version": "0.1.2", + "name": "@meshtastic/transport-web-bluetooth", + "version": "0.1.3", "description": "A transport layer for Meshtastic applications using Web Bluetooth.", "exports": { ".": "./mod.ts" }, - "imports": { + "devDependencies": { "@types/web-bluetooth": "npm:@types/web-bluetooth@^0.0.20" }, - "compilerOptions": { + "compilerOptions": { "types": ["@types/web-bluetooth"] } -} +} \ No newline at end of file diff --git a/packages/transport-web-bluetooth/src/transport.ts b/packages/transport-web-bluetooth/src/transport.ts index 364e6edc..0f80447f 100644 --- a/packages/transport-web-bluetooth/src/transport.ts +++ b/packages/transport-web-bluetooth/src/transport.ts @@ -1,133 +1,135 @@ import type { Types } from "@meshtastic/core"; export class TransportWebBluetooth implements Types.Transport { - private _toDevice: WritableStream; - private _fromDevice: ReadableStream; - private _fromDeviceController?: ReadableStreamDefaultController; - private _isFirstWrite = true; + private _toDevice: WritableStream; + private _fromDevice: ReadableStream; + private _fromDeviceController?: ReadableStreamDefaultController; + private _isFirstWrite = true; - private toRadioCharacteristic: BluetoothRemoteGATTCharacteristic; - private fromRadioCharacteristic: BluetoothRemoteGATTCharacteristic; - private fromNumCharacteristic: BluetoothRemoteGATTCharacteristic; + private toRadioCharacteristic: BluetoothRemoteGATTCharacteristic; + private fromRadioCharacteristic: BluetoothRemoteGATTCharacteristic; + private fromNumCharacteristic: BluetoothRemoteGATTCharacteristic; - static ToRadioUuid = "f75c76d2-129e-4dad-a1dd-7866124401e7"; - static FromRadioUuid = "2c55e69e-4993-11ed-b878-0242ac120002"; - static FromNumUuid = "ed9da18c-a800-4f66-a670-aa7547e34453"; - static ServiceUuid = "6ba1b218-15a8-461f-9fa8-5dcae273eafd"; + static ToRadioUuid = "f75c76d2-129e-4dad-a1dd-7866124401e7"; + static FromRadioUuid = "2c55e69e-4993-11ed-b878-0242ac120002"; + static FromNumUuid = "ed9da18c-a800-4f66-a670-aa7547e34453"; + static ServiceUuid = "6ba1b218-15a8-461f-9fa8-5dcae273eafd"; - public static async create(): Promise { - const device = await navigator.bluetooth.requestDevice({ - filters: [{ services: [this.ServiceUuid] }], - }); - return await this.prepareConnection(device); - } + public static async create(): Promise { + const device = await navigator.bluetooth.requestDevice({ + filters: [{ services: [TransportWebBluetooth.ServiceUuid] }], + }); + return await TransportWebBluetooth.prepareConnection(device); + } - public static async createFromDevice( - device: BluetoothDevice, - ): Promise { - return await this.prepareConnection(device); - } + public static async createFromDevice( + device: BluetoothDevice, + ): Promise { + return await TransportWebBluetooth.prepareConnection(device); + } - public static async prepareConnection( - device: BluetoothDevice, - ): Promise { - const gattServer = await device.gatt?.connect(); + public static async prepareConnection( + device: BluetoothDevice, + ): Promise { + const gattServer = await device.gatt?.connect(); - if (!gattServer) { - throw new Error("Failed to connect to GATT server"); - } + if (!gattServer) { + throw new Error("Failed to connect to GATT server"); + } - const service = await gattServer.getPrimaryService(this.ServiceUuid); + const service = await gattServer.getPrimaryService( + TransportWebBluetooth.ServiceUuid, + ); - const toRadioCharacteristic = await service.getCharacteristic( - this.ToRadioUuid, - ); - const fromRadioCharacteristic = await service.getCharacteristic( - this.FromRadioUuid, - ); - const fromNumCharacteristic = await service.getCharacteristic( - this.FromNumUuid, - ); + const toRadioCharacteristic = await service.getCharacteristic( + TransportWebBluetooth.ToRadioUuid, + ); + const fromRadioCharacteristic = await service.getCharacteristic( + TransportWebBluetooth.FromRadioUuid, + ); + const fromNumCharacteristic = await service.getCharacteristic( + TransportWebBluetooth.FromNumUuid, + ); - if ( - !toRadioCharacteristic || - !fromRadioCharacteristic || - !fromNumCharacteristic - ) { - throw new Error("Failed to find required characteristics"); - } + if ( + !toRadioCharacteristic || + !fromRadioCharacteristic || + !fromNumCharacteristic + ) { + throw new Error("Failed to find required characteristics"); + } - console.log("Connected to device", device.name); + console.log("Connected to device", device.name); - return new TransportWebBluetooth( - toRadioCharacteristic, - fromRadioCharacteristic, - fromNumCharacteristic, - ); - } + return new TransportWebBluetooth( + toRadioCharacteristic, + fromRadioCharacteristic, + fromNumCharacteristic, + ); + } - constructor( - toRadioCharacteristic: BluetoothRemoteGATTCharacteristic, - fromRadioCharacteristic: BluetoothRemoteGATTCharacteristic, - fromNumCharacteristic: BluetoothRemoteGATTCharacteristic, - ) { - this.toRadioCharacteristic = toRadioCharacteristic; - this.fromRadioCharacteristic = fromRadioCharacteristic; - this.fromNumCharacteristic = fromNumCharacteristic; + constructor( + toRadioCharacteristic: BluetoothRemoteGATTCharacteristic, + fromRadioCharacteristic: BluetoothRemoteGATTCharacteristic, + fromNumCharacteristic: BluetoothRemoteGATTCharacteristic, + ) { + this.toRadioCharacteristic = toRadioCharacteristic; + this.fromRadioCharacteristic = fromRadioCharacteristic; + this.fromNumCharacteristic = fromNumCharacteristic; - this._fromDevice = new ReadableStream({ - start: (ctrl) => { - this._fromDeviceController = ctrl; - }, - }); + this._fromDevice = new ReadableStream({ + start: (ctrl) => { + this._fromDeviceController = ctrl; + }, + }); - this._toDevice = new WritableStream({ - write: async (chunk) => { - await this.toRadioCharacteristic.writeValue(chunk); + this._toDevice = new WritableStream({ + write: async (chunk) => { + await this.toRadioCharacteristic.writeValue(chunk); - if (this._isFirstWrite && this._fromDeviceController) { - this._isFirstWrite = false; - setTimeout(() => { - this.readFromRadio(this._fromDeviceController!); - }, 50); - } - }, - }); + if (this._isFirstWrite && this._fromDeviceController) { + this._isFirstWrite = false; + setTimeout(() => { + this.readFromRadio(this._fromDeviceController!); + }, 50); + } + }, + }); - this.fromNumCharacteristic.addEventListener( - "characteristicvaluechanged", - () => { - if (this._fromDeviceController) { - this.readFromRadio(this._fromDeviceController); - } - }, - ); + this.fromNumCharacteristic.addEventListener( + "characteristicvaluechanged", + () => { + if (this._fromDeviceController) { + this.readFromRadio(this._fromDeviceController); + } + }, + ); - this.fromNumCharacteristic.startNotifications(); - } + this.fromNumCharacteristic.startNotifications(); + } - get toDevice(): WritableStream { - return this._toDevice; - } + get toDevice(): WritableStream { + return this._toDevice; + } - get fromDevice(): ReadableStream { - return this._fromDevice; - } + get fromDevice(): ReadableStream { + return this._fromDevice; + } - protected async readFromRadio( - controller: ReadableStreamDefaultController, - ): Promise { - let hasMoreData = true; - while (hasMoreData && this.fromRadioCharacteristic) { - const value = await this.fromRadioCharacteristic.readValue(); - if (value.byteLength === 0) { - hasMoreData = false; - continue; - } - controller.enqueue({ - type: "packet", - data: new Uint8Array(value.buffer), - }); - } - } + protected async readFromRadio( + controller: ReadableStreamDefaultController, + ): Promise { + let hasMoreData = true; + while (hasMoreData && this.fromRadioCharacteristic) { + const value = await this.fromRadioCharacteristic.readValue(); + if (value.byteLength === 0) { + hasMoreData = false; + continue; + } + controller.enqueue({ + type: "packet", + data: new Uint8Array(value.buffer), + }); + } + } } diff --git a/packages/transport-web-serial/deno.json b/packages/transport-web-serial/deno.json deleted file mode 100644 index b7fb9e73..00000000 --- a/packages/transport-web-serial/deno.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@meshtastic/transport-web-serial", - "version": "0.2.1", - "description": "A transport layer for Meshtastic applications using Web Serial API.", - "exports": { - ".": "./mod.ts" - }, - "imports": { - "@types/w3c-web-serial": "npm:@types/w3c-web-serial@^1.0.7" - }, - "compilerOptions": { - "types": ["@types/w3c-web-serial"] - } -} diff --git a/packages/transport-web-serial/jsr.json b/packages/transport-web-serial/jsr.json new file mode 100644 index 00000000..f5cb5459 --- /dev/null +++ b/packages/transport-web-serial/jsr.json @@ -0,0 +1,5 @@ +{ + "name": "@meshtastic/transport-web-serial", + "version": "0.2.2", + "exports": "./mod.ts" +} \ No newline at end of file diff --git a/packages/transport-web-serial/package.json b/packages/transport-web-serial/package.json new file mode 100644 index 00000000..2e000b31 --- /dev/null +++ b/packages/transport-web-serial/package.json @@ -0,0 +1,14 @@ +{ + "name": "@meshtastic/transport-web-serial", + "version": "0.2.2", + "description": "A transport layer for Meshtastic applications using Web Serial API.", + "exports": { + ".": "./mod.ts" + }, + "dependencies": { + "@types/w3c-web-serial": "npm:@types/w3c-web-serial@^1.0.7" + }, + "compilerOptions": { + "types": ["@types/w3c-web-serial"] + } +} \ No newline at end of file diff --git a/packages/transport-web-serial/src/transport.ts b/packages/transport-web-serial/src/transport.ts index 727dcad6..0f445cbe 100644 --- a/packages/transport-web-serial/src/transport.ts +++ b/packages/transport-web-serial/src/transport.ts @@ -2,41 +2,41 @@ import { Utils } from "@meshtastic/core"; import type { Types } from "@meshtastic/core"; export class TransportWebSerial implements Types.Transport { - private _toDevice: WritableStream; - private _fromDevice: ReadableStream; + private _toDevice: WritableStream; + private _fromDevice: ReadableStream; - public static async create(baudRate?: number): Promise { - const port = await navigator.serial.requestPort(); - await port.open({ baudRate: baudRate || 115200 }); - return new TransportWebSerial(port); - } + public static async create(baudRate?: number): Promise { + const port = await navigator.serial.requestPort(); + await port.open({ baudRate: baudRate || 115200 }); + return new TransportWebSerial(port); + } - public static async createFromPort( - port: SerialPort, - baudRate?: number, - ): Promise { - await port.open({ baudRate: baudRate || 115200 }); - return new TransportWebSerial(port); - } + public static async createFromPort( + port: SerialPort, + baudRate?: number, + ): Promise { + await port.open({ baudRate: baudRate || 115200 }); + return new TransportWebSerial(port); + } - constructor(connection: SerialPort) { - if (!connection.readable || !connection.writable) { - throw new Error("Stream not accessible"); - } + constructor(connection: SerialPort) { + if (!connection.readable || !connection.writable) { + throw new Error("Stream not accessible"); + } - Utils.toDeviceStream.readable.pipeTo(connection.writable); + Utils.toDeviceStream.readable.pipeTo(connection.writable); - this._toDevice = Utils.toDeviceStream.writable; - this._fromDevice = connection.readable.pipeThrough( - Utils.fromDeviceStream(), - ); - } + this._toDevice = Utils.toDeviceStream.writable; + this._fromDevice = connection.readable.pipeThrough( + Utils.fromDeviceStream(), + ); + } - get toDevice(): WritableStream { - return this._toDevice; - } + get toDevice(): WritableStream { + return this._toDevice; + } - get fromDevice(): ReadableStream { - return this._fromDevice; - } + get fromDevice(): ReadableStream { + return this._fromDevice; + } } diff --git a/packages/web/README.md b/packages/web/README.md index 3bd9f555..d0673097 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -18,23 +18,6 @@ or served from a node ![Alt](https://repobeats.axiom.co/api/embed/e5b062db986cb005d83e81724c00cb2b9cce8e4c.svg "Repobeats analytics image") -## Progress Web App Support (PWA) - -Meshtastic Web Client now includes Progressive Web App (PWA) functionality, -allowing users to: - -- Install the app on desktop and mobile devices -- Access the interface offline -- Receive updates automatically -- Experience faster load times with caching - -To install as a PWA: - -- On desktop: Look for the install icon in your browser's address bar -- On mobile: Use "Add to Home Screen" option in your browser menu - -PWA functionality works with both the hosted version and self-hosted instances. - ## Self-host The client can be self hosted using the precompiled container images with an OCI @@ -56,7 +39,7 @@ Our release process follows these guidelines: - **Versioning:** We use Semantic Versioning (`Major.Minor.Patch`). - **Stable Releases:** Published around the beginning of each month (e.g., - `v2.3.4`). + `v2.6.1`). - **Pre-releases:** A pre-release is typically issued mid-month for testing and early adoption. - **Nightly Builds:** An experimental Docker image containing the latest @@ -106,6 +89,7 @@ instructions listed on the home page. Install the dependencies. ```bash +cd packages/web && bun install ``` @@ -141,8 +125,6 @@ reasons: configuration, enhancing code quality and developer experience. - **Modern JavaScript**: First-class support for ESM imports, top-level await, and other modern JavaScript features. -- **All-in-One Tooling**: Built-in package manager, bundler, test runner, and - transpiler eliminate the need for multiple third-party tools. - **Node.js Compatibility**: Drop-in replacement for Node.js with better performance and built-in tooling. - **Reproducible Builds**: Lockfile ensures consistent builds across all diff --git a/packages/web/package.json b/packages/web/package.json index 7758275f..a13f27ea 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -11,9 +11,7 @@ "bugs": { "url": "https://github.com/meshtastic/web/issues" }, - "simple-git-hooks": { - "pre-commit": "bun run check:fix" - }, + "homepage": "https://meshtastic.org", "scripts": { "build": "bunx --bun vite build", diff --git a/packages/web/src/components/CommandPalette/index.tsx b/packages/web/src/components/CommandPalette/index.tsx index d27139bc..3f6bb94a 100644 --- a/packages/web/src/components/CommandPalette/index.tsx +++ b/packages/web/src/components/CommandPalette/index.tsx @@ -1,11 +1,11 @@ import { Avatar } from "@components/UI/Avatar.tsx"; import { - CommandDialog, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, } from "@components/UI/Command.tsx"; import { usePinnedItems } from "@core/hooks/usePinnedItems.ts"; import { useAppStore } from "@core/stores/appStore.ts"; @@ -14,345 +14,345 @@ import { cn } from "@core/utils/cn.ts"; import { useNavigate } from "@tanstack/react-router"; import { useCommandState } from "cmdk"; import { - ArrowLeftRightIcon, - BoxSelectIcon, - BugIcon, - CloudOff, - EraserIcon, - FactoryIcon, - LayersIcon, - LinkIcon, - type LucideIcon, - MapIcon, - MessageSquareIcon, - Pin, - PlusIcon, - PowerIcon, - QrCodeIcon, - RefreshCwIcon, - SettingsIcon, - SmartphoneIcon, - TrashIcon, - UsersIcon, + ArrowLeftRightIcon, + BoxSelectIcon, + BugIcon, + CloudOff, + EraserIcon, + FactoryIcon, + LayersIcon, + LinkIcon, + type LucideIcon, + MapIcon, + MessageSquareIcon, + Pin, + PlusIcon, + PowerIcon, + QrCodeIcon, + RefreshCwIcon, + SettingsIcon, + SmartphoneIcon, + TrashIcon, + UsersIcon, } from "lucide-react"; import { useEffect } from "react"; import { useTranslation } from "react-i18next"; export interface Group { - id: string; - label: string; - icon: LucideIcon; - commands: Command[]; + id: string; + label: string; + icon: LucideIcon; + commands: Command[]; } export interface Command { - label: string; - icon: LucideIcon; - action?: () => void; - subItems?: SubItem[]; - tags?: string[]; + label: string; + icon: LucideIcon; + action?: () => void; + subItems?: SubItem[]; + tags?: string[]; } export interface SubItem { - label: string; - icon: React.ReactNode; - action: () => void; + label: string; + icon: React.ReactNode; + action: () => void; } export const CommandPalette = () => { - const { - commandPaletteOpen, - setCommandPaletteOpen, - setConnectDialogOpen, - setSelectedDevice, - } = useAppStore(); - const { getDevices } = useDeviceStore(); - const { setDialogOpen, getNode, connection } = useDevice(); - const { pinnedItems, togglePinnedItem } = usePinnedItems({ - storageName: "pinnedCommandMenuGroups", - }); - const { t } = useTranslation("commandPalette"); - const navigate = useNavigate({ from: "/" }); + const { + commandPaletteOpen, + setCommandPaletteOpen, + setConnectDialogOpen, + setSelectedDevice, + } = useAppStore(); + const { getDevices } = useDeviceStore(); + const { setDialogOpen, getNode, connection } = useDevice(); + const { pinnedItems, togglePinnedItem } = usePinnedItems({ + storageName: "pinnedCommandMenuGroups", + }); + const { t } = useTranslation("commandPalette"); + const navigate = useNavigate({ from: "/" }); - const groups: Group[] = [ - { - id: "gotoGroup", - label: t("goto.label"), - icon: LinkIcon, - commands: [ - { - label: t("goto.command.messages"), - icon: MessageSquareIcon, - action() { - navigate({ to: "/messages" }); - }, - }, - { - label: t("goto.command.map"), - icon: MapIcon, - action() { - navigate({ to: "/map" }); - }, - }, - { - label: t("goto.command.config"), - icon: SettingsIcon, - action() { - navigate({ to: "/config" }); - }, - tags: ["settings"], - }, - { - label: t("goto.command.channels"), - icon: LayersIcon, - action() { - navigate({ to: "/channels" }); - }, - }, - { - label: t("goto.command.nodes"), - icon: UsersIcon, - action() { - navigate({ to: "/nodes" }); - }, - }, - ], - }, - { - id: "manageGroup", - label: t("manage.label"), - icon: SmartphoneIcon, - commands: [ - { - label: t("manage.command.switchNode"), - icon: ArrowLeftRightIcon, - subItems: getDevices().map((device) => ({ - label: - getNode(device.hardware.myNodeNum)?.user?.longName ?? - t("unknown.shortName"), - icon: ( - - ), - action() { - setSelectedDevice(device.id); - }, - })), - }, - { - label: t("manage.command.connectNewNode"), - icon: PlusIcon, - action() { - setConnectDialogOpen(true); - }, - }, - ], - }, - { - id: "contextualGroup", - label: t("contextual.label"), - icon: BoxSelectIcon, - commands: [ - { - label: t("contextual.command.qrCode"), - icon: QrCodeIcon, - subItems: [ - { - label: t("contextual.command.qrGenerator"), - icon: , - action() { - setDialogOpen("QR", true); - }, - }, - { - label: t("contextual.command.qrImport"), - icon: , - action() { - setDialogOpen("import", true); - }, - }, - ], - }, - { - label: t("contextual.command.scheduleShutdown"), - icon: PowerIcon, - action() { - setDialogOpen("shutdown", true); - }, - }, - { - label: t("contextual.command.scheduleReboot"), - icon: RefreshCwIcon, - action() { - setDialogOpen("reboot", true); - }, - }, - { - label: t("contextual.command.rebootToOtaMode"), - icon: RefreshCwIcon, - action() { - setDialogOpen("rebootOTA", true); - }, - }, - { - label: t("contextual.command.resetNodeDb"), - icon: TrashIcon, - action() { - connection?.resetNodes(); - }, - }, - { - label: t("contextual.command.disconnect"), - icon: CloudOff, - action() { - connection?.disconnect().catch((error) => { - console.error("Failed to disconnect:", error); - }); - }, - }, - { - label: t("contextual.command.factoryResetDevice"), - icon: FactoryIcon, - action() { - connection?.factoryResetDevice(); - }, - }, - { - label: t("contextual.command.factoryResetConfig"), - icon: FactoryIcon, - action() { - connection?.factoryResetConfig(); - }, - }, - ], - }, - { - id: "debugGroup", - label: t("debug.label"), - icon: BugIcon, - commands: [ - { - label: t("debug.command.reconfigure"), - icon: RefreshCwIcon, - action() { - void connection?.configure(); - }, - }, - { - label: t("debug.command.clearAllStoredMessages"), - icon: EraserIcon, - action() { - setDialogOpen("deleteMessages", true); - }, - }, - ], - }, - ]; + const groups: Group[] = [ + { + id: "gotoGroup", + label: t("goto.label"), + icon: LinkIcon, + commands: [ + { + label: t("goto.command.messages"), + icon: MessageSquareIcon, + action() { + navigate({ to: "/messages" }); + }, + }, + { + label: t("goto.command.map"), + icon: MapIcon, + action() { + navigate({ to: "/map" }); + }, + }, + { + label: t("goto.command.config"), + icon: SettingsIcon, + action() { + navigate({ to: "/config" }); + }, + tags: ["settings"], + }, + { + label: t("goto.command.channels"), + icon: LayersIcon, + action() { + navigate({ to: "/channels" }); + }, + }, + { + label: t("goto.command.nodes"), + icon: UsersIcon, + action() { + navigate({ to: "/nodes" }); + }, + }, + ], + }, + { + id: "manageGroup", + label: t("manage.label"), + icon: SmartphoneIcon, + commands: [ + { + label: t("manage.command.switchNode"), + icon: ArrowLeftRightIcon, + subItems: getDevices().map((device) => ({ + label: + getNode(device.hardware.myNodeNum)?.user?.longName ?? + t("unknown.shortName"), + icon: ( + + ), + action() { + setSelectedDevice(device.id); + }, + })), + }, + { + label: t("manage.command.connectNewNode"), + icon: PlusIcon, + action() { + setConnectDialogOpen(true); + }, + }, + ], + }, + { + id: "contextualGroup", + label: t("contextual.label"), + icon: BoxSelectIcon, + commands: [ + { + label: t("contextual.command.qrCode"), + icon: QrCodeIcon, + subItems: [ + { + label: t("contextual.command.qrGenerator"), + icon: , + action() { + setDialogOpen("QR", true); + }, + }, + { + label: t("contextual.command.qrImport"), + icon: , + action() { + setDialogOpen("import", true); + }, + }, + ], + }, + { + label: t("contextual.command.scheduleShutdown"), + icon: PowerIcon, + action() { + setDialogOpen("shutdown", true); + }, + }, + { + label: t("contextual.command.scheduleReboot"), + icon: RefreshCwIcon, + action() { + setDialogOpen("reboot", true); + }, + }, + { + label: t("contextual.command.rebootToOtaMode"), + icon: RefreshCwIcon, + action() { + setDialogOpen("rebootOTA", true); + }, + }, + { + label: t("contextual.command.resetNodeDb"), + icon: TrashIcon, + action() { + connection?.resetNodes(); + }, + }, + { + label: t("contextual.command.disconnect"), + icon: CloudOff, + action() { + connection?.disconnect().catch((error) => { + console.error("Failed to disconnect:", error); + }); + }, + }, + { + label: t("contextual.command.factoryResetDevice"), + icon: FactoryIcon, + action() { + connection?.factoryResetDevice(); + }, + }, + { + label: t("contextual.command.factoryResetConfig"), + icon: FactoryIcon, + action() { + connection?.factoryResetConfig(); + }, + }, + ], + }, + { + id: "debugGroup", + label: t("debug.label"), + icon: BugIcon, + commands: [ + { + label: t("debug.command.reconfigure"), + icon: RefreshCwIcon, + action() { + void connection?.configure(); + }, + }, + { + label: t("debug.command.clearAllStoredMessages"), + icon: EraserIcon, + action() { + setDialogOpen("deleteMessages", true); + }, + }, + ], + }, + ]; - const sortedGroups = [...groups].sort((a, b) => { - const aPinned = pinnedItems.includes(a.id) ? 1 : 0; - const bPinned = pinnedItems.includes(b.id) ? 1 : 0; - return bPinned - aPinned; - }); + const sortedGroups = [...groups].sort((a, b) => { + const aPinned = pinnedItems.includes(a.id) ? 1 : 0; + const bPinned = pinnedItems.includes(b.id) ? 1 : 0; + return bPinned - aPinned; + }); - useEffect(() => { - const handleKeydown = (e: KeyboardEvent) => { - if (e.key === "k" && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - setCommandPaletteOpen(true); - } - }; + useEffect(() => { + const handleKeydown = (e: KeyboardEvent) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setCommandPaletteOpen(true); + } + }; - globalThis.addEventListener("keydown", handleKeydown); - return () => globalThis.removeEventListener("keydown", handleKeydown); - }, [setCommandPaletteOpen]); + globalThis.addEventListener("keydown", handleKeydown); + return () => globalThis.removeEventListener("keydown", handleKeydown); + }, [setCommandPaletteOpen]); - return ( - - - - {t("emptyState")} - {sortedGroups.map((group) => ( - - {group.label} - - - } - > - {group.commands.map((command) => ( -
- { - command.action?.(); - setCommandPaletteOpen(false); - }} - > - - {command.label} - - {command.subItems?.map((subItem) => ( - - ))} -
- ))} -
- ))} -
-
- ); + return ( + + + + {t("emptyState")} + {sortedGroups.map((group) => ( + + {group.label} + + + } + > + {group.commands.map((command) => ( +
+ { + command.action?.(); + setCommandPaletteOpen(false); + }} + > + + {command.label} + + {command.subItems?.map((subItem) => ( + + ))} +
+ ))} +
+ ))} +
+
+ ); }; const SubItem = ({ - label, - icon, - action, + label, + icon, + action, }: { - label: string; - icon: React.ReactNode; - action: () => void; + label: string; + icon: React.ReactNode; + action: () => void; }) => { - const search = useCommandState((state) => state.search); - if (!search) { - return null; - } + const search = useCommandState((state) => state.search); + if (!search) { + return null; + } - return ( - - {icon} - {label} - - ); + return ( + + {icon} + {label} + + ); }; diff --git a/packages/web/vite-env.d.ts b/packages/web/vite-env.d.ts index 2997ee32..484e03a4 100644 --- a/packages/web/vite-env.d.ts +++ b/packages/web/vite-env.d.ts @@ -1,11 +1,11 @@ /// interface ImportMetaEnv { - readonly env: { - readonly VITE_COMMIT_HASH: string; - }; + readonly env: { + readonly VITE_COMMIT_HASH: string; + }; } interface ImportMeta { - readonly env: ImportMetaEnv; + readonly env: ImportMetaEnv; } diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts index 405b8dc6..6650a4f0 100644 --- a/packages/web/vite.config.ts +++ b/packages/web/vite.config.ts @@ -7,56 +7,56 @@ import { defineConfig } from "vite"; let hash = ""; try { - hash = execSync("git rev-parse --short HEAD", { encoding: "utf8" }).trim(); + hash = execSync("git rev-parse --short HEAD", { encoding: "utf8" }).trim(); } catch (error) { - console.error("Error getting git hash:", error); - hash = "DEV"; + console.error("Error getting git hash:", error); + hash = "DEV"; } const CONTENT_SECURITY_POLICY = - "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' data: https://rsms.me https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' data: https://rsms.me https://cdn.jsdelivr.net; worker-src 'self' blob:; object-src 'none'; base-uri 'self';"; + "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' data: https://rsms.me https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' data: https://rsms.me https://cdn.jsdelivr.net; worker-src 'self' blob:; object-src 'none'; base-uri 'self';"; export default defineConfig({ - plugins: [ - react(), - tailwindcss(), - // VitePWA({ - // registerType: "autoUpdate", - // strategies: "generateSW", - // devOptions: { - // enabled: true, - // }, - // workbox: { - // cleanupOutdatedCaches: true, - // sourcemap: true, - // }, - // }), - ], - optimizeDeps: { - include: ["react/jsx-runtime"], - }, - define: { - "import.meta.env.VITE_COMMIT_HASH": JSON.stringify(hash), - }, - build: { - emptyOutDir: true, - assetsDir: "./", - }, - resolve: { - alias: { - "@app": path.resolve(process.cwd(), "./src"), - "@pages": path.resolve(process.cwd(), "./src/pages"), - "@components": path.resolve(process.cwd(), "./src/components"), - "@core": path.resolve(process.cwd(), "./src/core"), - "@layouts": path.resolve(process.cwd(), "./src/layouts"), - }, - }, - server: { - port: 3000, - headers: { - "content-security-policy": CONTENT_SECURITY_POLICY, - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", - }, - }, + plugins: [ + react(), + tailwindcss(), + // VitePWA({ + // registerType: "autoUpdate", + // strategies: "generateSW", + // devOptions: { + // enabled: true, + // }, + // workbox: { + // cleanupOutdatedCaches: true, + // sourcemap: true, + // }, + // }), + ], + optimizeDeps: { + include: ["react/jsx-runtime"], + }, + define: { + "import.meta.env.VITE_COMMIT_HASH": JSON.stringify(hash), + }, + build: { + emptyOutDir: true, + assetsDir: "./", + }, + resolve: { + alias: { + "@app": path.resolve(process.cwd(), "./src"), + "@pages": path.resolve(process.cwd(), "./src/pages"), + "@components": path.resolve(process.cwd(), "./src/components"), + "@core": path.resolve(process.cwd(), "./src/core"), + "@layouts": path.resolve(process.cwd(), "./src/layouts"), + }, + }, + server: { + port: 3000, + headers: { + "content-security-policy": CONTENT_SECURITY_POLICY, + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, + }, }); diff --git a/packages/web/vitest.config.ts b/packages/web/vitest.config.ts index 6574b3e7..11b9ae65 100644 --- a/packages/web/vitest.config.ts +++ b/packages/web/vitest.config.ts @@ -7,25 +7,25 @@ import { enableMapSet } from "immer"; enableMapSet(); export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - "@app": path.resolve(process.cwd(), "./src"), - "@public": path.resolve(process.cwd(), "./public"), - "@core": path.resolve(process.cwd(), "./src/core"), - "@pages": path.resolve(process.cwd(), "./src/pages"), - "@components": path.resolve(process.cwd(), "./src/components"), - "@layouts": path.resolve(process.cwd(), "./src/layouts"), - }, - }, - test: { - environment: "happy-dom", - globals: true, - mockReset: true, - clearMocks: true, - restoreMocks: true, - root: path.resolve(process.cwd(), "./src"), - include: ["**/*.{test,spec}.{ts,tsx}"], - setupFiles: ["./src/tests/setup.ts"], - }, + plugins: [react()], + resolve: { + alias: { + "@app": path.resolve(process.cwd(), "./src"), + "@public": path.resolve(process.cwd(), "./public"), + "@core": path.resolve(process.cwd(), "./src/core"), + "@pages": path.resolve(process.cwd(), "./src/pages"), + "@components": path.resolve(process.cwd(), "./src/components"), + "@layouts": path.resolve(process.cwd(), "./src/layouts"), + }, + }, + test: { + environment: "happy-dom", + globals: true, + mockReset: true, + clearMocks: true, + restoreMocks: true, + root: path.resolve(process.cwd(), "./src"), + include: ["**/*.{test,spec}.{ts,tsx}"], + setupFiles: ["./src/tests/setup.ts"], + }, }); diff --git a/scripts/build_npm_package.ts b/scripts/build_npm_package.ts index 2d0167a6..bd3ded07 100644 --- a/scripts/build_npm_package.ts +++ b/scripts/build_npm_package.ts @@ -1,57 +1,57 @@ -import { join } from "jsr:@std/path@1/join"; -import { build, emptyDir } from "@deno/dnt"; +import { build, emptyDir } from "https://jsr.io/@deno/dnt/0.42.3/mod.ts"; +import { join } from "https://jsr.io/@std/path/1.1.1/mod.ts"; interface DenoJsonConfig { - name: string; - version: string; - description: string; - imports?: Record; - exports?: Record; + name: string; + version: string; + description: string; + imports?: Record; + exports?: Record; } async function getJson(filePath: string) { - try { - return JSON.parse(await Deno.readTextFile(filePath)); - } catch (e) { - if (e instanceof Error) { - throw new Error(`Error reading or parsing ${filePath}: ${e.message}`); - } - } + try { + return JSON.parse(await Deno.readTextFile(filePath)); + } catch (e) { + if (e instanceof Error) { + throw new Error(`Error reading or parsing ${filePath}: ${e.message}`); + } + } } if (Deno.args.length !== 1) { - console.error("Usage: deno task build:npm "); - console.error("Example: deno task build:npm packages/core"); - Deno.exit(1); + console.error("Usage: deno task build:npm "); + console.error("Example: deno task build:npm packages/core"); + Deno.exit(1); } const packagePath = Deno.args[0]; -const denoJsonPath = join(packagePath, "deno.json"); +const denoJsonPath = join(packagePath, "package.json"); const outDir = join(packagePath, "npm"); // Read the deno.json file to get the package metadata. let jsonContent: DenoJsonConfig; try { - jsonContent = await getJson(denoJsonPath); + jsonContent = await getJson(denoJsonPath); } catch (error) { - console.log(`Error reading or parsing ${denoJsonPath}:`, error); + console.log(`Error reading or parsing ${denoJsonPath}:`, error); - if (error instanceof Deno.errors.NotFound) { - console.error(`Error: Config file not found at ${denoJsonPath}`); - } else { - console.error(`Error reading or parsing ${denoJsonPath}:`, error); - } - Deno.exit(1); + if (error instanceof Deno.errors.NotFound) { + console.error(`Error: Config file not found at ${denoJsonPath}`); + } else { + console.error(`Error reading or parsing ${denoJsonPath}:`, error); + } + Deno.exit(1); } const { name, version, description } = jsonContent; if (!name || !version || !description) { - console.error( - `Error: 'name', 'version', and 'description' must be defined in ${denoJsonPath}`, - ); - Deno.exit(1); + console.error( + `Error: 'name', 'version', and 'description' must be defined in ${denoJsonPath}`, + ); + Deno.exit(1); } console.log(`Building ${name}@${version} from ${packagePath}...`); @@ -60,40 +60,42 @@ console.log(`Building ${name}@${version} from ${packagePath}...`); await emptyDir(outDir); try { - await build({ - entryPoints: [join(packagePath, "mod.ts")], - outDir, - test: false, - shims: { - deno: true, - }, - package: { - name, - version, - description, - license: "GPL-3.0-only", - repository: { - type: "git", - url: "git+https://github.com/meshtastic/web.git", - }, - bugs: { - url: "https://github.com/meshtastic/web/issues", - }, - }, - compilerOptions: { - lib: ["DOM", "ESNext"], - }, - postBuild() { - Deno.copyFileSync("LICENSE", join(outDir, "LICENSE")); - Deno.copyFileSync( - join(packagePath, "README.md"), - join(outDir, "README.md"), - ); - }, - }); + await build({ + entryPoints: [join(packagePath, "mod.ts")], + outDir, + test: false, + esModule: true, + declaration: false, + shims: { + deno: true, + }, + package: { + name, + version, + description, + license: "GPL-3.0-only", + repository: { + type: "git", + url: "git+https://github.com/meshtastic/web.git", + }, + bugs: { + url: "https://github.com/meshtastic/web/issues", + }, + }, + compilerOptions: { + lib: ["DOM", "ESNext"], + }, + postBuild() { + Deno.copyFileSync("LICENSE", join(outDir, "LICENSE")); + Deno.copyFileSync( + join(packagePath, "README.md"), + join(outDir, "README.md"), + ); + }, + }); } catch (error) { - console.error(`Error building ${name}@${version}:`, error); - Deno.exit(1); + console.error(`Error building ${name}@${version}:`, error); + Deno.exit(1); } console.log(`✅ Successfully built ${name}@${version} to ${outDir}`);