diff --git a/.editorconfig b/.editorconfig index 2c502401..bad0c989 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,22 +5,21 @@ charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = space -indent_size = 2 [*.{ts,tsx}] indent_style = space indent_size = 2 -rulers = 90 +rulers = 100 [*.{scss}] indent_style = space indent_size = 2 -rulers = 90 +rulers = 100 [*.{html}] indent_style = space indent_size = 2 -rulers = 90 +rulers = 100 [*.{json,yaml}] indent_style = space @@ -30,4 +29,4 @@ rulers = 80 [*.{rs}] indent_style = space indent_size = 4 -rulers = 90 +rulers = 100 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index d6bb8779..c5e3eb00 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,30 +1,39 @@ -name: 'lint' +name: "lint" on: push: branches: - main - dev + - "release/**" + paths-ignore: + - "*.md" + - "LICENSE" pull_request: branches: - main - dev + - "release/**" + paths-ignore: + - "*.md" + - "LICENSE" jobs: lint-web: - runs-on: [self-hosted, Linux, X64] + runs-on: + - codebuild-defguard-client-runner-${{ github.run_id }}-${{ github.run_attempt }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: - submodules: 'recursive' + submodules: recursive - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: "22" - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 run_install: false - name: Get pnpm store directory @@ -32,7 +41,7 @@ jobs: run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - uses: actions/cache@v3 + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} @@ -43,11 +52,8 @@ jobs: - name: Install deps run: pnpm install --frozen-lockfile - - name: Run Eslint and Prettier Lint + - name: Run Biome and Prettier Lint run: pnpm lint - - name: Check TSC - run: pnpm tsc - - name: Audit run: pnpm audit --prod diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5ad288b8..b04338aa 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: 'Build app and create release' +name: "Build app and create release" on: push: tags: @@ -12,7 +12,7 @@ jobs: architecture: [arm64, amd64] runs-on: [self-hosted, macOS] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: repository: WireGuard/wireguard-go ref: master @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.24' + go-version: "1.24" - name: Build wireguard-go binary run: make env: @@ -71,26 +71,26 @@ jobs: deb_arch: amd64 binary_arch: x86_64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: - submodules: 'recursive' + submodules: "recursive" - name: Write release version run: | VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d '-' -f1) echo Version: $VERSION - echo "VERSION=$VERSION" >> $GITHUB_ENV - - uses: actions/setup-node@v3 + echo "VERSION=$VERSION" >> ${GITHUB_ENV} + - uses: actions/setup-node@v4 with: - node-version: '20' - - uses: pnpm/action-setup@v2 + node-version: "22" + - uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 run_install: false - name: Get pnpm store directory shell: bash run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - uses: actions/cache@v3 + echo "STORE_PATH=$(pnpm store path --silent)" >> ${GITHUB_ENV} + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} @@ -103,21 +103,20 @@ jobs: - name: Install Linux dependencies run: | sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev unzip protobuf-compiler libprotobuf-dev rpm + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libssl-dev libxdo-dev unzip protobuf-compiler libprotobuf-dev rpm - name: Build packages uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create RPM - run: | - rpmbuild --build-in-place --define "_topdir $(pwd)" --define "version ${{ env.VERSION }}" -bb resources-linux/defguard-client.spec + with: + args: "--bundles deb,rpm" - name: Upload RPM uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.create-release.outputs.upload_url }} - asset_path: RPMS/${{ matrix.binary_arch }}/defguard-client-${{ env.VERSION }}-1.${{ matrix.binary_arch }}.rpm + asset_path: src-tauri/target/release/bundle/rpm/defguard-client-${{ env.VERSION }}-1.${{ matrix.binary_arch }}.rpm asset_name: defguard-client-${{ env.VERSION }}-1.${{ matrix.binary_arch }}.rpm asset_content_type: application/octet-stream - name: Upload DEB @@ -187,8 +186,8 @@ jobs: - name: Build dg deb uses: defGuard/fpm-action@main with: - fpm_args: 'dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}=/usr/sbin/dg dg.service=/usr/lib/systemd/system/dg.service src-tauri/cli/.env=/etc/defguard/dg.conf' - fpm_opts: '--architecture ${{ matrix.binary_arch }} --debug --output-type deb --version ${{ env.VERSION }} --package dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}.deb' + fpm_args: "dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}=/usr/sbin/dg dg.service=/usr/lib/systemd/system/dg.service src-tauri/cli/.env=/etc/defguard/dg.conf" + fpm_opts: "--architecture ${{ matrix.binary_arch }} --debug --output-type deb --version ${{ env.VERSION }} --package dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}.deb" - name: Upload DEB uses: actions/upload-release-asset@v1.0.2 env: @@ -201,8 +200,8 @@ jobs: - name: Build dg rpm uses: defGuard/fpm-action@main with: - fpm_args: 'dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}=/usr/sbin/dg dg.service=/usr/lib/systemd/system/dg.service src-tauri/cli/.env=/etc/defguard/dg.conf' - fpm_opts: '--architecture ${{ matrix.binary_arch }} --debug --output-type rpm --version ${{ env.VERSION }} --package dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}.rpm' + fpm_args: "dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}=/usr/sbin/dg dg.service=/usr/lib/systemd/system/dg.service src-tauri/cli/.env=/etc/defguard/dg.conf" + fpm_opts: "--architecture ${{ matrix.binary_arch }} --debug --output-type rpm --version ${{ env.VERSION }} --package dg-linux-${{ matrix.binary_arch }}-${{ github.ref_name }}.rpm" - name: Upload RPM uses: actions/upload-release-asset@v1.0.2 env: @@ -225,30 +224,30 @@ jobs: - self-hosted - macOS env: - APPLE_SIGNING_IDENTITY_APPLICATION: 'Developer ID Application: defguard sp. z o.o. (82GZ7KN29J)' - APPLE_SIGNING_IDENTITY_INSTALLER: 'Developer ID Installer: defguard sp. z o.o. (82GZ7KN29J)' - APPLE_ID: 'kamil@defguard.net' - APPLE_TEAM_ID: '82GZ7KN29J' + APPLE_SIGNING_IDENTITY_APPLICATION: "Developer ID Application: defguard sp. z o.o. (82GZ7KN29J)" + APPLE_SIGNING_IDENTITY_INSTALLER: "Developer ID Installer: defguard sp. z o.o. (82GZ7KN29J)" + APPLE_ID: "kamil@defguard.net" + APPLE_TEAM_ID: "82GZ7KN29J" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: - submodules: 'recursive' + submodules: "recursive" - name: Write release version run: | VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d '-' -f1) echo Version: $VERSION - echo "VERSION=$VERSION" >> $GITHUB_ENV - - uses: actions/setup-node@v3 + echo "VERSION=$VERSION" >> ${GITHUB_ENV} + - uses: actions/setup-node@v4 with: - node-version: '20' - - uses: pnpm/action-setup@v2 + node-version: "22" + - uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 run_install: false - name: Get pnpm store directory shell: bash - run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - uses: actions/cache@v3 + run: echo "STORE_PATH=$(pnpm store path --silent)" >> ${GITHUB_ENV} + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} @@ -312,25 +311,25 @@ jobs: - create-release runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: - submodules: 'recursive' + submodules: "recursive" - name: Write release version run: | $env:VERSION=echo ($env:GITHUB_REF_NAME.Substring(1) -Split "-")[0] echo Version: $env:VERSION echo "VERSION=$env:VERSION" >> $env:GITHUB_ENV - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: '20' - - uses: pnpm/action-setup@v2 + node-version: "22" + - uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 run_install: false - name: Get pnpm store directory shell: bash - run: echo "STORE_PATH=$(pnpm store path --silent)" >> $env:GITHUB_ENV - - uses: actions/cache@v3 + run: echo "STORE_PATH=$(pnpm store path --silent)" >> ${GITHUB_ENV} + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} @@ -376,7 +375,7 @@ jobs: run: | VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d '-' -f1) echo Version: $VERSION - echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "VERSION=$VERSION" >> ${GITHUB_ENV} - name: Download unsigned bundle & burn-engine uses: actions/download-artifact@v4 with: @@ -422,7 +421,7 @@ jobs: run: | VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d '-' -f1) echo Version: $VERSION - echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "VERSION=$VERSION" >> ${GITHUB_ENV} - name: Download unsigned bundle & signed burn-engine uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 62302763..7a837db4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,7 @@ on: branches: - main - dev + - 'release/**' paths-ignore: - '*.md' - 'LICENSE' @@ -12,6 +13,7 @@ on: branches: - main - dev + - 'release/**' paths-ignore: - '*.md' - 'LICENSE' @@ -21,30 +23,28 @@ env: jobs: test: - runs-on: [self-hosted, Linux, X64] - container: rust:1 + runs-on: + - codebuild-defguard-client-runner-${{ github.run_id }}-${{ github.run_attempt }} + + container: + image: public.ecr.aws/docker/library/rust:1 + options: --user root + defaults: run: working-directory: ./src-tauri steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive - - name: Debug - run: echo ${{ github.ref_name }} - name: Cache uses: Swatinem/rust-cache@v2 - name: Install required packages run: | apt-get update - apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev unzip - - name: Install protobuf compiler - run: | - PB_REL='https://github.com/protocolbuffers/protobuf/releases' - PB_VERSION='3.20.0' && curl -LO $PB_REL/download/v$PB_VERSION/protoc-$PB_VERSION-linux-x86_64.zip - unzip protoc-$PB_VERSION-linux-x86_64.zip bin/protoc include/google/* -d /usr/local + apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libssl-dev libxdo-dev unzip protobuf-compiler - name: Check format run: | rustup component add rustfmt @@ -55,8 +55,9 @@ jobs: rustup component add clippy cargo clippy --all-targets --all-features -- -D warnings - name: Run cargo deny - uses: EmbarkStudios/cargo-deny-action@v2 - with: - manifest-path: ./src-tauri/Cargo.toml + working-directory: ./src-tauri + run: | + cargo install cargo-deny + cargo deny check - name: Run tests run: cargo test --locked --no-fail-fast diff --git a/.gitignore b/.gitignore index dbfa7123..3f17dab1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,5 +27,11 @@ dist-ssr *.db-shm *.db-wal +src-tauri/gen/ + .direnv .envrc +.aider* + +# nix stuff +result diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..c2ca6e40 --- /dev/null +++ b/biome.json @@ -0,0 +1,110 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "src/**", + "!src/i18n/*.ts", + "!src/i18n/*.tsx", + "!src/i18n/i18n-util", + "!dist" + ] + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineEnding": "lf", + "lineWidth": 90, + "attributePosition": "auto", + "bracketSameLine": false, + "bracketSpacing": true, + "expand": "auto", + "useEditorconfig": true, + "includes": [ + "./src/**" + ] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": "off", + "complexity": { + "noBannedTypes": "error", + "noUselessTypeConstraint": "error" + }, + "correctness": { + "useUniqueElementIds": "off", + "noChildrenProp": "error", + "noPrecisionLoss": "error", + "noUnusedVariables": "error", + "useExhaustiveDependencies": "error", + "useHookAtTopLevel": "error", + "useJsxKeyInIterable": "error" + }, + "security": { + "noDangerouslySetInnerHtmlWithChildren": "error" + }, + "style": { + "noNamespace": "error", + "noNonNullAssertion": "error", + "useArrayLiterals": "error", + "useAsConstAssertion": "error", + "useBlockStatements": "off", + "useLiteralEnumMembers": "off" + }, + "suspicious": { + "noCommentText": "error", + "noDuplicateJsxProps": "error", + "noExplicitAny": "error", + "noExtraNonNullAssertion": "error", + "noMisleadingInstantiator": "error", + "noUnsafeDeclarationMerging": "error", + "noArrayIndexKey": "off" + } + }, + "includes": [ + "src/**" + ] + }, + "javascript": { + "formatter": { + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "all", + "semicolons": "always", + "arrowParentheses": "always", + "bracketSameLine": false, + "quoteStyle": "single", + "attributePosition": "auto", + "bracketSpacing": true + } + }, + "html": { + "formatter": { + "selfCloseVoidElements": "always" + } + }, + "overrides": [ + { + "includes": [ + "**/*.js" + ] + } + ], + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/ci.trigger b/ci.trigger deleted file mode 100644 index e69de29b..00000000 diff --git a/flake.lock b/flake.lock index 08ed6370..31754b0a 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751382304, - "narHash": "sha256-p+UruOjULI5lV16FkBqkzqgFasLqfx0bihLBeFHiZAs=", + "lastModified": 1756159630, + "narHash": "sha256-ohMvsjtSVdT/bruXf5ClBh8ZYXRmD4krmjKrXhEvwMg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d31a91c9b3bee464d054633d5f8b84e17a637862", + "rev": "84c256e42600cb0fdf25763b48d28df2f25a0c8b", "type": "github" }, "original": { @@ -60,11 +60,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1751423951, - "narHash": "sha256-AowKhJGplXRkAngSvb+32598DTiI6LOzhAnzgvbCtYM=", + "lastModified": 1756262090, + "narHash": "sha256-PQHSup4d0cVXxJ7mlHrrxBx1WVrmudKiNQgnNl5xRas=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "1684ed5b15859b655caf41b467d046e29a994d04", + "rev": "df7ea78aded79f195a92fc5423de96af2b8a85d1", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 96fe9c9e..6bd1404c 100644 --- a/flake.nix +++ b/flake.nix @@ -3,6 +3,9 @@ nixpkgs.url = "nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; rust-overlay.url = "github:oxalica/rust-overlay"; + + # include git submodules + self.submodules = true; }; outputs = { @@ -12,63 +15,23 @@ rust-overlay, }: flake-utils.lib.eachDefaultSystem (system: let + # add rust overlay pkgs = import nixpkgs { inherit system; overlays = [rust-overlay.overlays.default]; }; - - toolchain = pkgs.rust-bin.stable.latest.default.override { - extensions = ["rust-analyzer" "rust-src" "rustfmt" "clippy"]; - targets = ["wasm32-unknown-unknown"]; - }; - packages = with pkgs; [ - cargo - cargo-tauri - toolchain - rust-analyzer-unwrapped - nodejs_20 - nodePackages.pnpm - trunk - ]; - nativeBuildPackages = with pkgs; [ - pkg-config - dbus - openssl - glib - gtk3 - libsoup_2_4 - webkitgtk_4_0 - librsvg - protobuf - libayatana-appindicator - ]; - libraries = with pkgs; [ - gtk3 - cairo - gdk-pixbuf - glib - dbus - openssl - librsvg - libsoup_3 - webkitgtk_4_0 - libayatana-appindicator - ]; in { - devShells.default = pkgs.mkShell { - buildInputs = packages; - nativeBuildInputs = nativeBuildPackages; - shellHook = with pkgs; '' - export LD_LIBRARY_PATH="${ - lib.makeLibraryPath libraries - }:$LD_LIBRARY_PATH" - export OPENSSL_INCLUDE_DIR="${openssl.dev}/include/openssl" - export OPENSSL_LIB_DIR="${openssl.out}/lib" - export OPENSSL_ROOT_DIR="${openssl.out}" - # https://discourse.nixos.org/t/which-package-includes-org-gtk-gtk4-settings-filechooser/38063/12 - export XDG_DATA_DIRS="${gtk3}/share/gsettings-schemas/gtk+3-${gtk3.dev.version}:$XDG_DATA_DIRS" - export RUST_SRC_PATH="${toolchain}/lib/rustlib/src/rust/library" - ''; + devShells.default = import ./nix/shell.nix { + inherit pkgs; }; - }); + + packages.default = pkgs.callPackage ./nix/package.nix { + inherit pkgs; + }; + + formatter = pkgs.alejandra; + }) + // { + nixosModules.default = import ./nix/nixos-module.nix; + }; } diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix new file mode 100644 index 00000000..b21b0b1f --- /dev/null +++ b/nix/nixos-module.nix @@ -0,0 +1,75 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + defguard-client = pkgs.callPackage ./package.nix {}; + cfg = config.programs.defguard-client; +in { + options.programs.defguard-client = { + enable = mkEnableOption "Defguard VPN client and service"; + + package = mkOption { + type = types.package; + default = defguard-client; + description = "defguard-client package to use"; + }; + + logLevel = mkOption { + type = types.str; + default = "info"; + description = "Log level for defguard-service"; + }; + + statsPeriod = mkOption { + type = types.int; + default = 30; + description = "Interval in seconds for interface statistics updates"; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + systemd.services.defguard-service = { + description = "Defguard VPN Service"; + wantedBy = ["multi-user.target"]; + wants = ["network-online.target"]; + after = ["network-online.target"]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/defguard-service --log-level ${cfg.logLevel} --stats-period ${toString cfg.statsPeriod}"; + Restart = "on-failure"; + RestartSec = 5; + User = "defguard"; + Group = "defguard"; + StateDirectory = "defguard"; + LogsDirectory = "defguard"; + # Add capabilities to manage network interfaces + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE"; + AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE"; + # Allow access to /dev/net/tun for TUN/TAP devices + DeviceAllow = "/dev/net/tun rw"; + # Access to /sys for network configuration + BindReadOnlyPaths = [ + "/sys" + "/proc" + ]; + # Protect the system while giving necessary access + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + # Allow the service to manage network namespaces + PrivateNetwork = false; + }; + }; + + users.users.defguard = { + isSystemUser = true; + group = "defguard"; + }; + + users.groups.defguard = {}; + }; +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 00000000..ae9b6c50 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,115 @@ +{ + pkgs, + lib, + stdenv, + rustPlatform, + makeDesktopItem, +}: let + pname = "defguard-client"; + version = "1.5.0"; # TODO: Get this from Cargo.toml or git + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = pname; + desktopName = "Defguard"; + genericName = "Defguard VPN Client"; + categories = ["Network" "Security"]; + }; + + rustToolchain = pkgs.rust-bin.stable.latest.default; + + buildInputs = with pkgs; [ + at-spi2-atk + atkmm + cairo + dbus + gdk-pixbuf + glib + glib-networking + gtk4 + harfbuzz + librsvg + libsoup_3 + pango + webkitgtk_4_1 + openssl + libayatana-appindicator + desktop-file-utils + ]; + + nativeBuildInputs = with pkgs; [ + rustToolchain + pkg-config + gobject-introspection + cargo-tauri + nodejs_24 + protobuf + pnpm + # configures pnpm to use pre-fetched dependencies + pnpm.configHook + # configures cargo to use pre-fetched dependencies + rustPlatform.cargoSetupHook + # perl + wrapGAppsHook + # helper to add dynamic library paths + makeWrapper + ]; +in + stdenv.mkDerivation (finalAttrs: rec { + inherit pname version buildInputs nativeBuildInputs; + + src = ../.; + + # prefetch cargo dependencies + cargoRoot = "src-tauri"; + buildAndTestSubdir = "src-tauri"; + + cargoDeps = rustPlatform.importCargoLock { + lockFile = ../src-tauri/Cargo.lock; + }; + + # prefetch pnpm dependencies + pnpmDeps = pkgs.pnpm.fetchDeps { + inherit + (finalAttrs) + pname + version + src + ; + + fetcherVersion = 2; + hash = "sha256-GlgQuPpOibPrItt6X9EqV4QmCOyajZh5yy7gHh+O+ME="; + }; + + buildPhase = '' + pnpm tauri build + ''; + + postInstall = '' + # copy client binary + mkdir -p $out/bin + cp src-tauri/target/release/${pname} $out/bin/ + # copy service binary + mkdir -p $out/bin + cp src-tauri/target/release/defguard-service $out/bin/ + # copy cli binary + mkdir -p $out/bin + cp src-tauri/target/release/dg $out/bin/ + + # add required library to client binary RPATH + wrapProgram $out/bin/${pname} \ + --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [pkgs.libayatana-appindicator]} + + mkdir -p $out/share/applications + cp ${desktopItem}/share/applications/* $out/share/applications/ + ''; + + meta = with lib; { + description = "Defguard VPN Client"; + homepage = "https://defguard.net"; + # license = licenses.gpl3Only; + maintainers = with maintainers; []; + platforms = platforms.linux; + }; + }) diff --git a/nix/shell.nix b/nix/shell.nix new file mode 100644 index 00000000..3de733fd --- /dev/null +++ b/nix/shell.nix @@ -0,0 +1,37 @@ +{pkgs ? import {}}: let + # add development-related cargo tooling + rustToolchain = pkgs.rust-bin.stable.latest.default.override { + extensions = ["rust-analyzer" "rust-src" "rustfmt" "clippy"]; + targets = ["x86_64-apple-darwin" "aarch64-apple-darwin" "x86_64-pc-windows-gnu"]; + }; + + defguard-client = pkgs.callPackage ./package.nix {}; + + # runtime libraries needed to run the dev server + libraries = with pkgs; [ + libayatana-appindicator + ]; +in + pkgs.mkShell { + # inherit build inputs from the package + inputsFrom = [defguard-client]; + + # add additional dev tools + packages = with pkgs; [ + trunk + sqlx-cli + vtsls + ]; + + shellHook = with pkgs; '' + export LD_LIBRARY_PATH="${ + lib.makeLibraryPath libraries + }:$LD_LIBRARY_PATH" + export OPENSSL_INCLUDE_DIR="${pkgs.openssl.dev}/include/openssl" + export OPENSSL_LIB_DIR="${pkgs.openssl.out}/lib" + export OPENSSL_ROOT_DIR="${pkgs.openssl.out}" + # https://discourse.nixos.org/t/which-package-includes-org-gtk-gtk4-settings-filechooser/38063/12 + export XDG_DATA_DIRS="${pkgs.gtk3}/share/gsettings-schemas/gtk+3-${pkgs.gtk3.dev.version}:$XDG_DATA_DIRS" + export RUST_SRC_PATH="${rustToolchain}/lib/rustlib/src/rust/library" + ''; + } diff --git a/package.json b/package.json index 17f2e537..914e85fa 100644 --- a/package.json +++ b/package.json @@ -5,20 +5,23 @@ "type": "module", "scripts": { "dev": "npm-run-all --parallel vite typesafe-i18n", - "build": "tsc && vite build", + "typecheck": "tsc --project ./tsconfig.app.json", + "build": "pnpm run typecheck && vite build", "preview": "vite preview", "typesafe-i18n": "typesafe-i18n", "generate-translation-types": "typesafe-i18n --no-watch", - "lint": "eslint --config ./.eslintrc.cjs src && prettier --check src/**/*.{ts,tsx,scss} && tsc", - "fix": "prettier --end-of-line lf -w src/**/*.{ts,tsx,scss} && eslint --fix --config ./.eslintrc.cjs src", + "fix": "biome check --fix && prettier src/**/*.scss -w --log-level silent", + "fix-unsafe": "biome check --fix --unsafe && prettier src/**/*.scss -w --log-level silent", + "lint": "biome lint && pnpm run typecheck && prettier src/**/*.scss --check --log-level error", + "lint-ci": "biome ci && pnpm run typecheck && prettier src/**/*.scss --check --log-level error", "vite": "vite", - "eslint": "eslint", "prettier": "prettier", "parse-client-svgs": "svgr --no-index --jsx-runtime automatic --svgo-config ./svgo.config.json --prettier-config ./.prettierrc --out-dir ./src/shared/components/svg/ --typescript ./src/shared/images/svg/", "parse-ui-svgs": "svgr --no-index --jsx-runtime automatic --svgo-config ./svgo.config.json --prettier-config ./.prettierrc --out-dir ./src/shared/defguard-ui/components/svg/ --typescript ./src/shared/defguard-ui/images/svg/", "parse-svgs": "pnpm parse-ui-svgs && pnpm parse-client-svgs", "svgr": "svgr", - "tauri": "tauri" + "tauri": "tauri", + "biome": "biome" }, "browserslist": { "production": [ @@ -37,90 +40,94 @@ "ignoreMissing": [ "react-native" ] - } + }, + "onlyBuiltDependencies": [ + "@swc/core", + "esbuild" + ] }, "dependencies": { - "@floating-ui/react": "^0.26.28", + "@floating-ui/react": "^0.27.16", "@hookform/resolvers": "^3.10.0", "@react-hook/resize-observer": "^2.0.2", - "@stablelib/base64": "^1.0.1", - "@stablelib/x25519": "^1.0.3", - "@tanstack/query-core": "^5.76.0", - "@tanstack/react-virtual": "3.0.0-beta.54", - "@tauri-apps/api": "^1.6.0", + "@stablelib/base64": "^2.0.1", + "@stablelib/x25519": "^2.0.1", + "@tanstack/query-core": "^5.87.4", + "@tanstack/react-virtual": "3.13.12", + "@tauri-apps/api": "^2.8.0", + "@tauri-apps/plugin-clipboard-manager": "^2.3.0", + "@tauri-apps/plugin-deep-link": "^2.4.3", + "@tauri-apps/plugin-dialog": "^2.4.0", + "@tauri-apps/plugin-fs": "^2.4.2", + "@tauri-apps/plugin-http": "^2.5.2", + "@tauri-apps/plugin-log": "^2.7.0", + "@tauri-apps/plugin-notification": "^2.3.1", + "@tauri-apps/plugin-opener": "^2.5.0", + "@tauri-apps/plugin-os": "^2.3.1", + "@tauri-apps/plugin-window-state": "^2.4.0", "@types/byte-size": "^8.1.2", "@use-gesture/react": "^10.3.1", - "byte-size": "^8.2.1", + "byte-size": "^9.0.1", "classnames": "^2.5.1", "clsx": "^2.1.1", "compare-versions": "^6.1.1", - "dayjs": "^1.11.13", + "dayjs": "^1.11.18", "deepmerge-ts": "^7.1.5", "detect-browser": "^5.3.0", "fast-deep-equal": "^3.1.3", "file-saver": "^2.0.5", - "framer-motion": "^10.18.0", "get-text-width": "^1.0.3", - "html-react-parser": "^5.2.5", - "itertools": "^2.4.1", + "html-react-parser": "^5.2.6", + "itertools": "^2.5.0", + "js-base64": "^3.7.8", "lodash-es": "^4.17.21", - "merge-refs": "^1.3.0", + "merge-refs": "^2.0.0", "millify": "^6.1.0", + "motion": "^12.23.12", "p-timeout": "^6.1.4", "prop-types": "^15.8.1", - "radash": "^11.0.0", - "react": "^18.3.1", + "radash": "^12.1.1", + "react": "^19.1.1", "react-auth-code-input": "^3.2.1", "react-click-away-listener": "^2.4.0", - "react-dom": "^18.3.1", - "react-hook-form": "^7.56.4", + "react-dom": "^19.1.1", + "react-hook-form": "^7.62.0", "react-loading-skeleton": "^3.5.0", - "react-markdown": "^9.1.0", - "react-qr-code": "^2.0.15", - "react-router-dom": "^6.30.0", + "react-markdown": "^10.1.0", + "react-qr-code": "^2.0.18", + "react-router-dom": "^6.30.1", + "react-use-websocket": "^4.13.0", "react-virtualized-auto-sizer": "^1.0.26", - "recharts": "^2.15.3", + "recharts": "^3.2.0", "rehype-sanitize": "^6.0.0", "rxjs": "^7.8.2", - "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log", - "tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1", "use-breakpoint": "^4.0.6", - "zod": "^3.24.4", - "zustand": "^5.0.4" + "zod": "^3.25.76", + "zustand": "^5.0.8" }, "devDependencies": { + "@biomejs/biome": "^2.2.4", "@hookform/devtools": "^4.4.0", "@svgr/cli": "^8.1.0", - "@tanstack/react-query": "^5.76.1", - "@tanstack/react-query-devtools": "^5.76.1", - "@tauri-apps/cli": "^1.6.3", + "@tanstack/react-query": "^5.87.4", + "@tanstack/react-query-devtools": "^5.87.4", + "@tauri-apps/cli": "^2.8.4", "@types/file-saver": "^2.0.7", "@types/lodash-es": "^4.17.12", - "@types/node": "^20.17.48", - "@types/react": "^18.3.21", - "@types/react-dom": "^18.3.7", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "@vitejs/plugin-react": "^4.4.1", - "@vitejs/plugin-react-swc": "^3.9.0", + "@types/node": "^24.4.0", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.2", + "@vitejs/plugin-react-swc": "^4.0.1", "autoprefixer": "^10.4.21", - "eslint": "^8.57.1", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.4.0", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.20", - "eslint-plugin-simple-import-sort": "^10.0.0", "npm-run-all": "^4.1.5", - "postcss": "^8.5.3", - "prettier": "^3.5.3", - "sass": "~1.70.0", - "typedoc": "^0.25.13", + "postcss": "^8.5.6", + "prettier": "^3.6.2", + "sass": "~1.92.1", + "typedoc": "^0.28.13", "typesafe-i18n": "^5.26.2", - "typescript": "^5.8.3", - "typescript-eslint-language-service": "^5.0.5", - "vite": "^4.5.14" + "typescript": "^5.9.2", + "vite": "^7.1.5" }, "volta": { "node": "20.5.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b1d0ada..0aeabe7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,38 +9,68 @@ importers: .: dependencies: '@floating-ui/react': - specifier: ^0.26.28 - version: 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^0.27.16 + version: 0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@hookform/resolvers': specifier: ^3.10.0 - version: 3.10.0(react-hook-form@7.56.4(react@18.3.1)) + version: 3.10.0(react-hook-form@7.62.0(react@19.1.1)) '@react-hook/resize-observer': specifier: ^2.0.2 - version: 2.0.2(react@18.3.1) + version: 2.0.2(react@19.1.1) '@stablelib/base64': - specifier: ^1.0.1 - version: 1.0.1 + specifier: ^2.0.1 + version: 2.0.1 '@stablelib/x25519': - specifier: ^1.0.3 - version: 1.0.3 + specifier: ^2.0.1 + version: 2.0.1 '@tanstack/query-core': - specifier: ^5.76.0 - version: 5.76.0 + specifier: ^5.87.4 + version: 5.87.4 '@tanstack/react-virtual': - specifier: 3.0.0-beta.54 - version: 3.0.0-beta.54(react@18.3.1) + specifier: 3.13.12 + version: 3.13.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@tauri-apps/api': - specifier: ^1.6.0 - version: 1.6.0 + specifier: ^2.8.0 + version: 2.8.0 + '@tauri-apps/plugin-clipboard-manager': + specifier: ^2.3.0 + version: 2.3.0 + '@tauri-apps/plugin-deep-link': + specifier: ^2.4.3 + version: 2.4.3 + '@tauri-apps/plugin-dialog': + specifier: ^2.4.0 + version: 2.4.0 + '@tauri-apps/plugin-fs': + specifier: ^2.4.2 + version: 2.4.2 + '@tauri-apps/plugin-http': + specifier: ^2.5.2 + version: 2.5.2 + '@tauri-apps/plugin-log': + specifier: ^2.7.0 + version: 2.7.0 + '@tauri-apps/plugin-notification': + specifier: ^2.3.1 + version: 2.3.1 + '@tauri-apps/plugin-opener': + specifier: ^2.5.0 + version: 2.5.0 + '@tauri-apps/plugin-os': + specifier: ^2.3.1 + version: 2.3.1 + '@tauri-apps/plugin-window-state': + specifier: ^2.4.0 + version: 2.4.0 '@types/byte-size': specifier: ^8.1.2 version: 8.1.2 '@use-gesture/react': specifier: ^10.3.1 - version: 10.3.1(react@18.3.1) + version: 10.3.1(react@19.1.1) byte-size: - specifier: ^8.2.1 - version: 8.2.1 + specifier: ^9.0.1 + version: 9.0.1 classnames: specifier: ^2.5.1 version: 2.5.1 @@ -51,8 +81,8 @@ importers: specifier: ^6.1.1 version: 6.1.1 dayjs: - specifier: ^1.11.13 - version: 1.11.13 + specifier: ^1.11.18 + version: 1.11.18 deepmerge-ts: specifier: ^7.1.5 version: 7.1.5 @@ -65,27 +95,30 @@ importers: file-saver: specifier: ^2.0.5 version: 2.0.5 - framer-motion: - specifier: ^10.18.0 - version: 10.18.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) get-text-width: specifier: ^1.0.3 version: 1.0.3 html-react-parser: - specifier: ^5.2.5 - version: 5.2.5(@types/react@18.3.21)(react@18.3.1) + specifier: ^5.2.6 + version: 5.2.6(@types/react@19.1.13)(react@19.1.1) itertools: - specifier: ^2.4.1 - version: 2.4.1 + specifier: ^2.5.0 + version: 2.5.0 + js-base64: + specifier: ^3.7.8 + version: 3.7.8 lodash-es: specifier: ^4.17.21 version: 4.17.21 merge-refs: - specifier: ^1.3.0 - version: 1.3.0(@types/react@18.3.21) + specifier: ^2.0.0 + version: 2.0.0(@types/react@19.1.13) millify: specifier: ^6.1.0 version: 6.1.0 + motion: + specifier: ^12.23.12 + version: 12.23.12(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) p-timeout: specifier: ^6.1.4 version: 6.1.4 @@ -93,78 +126,78 @@ importers: specifier: ^15.8.1 version: 15.8.1 radash: - specifier: ^11.0.0 - version: 11.0.0 + specifier: ^12.1.1 + version: 12.1.1 react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.1.1 + version: 19.1.1 react-auth-code-input: specifier: ^3.2.1 - version: 3.2.1(react@18.3.1) + version: 3.2.1(react@19.1.1) react-click-away-listener: specifier: ^2.4.0 - version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: ^19.1.1 + version: 19.1.1(react@19.1.1) react-hook-form: - specifier: ^7.56.4 - version: 7.56.4(react@18.3.1) + specifier: ^7.62.0 + version: 7.62.0(react@19.1.1) react-loading-skeleton: specifier: ^3.5.0 - version: 3.5.0(react@18.3.1) + version: 3.5.0(react@19.1.1) react-markdown: - specifier: ^9.1.0 - version: 9.1.0(@types/react@18.3.21)(react@18.3.1) + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.1.13)(react@19.1.1) react-qr-code: - specifier: ^2.0.15 - version: 2.0.15(react@18.3.1) + specifier: ^2.0.18 + version: 2.0.18(react@19.1.1) react-router-dom: - specifier: ^6.30.0 - version: 6.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^6.30.1 + version: 6.30.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react-use-websocket: + specifier: ^4.13.0 + version: 4.13.0 react-virtualized-auto-sizer: specifier: ^1.0.26 - version: 1.0.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1) recharts: - specifier: ^2.15.3 - version: 2.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^3.2.0 + version: 3.2.0(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react-is@18.3.1)(react@19.1.1)(redux@5.0.1) rehype-sanitize: specifier: ^6.0.0 version: 6.0.0 rxjs: specifier: ^7.8.2 version: 7.8.2 - tauri-plugin-log-api: - specifier: github:tauri-apps/tauri-plugin-log - version: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/d33f0f4f8dbb02e705a83a471cdbbb9cb7b73a40 - tauri-plugin-window-state-api: - specifier: github:tauri-apps/tauri-plugin-window-state#v1 - version: https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/12ac7a31bf71ab5c3e87add90d44b47cbfe95652 use-breakpoint: specifier: ^4.0.6 - version: 4.0.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.0.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) zod: - specifier: ^3.24.4 - version: 3.24.4 + specifier: ^3.25.76 + version: 3.25.76 zustand: - specifier: ^5.0.4 - version: 5.0.4(@types/react@18.3.21)(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) + specifier: ^5.0.8 + version: 5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) devDependencies: + '@biomejs/biome': + specifier: ^2.2.4 + version: 2.2.4 '@hookform/devtools': specifier: ^4.4.0 - version: 4.4.0(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.4.0(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@svgr/cli': specifier: ^8.1.0 - version: 8.1.0(typescript@5.8.3) + version: 8.1.0(typescript@5.9.2) '@tanstack/react-query': - specifier: ^5.76.1 - version: 5.76.1(react@18.3.1) + specifier: ^5.87.4 + version: 5.87.4(react@19.1.1) '@tanstack/react-query-devtools': - specifier: ^5.76.1 - version: 5.76.1(@tanstack/react-query@5.76.1(react@18.3.1))(react@18.3.1) + specifier: ^5.87.4 + version: 5.87.4(@tanstack/react-query@5.87.4(react@19.1.1))(react@19.1.1) '@tauri-apps/cli': - specifier: ^1.6.3 - version: 1.6.3 + specifier: ^2.8.4 + version: 2.8.4 '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 @@ -172,113 +205,80 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^20.17.48 - version: 20.17.48 + specifier: ^24.4.0 + version: 24.4.0 '@types/react': - specifier: ^18.3.21 - version: 18.3.21 + specifier: ^19.1.13 + version: 19.1.13 '@types/react-dom': - specifier: ^18.3.7 - version: 18.3.7(@types/react@18.3.21) - '@typescript-eslint/eslint-plugin': - specifier: ^6.21.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/parser': - specifier: ^6.21.0 - version: 6.21.0(eslint@8.57.1)(typescript@5.8.3) + specifier: ^19.1.9 + version: 19.1.9(@types/react@19.1.13) '@vitejs/plugin-react': - specifier: ^4.4.1 - version: 4.4.1(vite@4.5.14(@types/node@20.17.48)(sass@1.70.0)) + specifier: ^5.0.2 + version: 5.0.2(vite@7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1)) '@vitejs/plugin-react-swc': - specifier: ^3.9.0 - version: 3.9.0(vite@4.5.14(@types/node@20.17.48)(sass@1.70.0)) + specifier: ^4.0.1 + version: 4.0.1(vite@7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1)) autoprefixer: specifier: ^10.4.21 - version: 10.4.21(postcss@8.5.3) - eslint: - specifier: ^8.57.1 - version: 8.57.1 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.1) - eslint-plugin-import: - specifier: ^2.31.0 - version: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1) - eslint-plugin-prettier: - specifier: ^5.4.0 - version: 5.4.0(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.5.3) - eslint-plugin-react: - specifier: ^7.37.5 - version: 7.37.5(eslint@8.57.1) - eslint-plugin-react-hooks: - specifier: ^4.6.2 - version: 4.6.2(eslint@8.57.1) - eslint-plugin-react-refresh: - specifier: ^0.4.20 - version: 0.4.20(eslint@8.57.1) - eslint-plugin-simple-import-sort: - specifier: ^10.0.0 - version: 10.0.0(eslint@8.57.1) + version: 10.4.21(postcss@8.5.6) npm-run-all: specifier: ^4.1.5 version: 4.1.5 postcss: - specifier: ^8.5.3 - version: 8.5.3 + specifier: ^8.5.6 + version: 8.5.6 prettier: - specifier: ^3.5.3 - version: 3.5.3 + specifier: ^3.6.2 + version: 3.6.2 sass: - specifier: ~1.70.0 - version: 1.70.0 + specifier: ~1.92.1 + version: 1.92.1 typedoc: - specifier: ^0.25.13 - version: 0.25.13(typescript@5.8.3) + specifier: ^0.28.13 + version: 0.28.13(typescript@5.9.2) typesafe-i18n: specifier: ^5.26.2 - version: 5.26.2(typescript@5.8.3) + version: 5.26.2(typescript@5.9.2) typescript: - specifier: ^5.8.3 - version: 5.8.3 - typescript-eslint-language-service: - specifier: ^5.0.5 - version: 5.0.5(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) + specifier: ^5.9.2 + version: 5.9.2 vite: - specifier: ^4.5.14 - version: 4.5.14(@types/node@20.17.48)(sass@1.70.0) + specifier: ^7.1.5 + version: 7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1) packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.27.2': - resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.1': - resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.1': - resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.27.1': - resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -299,12 +299,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.1': - resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true @@ -320,22 +320,75 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.1': - resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@biomejs/biome@2.2.4': + resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.4': + resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.4': + resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.4': + resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.4': + resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.4': + resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.4': + resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.4': + resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.4': + resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -345,14 +398,8 @@ packages: '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - '@emotion/is-prop-valid@0.8.8': - resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} - - '@emotion/is-prop-valid@1.3.1': - resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} - - '@emotion/memoize@0.7.4': - resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} '@emotion/memoize@0.9.0': resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} @@ -372,8 +419,8 @@ packages: '@emotion/sheet@1.4.0': resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} - '@emotion/styled@11.14.0': - resolution: {integrity: sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==} + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 '@types/react': '*' @@ -396,176 +443,185 @@ packages: '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} - '@floating-ui/core@1.7.0': - resolution: {integrity: sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==} - - '@floating-ui/dom@1.7.0': - resolution: {integrity: sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==} - - '@floating-ui/react-dom@2.1.2': - resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/react@0.26.28': - resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} + '@floating-ui/react@0.27.16': + resolution: {integrity: sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==} peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' + react: '>=17.0.0' + react-dom: '>=17.0.0' - '@floating-ui/utils@0.2.9': - resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@gerrit0/mini-shiki@3.12.2': + resolution: {integrity: sha512-HKZPmO8OSSAAo20H2B3xgJdxZaLTwtlMwxg0967scnrDlPwe6j5+ULGHyIqwgTbFCn9yv/ff8CmfWZLE9YKBzA==} '@hookform/devtools@4.4.0': resolution: {integrity: sha512-Mtlic+uigoYBPXlfvPBfiYYUZuyMrD3pTjDpVIhL6eCZTvQkHsKBSKeZCvXWUZr8fqrkzDg27N+ZuazLKq6Vmg==} @@ -578,52 +634,103 @@ packages: peerDependencies: react-hook-form: ^7.0.0 - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] - '@pkgr/core@0.2.4': - resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} '@react-hook/latest@1.0.3': resolution: {integrity: sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==} @@ -640,36 +747,176 @@ packages: peerDependencies: react: '>=18' + '@reduxjs/toolkit@2.9.0': + resolution: {integrity: sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@remix-run/router@1.23.0': resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} - '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@rolldown/pluginutils@1.0.0-beta.32': + resolution: {integrity: sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==} + + '@rolldown/pluginutils@1.0.0-beta.34': + resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} + + '@rollup/rollup-android-arm-eabi@4.50.2': + resolution: {integrity: sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.50.2': + resolution: {integrity: sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.50.2': + resolution: {integrity: sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.50.2': + resolution: {integrity: sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.50.2': + resolution: {integrity: sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.50.2': + resolution: {integrity: sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.50.2': + resolution: {integrity: sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.50.2': + resolution: {integrity: sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.50.2': + resolution: {integrity: sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.50.2': + resolution: {integrity: sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.50.2': + resolution: {integrity: sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.50.2': + resolution: {integrity: sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.50.2': + resolution: {integrity: sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.50.2': + resolution: {integrity: sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.50.2': + resolution: {integrity: sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.50.2': + resolution: {integrity: sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.50.2': + resolution: {integrity: sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.50.2': + resolution: {integrity: sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.2': + resolution: {integrity: sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.50.2': + resolution: {integrity: sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.50.2': + resolution: {integrity: sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==} + cpu: [x64] + os: [win32] + + '@shikijs/engine-oniguruma@3.12.2': + resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==} + + '@shikijs/langs@3.12.2': + resolution: {integrity: sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==} + + '@shikijs/themes@3.12.2': + resolution: {integrity: sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==} + + '@shikijs/types@3.12.2': + resolution: {integrity: sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@stablelib/base64@2.0.1': + resolution: {integrity: sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ==} + + '@stablelib/binary@2.0.1': + resolution: {integrity: sha512-U9iAO8lXgEDONsA0zPPSgcf3HUBNAqHiJmSHgZz62OvC3Hi2Bhc5kTnQ3S1/L+sthDTHtCMhcEiklmIly6uQ3w==} - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@stablelib/bytes@2.0.1': + resolution: {integrity: sha512-QIzI6V7nkJA5CjOZ7GoceBd4CIKrJoC471VaI6jh1xPQ2cMhkhQK4HddyzCXOR2y+fBF3/5B2HO3FXXI9C+Xzg==} - '@stablelib/binary@1.0.1': - resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} + '@stablelib/int@2.0.1': + resolution: {integrity: sha512-Ht63fQp3wz/F8U4AlXEPb7hfJOIILs8Lq55jgtD7KueWtyjhVuzcsGLSTAWtZs3XJDZYdF1WcSKn+kBtbzupww==} - '@stablelib/bytes@1.0.1': - resolution: {integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==} + '@stablelib/keyagreement@2.0.1': + resolution: {integrity: sha512-2+tWBLCMtWlHQ7GqjD5L+lQRyWtun4Lou0IOdTML8zuTuAS0EgihnHFx+4uMZwYU1In40J/WlpyKSLidHfStRQ==} - '@stablelib/int@1.0.1': - resolution: {integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==} + '@stablelib/random@2.0.1': + resolution: {integrity: sha512-W6GAtXEEs7r+dSbuBsvoFmlyL3gLxle41tQkjKu17dDWtDdjhVUbtRfRCQcCUeczwkgjQxMPopgwYEvxXtHXGw==} - '@stablelib/keyagreement@1.0.1': - resolution: {integrity: sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==} + '@stablelib/wipe@2.0.1': + resolution: {integrity: sha512-1eU2K9EgOcV4qc9jcP6G72xxZxEm5PfeI5H55l08W95b4oRJaqhmlWRc4xZAm6IVSKhVNxMi66V67hCzzuMTAg==} - '@stablelib/random@1.0.2': - resolution: {integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==} + '@stablelib/x25519@2.0.1': + resolution: {integrity: sha512-qi04HS2puHaBf50kM/kes5QcZFGsx8yF0YmCjLCOa/LPmnBaKEKX9ZR82OnnCwMn72YH13R/bBZgr/UP0aPFfA==} - '@stablelib/wipe@1.0.1': - resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@stablelib/x25519@1.0.3': - resolution: {integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} @@ -756,68 +1003,68 @@ packages: peerDependencies: '@svgr/core': '*' - '@swc/core-darwin-arm64@1.11.24': - resolution: {integrity: sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==} + '@swc/core-darwin-arm64@1.13.5': + resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.11.24': - resolution: {integrity: sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==} + '@swc/core-darwin-x64@1.13.5': + resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.11.24': - resolution: {integrity: sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==} + '@swc/core-linux-arm-gnueabihf@1.13.5': + resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.11.24': - resolution: {integrity: sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==} + '@swc/core-linux-arm64-gnu@1.13.5': + resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.11.24': - resolution: {integrity: sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==} + '@swc/core-linux-arm64-musl@1.13.5': + resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.11.24': - resolution: {integrity: sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==} + '@swc/core-linux-x64-gnu@1.13.5': + resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.11.24': - resolution: {integrity: sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==} + '@swc/core-linux-x64-musl@1.13.5': + resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.11.24': - resolution: {integrity: sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==} + '@swc/core-win32-arm64-msvc@1.13.5': + resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.11.24': - resolution: {integrity: sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==} + '@swc/core-win32-ia32-msvc@1.13.5': + resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.11.24': - resolution: {integrity: sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==} + '@swc/core-win32-x64-msvc@1.13.5': + resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.11.24': - resolution: {integrity: sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==} + '@swc/core@1.13.5': + resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -828,103 +1075,139 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/types@0.1.21': - resolution: {integrity: sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==} + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - '@tanstack/query-core@5.76.0': - resolution: {integrity: sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==} + '@tanstack/query-core@5.87.4': + resolution: {integrity: sha512-uNsg6zMxraEPDVO2Bn+F3/ctHi+Zsk+MMpcN8h6P7ozqD088F6mFY5TfGM7zuyIrL7HKpDyu6QHfLWiDxh3cuw==} - '@tanstack/query-devtools@5.76.0': - resolution: {integrity: sha512-1p92nqOBPYVqVDU0Ua5nzHenC6EGZNrLnB2OZphYw8CNA1exuvI97FVgIKON7Uug3uQqvH/QY8suUKpQo8qHNQ==} + '@tanstack/query-devtools@5.87.3': + resolution: {integrity: sha512-LkzxzSr2HS1ALHTgDmJH5eGAVsSQiuwz//VhFW5OqNk0OQ+Fsqba0Tsf+NzWRtXYvpgUqwQr4b2zdFZwxHcGvg==} - '@tanstack/react-query-devtools@5.76.1': - resolution: {integrity: sha512-LFVWgk/VtXPkerNLfYIeuGHh0Aim/k9PFGA+JxLdRaUiroQ4j4eoEqBrUpQ1Pd/KXoG4AB9vVE/M6PUQ9vwxBQ==} + '@tanstack/react-query-devtools@5.87.4': + resolution: {integrity: sha512-JYcnVJBBW1DCPyNGM0S2CyrLpe6KFiL2gpYd/k9tAp62Du7+Y27zkzd+dKFyxpFadYaTxsx4kUA7YvnkMLVUoQ==} peerDependencies: - '@tanstack/react-query': ^5.76.1 + '@tanstack/react-query': ^5.87.4 react: ^18 || ^19 - '@tanstack/react-query@5.76.1': - resolution: {integrity: sha512-YxdLZVGN4QkT5YT1HKZQWiIlcgauIXEIsMOTSjvyD5wLYK8YVvKZUPAysMqossFJJfDpJW3pFn7WNZuPOqq+fw==} + '@tanstack/react-query@5.87.4': + resolution: {integrity: sha512-T5GT/1ZaNsUXf5I3RhcYuT17I4CPlbZgyLxc/ZGv7ciS6esytlbjb3DgUFO6c8JWYMDpdjSWInyGZUErgzqhcA==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-virtual@3.0.0-beta.54': - resolution: {integrity: sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ==} + '@tanstack/react-virtual@3.13.12': + resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.0.0-beta.54': - resolution: {integrity: sha512-jtkwqdP2rY2iCCDVAFuaNBH3fiEi29aTn2RhtIoky8DTTiCdc48plpHHreLwmv1PICJ4AJUUESaq3xa8fZH8+g==} + '@tanstack/virtual-core@3.13.12': + resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} - '@tauri-apps/api@1.6.0': - resolution: {integrity: sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==} - engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} + '@tauri-apps/api@2.8.0': + resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} - '@tauri-apps/cli-darwin-arm64@1.6.3': - resolution: {integrity: sha512-fQN6IYSL8bG4NvkdKE4sAGF4dF/QqqQq4hOAU+t8ksOzHJr0hUlJYfncFeJYutr/MMkdF7hYKadSb0j5EE9r0A==} + '@tauri-apps/cli-darwin-arm64@2.8.4': + resolution: {integrity: sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@1.6.3': - resolution: {integrity: sha512-1yTXZzLajKAYINJOJhZfmMhCzweHSgKQ3bEgJSn6t+1vFkOgY8Yx4oFgWcybrrWI5J1ZLZAl47+LPOY81dLcyA==} + '@tauri-apps/cli-darwin-x64@2.8.4': + resolution: {integrity: sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@1.6.3': - resolution: {integrity: sha512-CjTEr9r9xgjcvos09AQw8QMRPuH152B1jvlZt4PfAsyJNPFigzuwed5/SF7XAd8bFikA7zArP4UT12RdBxrx7w==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': + resolution: {integrity: sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@1.6.3': - resolution: {integrity: sha512-G9EUUS4M8M/Jz1UKZqvJmQQCKOzgTb8/0jZKvfBuGfh5AjFBu8LHvlFpwkKVm1l4951Xg4ulUp6P9Q7WRJ9XSA==} + '@tauri-apps/cli-linux-arm64-gnu@2.8.4': + resolution: {integrity: sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@1.6.3': - resolution: {integrity: sha512-MuBTHJyNpZRbPVG8IZBN8+Zs7aKqwD22tkWVBcL1yOGL4zNNTJlkfL+zs5qxRnHlUsn6YAlbW/5HKocfpxVwBw==} + '@tauri-apps/cli-linux-arm64-musl@2.8.4': + resolution: {integrity: sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-x64-gnu@1.6.3': - resolution: {integrity: sha512-Uvi7M+NK3tAjCZEY1WGel+dFlzJmqcvu3KND+nqa22762NFmOuBIZ4KJR/IQHfpEYqKFNUhJfCGnpUDfiC3Oxg==} + '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': + resolution: {integrity: sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.8.4': + resolution: {integrity: sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@1.6.3': - resolution: {integrity: sha512-rc6B342C0ra8VezB/OJom9j/N+9oW4VRA4qMxS2f4bHY2B/z3J9NPOe6GOILeg4v/CV62ojkLsC3/K/CeF3fqQ==} + '@tauri-apps/cli-linux-x64-musl@2.8.4': + resolution: {integrity: sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-win32-arm64-msvc@1.6.3': - resolution: {integrity: sha512-cSH2qOBYuYC4UVIFtrc1YsGfc5tfYrotoHrpTvRjUGu0VywvmyNk82+ZsHEnWZ2UHmu3l3lXIGRqSWveLln0xg==} + '@tauri-apps/cli-win32-arm64-msvc@2.8.4': + resolution: {integrity: sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@1.6.3': - resolution: {integrity: sha512-T8V6SJQqE4PSWmYBl0ChQVmS6AR2hXFHURH2DwAhgSGSQ6uBXgwlYFcfIeQpBQA727K2Eq8X2hGfvmoySyHMRw==} + '@tauri-apps/cli-win32-ia32-msvc@2.8.4': + resolution: {integrity: sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@1.6.3': - resolution: {integrity: sha512-HUkWZ+lYHI/Gjkh2QjHD/OBDpqLVmvjZGpLK9losur1Eg974Jip6k+vsoTUxQBCBDfj30eDBct9E1FvXOspWeg==} + '@tauri-apps/cli-win32-x64-msvc@2.8.4': + resolution: {integrity: sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@1.6.3': - resolution: {integrity: sha512-q46umd6QLRKDd4Gg6WyZBGa2fWvk0pbeUA5vFomm4uOs1/17LIciHv2iQ4UD+2Yv5H7AO8YiE1t50V0POiEGEw==} + '@tauri-apps/cli@2.8.4': + resolution: {integrity: sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==} engines: {node: '>= 10'} hasBin: true + '@tauri-apps/plugin-clipboard-manager@2.3.0': + resolution: {integrity: sha512-81NOBA2P+OTY8RLkBwyl9ZR/0CeggLub4F6zxcxUIfFOAqtky7J61+K/MkH2SC1FMxNBxrX0swDuKvkjkHadlA==} + + '@tauri-apps/plugin-deep-link@2.4.3': + resolution: {integrity: sha512-yVCZpVG1ZrtfCvE7K5LRSrGqlyPlCrqlKgoREJHnfjyYdDtUhFmZqScOXpL8XL2PizJHDsoahEweuTaUPEokPA==} + + '@tauri-apps/plugin-dialog@2.4.0': + resolution: {integrity: sha512-OvXkrEBfWwtd8tzVCEXIvRfNEX87qs2jv6SqmVPiHcJjBhSF/GUvjqUNIDmKByb5N8nvDqVUM7+g1sXwdC/S9w==} + + '@tauri-apps/plugin-fs@2.4.2': + resolution: {integrity: sha512-YGhmYuTgXGsi6AjoV+5mh2NvicgWBfVJHHheuck6oHD+HC9bVWPaHvCP0/Aw4pHDejwrvT8hE3+zZAaWf+hrig==} + + '@tauri-apps/plugin-http@2.5.2': + resolution: {integrity: sha512-x1mQKHSLDk4mS2S938OTeyk8L7QyLpCrKZCZcjkljGsvTvRMojCvI9SeJ1kaxc7t8xSilkC7WdId8xER9TIGLg==} + + '@tauri-apps/plugin-log@2.7.0': + resolution: {integrity: sha512-81XQ2f93x4vmIB5OY0XlYAxy60cHdYLs0Ki8Qp38tNATRiuBit+Orh3frpY3qfYQnqEvYVyRub7YRJWlmW2RRA==} + + '@tauri-apps/plugin-notification@2.3.1': + resolution: {integrity: sha512-7gqgfANSREKhh35fY1L4j3TUjUdePmU735FYDqRGeIf8nMXWpcx6j4FhN9/4nYz+m0mv79DCTPLqIPTySggGgg==} + + '@tauri-apps/plugin-opener@2.5.0': + resolution: {integrity: sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA==} + + '@tauri-apps/plugin-os@2.3.1': + resolution: {integrity: sha512-ty5V8XDUIFbSnrk3zsFoP3kzN+vAufYzalJSlmrVhQTImIZa1aL1a03bOaP2vuBvfR+WDRC6NgV2xBl8G07d+w==} + + '@tauri-apps/plugin-window-state@2.4.0': + resolution: {integrity: sha512-hRSzPNi2NG0lPFthfVY0V5C1MyWN/gGaQtQYw7i9zZhLzrhZveHZ2omHG1rIiIsjfTGbO7fhjydSoeTTK9GqLw==} + '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -938,14 +1221,14 @@ packages: '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.7': - resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/byte-size@8.1.2': resolution: {integrity: sha512-jGyVzYu6avI8yuqQCNTZd65tzI8HZrLjKX9sdMqZrGWVlNChu0rf6p368oVEDCYJe5BMx2Ov04tD1wqtgTwGSA==} - '@types/d3-array@3.2.1': - resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} @@ -977,8 +1260,8 @@ packages: '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/file-saver@2.0.7': resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} @@ -986,17 +1269,11 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - '@types/lodash@4.17.16': - resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -1004,25 +1281,19 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@20.17.48': - resolution: {integrity: sha512-KpSfKOHPsiSC4IkZeu2LsusFwExAIVGkhG1KkbaBMLwau0uMhj0fCrvyg9ddM2sAvd+gtiBJLir4LAw1MNMIaw==} + '@types/node@24.4.0': + resolution: {integrity: sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - '@types/prop-types@15.7.14': - resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - - '@types/react-dom@18.3.7': - resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + '@types/react-dom@19.1.9': + resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} peerDependencies: - '@types/react': ^18.0.0 - - '@types/react@18.3.21': - resolution: {integrity: sha512-gXLBtmlcRJeT09/sI4PxVwyrku6SaNUj/6cMubjE6T6XdY1fDmBL7r0nX0jbSZPU/Xr0KuwLLZh6aOYY5d91Xw==} + '@types/react': ^19.0.0 - '@types/semver@7.7.0': - resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + '@types/react@19.1.13': + resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1030,63 +1301,8 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@6.21.0': - resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@6.21.0': - resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@6.21.0': - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/type-utils@6.21.0': - resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@6.21.0': - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/typescript-estree@6.21.0': - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@6.21.0': - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - - '@typescript-eslint/visitor-keys@6.21.0': - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} - engines: {node: ^16.0.0 || >=18.0.0} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -1099,37 +1315,22 @@ packages: peerDependencies: react: '>= 16.8.0' - '@vitejs/plugin-react-swc@3.9.0': - resolution: {integrity: sha512-jYFUSXhwMCYsh/aQTgSGLIN3Foz5wMbH9ahb0Zva//UzwZYbMiZd7oT3AU9jHT9DLswYDswsRwPU9jVF3yA48Q==} + '@vitejs/plugin-react-swc@4.0.1': + resolution: {integrity: sha512-NQhPjysi5duItyrMd5JWZFf2vNOuSMyw+EoZyTBDzk+DkfYD8WNrsUs09sELV2cr1P15nufsN25hsUBt4CKF9Q==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4 || ^5 || ^6 + vite: ^4 || ^5 || ^6 || ^7 - '@vitejs/plugin-react@4.4.1': - resolution: {integrity: sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==} - engines: {node: ^14.18.0 || >=16.0.0} + '@vitejs/plugin-react@5.0.2': + resolution: {integrity: sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-sequence-parser@1.1.3: - resolution: {integrity: sha512-+fksAx9eG3Ab6LDnLs3ZqZa8KVJ/jYnX+D4Qe1azX+LFGFAXqynCQLOdLpNYN/l9e7l6hMWwZbrnctqr6eSQSw==} - ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -1138,10 +1339,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1149,48 +1346,20 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} - array.prototype.findlastindex@1.2.6: - resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - - autoprefixer@10.4.21: - resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} @@ -1206,31 +1375,36 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} + baseline-browser-mapping@2.8.3: + resolution: {integrity: sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==} + hasBin: true boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.5: - resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} + browserslist@4.26.0: + resolution: {integrity: sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - byte-size@8.2.1: - resolution: {integrity: sha512-pph2jjc3PxRLeB2nWfJy/tMiHnS6j7xhDlkglBp3KlchnJkILdZ2DmUyZ8svDJ5Z+q431vbhuoiuOvuGhY8fjw==} + byte-size@9.0.1: + resolution: {integrity: sha512-YLe9x3rabBrcI0cueCdLS2l5ONUKywcRpTs02B8KP9/Cimhj7o3ZccGrPnRvcbyHMbb7W79/3MUJl7iGgTXKEw==} engines: {node: '>=12.17'} + peerDependencies: + '@75lb/nature': latest + peerDependenciesMeta: + '@75lb/nature': + optional: true call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} @@ -1252,8 +1426,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001718: - resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1278,9 +1452,9 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -1346,12 +1520,8 @@ packages: resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} engines: {node: '>=4.8'} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} @@ -1361,8 +1531,8 @@ packages: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} csso@5.0.5: @@ -1432,19 +1602,11 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1455,11 +1617,8 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - decode-named-character-reference@1.1.0: - resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} deepmerge-ts@7.1.5: resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} @@ -1484,24 +1643,14 @@ packages: detect-browser@5.3.0: resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1522,8 +1671,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.155: - resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} + electron-to-chromium@1.5.218: + resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1532,15 +1681,15 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.0: - resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-abstract@1.23.9: - resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -1551,10 +1700,6 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} - engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1563,17 +1708,16 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} + es-toolkit@1.39.10: + resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} + + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: @@ -1588,121 +1732,11 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-module-utils@2.12.0: - resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.31.0: - resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-prettier@5.4.0: - resolution: {integrity: sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-plugin-react-hooks@4.6.2: - resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - - eslint-plugin-react-refresh@0.4.20: - resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} - peerDependencies: - eslint: '>=8.40' - - eslint-plugin-react@7.37.5: - resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - - eslint-plugin-simple-import-sort@10.0.0: - resolution: {integrity: sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==} - peerDependencies: - eslint: '>=5.0.0' - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1710,29 +1744,14 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-equals@5.2.2: - resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} - engines: {node: '>=6.0.0'} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} @@ -1744,17 +1763,6 @@ packages: find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -1762,12 +1770,15 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@10.18.0: - resolution: {integrity: sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==} + framer-motion@12.23.12: + resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true react: optional: true react-dom: @@ -1814,39 +1825,15 @@ packages: get-text-width@1.0.3: resolution: {integrity: sha512-kv1MaexPcR/qaZ4kN8sUDjG5pRp5ptHvxcDGDBTeGld1cmo7MnlCMH22jevyvs/VV7Ran203o7qAOq2+kWw9cA==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} deprecated: Glob versions prior to v9 are no longer supported - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -1854,9 +1841,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -1906,8 +1890,8 @@ packages: html-dom-parser@5.1.1: resolution: {integrity: sha512-+o4Y4Z0CLuyemeccvGN4bAO20aauB2N9tFEAep5x4OW34kV4PTarBHm6RL02afYt2BMKcr0D2Agep8S3nJPIBg==} - html-react-parser@5.2.5: - resolution: {integrity: sha512-bRPdv8KTqG9CEQPMNGksDqmbiRfVQeOidry8pVetdh/1jQ1Edx4KX5m0lWvDD89Pt4CqTYjK1BLz6NoNVxN/Uw==} + html-react-parser@5.2.6: + resolution: {integrity: sha512-qcpPWLaSvqXi+TndiHbCa+z8qt0tVzjMwFGFBAa41ggC+ZA5BHaMIeMJla9g3VSp4SmiZb9qyQbmbpHYpIfPOg==} peerDependencies: '@types/react': 0.14 || 15 || 16 || 17 || 18 || 19 react: 0.14 || 15 || 16 || 17 || 18 || 19 @@ -1921,21 +1905,16 @@ packages: htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} + immer@10.1.3: + resolution: {integrity: sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -1975,10 +1954,6 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -2029,6 +2004,10 @@ packages: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -2037,10 +2016,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -2087,12 +2062,11 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} - engines: {node: '>= 0.4'} + itertools@2.5.0: + resolution: {integrity: sha512-4ghJEXkRGkw4veNQhfO0cLY8+zePMXbe9wGt3ckSVFtrQVyyoKCUESaG2HsjuEfidVtuIEj1Dt1BlmTL3GUWFg==} - itertools@2.4.1: - resolution: {integrity: sha512-dFTSYzmbfeNE3q/qxwAr/QdKsK6/rp+LTz8SJdTg1+lo9omXFYpDcOKw47/7TevlnC0LorR5pRSf68+yB3N0GA==} + js-base64@3.7.8: + resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2106,47 +2080,23 @@ packages: engines: {node: '>=6'} hasBin: true - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + little-state-machine@4.8.1: resolution: {integrity: sha512-liPHqaWMQ7rzZryQUDnbZ1Gclnnai3dIyaJ0nAgwZRXMzqbYrydrlCI0NDojRUbE5VYh5vu6hygEUZiH77nQkQ==} peerDependencies: @@ -2156,16 +2106,9 @@ packages: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2185,9 +2128,8 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - marked@4.3.0: - resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} - engines: {node: '>= 12'} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true math-intrinsics@1.1.0: @@ -2224,22 +2166,21 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} - merge-refs@1.3.0: - resolution: {integrity: sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==} + merge-refs@2.0.0: + resolution: {integrity: sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2318,16 +2259,29 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + motion-dom@12.23.12: + resolution: {integrity: sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + motion@12.23.12: + resolution: {integrity: sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2337,25 +2291,21 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-releases@2.0.21: + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - normalize-range@0.1.2: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} @@ -2384,41 +2334,13 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - p-timeout@6.1.4: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} @@ -2438,22 +2360,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@2.0.1: resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} engines: {node: '>=4'} - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -2472,6 +2382,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pidtree@0.3.1: resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} engines: {node: '>=0.10'} @@ -2488,25 +2402,17 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} hasBin: true - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -2516,18 +2422,15 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} qr.js@0.0.0: resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - radash@11.0.0: - resolution: {integrity: sha512-CRWxTFTDff0IELGJ/zz58yY4BDgyI14qSM5OLNKbCItJrff7m7dXbVF0kWYVCXQtPb3SXIVhXvAImH6eT7VLSg==} + radash@12.1.1: + resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==} engines: {node: '>=14.18.0'} react-auth-code-input@3.2.1: @@ -2542,13 +2445,13 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} peerDependencies: - react: ^18.3.1 + react: ^19.1.1 - react-hook-form@7.56.4: - resolution: {integrity: sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==} + react-hook-form@7.62.0: + resolution: {integrity: sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -2564,8 +2467,8 @@ packages: peerDependencies: react: '>=16.8.0' - react-markdown@9.1.0: - resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: '@types/react': '>=18' react: '>=18' @@ -2573,24 +2476,36 @@ packages: react-property@2.0.2: resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} - react-qr-code@2.0.15: - resolution: {integrity: sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw==} + react-qr-code@2.0.18: + resolution: {integrity: sha512-v1Jqz7urLMhkO6jkgJuBYhnqvXagzceg3qJUWayuCK/c6LTIonpWbwxR1f1APGd4xrW/QcQEovNrAojbUz65Tg==} peerDependencies: react: '*' + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} - react-router-dom@6.30.0: - resolution: {integrity: sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==} + react-router-dom@6.30.1: + resolution: {integrity: sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' - react-router@6.30.0: - resolution: {integrity: sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==} + react-router@6.30.1: + resolution: {integrity: sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' @@ -2600,17 +2515,8 @@ packages: peerDependencies: react-dom: ^16.8.0 || ^17 || ^18 || ^19 - react-smooth@4.0.4: - resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-transition-group@4.4.5: - resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' + react-use-websocket@4.13.0: + resolution: {integrity: sha512-anMuVoV//g2N76Wxqvqjjo1X48r9Np3y1/gMl7arX84tAPXdy5R7sB5lO5hvCzQRYjqXwV8XMAiEBOUbyrZFrw==} react-virtualized-auto-sizer@1.0.26: resolution: {integrity: sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==} @@ -2618,27 +2524,33 @@ packages: react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} engines: {node: '>=0.10.0'} read-pkg@3.0.0: resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} engines: {node: '>=4'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - recharts-scale@0.4.5: - resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} - recharts@2.15.3: - resolution: {integrity: sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==} - engines: {node: '>=14'} + recharts@3.2.0: + resolution: {integrity: sha512-fX0xCgNXo6mag9wz3oLuANR+dUQM4uIlTYBGTGq9CBRgW/8TZPzqPGYs5NTt8aENCf+i1CI8vqxT1py8L/5J2w==} + engines: {node: '>=18'} peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} @@ -2661,6 +2573,9 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2670,27 +2585,11 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rollup@3.29.5: - resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} + rollup@4.50.2: + resolution: {integrity: sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -2706,13 +2605,13 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - sass@1.70.0: - resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==} + sass@1.92.1: + resolution: {integrity: sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==} engines: {node: '>=14.0.0'} hasBin: true - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} @@ -2722,11 +2621,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -2743,25 +2637,14 @@ packages: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - shebang-regex@1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shell-quote@1.8.2: - resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} - shiki@0.14.7: - resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -2778,10 +2661,6 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -2805,24 +2684,21 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.21: - resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} - engines: {node: '>= 0.4'} - string.prototype.padend@3.1.6: resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} engines: {node: '>= 0.4'} - string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - string.prototype.trim@1.2.10: resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} @@ -2846,15 +2722,11 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - style-to-js@1.1.16: - resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} + style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} - style-to-object@1.0.8: - resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} @@ -2879,27 +2751,16 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - synckit@0.11.6: - resolution: {integrity: sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==} - engines: {node: ^14.18.0 || >=16.0.0} - tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - tauri-plugin-log-api@https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/d33f0f4f8dbb02e705a83a471cdbbb9cb7b73a40: - resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/d33f0f4f8dbb02e705a83a471cdbbb9cb7b73a40} - version: 0.0.0 - - tauri-plugin-window-state-api@https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/12ac7a31bf71ab5c3e87add90d44b47cbfe95652: - resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/12ac7a31bf71ab5c3e87add90d44b47cbfe95652} - version: 0.0.0 - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2910,26 +2771,9 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -2946,12 +2790,12 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typedoc@0.25.13: - resolution: {integrity: sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==} - engines: {node: '>= 16'} + typedoc@0.28.13: + resolution: {integrity: sha512-dNWY8msnYB2a+7Audha+aTF1Pu3euiE7ySp53w8kEsXoYw7dMouV5A1UsTUY345aB152RHnmRMDiovuBi7BD+w==} + engines: {node: '>= 18', pnpm: '>= 10'} hasBin: true peerDependencies: - typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x typesafe-i18n@5.26.2: resolution: {integrity: sha512-2QAriFmiY5JwUAJtG7yufoE/XZ1aFBY++wj7YFS2yo89a3jLBfKoWSdq5JfQYk1V2BS7V2c/u+KEcaCQoE65hw==} @@ -2959,24 +2803,20 @@ packages: peerDependencies: typescript: '>=3.5.1' - typescript-eslint-language-service@5.0.5: - resolution: {integrity: sha512-b7gWXpwSTqMVKpPX3WttNZEyVAMKs/2jsHKF79H+qaD6mjzCyU5jboJe/lOZgLJD+QRsXCr0GjIVxvl5kI1NMw==} - peerDependencies: - '@typescript-eslint/parser': '>= 5.0.0' - eslint: '>= 8.0.0' - typescript: '>= 4.0.0' - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@7.11.0: + resolution: {integrity: sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -3002,9 +2842,6 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - use-breakpoint@4.0.6: resolution: {integrity: sha512-1s7vUjf36eeZYTgY1KkmPNXrTbKJVRA9cjBFQdYjK8+pDr0qJgH6/cuX5qQ2zcfkqxN5LieVd/DTVK6ofnwRTQ==} peerDependencies: @@ -3017,10 +2854,10 @@ packages: peerDependencies: react: '>=16.13' - use-sync-external-store@1.2.2: - resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} @@ -3029,48 +2866,54 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - victory-vendor@36.9.2: - resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - vite@4.5.14: - resolution: {integrity: sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==} - engines: {node: ^14.18.0 || >=16.0.0} + vite@7.1.5: + resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': '>= 14' - less: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: optional: true terser: optional: true - - vscode-oniguruma@1.7.0: - resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - - vscode-textmate@8.0.0: - resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + tsx: + optional: true + yaml: + optional: true which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} @@ -3092,15 +2935,6 @@ packages: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3119,6 +2953,11 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -3127,15 +2966,11 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@3.24.4: - resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} - - zustand@5.0.4: - resolution: {integrity: sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==} + zustand@5.0.8: + resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -3157,68 +2992,65 @@ packages: snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.27.2': {} + '@babel/compat-data@7.28.4': {} - '@babel/core@7.27.1': + '@babel/core@7.28.4': dependencies: - '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 + '@babel/generator': 7.28.3 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) - '@babel/helpers': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.27.1': + '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.2 + '@babel/compat-data': 7.28.4 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.24.5 + browserslist: 4.26.0 lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-globals@7.28.0': {} + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -3230,54 +3062,89 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.27.1': + '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.28.4 - '@babel/parser@7.27.2': + '@babel/parser@7.28.4': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.4 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.27.1': {} + '@babel/runtime@7.28.4': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@babel/traverse@7.27.1': + '@babel/traverse@7.28.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/types': 7.27.1 - debug: 4.4.1 - globals: 11.12.0 + '@babel/types': 7.28.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.27.1': + '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@biomejs/biome@2.2.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.4 + '@biomejs/cli-darwin-x64': 2.2.4 + '@biomejs/cli-linux-arm64': 2.2.4 + '@biomejs/cli-linux-arm64-musl': 2.2.4 + '@biomejs/cli-linux-x64': 2.2.4 + '@biomejs/cli-linux-x64-musl': 2.2.4 + '@biomejs/cli-win32-arm64': 2.2.4 + '@biomejs/cli-win32-x64': 2.2.4 + + '@biomejs/cli-darwin-arm64@2.2.4': + optional: true + + '@biomejs/cli-darwin-x64@2.2.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.4': + optional: true + + '@biomejs/cli-linux-arm64@2.2.4': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.4': + optional: true + + '@biomejs/cli-linux-x64@2.2.4': + optional: true + + '@biomejs/cli-win32-arm64@2.2.4': + optional: true + + '@biomejs/cli-win32-x64@2.2.4': + optional: true + '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.27.1 - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.4 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -3300,33 +3167,25 @@ snapshots: '@emotion/hash@0.9.2': {} - '@emotion/is-prop-valid@0.8.8': - dependencies: - '@emotion/memoize': 0.7.4 - optional: true - - '@emotion/is-prop-valid@1.3.1': + '@emotion/is-prop-valid@1.4.0': dependencies: '@emotion/memoize': 0.9.0 - '@emotion/memoize@0.7.4': - optional: true - '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@18.3.21)(react@18.3.1)': + '@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) '@emotion/utils': 1.4.2 '@emotion/weak-memoize': 0.4.0 hoist-non-react-statics: 3.3.2 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.21 + '@types/react': 19.1.13 transitivePeerDependencies: - supports-color @@ -3340,303 +3199,438 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.21)(react@18.3.1))(@types/react@18.3.21)(react@18.3.1)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 - '@emotion/is-prop-valid': 1.3.1 - '@emotion/react': 11.14.0(@types/react@18.3.21)(react@18.3.1) + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) '@emotion/utils': 1.4.2 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.21 + '@types/react': 19.1.13 transitivePeerDependencies: - supports-color '@emotion/unitless@0.10.0': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 '@emotion/utils@1.4.2': {} '@emotion/weak-memoize@0.4.0': {} - '@esbuild/android-arm64@0.18.20': + '@esbuild/aix-ppc64@0.25.9': optional: true - '@esbuild/android-arm@0.18.20': + '@esbuild/android-arm64@0.25.9': optional: true - '@esbuild/android-x64@0.18.20': + '@esbuild/android-arm@0.25.9': optional: true - '@esbuild/darwin-arm64@0.18.20': + '@esbuild/android-x64@0.25.9': optional: true - '@esbuild/darwin-x64@0.18.20': + '@esbuild/darwin-arm64@0.25.9': optional: true - '@esbuild/freebsd-arm64@0.18.20': + '@esbuild/darwin-x64@0.25.9': optional: true - '@esbuild/freebsd-x64@0.18.20': + '@esbuild/freebsd-arm64@0.25.9': optional: true - '@esbuild/linux-arm64@0.18.20': + '@esbuild/freebsd-x64@0.25.9': optional: true - '@esbuild/linux-arm@0.18.20': + '@esbuild/linux-arm64@0.25.9': optional: true - '@esbuild/linux-ia32@0.18.20': + '@esbuild/linux-arm@0.25.9': optional: true - '@esbuild/linux-loong64@0.18.20': + '@esbuild/linux-ia32@0.25.9': optional: true - '@esbuild/linux-mips64el@0.18.20': + '@esbuild/linux-loong64@0.25.9': optional: true - '@esbuild/linux-ppc64@0.18.20': + '@esbuild/linux-mips64el@0.25.9': optional: true - '@esbuild/linux-riscv64@0.18.20': + '@esbuild/linux-ppc64@0.25.9': optional: true - '@esbuild/linux-s390x@0.18.20': + '@esbuild/linux-riscv64@0.25.9': optional: true - '@esbuild/linux-x64@0.18.20': + '@esbuild/linux-s390x@0.25.9': optional: true - '@esbuild/netbsd-x64@0.18.20': + '@esbuild/linux-x64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.18.20': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/sunos-x64@0.18.20': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/win32-arm64@0.18.20': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/win32-ia32@0.18.20': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/win32-x64@0.18.20': + '@esbuild/openharmony-arm64@0.25.9': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 + '@esbuild/sunos-x64@0.25.9': + optional: true - '@eslint-community/regexpp@4.12.1': {} + '@esbuild/win32-arm64@0.25.9': + optional: true - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.4.1 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color + '@esbuild/win32-ia32@0.25.9': + optional: true - '@eslint/js@8.57.1': {} + '@esbuild/win32-x64@0.25.9': + optional: true - '@floating-ui/core@1.7.0': + '@floating-ui/core@1.7.3': dependencies: - '@floating-ui/utils': 0.2.9 + '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.0': + '@floating-ui/dom@1.7.4': dependencies: - '@floating-ui/core': 1.7.0 - '@floating-ui/utils': 0.2.9 + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@floating-ui/dom': 1.7.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@floating-ui/dom': 1.7.4 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - '@floating-ui/react@0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react@0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@floating-ui/utils': 0.2.9 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@floating-ui/utils': 0.2.10 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) tabbable: 6.2.0 - '@floating-ui/utils@0.2.9': {} + '@floating-ui/utils@0.2.10': {} + + '@gerrit0/mini-shiki@3.12.2': + dependencies: + '@shikijs/engine-oniguruma': 3.12.2 + '@shikijs/langs': 3.12.2 + '@shikijs/themes': 3.12.2 + '@shikijs/types': 3.12.2 + '@shikijs/vscode-textmate': 10.0.2 - '@hookform/devtools@4.4.0(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@hookform/devtools@4.4.0(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@emotion/react': 11.14.0(@types/react@18.3.21)(react@18.3.1) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@18.3.21)(react@18.3.1))(@types/react@18.3.21)(react@18.3.1) - '@types/lodash': 4.17.16 - little-state-machine: 4.8.1(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) + '@types/lodash': 4.17.20 + little-state-machine: 4.8.1(react@19.1.1) lodash: 4.17.21 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-simple-animate: 3.5.3(react-dom@18.3.1(react@18.3.1)) - use-deep-compare-effect: 1.8.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-simple-animate: 3.5.3(react-dom@19.1.1(react@19.1.1)) + use-deep-compare-effect: 1.8.1(react@19.1.1) uuid: 8.3.2 transitivePeerDependencies: - '@types/react' - supports-color - '@hookform/resolvers@3.10.0(react-hook-form@7.56.4(react@18.3.1))': + '@hookform/resolvers@3.10.0(react-hook-form@7.62.0(react@19.1.1))': dependencies: - react-hook-form: 7.56.4(react@18.3.1) + react-hook-form: 7.62.0(react@19.1.1) - '@humanwhocodes/config-array@0.13.0': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.1 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - '@humanwhocodes/module-importer@1.0.1': {} + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@humanwhocodes/object-schema@2.0.3': {} + '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/resolve-uri@3.1.2': {} + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true - '@jridgewell/set-array@1.2.1': {} + '@parcel/watcher-win32-arm64@2.5.1': + optional: true - '@jridgewell/sourcemap-codec@1.5.0': {} + '@parcel/watcher-win32-ia32@2.5.1': + optional: true - '@jridgewell/trace-mapping@0.3.25': + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@react-hook/latest@1.0.3(react@19.1.1)': + dependencies: + react: 19.1.1 - '@nodelib/fs.scandir@2.1.5': + '@react-hook/passive-layout-effect@1.2.1(react@19.1.1)': dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + react: 19.1.1 - '@nodelib/fs.stat@2.0.5': {} + '@react-hook/resize-observer@2.0.2(react@19.1.1)': + dependencies: + '@react-hook/latest': 1.0.3(react@19.1.1) + '@react-hook/passive-layout-effect': 1.2.1(react@19.1.1) + react: 19.1.1 - '@nodelib/fs.walk@1.2.8': + '@reduxjs/toolkit@2.9.0(react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1))(react@19.1.1)': dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 10.1.3 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.1.1 + react-redux: 9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1) + + '@remix-run/router@1.23.0': {} - '@pkgr/core@0.2.4': {} + '@rolldown/pluginutils@1.0.0-beta.32': {} - '@react-hook/latest@1.0.3(react@18.3.1)': + '@rolldown/pluginutils@1.0.0-beta.34': {} + + '@rollup/rollup-android-arm-eabi@4.50.2': + optional: true + + '@rollup/rollup-android-arm64@4.50.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.50.2': + optional: true + + '@rollup/rollup-darwin-x64@4.50.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.50.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.50.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.50.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.50.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.50.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.50.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.50.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.50.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.50.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.50.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.50.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.50.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.50.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.50.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.50.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.2': + optional: true + + '@shikijs/engine-oniguruma@3.12.2': dependencies: - react: 18.3.1 + '@shikijs/types': 3.12.2 + '@shikijs/vscode-textmate': 10.0.2 - '@react-hook/passive-layout-effect@1.2.1(react@18.3.1)': + '@shikijs/langs@3.12.2': dependencies: - react: 18.3.1 + '@shikijs/types': 3.12.2 - '@react-hook/resize-observer@2.0.2(react@18.3.1)': + '@shikijs/themes@3.12.2': dependencies: - '@react-hook/latest': 1.0.3(react@18.3.1) - '@react-hook/passive-layout-effect': 1.2.1(react@18.3.1) - react: 18.3.1 + '@shikijs/types': 3.12.2 - '@remix-run/router@1.23.0': {} + '@shikijs/types@3.12.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 - '@rtsao/scc@1.1.0': {} + '@shikijs/vscode-textmate@10.0.2': {} - '@stablelib/base64@1.0.1': {} + '@stablelib/base64@2.0.1': {} - '@stablelib/binary@1.0.1': + '@stablelib/binary@2.0.1': dependencies: - '@stablelib/int': 1.0.1 + '@stablelib/int': 2.0.1 - '@stablelib/bytes@1.0.1': {} + '@stablelib/bytes@2.0.1': {} - '@stablelib/int@1.0.1': {} + '@stablelib/int@2.0.1': {} - '@stablelib/keyagreement@1.0.1': + '@stablelib/keyagreement@2.0.1': dependencies: - '@stablelib/bytes': 1.0.1 + '@stablelib/bytes': 2.0.1 - '@stablelib/random@1.0.2': + '@stablelib/random@2.0.1': dependencies: - '@stablelib/binary': 1.0.1 - '@stablelib/wipe': 1.0.1 + '@stablelib/binary': 2.0.1 + '@stablelib/wipe': 2.0.1 - '@stablelib/wipe@1.0.1': {} + '@stablelib/wipe@2.0.1': {} - '@stablelib/x25519@1.0.3': + '@stablelib/x25519@2.0.1': dependencies: - '@stablelib/keyagreement': 1.0.1 - '@stablelib/random': 1.0.2 - '@stablelib/wipe': 1.0.1 + '@stablelib/keyagreement': 2.0.1 + '@stablelib/random': 2.0.1 + '@stablelib/wipe': 2.0.1 - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.27.1)': + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.27.1)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.4 - '@svgr/babel-preset@8.1.0(@babel/core@7.27.1)': + '@svgr/babel-preset@8.1.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.1 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.27.1) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.27.1) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.27.1) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.27.1) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.27.1) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.27.1) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.27.1) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.27.1) + '@babel/core': 7.28.4 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.4) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.4) - '@svgr/cli@8.1.0(typescript@5.8.3)': + '@svgr/cli@8.1.0(typescript@5.9.2)': dependencies: - '@svgr/core': 8.1.0(typescript@5.8.3) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) - '@svgr/plugin-prettier': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3))(typescript@5.8.3) + '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) + '@svgr/plugin-prettier': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2) camelcase: 6.3.0 chalk: 4.1.2 commander: 9.5.0 @@ -3647,12 +3641,12 @@ snapshots: - supports-color - typescript - '@svgr/core@8.1.0(typescript@5.8.3)': + '@svgr/core@8.1.0(typescript@5.9.2)': dependencies: - '@babel/core': 7.27.1 - '@svgr/babel-preset': 8.1.0(@babel/core@7.27.1) + '@babel/core': 7.28.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.8.3) + cosmiconfig: 8.3.6(typescript@5.9.2) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -3660,181 +3654,224 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.4 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))': dependencies: - '@babel/core': 7.27.1 - '@svgr/babel-preset': 8.1.0(@babel/core@7.27.1) - '@svgr/core': 8.1.0(typescript@5.8.3) + '@babel/core': 7.28.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4) + '@svgr/core': 8.1.0(typescript@5.9.2) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-prettier@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))': + '@svgr/plugin-prettier@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))': dependencies: - '@svgr/core': 8.1.0(typescript@5.8.3) + '@svgr/core': 8.1.0(typescript@5.9.2) deepmerge: 4.3.1 prettier: 2.8.8 - '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))(typescript@5.8.3)': + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)': dependencies: - '@svgr/core': 8.1.0(typescript@5.8.3) - cosmiconfig: 8.3.6(typescript@5.8.3) + '@svgr/core': 8.1.0(typescript@5.9.2) + cosmiconfig: 8.3.6(typescript@5.9.2) deepmerge: 4.3.1 svgo: 3.3.2 transitivePeerDependencies: - typescript - '@swc/core-darwin-arm64@1.11.24': + '@swc/core-darwin-arm64@1.13.5': optional: true - '@swc/core-darwin-x64@1.11.24': + '@swc/core-darwin-x64@1.13.5': optional: true - '@swc/core-linux-arm-gnueabihf@1.11.24': + '@swc/core-linux-arm-gnueabihf@1.13.5': optional: true - '@swc/core-linux-arm64-gnu@1.11.24': + '@swc/core-linux-arm64-gnu@1.13.5': optional: true - '@swc/core-linux-arm64-musl@1.11.24': + '@swc/core-linux-arm64-musl@1.13.5': optional: true - '@swc/core-linux-x64-gnu@1.11.24': + '@swc/core-linux-x64-gnu@1.13.5': optional: true - '@swc/core-linux-x64-musl@1.11.24': + '@swc/core-linux-x64-musl@1.13.5': optional: true - '@swc/core-win32-arm64-msvc@1.11.24': + '@swc/core-win32-arm64-msvc@1.13.5': optional: true - '@swc/core-win32-ia32-msvc@1.11.24': + '@swc/core-win32-ia32-msvc@1.13.5': optional: true - '@swc/core-win32-x64-msvc@1.11.24': + '@swc/core-win32-x64-msvc@1.13.5': optional: true - '@swc/core@1.11.24': + '@swc/core@1.13.5': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.21 + '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.11.24 - '@swc/core-darwin-x64': 1.11.24 - '@swc/core-linux-arm-gnueabihf': 1.11.24 - '@swc/core-linux-arm64-gnu': 1.11.24 - '@swc/core-linux-arm64-musl': 1.11.24 - '@swc/core-linux-x64-gnu': 1.11.24 - '@swc/core-linux-x64-musl': 1.11.24 - '@swc/core-win32-arm64-msvc': 1.11.24 - '@swc/core-win32-ia32-msvc': 1.11.24 - '@swc/core-win32-x64-msvc': 1.11.24 + '@swc/core-darwin-arm64': 1.13.5 + '@swc/core-darwin-x64': 1.13.5 + '@swc/core-linux-arm-gnueabihf': 1.13.5 + '@swc/core-linux-arm64-gnu': 1.13.5 + '@swc/core-linux-arm64-musl': 1.13.5 + '@swc/core-linux-x64-gnu': 1.13.5 + '@swc/core-linux-x64-musl': 1.13.5 + '@swc/core-win32-arm64-msvc': 1.13.5 + '@swc/core-win32-ia32-msvc': 1.13.5 + '@swc/core-win32-x64-msvc': 1.13.5 '@swc/counter@0.1.3': {} - '@swc/types@0.1.21': + '@swc/types@0.1.25': dependencies: '@swc/counter': 0.1.3 - '@tanstack/query-core@5.76.0': {} + '@tanstack/query-core@5.87.4': {} - '@tanstack/query-devtools@5.76.0': {} + '@tanstack/query-devtools@5.87.3': {} - '@tanstack/react-query-devtools@5.76.1(@tanstack/react-query@5.76.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-devtools@5.87.4(@tanstack/react-query@5.87.4(react@19.1.1))(react@19.1.1)': dependencies: - '@tanstack/query-devtools': 5.76.0 - '@tanstack/react-query': 5.76.1(react@18.3.1) - react: 18.3.1 + '@tanstack/query-devtools': 5.87.3 + '@tanstack/react-query': 5.87.4(react@19.1.1) + react: 19.1.1 - '@tanstack/react-query@5.76.1(react@18.3.1)': + '@tanstack/react-query@5.87.4(react@19.1.1)': dependencies: - '@tanstack/query-core': 5.76.0 - react: 18.3.1 + '@tanstack/query-core': 5.87.4 + react: 19.1.1 - '@tanstack/react-virtual@3.0.0-beta.54(react@18.3.1)': + '@tanstack/react-virtual@3.13.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@tanstack/virtual-core': 3.0.0-beta.54 - react: 18.3.1 + '@tanstack/virtual-core': 3.13.12 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - '@tanstack/virtual-core@3.0.0-beta.54': {} + '@tanstack/virtual-core@3.13.12': {} - '@tauri-apps/api@1.6.0': {} + '@tauri-apps/api@2.8.0': {} - '@tauri-apps/cli-darwin-arm64@1.6.3': + '@tauri-apps/cli-darwin-arm64@2.8.4': optional: true - '@tauri-apps/cli-darwin-x64@1.6.3': + '@tauri-apps/cli-darwin-x64@2.8.4': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@1.6.3': + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': optional: true - '@tauri-apps/cli-linux-arm64-gnu@1.6.3': + '@tauri-apps/cli-linux-arm64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-arm64-musl@1.6.3': + '@tauri-apps/cli-linux-arm64-musl@2.8.4': optional: true - '@tauri-apps/cli-linux-x64-gnu@1.6.3': + '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-x64-musl@1.6.3': + '@tauri-apps/cli-linux-x64-gnu@2.8.4': optional: true - '@tauri-apps/cli-win32-arm64-msvc@1.6.3': + '@tauri-apps/cli-linux-x64-musl@2.8.4': optional: true - '@tauri-apps/cli-win32-ia32-msvc@1.6.3': + '@tauri-apps/cli-win32-arm64-msvc@2.8.4': optional: true - '@tauri-apps/cli-win32-x64-msvc@1.6.3': + '@tauri-apps/cli-win32-ia32-msvc@2.8.4': optional: true - '@tauri-apps/cli@1.6.3': - dependencies: - semver: 7.7.2 + '@tauri-apps/cli-win32-x64-msvc@2.8.4': + optional: true + + '@tauri-apps/cli@2.8.4': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 1.6.3 - '@tauri-apps/cli-darwin-x64': 1.6.3 - '@tauri-apps/cli-linux-arm-gnueabihf': 1.6.3 - '@tauri-apps/cli-linux-arm64-gnu': 1.6.3 - '@tauri-apps/cli-linux-arm64-musl': 1.6.3 - '@tauri-apps/cli-linux-x64-gnu': 1.6.3 - '@tauri-apps/cli-linux-x64-musl': 1.6.3 - '@tauri-apps/cli-win32-arm64-msvc': 1.6.3 - '@tauri-apps/cli-win32-ia32-msvc': 1.6.3 - '@tauri-apps/cli-win32-x64-msvc': 1.6.3 + '@tauri-apps/cli-darwin-arm64': 2.8.4 + '@tauri-apps/cli-darwin-x64': 2.8.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.8.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.8.4 + '@tauri-apps/cli-linux-arm64-musl': 2.8.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.8.4 + '@tauri-apps/cli-linux-x64-gnu': 2.8.4 + '@tauri-apps/cli-linux-x64-musl': 2.8.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.8.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.8.4 + '@tauri-apps/cli-win32-x64-msvc': 2.8.4 + + '@tauri-apps/plugin-clipboard-manager@2.3.0': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-deep-link@2.4.3': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-dialog@2.4.0': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-fs@2.4.2': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-http@2.5.2': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-log@2.7.0': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-notification@2.3.1': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-opener@2.5.0': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-os@2.3.1': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-window-state@2.4.0': + dependencies: + '@tauri-apps/api': 2.8.0 '@trysound/sax@0.2.0': {} '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.7 + '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@types/babel__traverse@7.20.7': + '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.4 '@types/byte-size@8.1.2': {} - '@types/d3-array@3.2.1': {} + '@types/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -3864,9 +3901,9 @@ snapshots: '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 - '@types/estree@1.0.7': {} + '@types/estree@1.0.8': {} '@types/file-saver@2.0.7': {} @@ -3874,15 +3911,11 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/json-schema@7.0.15': {} - - '@types/json5@0.0.29': {} - '@types/lodash-es@4.17.12': dependencies: - '@types/lodash': 4.17.16 + '@types/lodash': 4.17.20 - '@types/lodash@4.17.16': {} + '@types/lodash@4.17.20': {} '@types/mdast@4.0.4': dependencies: @@ -3890,159 +3923,57 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@20.17.48': + '@types/node@24.4.0': dependencies: - undici-types: 6.19.8 + undici-types: 7.11.0 '@types/parse-json@4.0.2': {} - '@types/prop-types@15.7.14': {} - - '@types/react-dom@18.3.7(@types/react@18.3.21)': + '@types/react-dom@19.1.9(@types/react@19.1.13)': dependencies: - '@types/react': 18.3.21 + '@types/react': 19.1.13 - '@types/react@18.3.21': + '@types/react@19.1.13': dependencies: - '@types/prop-types': 15.7.14 csstype: 3.1.3 - '@types/semver@7.7.0': {} - '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.4.1 - eslint: 8.57.1 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - semver: 7.7.2 - ts-api-utils: 1.4.3(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3)': - dependencies: - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.4.1 - eslint: 8.57.1 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@6.21.0': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - - '@typescript-eslint/type-utils@6.21.0(eslint@8.57.1)(typescript@5.8.3)': - dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.8.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - debug: 4.4.1 - eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@6.21.0': {} - - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.8.3)': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.4.1 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.7.2 - ts-api-utils: 1.4.3(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.8.3)': - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) - '@types/json-schema': 7.0.15 - '@types/semver': 7.7.0 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.8.3) - eslint: 8.57.1 - semver: 7.7.2 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@6.21.0': - dependencies: - '@typescript-eslint/types': 6.21.0 - eslint-visitor-keys: 3.4.3 + '@types/use-sync-external-store@0.0.6': {} '@ungap/structured-clone@1.3.0': {} '@use-gesture/core@10.3.1': {} - '@use-gesture/react@10.3.1(react@18.3.1)': + '@use-gesture/react@10.3.1(react@19.1.1)': dependencies: '@use-gesture/core': 10.3.1 - react: 18.3.1 + react: 19.1.1 - '@vitejs/plugin-react-swc@3.9.0(vite@4.5.14(@types/node@20.17.48)(sass@1.70.0))': + '@vitejs/plugin-react-swc@4.0.1(vite@7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1))': dependencies: - '@swc/core': 1.11.24 - vite: 4.5.14(@types/node@20.17.48)(sass@1.70.0) + '@rolldown/pluginutils': 1.0.0-beta.32 + '@swc/core': 1.13.5 + vite: 7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.4.1(vite@4.5.14(@types/node@20.17.48)(sass@1.70.0))': + '@vitejs/plugin-react@5.0.2(vite@7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1))': dependencies: - '@babel/core': 7.27.1 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.1) + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.34 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 4.5.14(@types/node@20.17.48)(sass@1.70.0) + vite: 7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - acorn-jsx@5.3.2(acorn@8.14.1): - dependencies: - acorn: 8.14.1 - - acorn@8.14.1: {} - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ansi-regex@5.0.1: {} - ansi-sequence-parser@1.1.3: {} - ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -4051,11 +3982,6 @@ snapshots: dependencies: color-convert: 2.0.1 - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - argparse@2.0.1: {} array-buffer-byte-length@1.0.2: @@ -4063,78 +3989,26 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 - array-includes@3.1.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - - array-union@2.1.0: {} - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.findlastindex@1.2.6: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-shim-unscopables: 1.1.0 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-shim-unscopables: 1.1.0 - arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 async-function@1.0.0: {} - autoprefixer@10.4.21(postcss@8.5.3): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - caniuse-lite: 1.0.30001718 + browserslist: 4.26.0 + caniuse-lite: 1.0.30001741 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.3 + postcss: 8.5.6 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -4143,7 +4017,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.4 cosmiconfig: 7.1.0 resolve: 1.22.10 @@ -4151,31 +4025,33 @@ snapshots: balanced-match@1.0.2: {} - binary-extensions@2.3.0: {} + baseline-browser-mapping@2.8.3: {} boolbase@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 braces@3.0.3: dependencies: fill-range: 7.1.1 + optional: true - browserslist@4.24.5: + browserslist@4.26.0: dependencies: - caniuse-lite: 1.0.30001718 - electron-to-chromium: 1.5.155 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.5) + baseline-browser-mapping: 2.8.3 + caniuse-lite: 1.0.30001741 + electron-to-chromium: 1.5.218 + node-releases: 2.0.21 + update-browserslist-db: 1.1.3(browserslist@4.26.0) - byte-size@8.2.1: {} + byte-size@9.0.1: {} call-bind-apply-helpers@1.0.2: dependencies: @@ -4198,7 +4074,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001718: {} + caniuse-lite@1.0.30001741: {} ccount@2.0.1: {} @@ -4221,17 +4097,9 @@ snapshots: character-reference-invalid@2.0.1: {} - chokidar@3.6.0: + chokidar@4.0.3: dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + readdirp: 4.1.2 classnames@2.5.1: {} @@ -4277,14 +4145,14 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 - cosmiconfig@8.3.6(typescript@5.8.3): + cosmiconfig@8.3.6(typescript@5.9.2): dependencies: import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.2 cross-spawn@6.0.6: dependencies: @@ -4294,16 +4162,10 @@ snapshots: shebang-command: 1.2.0 which: 1.3.1 - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - css-select@5.1.0: + css-select@5.2.2: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 5.0.3 domutils: 3.2.2 nth-check: 2.1.1 @@ -4318,7 +4180,7 @@ snapshots: mdn-data: 2.0.30 source-map-js: 1.2.1 - css-what@6.1.0: {} + css-what@6.2.2: {} csso@5.0.5: dependencies: @@ -4384,24 +4246,18 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - dayjs@1.11.13: {} - - debug@3.2.7: - dependencies: - ms: 2.1.3 + dayjs@1.11.18: {} - debug@4.4.1: + debug@4.4.3: dependencies: ms: 2.1.3 decimal.js-light@2.5.1: {} - decode-named-character-reference@1.1.0: + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 - deep-is@0.1.4: {} - deepmerge-ts@7.1.5: {} deepmerge@4.3.1: {} @@ -4422,27 +4278,13 @@ snapshots: detect-browser@5.3.0: {} + detect-libc@1.0.3: + optional: true + devlop@1.1.0: dependencies: dequal: 2.0.3 - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - dom-helpers@5.2.1: - dependencies: - '@babel/runtime': 7.27.1 - csstype: 3.1.3 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -4472,19 +4314,19 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.155: {} + electron-to-chromium@1.5.218: {} emoji-regex@8.0.0: {} entities@4.5.0: {} - entities@6.0.0: {} + entities@6.0.1: {} error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - es-abstract@1.23.9: + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -4513,7 +4355,9 @@ snapshots: is-array-buffer: 3.0.5 is-callable: 1.2.7 is-data-view: 1.0.2 + is-negative-zero: 2.0.3 is-regex: 1.2.1 + is-set: 2.0.3 is-shared-array-buffer: 1.0.4 is-string: 1.1.1 is-typed-array: 1.1.15 @@ -4528,6 +4372,7 @@ snapshots: safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 string.prototype.trim: 1.2.10 string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 @@ -4536,311 +4381,102 @@ snapshots: typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-iterator-helpers@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - - escalade@3.2.0: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - eslint-config-prettier@9.1.0(eslint@8.57.1): - dependencies: - eslint: 8.57.1 - - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.16.1 - resolve: 1.22.10 - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - eslint: 8.57.1 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.1 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color + which-typed-array: 1.1.19 - eslint-plugin-prettier@5.4.0(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.5.3): - dependencies: - eslint: 8.57.1 - prettier: 3.5.3 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.6 - optionalDependencies: - eslint-config-prettier: 9.1.0(eslint@8.57.1) + es-define-property@1.0.1: {} - eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): - dependencies: - eslint: 8.57.1 + es-errors@1.3.0: {} - eslint-plugin-react-refresh@0.4.20(eslint@8.57.1): + es-object-atoms@1.1.1: dependencies: - eslint: 8.57.1 + es-errors: 1.3.0 - eslint-plugin-react@7.37.5(eslint@8.57.1): + es-set-tostringtag@2.1.0: dependencies: - array-includes: 3.1.8 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 8.57.1 - estraverse: 5.3.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - - eslint-plugin-simple-import-sort@10.0.0(eslint@8.57.1): - dependencies: - eslint: 8.57.1 - eslint-scope@7.2.2: + es-to-primitive@1.3.0: dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 - eslint@8.57.1: - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.1 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.1 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color + es-toolkit@1.39.10: {} - espree@9.6.1: - dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) - eslint-visitor-keys: 3.4.3 + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 + escalade@3.2.0: {} - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 + escape-string-regexp@1.0.5: {} - estraverse@5.3.0: {} + escape-string-regexp@4.0.0: {} estree-util-is-identifier-name@3.0.0: {} - esutils@2.0.3: {} - - eventemitter3@4.0.7: {} + eventemitter3@5.0.1: {} extend@3.0.2: {} fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - - fast-equals@5.2.2: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 file-saver@2.0.5: {} fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + optional: true find-root@1.1.0: {} - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@3.2.0: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.3.3: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 fraction.js@4.3.7: {} - framer-motion@10.18.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@12.23.12(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: + motion-dom: 12.23.12 + motion-utils: 12.23.6 tslib: 2.8.1 optionalDependencies: - '@emotion/is-prop-valid': 0.8.8 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@emotion/is-prop-valid': 1.4.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) fs.realpath@1.0.0: {} @@ -4890,23 +4526,6 @@ snapshots: get-text-width@1.0.3: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - glob@8.1.0: dependencies: fs.realpath: 1.0.0 @@ -4915,32 +4534,15 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 - globals@11.12.0: {} - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - gopd@1.2.0: {} graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-bigints@1.1.0: {} has-flag@3.0.0: {} @@ -4973,7 +4575,7 @@ snapshots: hast-util-to-jsx-runtime@2.3.6: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 '@types/hast': 3.0.4 '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 @@ -4985,9 +4587,9 @@ snapshots: mdast-util-mdxjs-esm: 2.0.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 - style-to-js: 1.1.16 + style-to-js: 1.1.17 unist-util-position: 5.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 transitivePeerDependencies: - supports-color @@ -5006,15 +4608,15 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.0.0 - html-react-parser@5.2.5(@types/react@18.3.21)(react@18.3.1): + html-react-parser@5.2.6(@types/react@19.1.13)(react@19.1.1): dependencies: domhandler: 5.0.3 html-dom-parser: 5.1.1 - react: 18.3.1 + react: 19.1.1 react-property: 2.0.2 - style-to-js: 1.1.16 + style-to-js: 1.1.17 optionalDependencies: - '@types/react': 18.3.21 + '@types/react': 19.1.13 html-url-attributes@3.0.1: {} @@ -5023,19 +4625,17 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - entities: 6.0.0 + entities: 6.0.1 - ignore@5.3.2: {} + immer@10.1.3: {} - immutable@4.3.7: {} + immutable@5.1.3: {} import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -5080,10 +4680,6 @@ snapshots: dependencies: has-bigints: 1.1.0 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -5108,7 +4704,8 @@ snapshots: is-decimal@2.0.1: {} - is-extglob@2.1.1: {} + is-extglob@2.1.1: + optional: true is-finalizationregistry@1.1.1: dependencies: @@ -5126,19 +4723,21 @@ snapshots: is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + optional: true is-hexadecimal@2.0.1: {} is-map@2.0.3: {} + is-negative-zero@2.0.3: {} + is-number-object@1.1.1: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-number@7.0.0: {} - - is-path-inside@3.0.3: {} + is-number@7.0.0: + optional: true is-plain-obj@4.1.0: {} @@ -5185,16 +4784,9 @@ snapshots: isexe@2.0.0: {} - iterator.prototype@1.1.5: - dependencies: - define-data-property: 1.1.4 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - has-symbols: 1.1.0 - set-function-name: 2.0.2 + itertools@2.5.0: {} - itertools@2.4.1: {} + js-base64@3.7.8: {} js-tokens@4.0.0: {} @@ -5204,45 +4796,21 @@ snapshots: jsesc@3.1.0: {} - json-buffer@3.0.1: {} - json-parse-better-errors@1.0.2: {} json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@1.0.2: - dependencies: - minimist: 1.2.8 - json5@2.2.3: {} - jsonc-parser@3.3.1: {} - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 + lines-and-columns@1.2.4: {} - levn@0.4.1: + linkify-it@5.0.0: dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 + uc.micro: 2.1.0 - lines-and-columns@1.2.4: {} - - little-state-machine@4.8.1(react@18.3.1): + little-state-machine@4.8.1(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 load-json-file@4.0.0: dependencies: @@ -5251,14 +4819,8 @@ snapshots: pify: 3.0.0 strip-bom: 3.0.0 - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - lodash-es@4.17.21: {} - lodash.merge@4.6.2: {} - lodash@4.17.21: {} longest-streak@3.1.0: {} @@ -5277,7 +4839,14 @@ snapshots: lunr@2.3.9: {} - marked@4.3.0: {} + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 math-intrinsics@1.1.0: {} @@ -5285,7 +4854,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 micromark: 4.0.2 @@ -5322,7 +4891,7 @@ snapshots: parse-entities: 4.0.2 stringify-entities: 4.0.4 unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.2 + vfile-message: 4.0.3 transitivePeerDependencies: - supports-color @@ -5374,17 +4943,17 @@ snapshots: mdn-data@2.0.30: {} + mdurl@2.0.0: {} + memorystream@0.3.1: {} - merge-refs@1.3.0(@types/react@18.3.21): + merge-refs@2.0.0(@types/react@19.1.13): optionalDependencies: - '@types/react': 18.3.21 - - merge2@1.4.1: {} + '@types/react': 19.1.13 micromark-core-commonmark@2.0.3: dependencies: - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-factory-destination: 2.0.1 micromark-factory-label: 2.0.1 @@ -5459,7 +5028,7 @@ snapshots: micromark-util-decode-string@2.0.1: dependencies: - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 micromark-util-character: 2.1.1 micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-symbol: 2.0.1 @@ -5496,8 +5065,8 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.1 - decode-named-character-reference: 1.1.0 + debug: 4.4.3 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-factory-space: 2.0.1 @@ -5519,6 +5088,7 @@ snapshots: dependencies: braces: 3.0.3 picomatch: 2.3.1 + optional: true millify@6.1.0: dependencies: @@ -5526,28 +5096,35 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@5.1.6: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 - minimatch@9.0.3: + minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 - minimatch@9.0.5: + motion-dom@12.23.12: dependencies: - brace-expansion: 2.0.1 + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} - minimist@1.2.8: {} + motion@12.23.12(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + framer-motion: 12.23.12(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.4.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) ms@2.1.3: {} nanoid@3.3.11: {} - natural-compare@1.4.0: {} - nice-try@1.0.5: {} no-case@3.0.4: @@ -5555,7 +5132,10 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 - node-releases@2.0.19: {} + node-addon-api@7.1.1: + optional: true + + node-releases@2.0.21: {} normalize-package-data@2.5.0: dependencies: @@ -5564,8 +5144,6 @@ snapshots: semver: 5.7.2 validate-npm-package-license: 3.0.4 - normalize-path@3.0.0: {} - normalize-range@0.1.2: {} npm-run-all@4.1.5: @@ -5577,7 +5155,7 @@ snapshots: minimatch: 3.1.2 pidtree: 0.3.1 read-pkg: 3.0.0 - shell-quote: 1.8.2 + shell-quote: 1.8.3 string.prototype.padend: 3.1.6 nth-check@2.1.1: @@ -5599,60 +5177,16 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 - object.entries@1.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.1.1 - - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - p-timeout@6.1.4: {} parent-module@1.0.1: @@ -5664,7 +5198,7 @@ snapshots: '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 @@ -5681,14 +5215,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - path-key@2.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} path-type@3.0.0: @@ -5699,7 +5227,10 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.1: + optional: true + + picomatch@4.0.3: {} pidtree@0.3.1: {} @@ -5709,21 +5240,15 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.3: + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - prelude-ls@1.2.1: {} - - prettier-linter-helpers@1.0.0: - dependencies: - fast-diff: 1.3.0 - prettier@2.8.8: {} - prettier@3.5.3: {} + prettier@3.6.2: {} prop-types@15.8.1: dependencies: @@ -5733,51 +5258,48 @@ snapshots: property-information@7.1.0: {} - punycode@2.3.1: {} + punycode.js@2.3.1: {} qr.js@0.0.0: {} - queue-microtask@1.2.3: {} - - radash@11.0.0: {} + radash@12.1.1: {} - react-auth-code-input@3.2.1(react@18.3.1): + react-auth-code-input@3.2.1(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 - react-click-away-listener@2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-click-away-listener@2.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - react-dom@18.3.1(react@18.3.1): + react-dom@19.1.1(react@19.1.1): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.1.1 + scheduler: 0.26.0 - react-hook-form@7.56.4(react@18.3.1): + react-hook-form@7.62.0(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 react-is@16.13.1: {} react-is@18.3.1: {} - react-loading-skeleton@3.5.0(react@18.3.1): + react-loading-skeleton@3.5.0(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 - react-markdown@9.1.0(@types/react@18.3.21)(react@18.3.1): + react-markdown@10.1.0(@types/react@19.1.13)(react@19.1.1): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 18.3.21 + '@types/react': 19.1.13 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.0 - react: 18.3.1 + react: 19.1.1 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -5788,55 +5310,47 @@ snapshots: react-property@2.0.2: {} - react-qr-code@2.0.15(react@18.3.1): + react-qr-code@2.0.18(react@19.1.1): dependencies: prop-types: 15.8.1 qr.js: 0.0.0 - react: 18.3.1 + react: 19.1.1 + + react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.1.1 + use-sync-external-store: 1.5.0(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.13 + redux: 5.0.1 react-refresh@0.17.0: {} - react-router-dom@6.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router-dom@6.30.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@remix-run/router': 1.23.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-router: 6.30.0(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-router: 6.30.1(react@19.1.1) - react-router@6.30.0(react@18.3.1): + react-router@6.30.1(react@19.1.1): dependencies: '@remix-run/router': 1.23.0 - react: 18.3.1 - - react-simple-animate@3.5.3(react-dom@18.3.1(react@18.3.1)): - dependencies: - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 - react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-simple-animate@3.5.3(react-dom@19.1.1(react@19.1.1)): dependencies: - fast-equals: 5.2.2 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: 19.1.1(react@19.1.1) - react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.1 - dom-helpers: 5.2.1 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-use-websocket@4.13.0: {} - react-virtualized-auto-sizer@1.0.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-virtualized-auto-sizer@1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - react@18.3.1: - dependencies: - loose-envify: 1.4.0 + react@19.1.1: {} read-pkg@3.0.0: dependencies: @@ -5844,32 +5358,39 @@ snapshots: normalize-package-data: 2.5.0 path-type: 3.0.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - recharts-scale@0.4.5: - dependencies: - decimal.js-light: 2.5.1 + readdirp@4.1.2: {} - recharts@2.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + recharts@3.2.0(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react-is@18.3.1)(react@19.1.1)(redux@5.0.1): dependencies: + '@reduxjs/toolkit': 2.9.0(react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1))(react@19.1.1) clsx: 2.1.1 - eventemitter3: 4.0.7 - lodash: 4.17.21 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + decimal.js-light: 2.5.1 + es-toolkit: 1.39.10 + eventemitter3: 5.0.1 + immer: 10.1.3 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) react-is: 18.3.1 - react-smooth: 4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - recharts-scale: 0.4.5 + react-redux: 9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1) + reselect: 5.1.1 tiny-invariant: 1.3.3 - victory-vendor: 36.9.2 + use-sync-external-store: 1.5.0(react@19.1.1) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -5909,6 +5430,8 @@ snapshots: require-directory@2.1.1: {} + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve@1.22.10: @@ -5917,26 +5440,33 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.1.0: {} - - rimraf@3.0.2: + rollup@4.50.2: dependencies: - glob: 7.2.3 - - rollup@3.29.5: + '@types/estree': 1.0.8 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.50.2 + '@rollup/rollup-android-arm64': 4.50.2 + '@rollup/rollup-darwin-arm64': 4.50.2 + '@rollup/rollup-darwin-x64': 4.50.2 + '@rollup/rollup-freebsd-arm64': 4.50.2 + '@rollup/rollup-freebsd-x64': 4.50.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.2 + '@rollup/rollup-linux-arm-musleabihf': 4.50.2 + '@rollup/rollup-linux-arm64-gnu': 4.50.2 + '@rollup/rollup-linux-arm64-musl': 4.50.2 + '@rollup/rollup-linux-loong64-gnu': 4.50.2 + '@rollup/rollup-linux-ppc64-gnu': 4.50.2 + '@rollup/rollup-linux-riscv64-gnu': 4.50.2 + '@rollup/rollup-linux-riscv64-musl': 4.50.2 + '@rollup/rollup-linux-s390x-gnu': 4.50.2 + '@rollup/rollup-linux-x64-gnu': 4.50.2 + '@rollup/rollup-linux-x64-musl': 4.50.2 + '@rollup/rollup-openharmony-arm64': 4.50.2 + '@rollup/rollup-win32-arm64-msvc': 4.50.2 + '@rollup/rollup-win32-ia32-msvc': 4.50.2 + '@rollup/rollup-win32-x64-msvc': 4.50.2 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -5960,22 +5490,20 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - sass@1.70.0: + sass@1.92.1: dependencies: - chokidar: 3.6.0 - immutable: 4.3.7 + chokidar: 4.0.3 + immutable: 5.1.3 source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 + scheduler@0.26.0: {} semver@5.7.2: {} semver@6.3.1: {} - semver@7.7.2: {} - set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -6002,22 +5530,9 @@ snapshots: dependencies: shebang-regex: 1.0.0 - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - shebang-regex@1.0.0: {} - shebang-regex@3.0.0: {} - - shell-quote@1.8.2: {} - - shiki@0.14.7: - dependencies: - ansi-sequence-parser: 1.1.3 - jsonc-parser: 3.3.1 - vscode-oniguruma: 1.7.0 - vscode-textmate: 8.0.0 + shell-quote@1.8.3: {} side-channel-list@1.0.0: dependencies: @@ -6047,8 +5562,6 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - slash@3.0.0: {} - snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -6063,16 +5576,21 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.21 + spdx-license-ids: 3.0.22 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.21 + spdx-license-ids: 3.0.22 - spdx-license-ids@3.0.21: {} + spdx-license-ids@3.0.22: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 string-width@4.2.3: dependencies: @@ -6080,41 +5598,20 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string.prototype.matchall@4.0.12: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.4 - set-function-name: 2.0.2 - side-channel: 1.1.0 - string.prototype.padend@3.1.6: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-object-atoms: 1.1.1 - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.9 - string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -6142,13 +5639,11 @@ snapshots: strip-bom@3.0.0: {} - strip-json-comments@3.1.1: {} - - style-to-js@1.1.16: + style-to-js@1.1.17: dependencies: - style-to-object: 1.0.8 + style-to-object: 1.0.9 - style-to-object@1.0.8: + style-to-object@1.0.9: dependencies: inline-style-parser: 0.2.4 @@ -6170,57 +5665,32 @@ snapshots: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 - css-select: 5.1.0 + css-select: 5.2.2 css-tree: 2.3.1 - css-what: 6.1.0 + css-what: 6.2.2 csso: 5.0.5 picocolors: 1.1.1 - synckit@0.11.6: - dependencies: - '@pkgr/core': 0.2.4 - tabbable@6.2.0: {} - tauri-plugin-log-api@https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/d33f0f4f8dbb02e705a83a471cdbbb9cb7b73a40: - dependencies: - '@tauri-apps/api': 1.6.0 + tiny-invariant@1.3.3: {} - tauri-plugin-window-state-api@https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/12ac7a31bf71ab5c3e87add90d44b47cbfe95652: + tinyglobby@0.2.15: dependencies: - '@tauri-apps/api': 1.6.0 - - text-table@0.2.0: {} - - tiny-invariant@1.3.3: {} + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + optional: true trim-lines@3.0.1: {} trough@2.2.0: {} - ts-api-utils@1.4.3(typescript@5.8.3): - dependencies: - typescript: 5.8.3 - - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - tslib@2.8.1: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.20.2: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -6254,25 +5724,22 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typedoc@0.25.13(typescript@5.8.3): + typedoc@0.28.13(typescript@5.9.2): dependencies: + '@gerrit0/mini-shiki': 3.12.2 lunr: 2.3.9 - marked: 4.3.0 + markdown-it: 14.1.0 minimatch: 9.0.5 - shiki: 0.14.7 - typescript: 5.8.3 + typescript: 5.9.2 + yaml: 2.8.1 - typesafe-i18n@5.26.2(typescript@5.8.3): + typesafe-i18n@5.26.2(typescript@5.9.2): dependencies: - typescript: 5.8.3 + typescript: 5.9.2 - typescript-eslint-language-service@5.0.5(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3): - dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - eslint: 8.57.1 - typescript: 5.8.3 + typescript@5.9.2: {} - typescript@5.8.3: {} + uc.micro@2.1.0: {} unbox-primitive@1.1.0: dependencies: @@ -6281,7 +5748,7 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@6.19.8: {} + undici-types@7.11.0: {} unified@11.0.5: dependencies: @@ -6316,31 +5783,26 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - update-browserslist-db@1.1.3(browserslist@4.24.5): + update-browserslist-db@1.1.3(browserslist@4.26.0): dependencies: - browserslist: 4.24.5 + browserslist: 4.26.0 escalade: 3.2.0 picocolors: 1.1.1 - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - use-breakpoint@4.0.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + use-breakpoint@4.0.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - use-deep-compare-effect@1.8.1(react@18.3.1): + use-deep-compare-effect@1.8.1(react@19.1.1): dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.4 dequal: 2.0.3 - react: 18.3.1 + react: 19.1.1 - use-sync-external-store@1.2.2(react@18.3.1): + use-sync-external-store@1.5.0(react@19.1.1): dependencies: - react: 18.3.1 - optional: true + react: 19.1.1 uuid@8.3.2: {} @@ -6349,7 +5811,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vfile-message@4.0.2: + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 @@ -6357,11 +5819,11 @@ snapshots: vfile@6.0.3: dependencies: '@types/unist': 3.0.3 - vfile-message: 4.0.2 + vfile-message: 4.0.3 - victory-vendor@36.9.2: + victory-vendor@37.3.6: dependencies: - '@types/d3-array': 3.2.1 + '@types/d3-array': 3.2.2 '@types/d3-ease': 3.0.2 '@types/d3-interpolate': 3.0.4 '@types/d3-scale': 4.0.9 @@ -6376,19 +5838,19 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite@4.5.14(@types/node@20.17.48)(sass@1.70.0): + vite@7.1.5(@types/node@24.4.0)(sass@1.92.1)(yaml@2.8.1): dependencies: - esbuild: 0.18.20 - postcss: 8.5.3 - rollup: 3.29.5 + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.2 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 20.17.48 + '@types/node': 24.4.0 fsevents: 2.3.3 - sass: 1.70.0 - - vscode-oniguruma@1.7.0: {} - - vscode-textmate@8.0.0: {} + sass: 1.92.1 + yaml: 2.8.1 which-boxed-primitive@1.1.1: dependencies: @@ -6435,12 +5897,6 @@ snapshots: dependencies: isexe: 2.0.0 - which@2.0.2: - dependencies: - isexe: 2.0.0 - - word-wrap@1.2.5: {} - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -6455,6 +5911,8 @@ snapshots: yaml@1.10.2: {} + yaml@2.8.1: {} + yargs-parser@21.1.1: {} yargs@17.7.2: @@ -6467,14 +5925,13 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} - - zod@3.24.4: {} + zod@3.25.76: {} - zustand@5.0.4(@types/react@18.3.21)(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)): + zustand@5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): optionalDependencies: - '@types/react': 18.3.21 - react: 18.3.1 - use-sync-external-store: 1.2.2(react@18.3.1) + '@types/react': 19.1.13 + immer: 10.1.3 + react: 19.1.1 + use-sync-external-store: 1.5.0(react@19.1.1) zwitch@2.0.4: {} diff --git a/resources-linux/defguard-client.spec b/resources-linux/defguard-client.spec index 230d2a66..55391de0 100644 --- a/resources-linux/defguard-client.spec +++ b/resources-linux/defguard-client.spec @@ -5,7 +5,7 @@ Summary: Defguard desktop client License: Apache-2.0 URL: https://defguard.net/ -Requires: libappindicator-gtk3 webkit2gtk4.0 +Requires: libayatana-appindicator3-dev webkit2gtk4.1 %description Desktop client for managing WireGuard VPN connections @@ -22,9 +22,13 @@ Desktop client for managing WireGuard VPN connections %{__install} -m 755 src-tauri/target/release/defguard-client %{buildroot}/%{_bindir}/ %{__install} -m 755 src-tauri/target/release/defguard-service %{buildroot}/%{_sbindir}/ %{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-black.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-black.png +%{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-black-active.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-black-active.png %{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-color.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-color.png +%{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-color-active.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-color-active.png %{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-gray.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-gray.png +%{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-gray-active.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-gray-active.png %{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-white.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-white.png +%{__install} -m 644 src-tauri/target/release/resources/icons/tray-32x32-white-active.png %{buildroot}/%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-white-active.png %{__install} -m 644 resources-linux/defguard-service.service %{buildroot}/%{_prefix}/lib/systemd/system/ %{__install} -m 644 resources-linux/defguard-client.desktop %{buildroot}/%{_datadir}/applications/defguard-client.desktop %{__install} -m 644 src-tauri/icons/128x128.png %{buildroot}/%{_datadir}/icons/hicolor/128x128/apps/defguard-client.png @@ -54,9 +58,13 @@ systemctl daemon-reload %{_bindir}/defguard-client %{_sbindir}/defguard-service %{_prefix}/lib/defguard-client/resources/icons/tray-32x32-black.png +%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-black-active.png %{_prefix}/lib/defguard-client/resources/icons/tray-32x32-color.png +%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-color-active.png %{_prefix}/lib/defguard-client/resources/icons/tray-32x32-gray.png +%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-gray-active.png %{_prefix}/lib/defguard-client/resources/icons/tray-32x32-white.png +%{_prefix}/lib/defguard-client/resources/icons/tray-32x32-white-active.png %{_prefix}/lib/systemd/system/defguard-service.service %{_datadir}/applications/defguard-client.desktop %{_datadir}/icons/hicolor/128x128/apps/defguard-client.png diff --git a/resources-linux/defguard-service.service b/resources-linux/defguard-service.service index bce9f07b..cdfb3dc7 100644 --- a/resources-linux/defguard-service.service +++ b/resources-linux/defguard-service.service @@ -5,6 +5,7 @@ Wants=network-online.target After=network-online.target [Service] +Group=defguard ExecReload=/bin/kill -HUP $MAINPID ExecStart=/usr/sbin/defguard-service KillMode=process diff --git a/resources-linux/postinst b/resources-linux/postinst index fabecdca..00c814e9 100644 --- a/resources-linux/postinst +++ b/resources-linux/postinst @@ -1,3 +1,70 @@ -systemctl daemon-reload -systemctl enable defguard-service -systemctl start defguard-service +#!/bin/sh +set -e + +GROUP_NAME="defguard" +SERVICE_NAME="defguard-service" + +case "$1" in + configure) + # Create the group if it doesn't exist + if ! getent group "$GROUP_NAME" >/dev/null; then + addgroup --system "$GROUP_NAME" + echo "Created group $GROUP_NAME" + fi + + # Determine target user + TARGET_USER="" + if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then + TARGET_USER="$SUDO_USER" + elif [ -n "$USER" ] && [ "$USER" != "root" ]; then + TARGET_USER="$USER" + fi + + # Add user to group if we found a valid target + if [ -n "$TARGET_USER" ]; then + if getent passwd "$TARGET_USER" >/dev/null; then + # Try to add user to group and check if it succeeded + if usermod -a -G "$GROUP_NAME" "$TARGET_USER"; then + echo "Added user $TARGET_USER to group $GROUP_NAME" + + # Only show reboot message if user was actually added + echo "================================================" + echo " IMPORTANT: Reboot or Re-login Required" + echo "================================================" + echo "The user has been added to the defguard group." + echo "Please reboot or log out and back in for the" + echo "group membership changes to take effect." + echo "================================================" + else + echo "Warning: Failed to add user $TARGET_USER to group $GROUP_NAME" + exit 1 + fi + fi + fi + + # Handle systemd service + if [ -d /run/systemd/system ]; then + # Reload systemd to recognize new service file + systemctl daemon-reload + + # Enable service to start on boot + systemctl enable "$SERVICE_NAME" + + # Start the service now + systemctl start "$SERVICE_NAME" + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + # On failed operations, ensure service is running if it should be + if [ -d /run/systemd/system ]; then + systemctl daemon-reload + if systemctl is-enabled "$SERVICE_NAME" >/dev/null 2>&1; then + systemctl start "$SERVICE_NAME" || true + fi + fi + ;; +esac + +#DEBHELPER# + diff --git a/resources-linux/postrm b/resources-linux/postrm index 9b76642f..6fb17532 100644 --- a/resources-linux/postrm +++ b/resources-linux/postrm @@ -1 +1,24 @@ -systemctl daemon-reload +#!/bin/sh +set -e + +GROUP_NAME="defguard" +SERVICE_NAME="defguard-service" + +case "$1" in + remove) + # Service file still exists, just disable it + if [ -d /run/systemd/system ]; then + systemctl disable "$SERVICE_NAME" || true + systemctl daemon-reload + fi + ;; + + purge) + # Complete removal - clean up group too + if getent group "$GROUP_NAME" >/dev/null; then + delgroup "$GROUP_NAME" || true + fi + ;; +esac + +#DEBHELPER# diff --git a/resources-linux/prerm b/resources-linux/prerm index aaf1ae3c..3c602373 100644 --- a/resources-linux/prerm +++ b/resources-linux/prerm @@ -1,2 +1,15 @@ -systemctl stop defguard-service -systemctl disable defguard-service +#!/bin/sh +set -e + +SERVICE_NAME="defguard-service" + +case "$1" in + remove|upgrade|deconfigure) + if [ -d /run/systemd/system ]; then + # Stop the service before removal/upgrade + systemctl stop "$SERVICE_NAME" || true + fi + ;; +esac + +#DEBHELPER# diff --git a/src-tauri/.sqlx/query-a9ca7a4f46ce92ab02a213479797b24e04caad767c088dd490dc38663494c22d.json b/src-tauri/.sqlx/query-3421da72f01d726c2931071203d663b197cb518dd65ec73108f85b2cb7270741.json similarity index 57% rename from src-tauri/.sqlx/query-a9ca7a4f46ce92ab02a213479797b24e04caad767c088dd490dc38663494c22d.json rename to src-tauri/.sqlx/query-3421da72f01d726c2931071203d663b197cb518dd65ec73108f85b2cb7270741.json index ce79c660..a994e60f 100644 --- a/src-tauri/.sqlx/query-a9ca7a4f46ce92ab02a213479797b24e04caad767c088dd490dc38663494c22d.json +++ b/src-tauri/.sqlx/query-3421da72f01d726c2931071203d663b197cb518dd65ec73108f85b2cb7270741.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "UPDATE location SET instance_id = $1, name = $2, address = $3, pubkey = $4, endpoint = $5, allowed_ips = $6, dns = $7, network_id = $8, route_all_traffic = $9, mfa_enabled = $10, keepalive_interval = $11 WHERE id = $12", + "query": "UPDATE location SET instance_id = $1, name = $2, address = $3, pubkey = $4, endpoint = $5, allowed_ips = $6, dns = $7, network_id = $8, route_all_traffic = $9, keepalive_interval = $10, location_mfa_mode = $11 WHERE id = $12", "describe": { "columns": [], "parameters": { @@ -8,5 +8,5 @@ }, "nullable": [] }, - "hash": "a9ca7a4f46ce92ab02a213479797b24e04caad767c088dd490dc38663494c22d" + "hash": "3421da72f01d726c2931071203d663b197cb518dd65ec73108f85b2cb7270741" } diff --git a/src-tauri/.sqlx/query-79c5c710bc12a1353875219d3cd2198b6c2cf5b476774034d3160a3f28df08d7.json b/src-tauri/.sqlx/query-65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1.json similarity index 84% rename from src-tauri/.sqlx/query-79c5c710bc12a1353875219d3cd2198b6c2cf5b476774034d3160a3f28df08d7.json rename to src-tauri/.sqlx/query-65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1.json index 565650b6..57f2b8f7 100644 --- a/src-tauri/.sqlx/query-79c5c710bc12a1353875219d3cd2198b6c2cf5b476774034d3160a3f28df08d7.json +++ b/src-tauri/.sqlx/query-65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token \"token?\", disable_all_traffic, enterprise_enabled, use_openid_for_mfa, openid_display_name FROM instance;", + "query": "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token \"token?\", disable_all_traffic, enterprise_enabled, openid_display_name FROM instance ORDER BY name ASC;", "describe": { "columns": [ { @@ -48,14 +48,9 @@ "ordinal": 8, "type_info": "Bool" }, - { - "name": "use_openid_for_mfa", - "ordinal": 9, - "type_info": "Bool" - }, { "name": "openid_display_name", - "ordinal": 10, + "ordinal": 9, "type_info": "Text" } ], @@ -72,9 +67,8 @@ true, false, false, - false, true ] }, - "hash": "79c5c710bc12a1353875219d3cd2198b6c2cf5b476774034d3160a3f28df08d7" + "hash": "65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1" } diff --git a/src-tauri/.sqlx/query-a490103ccb68cc382bd1fc84c2d1ebd6f344bbda5618980943a62677683f0a85.json b/src-tauri/.sqlx/query-758d3c67336eecafab5fd20e4b03574995383092cdb4d7c3ebda6f933ea0f472.json similarity index 55% rename from src-tauri/.sqlx/query-a490103ccb68cc382bd1fc84c2d1ebd6f344bbda5618980943a62677683f0a85.json rename to src-tauri/.sqlx/query-758d3c67336eecafab5fd20e4b03574995383092cdb4d7c3ebda6f933ea0f472.json index 695d90f1..40a4087a 100644 --- a/src-tauri/.sqlx/query-a490103ccb68cc382bd1fc84c2d1ebd6f344bbda5618980943a62677683f0a85.json +++ b/src-tauri/.sqlx/query-758d3c67336eecafab5fd20e4b03574995383092cdb4d7c3ebda6f933ea0f472.json @@ -1,12 +1,12 @@ { "db_name": "SQLite", - "query": "UPDATE instance SET name = $1, uuid = $2, url = $3, proxy_url = $4, username = $5, disable_all_traffic = $6, enterprise_enabled = $7, token = $8, use_openid_for_mfa = $9, openid_display_name = $10 WHERE id = $11;", + "query": "UPDATE instance SET name = $1, uuid = $2, url = $3, proxy_url = $4, username = $5, disable_all_traffic = $6, enterprise_enabled = $7, token = $8, openid_display_name = $9 WHERE id = $10;", "describe": { "columns": [], "parameters": { - "Right": 11 + "Right": 10 }, "nullable": [] }, - "hash": "a490103ccb68cc382bd1fc84c2d1ebd6f344bbda5618980943a62677683f0a85" + "hash": "758d3c67336eecafab5fd20e4b03574995383092cdb4d7c3ebda6f933ea0f472" } diff --git a/src-tauri/.sqlx/query-7b6cba2ce07597ff499b692055f18241f97d604243f387ca9f1a12264b79fb16.json b/src-tauri/.sqlx/query-7bbc28ee5a141e5b531a6ac5a1cbf120828a0b9c19301c92a3f71531c08c698d.json similarity index 83% rename from src-tauri/.sqlx/query-7b6cba2ce07597ff499b692055f18241f97d604243f387ca9f1a12264b79fb16.json rename to src-tauri/.sqlx/query-7bbc28ee5a141e5b531a6ac5a1cbf120828a0b9c19301c92a3f71531c08c698d.json index d373fcd3..f5faadd8 100644 --- a/src-tauri/.sqlx/query-7b6cba2ce07597ff499b692055f18241f97d604243f387ca9f1a12264b79fb16.json +++ b/src-tauri/.sqlx/query-7bbc28ee5a141e5b531a6ac5a1cbf120828a0b9c19301c92a3f71531c08c698d.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, mfa_enabled, keepalive_interval FROM location WHERE instance_id = $1", + "query": "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, keepalive_interval, location_mfa_mode \"location_mfa_mode: LocationMfaMode\" FROM location WHERE instance_id = $1 ORDER BY name ASC", "describe": { "columns": [ { @@ -54,12 +54,12 @@ "type_info": "Bool" }, { - "name": "mfa_enabled", + "name": "keepalive_interval", "ordinal": 10, - "type_info": "Bool" + "type_info": "Integer" }, { - "name": "keepalive_interval", + "name": "location_mfa_mode: LocationMfaMode", "ordinal": 11, "type_info": "Integer" } @@ -82,5 +82,5 @@ false ] }, - "hash": "7b6cba2ce07597ff499b692055f18241f97d604243f387ca9f1a12264b79fb16" + "hash": "7bbc28ee5a141e5b531a6ac5a1cbf120828a0b9c19301c92a3f71531c08c698d" } diff --git a/src-tauri/.sqlx/query-f76a3953576d685d4057541b04aabfafdb61c86b0e9486ff0561a7bd80fd6ba8.json b/src-tauri/.sqlx/query-9c866377cd618f3fbafae6ed62e13076669cf582baa5933f25ebdb0acd93f1b7.json similarity index 94% rename from src-tauri/.sqlx/query-f76a3953576d685d4057541b04aabfafdb61c86b0e9486ff0561a7bd80fd6ba8.json rename to src-tauri/.sqlx/query-9c866377cd618f3fbafae6ed62e13076669cf582baa5933f25ebdb0acd93f1b7.json index 5f109398..5d2930f7 100644 --- a/src-tauri/.sqlx/query-f76a3953576d685d4057541b04aabfafdb61c86b0e9486ff0561a7bd80fd6ba8.json +++ b/src-tauri/.sqlx/query-9c866377cd618f3fbafae6ed62e13076669cf582baa5933f25ebdb0acd93f1b7.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", name, pubkey, prvkey, address, server_pubkey, preshared_key, allowed_ips, endpoint, dns, persistent_keep_alive, route_all_traffic, pre_up, post_up, pre_down, post_down FROM tunnel;", + "query": "SELECT id \"id: _\", name, pubkey, prvkey, address, server_pubkey, preshared_key, allowed_ips, endpoint, dns, persistent_keep_alive, route_all_traffic, pre_up, post_up, pre_down, post_down FROM tunnel ORDER BY name ASC;", "describe": { "columns": [ { @@ -106,5 +106,5 @@ true ] }, - "hash": "f76a3953576d685d4057541b04aabfafdb61c86b0e9486ff0561a7bd80fd6ba8" + "hash": "9c866377cd618f3fbafae6ed62e13076669cf582baa5933f25ebdb0acd93f1b7" } diff --git a/src-tauri/.sqlx/query-7f1db6e3022b3bef4515d183feb6dc2e3b787c1fe5c4ce26c1c14c356d0c85d5.json b/src-tauri/.sqlx/query-aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a.json similarity index 83% rename from src-tauri/.sqlx/query-7f1db6e3022b3bef4515d183feb6dc2e3b787c1fe5c4ce26c1c14c356d0c85d5.json rename to src-tauri/.sqlx/query-aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a.json index dab05ae6..bfefdf36 100644 --- a/src-tauri/.sqlx/query-7f1db6e3022b3bef4515d183feb6dc2e3b787c1fe5c4ce26c1c14c356d0c85d5.json +++ b/src-tauri/.sqlx/query-aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token \"token?\", disable_all_traffic, enterprise_enabled, use_openid_for_mfa, openid_display_name FROM instance WHERE id = $1;", + "query": "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token \"token?\", disable_all_traffic, enterprise_enabled, openid_display_name FROM instance WHERE id = $1;", "describe": { "columns": [ { @@ -48,14 +48,9 @@ "ordinal": 8, "type_info": "Bool" }, - { - "name": "use_openid_for_mfa", - "ordinal": 9, - "type_info": "Bool" - }, { "name": "openid_display_name", - "ordinal": 10, + "ordinal": 9, "type_info": "Text" } ], @@ -72,9 +67,8 @@ true, false, false, - false, true ] }, - "hash": "7f1db6e3022b3bef4515d183feb6dc2e3b787c1fe5c4ce26c1c14c356d0c85d5" + "hash": "aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a" } diff --git a/src-tauri/.sqlx/query-9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a.json b/src-tauri/.sqlx/query-ac02b04f6490a768571290d7dc77444eb0ca55a3a7e159c3b2e529ebf75f224f.json similarity index 84% rename from src-tauri/.sqlx/query-9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a.json rename to src-tauri/.sqlx/query-ac02b04f6490a768571290d7dc77444eb0ca55a3a7e159c3b2e529ebf75f224f.json index fac4f5fc..6df78777 100644 --- a/src-tauri/.sqlx/query-9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a.json +++ b/src-tauri/.sqlx/query-ac02b04f6490a768571290d7dc77444eb0ca55a3a7e159c3b2e529ebf75f224f.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, mfa_enabled, keepalive_interval FROM location WHERE id = $1", + "query": "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, keepalive_interval, location_mfa_mode \"location_mfa_mode: LocationMfaMode\" FROM location WHERE pubkey = $1;", "describe": { "columns": [ { @@ -54,12 +54,12 @@ "type_info": "Bool" }, { - "name": "mfa_enabled", + "name": "keepalive_interval", "ordinal": 10, - "type_info": "Bool" + "type_info": "Integer" }, { - "name": "keepalive_interval", + "name": "location_mfa_mode: LocationMfaMode", "ordinal": 11, "type_info": "Integer" } @@ -82,5 +82,5 @@ false ] }, - "hash": "9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a" + "hash": "ac02b04f6490a768571290d7dc77444eb0ca55a3a7e159c3b2e529ebf75f224f" } diff --git a/src-tauri/.sqlx/query-e034dbe839ed5cd19a8430ac5786e68ace8457b4983724957092db618b2535d4.json b/src-tauri/.sqlx/query-e02047df7deea862cceca537e49ae16a8237e91eff0ee684cacd2ec1c77adb58.json similarity index 65% rename from src-tauri/.sqlx/query-e034dbe839ed5cd19a8430ac5786e68ace8457b4983724957092db618b2535d4.json rename to src-tauri/.sqlx/query-e02047df7deea862cceca537e49ae16a8237e91eff0ee684cacd2ec1c77adb58.json index 0e926b52..3d77f025 100644 --- a/src-tauri/.sqlx/query-e034dbe839ed5cd19a8430ac5786e68ace8457b4983724957092db618b2535d4.json +++ b/src-tauri/.sqlx/query-e02047df7deea862cceca537e49ae16a8237e91eff0ee684cacd2ec1c77adb58.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "INSERT INTO location (instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, mfa_enabled, keepalive_interval) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id \"id!\"", + "query": "INSERT INTO location (instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, keepalive_interval, location_mfa_mode) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id \"id!\"", "describe": { "columns": [ { @@ -16,5 +16,5 @@ true ] }, - "hash": "e034dbe839ed5cd19a8430ac5786e68ace8457b4983724957092db618b2535d4" + "hash": "e02047df7deea862cceca537e49ae16a8237e91eff0ee684cacd2ec1c77adb58" } diff --git a/src-tauri/.sqlx/query-afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21.json b/src-tauri/.sqlx/query-e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11.json similarity index 85% rename from src-tauri/.sqlx/query-afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21.json rename to src-tauri/.sqlx/query-e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11.json index 8ab4e1e5..eb35ee44 100644 --- a/src-tauri/.sqlx/query-afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21.json +++ b/src-tauri/.sqlx/query-e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, mfa_enabled, keepalive_interval FROM location WHERE pubkey = $1;", + "query": "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id, route_all_traffic, keepalive_interval, location_mfa_mode \"location_mfa_mode: LocationMfaMode\" FROM location WHERE id = $1", "describe": { "columns": [ { @@ -54,12 +54,12 @@ "type_info": "Bool" }, { - "name": "mfa_enabled", + "name": "keepalive_interval", "ordinal": 10, - "type_info": "Bool" + "type_info": "Integer" }, { - "name": "keepalive_interval", + "name": "location_mfa_mode: LocationMfaMode", "ordinal": 11, "type_info": "Integer" } @@ -82,5 +82,5 @@ false ] }, - "hash": "afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21" + "hash": "e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11" } diff --git a/src-tauri/.sqlx/query-e50367bf0ea4627fb3125e541c2a2b2da96589b3aeb6b1c902f436ae98e9ffe9.json b/src-tauri/.sqlx/query-f4b187b6f90edb7fd65e82a60e786942680cd7c66c42a315de0e62e47c4f2df4.json similarity index 78% rename from src-tauri/.sqlx/query-e50367bf0ea4627fb3125e541c2a2b2da96589b3aeb6b1c902f436ae98e9ffe9.json rename to src-tauri/.sqlx/query-f4b187b6f90edb7fd65e82a60e786942680cd7c66c42a315de0e62e47c4f2df4.json index 0ed03e46..881650b4 100644 --- a/src-tauri/.sqlx/query-e50367bf0ea4627fb3125e541c2a2b2da96589b3aeb6b1c902f436ae98e9ffe9.json +++ b/src-tauri/.sqlx/query-f4b187b6f90edb7fd65e82a60e786942680cd7c66c42a315de0e62e47c4f2df4.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token, disable_all_traffic, enterprise_enabled, use_openid_for_mfa, openid_display_name FROM instance\n WHERE token IS NOT NULL;", + "query": "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token, disable_all_traffic, enterprise_enabled, openid_display_name FROM instance\n WHERE token IS NOT NULL ORDER BY name ASC;", "describe": { "columns": [ { @@ -48,14 +48,9 @@ "ordinal": 8, "type_info": "Bool" }, - { - "name": "use_openid_for_mfa", - "ordinal": 9, - "type_info": "Bool" - }, { "name": "openid_display_name", - "ordinal": 10, + "ordinal": 9, "type_info": "Text" } ], @@ -72,9 +67,8 @@ true, false, false, - false, true ] }, - "hash": "e50367bf0ea4627fb3125e541c2a2b2da96589b3aeb6b1c902f436ae98e9ffe9" + "hash": "f4b187b6f90edb7fd65e82a60e786942680cd7c66c42a315de0e62e47c4f2df4" } diff --git a/src-tauri/.sqlx/query-5953a81f34f906e34aabec089dfe0cebf2afc3ad798638db9ea0aabcd506192b.json b/src-tauri/.sqlx/query-f660459ee3beed1e88815560c3f16259e63975a3ec89a3c9b95d833774e9dfef.json similarity index 81% rename from src-tauri/.sqlx/query-5953a81f34f906e34aabec089dfe0cebf2afc3ad798638db9ea0aabcd506192b.json rename to src-tauri/.sqlx/query-f660459ee3beed1e88815560c3f16259e63975a3ec89a3c9b95d833774e9dfef.json index ab312bb2..e895e552 100644 --- a/src-tauri/.sqlx/query-5953a81f34f906e34aabec089dfe0cebf2afc3ad798638db9ea0aabcd506192b.json +++ b/src-tauri/.sqlx/query-f660459ee3beed1e88815560c3f16259e63975a3ec89a3c9b95d833774e9dfef.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id, instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id,route_all_traffic, mfa_enabled, keepalive_interval FROM location;", + "query": "SELECT id, instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id,route_all_traffic, keepalive_interval, location_mfa_mode \"location_mfa_mode: LocationMfaMode\" FROM location ORDER BY name ASC;", "describe": { "columns": [ { @@ -54,12 +54,12 @@ "type_info": "Bool" }, { - "name": "mfa_enabled", + "name": "keepalive_interval", "ordinal": 10, - "type_info": "Bool" + "type_info": "Integer" }, { - "name": "keepalive_interval", + "name": "location_mfa_mode: LocationMfaMode", "ordinal": 11, "type_info": "Integer" } @@ -82,5 +82,5 @@ false ] }, - "hash": "5953a81f34f906e34aabec089dfe0cebf2afc3ad798638db9ea0aabcd506192b" + "hash": "f660459ee3beed1e88815560c3f16259e63975a3ec89a3c9b95d833774e9dfef" } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 9be7f380..ad7f4f36 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -59,10 +59,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] [[package]] name = "android_system_properties" @@ -75,9 +86,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -105,47 +116,47 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "arboard" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f533f8e0af236ffe5eb979b99381df3258853f00ba2e44b6e1955292c75227" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ "clipboard-win", - "image 0.25.6", + "image", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.1", "parking_lot", "percent-encoding", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "wl-clipboard-rs", "x11rb", ] @@ -162,7 +173,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3d60bee1a1d38c2077030f4788e1b4e31058d2e79a8cfc8f2b440bd44db290" dependencies = [ - "async-fs 2.1.2", + "async-fs", "async-net", "enumflags2", "futures-channel", @@ -171,17 +182,28 @@ dependencies = [ "serde", "serde_repr", "url", - "zbus 5.7.1", + "zbus", ] [[package]] -name = "async-broadcast" -version = "0.5.1" +name = "ashpd" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" dependencies = [ - "event-listener 2.5.3", - "futures-core", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", ] [[package]] @@ -190,7 +212,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -209,9 +231,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -221,39 +243,27 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.0", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] [[package]] name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-fs" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ - "async-lock 3.4.0", + "async-lock", "blocking", - "futures-lite 2.6.0", + "futures-lite", ] [[package]] @@ -262,70 +272,40 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", - "async-io 2.4.1", - "async-lock 3.4.0", + "async-io", + "async-lock", "blocking", - "futures-lite 2.6.0", + "futures-lite", "once_cell", ] [[package]] name = "async-io" -version = "1.13.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" -dependencies = [ - "async-lock 3.4.0", - "cfg-if", - "concurrent-queue", "futures-io", - "futures-lite 2.6.0", + "futures-lite", "parking", - "polling 3.8.0", - "rustix 1.0.7", + "polling", + "rustix 1.1.2", "slab", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", + "windows-sys 0.61.0", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] @@ -336,45 +316,27 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-io 2.4.1", - "blocking", - "futures-lite 2.6.0", -] - -[[package]] -name = "async-process" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" -dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", - "async-signal", + "async-io", "blocking", - "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.44", - "windows-sys 0.48.0", + "futures-lite", ] [[package]] name = "async-process" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ - "async-channel 2.3.1", - "async-io 2.4.1", - "async-lock 3.4.0", + "async-channel 2.5.0", + "async-io", + "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", - "futures-lite 2.6.0", - "rustix 1.0.7", - "tracing", + "event-listener 5.4.1", + "futures-lite", + "rustix 1.1.2", ] [[package]] @@ -385,42 +347,42 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ - "async-io 2.4.1", - "async-lock 3.4.0", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 1.0.7", + "rustix 1.1.2", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "async-std" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 2.4.1", - "async-lock 3.4.0", + "async-io", + "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 2.6.0", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -432,28 +394,6 @@ dependencies = [ "wasm-bindgen-futures", ] -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "async-task" version = "4.7.1" @@ -462,37 +402,36 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "atk" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", - "bitflags 1.3.2", "glib", "libc", ] [[package]] name = "atk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] @@ -518,18 +457,17 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.7.9" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ - "async-trait", "axum-core", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "itoa 1.0.15", + "itoa", "matchit", "memchr", "mime", @@ -537,28 +475,27 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper 1.0.2", - "tower 0.5.2", + "sync_wrapper", + "tower", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ - "async-trait", "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "futures-core", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", ] @@ -578,12 +515,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -602,15 +533,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -619,9 +541,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "serde", ] @@ -638,12 +560,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -668,19 +584,19 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.0", + "futures-lite", "piper", ] @@ -704,14 +620,14 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -720,24 +636,14 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] -[[package]] -name = "bstr" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.19.0" @@ -779,9 +685,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" [[package]] name = "byteorder" @@ -806,44 +712,78 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror 1.0.69", ] [[package]] name = "cairo-sys-rs" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", - "system-deps 6.2.2", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.16", ] [[package]] name = "cargo_toml" -version = "0.15.3" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.7.8", + "toml 0.9.5", ] [[package]] name = "cc" -version = "1.2.27" +version = "1.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -866,15 +806,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "cfg-expr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" -dependencies = [ - "smallvec", -] - [[package]] name = "cfg-expr" version = "0.15.8" @@ -887,9 +818,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -899,24 +830,23 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.0", ] [[package]] name = "clap" -version = "4.5.40" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -924,9 +854,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -936,14 +866,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -954,49 +884,13 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clipboard-win" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" dependencies = [ "error-code", ] -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation 0.9.4", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation 0.9.4", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.4" @@ -1017,7 +911,7 @@ dependencies = [ name = "common" version = "1.5.0" dependencies = [ - "nix 0.30.1", + "nix", ] [[package]] @@ -1118,25 +1012,25 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", + "bitflags 2.9.4", + "core-foundation 0.10.1", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] [[package]] name = "core-graphics-types" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", + "bitflags 2.9.4", + "core-foundation 0.10.1", "libc", ] @@ -1166,9 +1060,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1182,25 +1076,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -1234,15 +1109,15 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.29.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 0.4.8", + "itoa", "matches", - "phf 0.8.0", + "phf 0.10.1", "proc-macro2", "quote", "smallvec", @@ -1256,7 +1131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1266,7 +1141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1293,7 +1168,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1302,7 +1177,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18e1a09f280e29a8b00bc7e81eca5ac87dca0575639c9422a5fa25a07bb884b8" dependencies = [ - "ashpd", + "ashpd 0.10.3", "async-std", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1331,7 +1206,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1342,9 +1217,15 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + [[package]] name = "defguard-client" version = "1.5.0" @@ -1359,11 +1240,12 @@ dependencies = [ "dirs-next", "hyper-util", "log", + "nix", "prost", - "prost-build", "regex", - "reqwest 0.12.22", + "reqwest", "rust-ini", + "semver", "serde", "serde_json", "serde_with", @@ -1372,17 +1254,26 @@ dependencies = [ "strum", "tauri", "tauri-build", + "tauri-plugin-clipboard-manager", + "tauri-plugin-deep-link", + "tauri-plugin-dialog", + "tauri-plugin-fs", + "tauri-plugin-http", "tauri-plugin-log", + "tauri-plugin-notification", + "tauri-plugin-opener", + "tauri-plugin-os", "tauri-plugin-single-instance", "tauri-plugin-window-state", - "thiserror 2.0.12", + "thiserror 2.0.16", "time", "tokio", "tokio-stream", "tokio-util", "tonic", - "tonic-build", - "tower 0.5.2", + "tonic-prost", + "tonic-prost-build", + "tower", "tracing", "tracing-appender", "tracing-subscriber", @@ -1403,10 +1294,10 @@ dependencies = [ "dirs-next", "prost", "prost-build", - "reqwest 0.12.22", + "reqwest", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tracing", "tracing-subscriber", @@ -1414,8 +1305,9 @@ dependencies = [ [[package]] name = "defguard_wireguard_rs" -version = "0.7.4" -source = "git+https://github.com/DefGuard/wireguard-rs.git?rev=v0.7.4#37c1ed8aa3bb38f1fa7662a9e413d52828f18486" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093cede63322e14eede3916a6a5de2518788f438a6cdfc71d262c72d0ae865d0" dependencies = [ "base64 0.22.1", "libc", @@ -1426,9 +1318,9 @@ dependencies = [ "netlink-packet-utils", "netlink-packet-wireguard", "netlink-sys", - "nix 0.30.1", + "nix", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", "x25519-dalek", ] @@ -1445,25 +1337,14 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_builder" version = "0.20.2" @@ -1482,7 +1363,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1492,7 +1373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1505,7 +1386,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1520,6 +1401,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1531,14 +1421,26 @@ dependencies = [ ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", - "redox_users", - "winapi", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.61.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", ] [[package]] @@ -1553,8 +1455,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.4", + "block2 0.6.1", + "libc", + "objc2 0.6.2", ] [[package]] @@ -1565,7 +1469,39 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.8", +] + +[[package]] +name = "dlopen2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -1598,6 +1534,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + [[package]] name = "dtoa" version = "1.0.10" @@ -1621,9 +1566,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" @@ -1636,16 +1581,16 @@ dependencies = [ [[package]] name = "embed-resource" -version = "2.5.2" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d506610004cfc74a6f5ee7e8c632b355de5eca1f03ee5e5e0ec11b77d4eb3d61" +checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.23", + "toml 0.9.5", "vswhom", - "winreg 0.52.0", + "winreg 0.55.0", ] [[package]] @@ -1687,7 +1632,17 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", ] [[package]] @@ -1696,14 +1651,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -1731,20 +1697,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1757,24 +1712,35 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "pin-project-lite", ] [[package]] name = "fastrand" -version = "1.9.0" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fax" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" dependencies = [ - "instant", + "fax_derive", ] [[package]] -name = "fastrand" -version = "2.3.0" +name = "fax_derive" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] [[package]] name = "fdeflate" @@ -1787,9 +1753,9 @@ dependencies = [ [[package]] name = "fern" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" dependencies = [ "log", ] @@ -1806,21 +1772,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.1", + "memoffset", "rustc_version", ] [[package]] -name = "filetime" -version = "0.2.25" +name = "find-msvc-tools" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "fixedbitset" @@ -1844,15 +1804,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "flume" version = "0.11.1" @@ -1882,7 +1833,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -1891,11 +1863,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1962,26 +1940,11 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.3.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -1996,7 +1959,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2039,11 +2002,10 @@ dependencies = [ [[package]] name = "gdk" -version = "0.15.4" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -2055,35 +2017,35 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.15.11" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gdk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -2093,47 +2055,48 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gdkwayland-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ "gdk-sys", "glib-sys", "gobject-sys", "libc", "pkg-config", - "system-deps 6.2.2", + "system-deps", ] [[package]] -name = "gdkx11-sys" -version = "0.15.1" +name = "gdkx11" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" dependencies = [ - "gdk-sys", - "glib-sys", + "gdk", + "gdkx11-sys", + "gio", + "glib", "libc", - "system-deps 6.2.2", "x11", ] [[package]] -name = "generator" -version = "0.7.5" +name = "gdkx11-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ - "cc", + "gdk-sys", + "glib-sys", "libc", - "log", - "rustversion", - "windows 0.48.0", + "system-deps", + "x11", ] [[package]] @@ -2148,12 +2111,12 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.2", + "windows-targets 0.52.6", ] [[package]] @@ -2174,8 +2137,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2185,9 +2150,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.5+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -2198,31 +2165,33 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" -version = "0.15.12" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror 1.0.69", ] [[package]] name = "gio-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", "winapi", ] @@ -2232,7 +2201,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "libc", "libgit2-sys", "log", @@ -2241,19 +2210,22 @@ dependencies = [ [[package]] name = "glib" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", + "memchr", "once_cell", "smallvec", "thiserror 1.0.69", @@ -2261,47 +2233,33 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.13" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] -name = "globset" -version = "0.4.16" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gloo-timers" @@ -2317,23 +2275,22 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gtk" -version = "0.15.5" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -2344,16 +2301,15 @@ dependencies = [ "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -2364,36 +2320,35 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gtk3-macros" -version = "0.15.6" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ - "anyhow", "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] name = "h2" -version = "0.3.26" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.10.0", + "http", + "indexmap 2.11.1", "slab", "tokio", "tokio-util", @@ -2401,22 +2356,13 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.4.11" +name = "half" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.3.1", - "indexmap 2.10.0", - "slab", - "tokio", - "tokio-util", - "tracing", + "cfg-if", + "crunchy", ] [[package]] @@ -2436,9 +2382,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -2451,16 +2397,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.4", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", + "hashbrown 0.15.5", ] [[package]] @@ -2475,12 +2412,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -2522,27 +2453,14 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.15", + "match_token", ] [[package]] @@ -2553,18 +2471,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", + "itoa", ] [[package]] @@ -2574,7 +2481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http", ] [[package]] @@ -2585,17 +2492,11 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - [[package]] name = "httparse" version = "1.10.1" @@ -2610,44 +2511,22 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.32" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa 1.0.15", - "pin-project-lite", - "socket2 0.5.10", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.11", - "http 1.3.1", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", - "itoa 1.0.15", + "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2659,14 +2538,15 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.6.0", + "http", + "hyper", "hyper-util", "rustls", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -2675,26 +2555,13 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.6.0", + "hyper", "hyper-util", "pin-project-lite", "tokio", "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.32", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -2703,7 +2570,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-util", "native-tls", "tokio", @@ -2713,24 +2580,24 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "hyper 1.6.0", + "http", + "http-body", + "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", - "system-configuration 0.6.1", + "socket2", + "system-configuration", "tokio", "tower-service", "tracing", @@ -2739,9 +2606,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2749,7 +2616,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.0", ] [[package]] @@ -2768,7 +2635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" dependencies = [ "byteorder", - "png", + "png 0.17.16", ] [[package]] @@ -2865,9 +2732,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2884,44 +2751,17 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata 0.4.9", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-traits", -] - [[package]] name = "image" -version = "0.25.6" +version = "0.25.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" dependencies = [ "bytemuck", "byteorder-lite", + "moxcms", "num-traits", - "png", + "png 0.18.0", "tiff", ] @@ -2938,51 +2778,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] [[package]] name = "infer" -version = "0.13.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" dependencies = [ "cfb", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "libc", ] @@ -3003,6 +2823,25 @@ dependencies = [ "serde", ] +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -3018,12 +2857,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.15" @@ -3032,9 +2865,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "javascriptcore-rs" -version = "0.16.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ "bitflags 1.3.2", "glib", @@ -3043,28 +2876,14 @@ dependencies = [ [[package]] name = "javascriptcore-rs-sys" -version = "0.4.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", -] - -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", + "system-deps", ] [[package]] @@ -3091,25 +2910,19 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" - [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" dependencies = [ "once_cell", "wasm-bindgen", @@ -3117,9 +2930,9 @@ dependencies = [ [[package]] name = "json-patch" -version = "2.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ "jsonptr", "serde", @@ -3129,25 +2942,34 @@ dependencies = [ [[package]] name = "jsonptr" -version = "0.4.7" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" dependencies = [ - "fluent-uri", "serde", "serde_json", ] +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.9.4", + "serde", + "unicode-segmentation", +] + [[package]] name = "kuchikiki" -version = "0.8.2" +version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 1.9.3", - "matches", + "indexmap 2.11.1", "selectors", ] @@ -3171,9 +2993,9 @@ dependencies = [ [[package]] name = "libappindicator" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" dependencies = [ "glib", "gtk", @@ -3184,20 +3006,20 @@ dependencies = [ [[package]] name = "libappindicator-sys" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libgit2-sys" @@ -3221,6 +3043,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + [[package]] name = "libm" version = "0.2.15" @@ -3229,11 +3061,11 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "libc", "redox_syscall", ] @@ -3261,12 +3093,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3275,9 +3101,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -3287,9 +3113,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" @@ -3303,28 +3129,19 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" dependencies = [ "serde", "value-bag", ] [[package]] -name = "loom" -version = "0.5.6" +name = "lru-slab" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "mac" @@ -3334,46 +3151,48 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mac-notification-sys" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b95dfb34071d1592b45622bf93e315e3a72d414b6782aca9a015c12bec367ef" +checksum = "119c8490084af61b44c9eda9d626475847a186737c0378c85e32d77c33a01cd4" dependencies = [ "cc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "time", ] -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", ] [[package]] -name = "matchers" +name = "match_token" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -3384,9 +3203,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "md-5" @@ -3404,15 +3223,6 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -3455,6 +3265,37 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "moxcms" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "once_cell", + "png 0.17.16", + "serde", + "thiserror 2.0.16", + "windows-sys 0.60.2", +] + [[package]] name = "multimap" version = "0.10.1" @@ -3473,21 +3314,23 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] [[package]] name = "ndk" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "jni-sys", + "log", "ndk-sys", "num_enum", + "raw-window-handle", "thiserror 1.0.69", ] @@ -3499,75 +3342,64 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.3.0" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] [[package]] name = "netlink-packet-core" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +checksum = "745d789fe0958caf7252f5e1e900ce5c09b6a5bf05c7bba02a9cc600866ce31e" dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", + "pastey", ] [[package]] name = "netlink-packet-generic" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7eb8ad331c84c6b8cb7f685b448133e5ad82e1ffd5acafac374af4a5a308b" +checksum = "2f891b2e0054cac5a684a06628f59568f841c93da4e551239da6e518f539e775" dependencies = [ - "anyhow", - "byteorder", "netlink-packet-core", - "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" -version = "0.22.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" +checksum = "3ec2f5b6839be2a19d7fa5aab5bc444380f6311c2b693551cb80f45caaa7b5ef" dependencies = [ - "anyhow", - "bitflags 2.9.1", - "byteorder", + "bitflags 2.9.4", "libc", "log", "netlink-packet-core", - "netlink-packet-utils", ] [[package]] name = "netlink-packet-utils" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +checksum = "3176f18d11a1ae46053e59ec89d46ba318ae1343615bd3f8c908bfc84edae35c" dependencies = [ - "anyhow", "byteorder", - "paste", - "thiserror 1.0.69", + "pastey", + "thiserror 2.0.16", ] [[package]] name = "netlink-packet-wireguard" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b25b050ff1f6a1e23c6777b72db22790fe5b6b5ccfd3858672587a79876c8f" +checksum = "598962d9067d3153a00106da10e7b8276cea68f396f4a22f5b4a079270d92e29" dependencies = [ - "anyhow", - "byteorder", "libc", "log", + "netlink-packet-core", "netlink-packet-generic", - "netlink-packet-utils", ] [[package]] @@ -3587,29 +3419,17 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", -] - [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", - "memoffset 0.9.1", + "memoffset", ] [[package]] @@ -3634,22 +3454,21 @@ version = "4.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400" dependencies = [ - "futures-lite 2.6.0", + "futures-lite", "log", "mac-notification-sys", "serde", "tauri-winrt-notification", - "zbus 5.7.1", + "zbus", ] [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -3707,23 +3526,24 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] @@ -3735,27 +3555,6 @@ dependencies = [ "libc", ] -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - [[package]] name = "objc-sys" version = "0.3.5" @@ -3774,11 +3573,12 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", + "objc2-exception-helper", ] [[package]] @@ -3787,9 +3587,38 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.4", + "block2 0.6.1", + "libc", + "objc2 0.6.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", "objc2-core-graphics", + "objc2-core-image", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -3799,9 +3628,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -3810,26 +3639,45 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-foundation", "objc2-io-surface", ] +[[package]] +name = "objc2-core-image" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +dependencies = [ + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-encode" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + [[package]] name = "objc2-foundation" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -3841,10 +3689,10 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -3854,27 +3702,94 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.4", + "objc2 0.6.2", "objc2-core-foundation", ] [[package]] -name = "objc_exception" -version = "0.1.2" +name = "objc2-javascript-core" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a" dependencies = [ - "cc", + "objc2 0.6.2", + "objc2-core-foundation", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.4", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.4", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-security" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" dependencies = [ - "objc", + "bitflags 2.9.4", + "block2 0.6.1", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-javascript-core", + "objc2-security", ] [[package]] @@ -3898,15 +3813,27 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "dunce", + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -3921,7 +3848,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3932,9 +3859,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.1+3.5.1" +version = "300.5.2+3.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" +checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" dependencies = [ "cc", ] @@ -3952,6 +3879,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-multimap" version = "0.7.3" @@ -3972,6 +3905,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +dependencies = [ + "log", + "plist", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_pipe" version = "1.2.2" @@ -3982,19 +3927,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "pango" -version = "0.15.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", + "gio", "glib", "libc", "once_cell", @@ -4003,14 +3942,14 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] @@ -4043,10 +3982,16 @@ dependencies = [ ] [[package]] -name = "paste" -version = "1.0.15" +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "pathdiff" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pem-rfc7468" @@ -4059,9 +4004,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -4070,7 +4015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.10.0", + "indexmap 2.11.1", ] [[package]] @@ -4080,7 +4025,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.10.0", + "indexmap 2.11.1", ] [[package]] @@ -4089,9 +4034,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", "phf_shared 0.8.0", - "proc-macro-hack", ] [[package]] @@ -4100,7 +4043,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -4125,12 +4070,12 @@ dependencies = [ [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -4165,12 +4110,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro-hack", "proc-macro2", "quote", @@ -4187,7 +4132,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4234,7 +4179,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4256,7 +4201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.3.0", + "fastrand", "futures-io", ] @@ -4289,13 +4234,13 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.2" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", - "indexmap 2.10.0", - "quick-xml", + "indexmap 2.11.1", + "quick-xml 0.38.3", "serde", "time", ] @@ -4314,41 +4259,37 @@ dependencies = [ ] [[package]] -name = "polling" -version = "2.8.0" +name = "png" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", + "bitflags 2.9.4", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", ] [[package]] name = "polling" -version = "3.8.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.0", ] [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -4376,12 +4317,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4394,6 +4335,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -4435,18 +4385,18 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", "prost-derive", @@ -4454,9 +4404,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" dependencies = [ "heck 0.5.0", "itertools", @@ -4467,29 +4417,31 @@ dependencies = [ "prettyplease", "prost", "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", "regex", - "syn 2.0.104", + "syn 2.0.106", "tempfile", ] [[package]] name = "prost-derive" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "prost-types" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ "prost", ] @@ -4530,6 +4482,41 @@ dependencies = [ "psl-types", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +dependencies = [ + "bitflags 2.9.4", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" +dependencies = [ + "pulldown-cmark", +] + +[[package]] +name = "pxfm" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55f4fedc84ed39cb7a489322318976425e42a147e2be79d8f878e2884f94e84" +dependencies = [ + "num-traits", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.37.5" @@ -4539,6 +4526,70 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.16", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.16", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.40" @@ -4585,6 +4636,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -4605,6 +4666,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -4623,6 +4694,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -4643,17 +4723,17 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] @@ -4668,7 +4748,18 @@ dependencies = [ ] [[package]] -name = "ref-cast" +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.16", +] + +[[package]] +name = "ref-cast" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" @@ -4684,52 +4775,37 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rend" @@ -4742,51 +4818,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "winreg 0.50.0", -] - -[[package]] -name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", @@ -4794,13 +4828,14 @@ dependencies = [ "cookie_store", "encoding_rs", "futures-core", - "h2 0.4.11", - "http 1.3.1", - "http-body 1.0.1", + "futures-util", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-rustls", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", "js-sys", "log", @@ -4808,44 +4843,51 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tokio-native-tls", - "tower 0.5.2", + "tokio-rustls", + "tokio-util", + "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", + "webpki-roots", ] [[package]] name = "rfd" -version = "0.10.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ - "block", - "dispatch", + "ashpd 0.11.0", + "block2 0.6.1", + "dispatch2", "glib-sys", "gobject-sys", "gtk-sys", "js-sys", - "lazy_static", "log", - "objc", - "objc-foundation", - "objc_id", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.37.0", + "windows-sys 0.59.0", ] [[package]] @@ -4924,9 +4966,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.2" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" +checksum = "c8975fc98059f365204d635119cf9c5a60ae67b841ed49b5422a9a7e56cdfac0" dependencies = [ "arrayvec", "borsh", @@ -4940,9 +4982,15 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -4953,27 +5001,13 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4982,24 +5016,26 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.0", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ + "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -5007,12 +5043,15 @@ dependencies = [ ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustls-native-certs" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ - "base64 0.21.7", + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.4.0", ] [[package]] @@ -5021,14 +5060,15 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8" dependencies = [ "ring", "rustls-pki-types", @@ -5037,9 +5077,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -5058,11 +5098,26 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", ] [[package]] @@ -5079,9 +5134,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -5089,6 +5144,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.106", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -5113,18 +5180,31 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" +dependencies = [ + "bitflags 2.9.4", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -5132,64 +5212,96 @@ dependencies = [ [[package]] name = "selectors" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", "log", - "matches", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", - "thin-slice", ] [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.10.0", - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -5200,7 +5312,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5212,6 +5324,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5219,7 +5340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] @@ -5234,9 +5355,9 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.11.1", "schemars 0.9.0", - "schemars 1.0.3", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -5253,7 +5374,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5275,14 +5396,14 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "servo_arc" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" dependencies = [ "nodrop", "stable_deref_trait", @@ -5327,9 +5448,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -5370,9 +5491,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -5385,50 +5506,60 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "winapi", + "windows-sys 0.59.0", ] [[package]] -name = "socket2" -version = "0.5.10" +name = "softbuffer" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ - "libc", - "windows-sys 0.52.0", + "bytemuck", + "cfg_aliases", + "core-graphics", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", + "raw-window-handle", + "redox_syscall", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", ] [[package]] -name = "soup2" -version = "0.2.1" +name = "soup3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" dependencies = [ - "bitflags 1.3.2", + "futures-channel", "gio", "glib", "libc", - "once_cell", - "soup2-sys", + "soup3-sys", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "soup3-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" dependencies = [ - "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] @@ -5475,14 +5606,14 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-core", "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "hashlink", - "indexmap 2.10.0", + "indexmap 2.11.1", "log", "memchr", "once_cell", @@ -5491,7 +5622,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tokio-stream", "tracing", @@ -5509,7 +5640,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5532,7 +5663,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.104", + "syn 2.0.106", "tokio", "url", ] @@ -5545,7 +5676,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.1", + "bitflags 2.9.4", "byteorder", "bytes", "chrono", @@ -5561,7 +5692,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "itoa 1.0.15", + "itoa", "log", "md-5", "memchr", @@ -5575,7 +5706,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "uuid", "whoami", @@ -5589,7 +5720,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.1", + "bitflags 2.9.4", "byteorder", "chrono", "crc", @@ -5602,7 +5733,7 @@ dependencies = [ "hkdf", "hmac", "home", - "itoa 1.0.15", + "itoa", "log", "md-5", "memchr", @@ -5614,7 +5745,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "uuid", "whoami", @@ -5640,7 +5771,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "url", "uuid", @@ -5652,15 +5783,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -5711,44 +5833,43 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "struct-patch" -version = "0.9.4" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c4161ecd2bdb6fac38c4c627b5b3adc62a0ad53d0ba8b984f1df9631ebef0" +checksum = "9e986d2cf6e819bd843319120453d837dfdfa31497c3fee4cefa614b2d182d8c" dependencies = [ "struct-patch-derive", ] [[package]] name = "struct-patch-derive" -version = "0.9.4" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403a2a4b4fae123baa030e9a13f1d366647164fa6222a92c49730d10e8df1b93" +checksum = "68c6387c1c7b53060605101b63d93edca618c6cf7ce61839f2ec2a527419fdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5757,6 +5878,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.109" @@ -5770,21 +5902,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -5802,18 +5928,16 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "sys-locale" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys 0.5.0", + "libc", ] [[package]] @@ -5822,19 +5946,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "core-foundation 0.9.4", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", + "system-configuration-sys", ] [[package]] @@ -5847,78 +5961,56 @@ dependencies = [ "libc", ] -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", -] - [[package]] name = "system-deps" version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr 0.15.8", + "cfg-expr", "heck 0.5.0", "pkg-config", "toml 0.8.23", - "version-compare 0.2.0", + "version-compare", ] [[package]] name = "tao" -version = "0.16.10" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d298c441a1da46e28e8ad8ec205aab7fd8cd71b9d10e05454224eef422e1ae" +checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7" dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation 0.9.4", + "bitflags 2.9.4", + "block2 0.6.1", + "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", - "dirs-next", "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", + "dlopen2", + "dpi", "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib", - "glib-sys", "gtk", - "image 0.24.9", - "instant", - "jni 0.20.0", + "jni", "lazy_static", - "libappindicator", "libc", "log", "ndk", "ndk-context", "ndk-sys", - "objc", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-foundation 0.3.1", "once_cell", "parking_lot", - "png", "raw-window-handle", "scopeguard", - "serde", "tao-macros", "unicode-segmentation", - "uuid", - "windows 0.39.0", - "windows-implement 0.39.0", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", "x11-dl", ] @@ -5930,7 +6022,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5939,17 +6031,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tar" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "target-lexicon" version = "0.12.16" @@ -5958,216 +6039,415 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "1.8.3" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae1f57c291a6ab8e1d2e6b8ad0a35ff769c9925deb8a89de85425ff08762d0c" +checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c" dependencies = [ "anyhow", "bytes", - "cocoa", - "dirs-next", + "cookie", + "dirs", "dunce", "embed_plist", - "encoding_rs", - "flate2", - "futures-util", - "getrandom 0.2.16", - "glib", + "getrandom 0.3.3", "glob", "gtk", "heck 0.5.0", - "http 0.2.12", - "ignore", - "indexmap 1.9.3", - "infer", + "http", + "image", + "jni", + "libc", "log", - "nix 0.26.4", - "notify-rust", - "objc", - "once_cell", + "mime", + "muda", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "objc2-ui-kit", + "objc2-web-kit", "percent-encoding", "plist", - "png", - "rand 0.8.5", "raw-window-handle", - "reqwest 0.11.27", - "rfd", - "semver", + "reqwest", "serde", "serde_json", "serde_repr", "serialize-to-javascript", - "state", - "tar", + "swift-rs", + "tauri-build", "tauri-macros", "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.16", "tokio", + "tray-icon", "url", - "uuid", + "urlpattern", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "window-vibrancy", + "windows", ] [[package]] name = "tauri-build" -version = "1.5.6" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db08694eec06f53625cfc6fff3a363e084e5e9a238166d2989996413c346453" +checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f" dependencies = [ "anyhow", "cargo_toml", - "dirs-next", + "dirs", + "glob", "heck 0.5.0", "json-patch", + "schemars 0.8.22", "semver", "serde", "serde_json", "tauri-utils", "tauri-winres", + "toml 0.9.5", "walkdir", ] [[package]] name = "tauri-codegen" -version = "1.4.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53438d78c4a037ffe5eafa19e447eea599bedfb10844cb08ec53c2471ac3ac3f" +checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "brotli", "ico", "json-patch", "plist", - "png", + "png 0.17.16", "proc-macro2", "quote", "semver", "serde", "serde_json", "sha2", + "syn 2.0.106", "tauri-utils", - "thiserror 1.0.69", + "thiserror 2.0.16", "time", + "url", "uuid", "walkdir", ] [[package]] name = "tauri-macros" -version = "1.4.7" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233988ac08c1ed3fe794cd65528d48d8f7ed4ab3895ca64cdaa6ad4d00c45c0b" +checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", "tauri-codegen", "tauri-utils", ] +[[package]] +name = "tauri-plugin" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "toml 0.9.5", + "walkdir", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adddd9e9275b20e77af3061d100a25a884cced3c4c9ef680bd94dd0f7e26c1ca" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.16", +] + +[[package]] +name = "tauri-plugin-deep-link" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd67112fb1131834c2a7398ffcba520dbbf62c17de3b10329acd1a3554b1a9bb" +dependencies = [ + "dunce", + "plist", + "rust-ini", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.16", + "tracing", + "url", + "windows-registry", + "windows-result 0.3.4", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beee42a4002bc695550599b011728d9dfabf82f767f134754ed6655e434824e" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.16", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "315784ec4be45e90a987687bae7235e6be3d6e9e350d2b75c16b8a4bf22c1db7" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.16", + "toml 0.9.5", + "url", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938a3d7051c9a82b431e3a0f3468f85715b3442b3c3a3913095e9fa509e2652c" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http", + "regex", + "reqwest", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.16", + "tokio", + "url", + "urlpattern", +] + [[package]] name = "tauri-plugin-log" -version = "0.0.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#a0a310756ab3d770ed554c9801d3bea5940eb31e" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c1438bc7662acd16d508c919b3c087efd63669a4c75625dff829b1c75975ec" dependencies = [ + "android_logger", "byte-unit", "fern", "log", + "objc2 0.6.2", + "objc2-foundation 0.3.1", "serde", "serde_json", "serde_repr", + "swift-rs", "tauri", + "tauri-plugin", + "thiserror 2.0.16", "time", ] [[package]] -name = "tauri-plugin-single-instance" -version = "0.0.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#a0a310756ab3d770ed554c9801d3bea5940eb31e" +name = "tauri-plugin-notification" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fbc86b929b5376ab84b25c060f966d146b2fbd59b6af8264027b343c82c219" dependencies = [ "log", + "notify-rust", + "rand 0.9.2", "serde", "serde_json", + "serde_repr", "tauri", - "thiserror 1.0.69", - "windows-sys 0.59.0", - "zbus 3.15.2", + "tauri-plugin", + "thiserror 2.0.16", + "time", + "url", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "open", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.16", + "url", + "windows", + "zbus", +] + +[[package]] +name = "tauri-plugin-os" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.16", +] + +[[package]] +name = "tauri-plugin-single-instance" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb9cac815bf11c4a80fb498666bcdad66d65b89e3ae24669e47806febb76389c" +dependencies = [ + "serde", + "serde_json", + "tauri", + "tauri-plugin-deep-link", + "thiserror 2.0.16", + "tracing", + "windows-sys 0.60.2", + "zbus", ] [[package]] name = "tauri-plugin-window-state" -version = "0.1.1" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#a0a310756ab3d770ed554c9801d3bea5940eb31e" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5f6fe3291bfa609c7e0b0ee3bedac294d94c7018934086ce782c1d0f2a468e" dependencies = [ - "bincode", - "bitflags 2.9.1", + "bitflags 2.9.4", "log", "serde", "serde_json", "tauri", - "thiserror 1.0.69", + "tauri-plugin", + "thiserror 2.0.16", ] [[package]] name = "tauri-runtime" -version = "0.14.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8066855882f00172935e3fa7d945126580c34dcbabab43f5d4f0c2398a67d47b" +checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" dependencies = [ + "cookie", + "dpi", "gtk", - "http 0.2.12", - "http-range", - "rand 0.8.5", + "http", + "jni", + "objc2 0.6.2", + "objc2-ui-kit", + "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror 1.0.69", + "thiserror 2.0.16", "url", - "uuid", + "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows", ] [[package]] name = "tauri-runtime-wry" -version = "0.14.11" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce361fec1e186705371f1c64ae9dd2a3a6768bc530d0a2d5e75a634bb416ad4d" +checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807" dependencies = [ - "arboard", - "cocoa", "gtk", + "http", + "jni", + "log", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "once_cell", "percent-encoding", - "rand 0.8.5", "raw-window-handle", + "softbuffer", + "tao", "tauri-runtime", "tauri-utils", - "uuid", + "url", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows", "wry", ] [[package]] name = "tauri-utils" -version = "1.6.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c357952645e679de02cd35007190fcbce869b93ffc61b029f33fe02648453774" +checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" dependencies = [ + "anyhow", "brotli", + "cargo_metadata", "ctor", "dunce", "glob", - "heck 0.5.0", "html5ever", + "http", "infer", "json-patch", "kuchikiki", @@ -6176,24 +6456,30 @@ dependencies = [ "phf 0.11.3", "proc-macro2", "quote", + "regex", + "schemars 0.8.22", "semver", "serde", + "serde-untagged", "serde_json", "serde_with", - "thiserror 1.0.69", + "swift-rs", + "thiserror 2.0.16", + "toml 0.9.5", "url", + "urlpattern", + "uuid", "walkdir", - "windows-version", ] [[package]] name = "tauri-winres" -version = "0.1.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" dependencies = [ "embed-resource", - "toml 0.7.8", + "toml 0.9.5", ] [[package]] @@ -6202,23 +6488,23 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" dependencies = [ - "quick-xml", - "thiserror 2.0.12", - "windows 0.61.3", + "quick-xml 0.37.5", + "thiserror 2.0.16", + "windows", "windows-version", ] [[package]] name = "tempfile" -version = "3.20.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ - "fastrand 2.3.0", + "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.0", ] [[package]] @@ -6232,12 +6518,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - [[package]] name = "thiserror" version = "1.0.69" @@ -6249,11 +6529,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -6264,18 +6544,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6289,23 +6569,25 @@ dependencies = [ [[package]] name = "tiff" -version = "0.9.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" dependencies = [ + "fax", "flate2", - "jpeg-decoder", + "half", + "quick-error", "weezl", + "zune-jpeg", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", - "itoa 1.0.15", "libc", "num-conv", "num_threads", @@ -6317,15 +6599,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -6352,9 +6634,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -6367,9 +6649,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -6380,9 +6662,10 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2 0.5.10", + "socket2", "tokio-macros", - "windows-sys 0.52.0", + "tracing", + "windows-sys 0.59.0", ] [[package]] @@ -6393,7 +6676,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6429,9 +6712,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -6442,42 +6725,45 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] name = "toml" -version = "0.7.8" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ + "indexmap 2.11.1", "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.13", ] [[package]] -name = "toml" -version = "0.8.23" +name = "toml_datetime" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] @@ -6488,10 +6774,19 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.10.0", - "serde", - "serde_spanned", - "toml_datetime", + "indexmap 2.11.1", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.11.1", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -6501,45 +6796,55 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.1", "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow 0.7.11", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "winnow 0.7.13", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow 0.7.13", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tonic" -version = "0.12.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" dependencies = [ - "async-stream", "async-trait", "axum", "base64 0.22.1", "bytes", - "h2 0.4.11", - "http 1.3.1", - "http-body 1.0.1", + "flate2", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", - "prost", - "socket2 0.5.10", + "rustls-native-certs", + "socket2", + "sync_wrapper", "tokio", + "tokio-rustls", "tokio-stream", - "tower 0.4.13", + "tower", "tower-layer", "tower-service", "tracing", @@ -6547,36 +6852,41 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +checksum = "4c40aaccc9f9eccf2cd82ebc111adc13030d23e887244bc9cfa5d1d636049de3" dependencies = [ "prettyplease", "proc-macro2", - "prost-build", - "prost-types", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] -name = "tower" -version = "0.4.13" +name = "tonic-prost" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a16cba4043dc3ff43fcb3f96b4c5c154c64cbd18ca8dce2ab2c6a451d058a2" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.106", + "tempfile", + "tonic-build", ] [[package]] @@ -6587,11 +6897,15 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "indexmap 2.11.1", "pin-project-lite", - "sync_wrapper 1.0.2", + "slab", + "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -6600,14 +6914,14 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "iri-string", "pin-project-lite", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", ] @@ -6656,7 +6970,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6692,14 +7006,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "serde", "serde_json", "sharded-slab", @@ -6711,13 +7025,34 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tray-icon" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", + "once_cell", + "png 0.17.16", + "serde", + "thiserror 2.0.16", + "windows-sys 0.59.0", +] + [[package]] name = "tree_magic_mini" -version = "3.1.6" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" +checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c" dependencies = [ - "fnv", "memchr", "nom", "once_cell", @@ -6736,6 +7071,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.18.0" @@ -6748,11 +7089,58 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "winapi", ] +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -6761,9 +7149,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-normalization" @@ -6794,9 +7182,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -6804,6 +7192,18 @@ dependencies = [ "serde", ] +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -6830,12 +7230,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", + "serde", "wasm-bindgen", ] @@ -6896,12 +7297,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - [[package]] name = "version-compare" version = "0.2.0" @@ -6934,12 +7329,6 @@ dependencies = [ "libc", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -6973,11 +7362,20 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.5+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4" dependencies = [ - "wit-bindgen-rt", + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.0+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" +dependencies = [ + "wit-bindgen", ] [[package]] @@ -6988,35 +7386,36 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" dependencies = [ "cfg-if", "js-sys", @@ -7027,9 +7426,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7037,22 +7436,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" dependencies = [ "unicode-ident", ] @@ -7072,36 +7471,37 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.1.2", + "scoped-tls", "smallvec", "wayland-sys", ] [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.1", - "rustix 0.38.44", + "bitflags 2.9.4", + "rustix 1.1.2", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7109,11 +7509,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7122,29 +7522,41 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.37.5", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ + "dlib", + "log", "pkg-config", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -7157,10 +7569,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf4f3c0ba838e82b4e5ccc4157003fb8c324ee24c058470ffb82820becbde98" dependencies = [ "core-foundation 0.10.1", - "jni 0.21.1", + "jni", "log", "ndk-context", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "url", "web-sys", @@ -7168,9 +7580,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -7186,20 +7598,18 @@ dependencies = [ "javascriptcore-rs", "libc", "once_cell", - "soup2", + "soup3", "webkit2gtk-sys", ] [[package]] name = "webkit2gtk-sys" -version = "0.18.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" dependencies = [ - "atk-sys", "bitflags 1.3.2", "cairo-sys-rs", - "gdk-pixbuf-sys", "gdk-sys", "gio-sys", "glib-sys", @@ -7207,48 +7617,54 @@ dependencies = [ "gtk-sys", "javascriptcore-rs-sys", "libc", - "pango-sys", "pkg-config", - "soup2-sys", - "system-deps 6.2.2", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", ] [[package]] name = "webview2-com" -version = "0.19.1" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.39.0", - "windows-implement 0.39.0", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] name = "webview2-com-sys" -version = "0.19.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror 1.0.69", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", + "thiserror 2.0.16", + "windows", + "windows-core 0.61.2", ] [[package]] @@ -7259,11 +7675,11 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "whoami" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall", + "libredox", "wasite", ] @@ -7291,11 +7707,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -7305,39 +7721,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows-implement 0.39.0", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - -[[package]] -name = "windows" -version = "0.48.0" +name = "window-vibrancy" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "windows-targets 0.48.5", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", ] [[package]] @@ -7347,29 +7742,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] -[[package]] -name = "windows-bindgen" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" -dependencies = [ - "windows-metadata", - "windows-tokens", -] - [[package]] name = "windows-collections" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -7378,32 +7763,35 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.0", + "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] -name = "windows-future" -version = "0.2.1" +name = "windows-core" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" dependencies = [ - "windows-core", - "windows-link", - "windows-threading", + "windows-implement", + "windows-interface", + "windows-link 0.2.0", + "windows-result 0.4.0", + "windows-strings 0.5.0", ] [[package]] -name = "windows-implement" -version = "0.39.0" +name = "windows-future" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "syn 1.0.109", - "windows-tokens", + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] @@ -7414,7 +7802,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7425,7 +7813,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7435,10 +7823,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-metadata" -version = "0.39.0" +name = "windows-link" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-numerics" @@ -7446,8 +7834,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", ] [[package]] @@ -7456,9 +7844,9 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -7467,7 +7855,16 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -7476,7 +7873,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "widestring", "windows-sys 0.52.0", ] @@ -7487,7 +7884,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -7532,7 +7938,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -7583,10 +7998,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7603,22 +8019,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] -[[package]] -name = "windows-tokens" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" - [[package]] name = "windows-version" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +checksum = "69e061eb0a22b4a1d778ad70f7575ec7845490abb35b08fa320df7895882cacb" dependencies = [ - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -7645,18 +8055,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7681,18 +8079,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7729,18 +8115,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7765,18 +8139,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7825,18 +8187,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7872,18 +8222,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.50.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -7891,22 +8241,19 @@ dependencies = [ [[package]] name = "winreg" -version = "0.52.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" [[package]] name = "wl-clipboard-rs" @@ -7919,7 +8266,7 @@ dependencies = [ "os_pipe", "rustix 0.38.44", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.16", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -7935,40 +8282,47 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wry" -version = "0.24.11" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55c80b12287eb1ff7c365fc2f7a5037cb6181bd44c9fce81c8d1cf7605ffad6" +checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90" dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics", + "base64 0.22.1", + "block2 0.6.1", + "cookie", "crossbeam-channel", + "dirs", + "dpi", "dunce", - "gdk", - "gio", - "glib", + "gdkx11", "gtk", "html5ever", - "http 0.2.12", + "http", + "javascriptcore-rs", + "jni", "kuchikiki", "libc", - "log", - "objc", - "objc_id", + "ndk", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-ui-kit", + "objc2-web-kit", "once_cell", - "serde", - "serde_json", + "percent-encoding", + "raw-window-handle", "sha2", - "soup2", - "tao", - "thiserror 1.0.69", + "soup3", + "tao-macros", + "thiserror 2.0.16", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.39.0", - "windows-implement 0.39.0", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", ] [[package]] @@ -8003,20 +8357,20 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ "gethostname", - "rustix 0.38.44", + "rustix 1.1.2", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "x25519-dalek" @@ -8030,26 +8384,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "xattr" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" -dependencies = [ - "libc", - "rustix 1.0.7", -] - -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "yoke" version = "0.8.0" @@ -8070,122 +8404,57 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] [[package]] name = "zbus" -version = "3.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" -dependencies = [ - "async-broadcast 0.5.1", - "async-executor", - "async-fs 1.6.0", - "async-io 1.13.0", - "async-lock 2.8.0", - "async-process 1.8.1", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "byteorder", - "derivative", - "enumflags2", - "event-listener 2.5.3", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix 0.26.4", - "once_cell", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "winapi", - "xdg-home", - "zbus_macros 3.15.2", - "zbus_names 2.6.1", - "zvariant 3.15.2", -] - -[[package]] -name = "zbus" -version = "5.7.1" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" dependencies = [ - "async-broadcast 0.7.2", + "async-broadcast", "async-executor", - "async-io 2.4.1", - "async-lock 3.4.0", - "async-process 2.3.1", + "async-io", + "async-lock", + "async-process", "async-recursion", "async-task", "async-trait", "blocking", "enumflags2", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-core", - "futures-lite 2.6.0", + "futures-lite", "hex", - "nix 0.30.1", + "nix", "ordered-stream", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", - "windows-sys 0.59.0", - "winnow 0.7.11", - "zbus_macros 5.7.1", - "zbus_names 4.2.0", - "zvariant 5.5.3", -] - -[[package]] -name = "zbus_macros" -version = "3.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils 1.0.1", + "windows-sys 0.60.2", + "winnow 0.7.13", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] name = "zbus_macros" -version = "5.7.1" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.104", - "zbus_names 4.2.0", - "zvariant 5.5.3", - "zvariant_utils 3.2.0", -] - -[[package]] -name = "zbus_names" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" -dependencies = [ - "serde", - "static_assertions", - "zvariant 3.15.2", + "syn 2.0.106", + "zbus_names", + "zvariant", + "zvariant_utils", ] [[package]] @@ -8196,28 +8465,28 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.11", - "zvariant 5.5.3", + "winnow 0.7.13", + "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8237,7 +8506,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -8258,7 +8527,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8274,9 +8543,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -8291,85 +8560,61 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] -name = "zvariant" -version = "3.15.2" +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ - "byteorder", - "enumflags2", - "libc", - "serde", - "static_assertions", - "zvariant_derive 3.15.2", + "zune-core", ] [[package]] name = "zvariant" -version = "5.5.3" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 0.7.11", - "zvariant_derive 5.5.3", - "zvariant_utils 3.2.0", -] - -[[package]] -name = "zvariant_derive" -version = "3.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils 1.0.1", + "winnow 0.7.13", + "zvariant_derive", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.5.3" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.104", - "zvariant_utils 3.2.0", -] - -[[package]] -name = "zvariant_utils" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "syn 2.0.106", + "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", - "syn 2.0.104", - "winnow 0.7.11", + "syn 2.0.106", + "winnow 0.7.13", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 17526c32..c9b985ed 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ members = ["cli", "common"] default-members = [".", "cli"] [workspace.dependencies] -defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.7.4" } +defguard_wireguard_rs = "0.7.5" [workspace.package] authors = ["Defguard"] @@ -26,9 +26,8 @@ rust-version.workspace = true version.workspace = true [build-dependencies] -tauri-build = { version = "1.5", features = [] } -tonic-build = { version = "0.12" } -prost-build = { version = "0.13" } +tauri-build = { version = "2", features = [] } +tonic-prost-build = { version = "0.14" } vergen-git2 = { version = "1.0", features = ["build"] } [dependencies] @@ -41,55 +40,69 @@ dark-light = "2.0" defguard_wireguard_rs = { workspace = true, features = ["check_dependencies"] } dirs-next = "2.0" log = { version = "0.4", features = ["serde"] } -prost = "0.13" +prost = "0.14" regex = "1.11" reqwest = { version = "0.12", features = ["cookies", "json"] } -rust-ini = "0.21" +# 0.21.2 causes config parsing errors +rust-ini = "=0.21.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_with = "3.11" sqlx = { version = "0.8", features = [ - "chrono", - "sqlite", - "runtime-tokio", - "uuid", - "macros", + "chrono", + "sqlite", + "runtime-tokio", + "uuid", + "macros", ] } -struct-patch = "0.9" +struct-patch = "0.10" strum = { version = "0.27", features = ["derive"] } -tauri = { version = "1.8", features = [ - "notification-all", - "dialog-all", - "clipboard-all", - "http-all", - "window-all", - "system-tray", - "native-tls-vendored", - "icon-png", - "fs-all", +tauri = { version = "2", features = [ + "native-tls-vendored", + "image-png", + "tray-icon", ] } -tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } -tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } -tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } +# Must match Tauri plugins in package.json. +tauri-plugin-clipboard-manager = "2" +tauri-plugin-deep-link = "2" +tauri-plugin-dialog = "2" +tauri-plugin-fs = "2" +tauri-plugin-http = { version = "2", features = ["unsafe-headers"] } +tauri-plugin-log = "2" +tauri-plugin-notification = "2" +tauri-plugin-single-instance = { version = "2", features = ["deep-link"] } +tauri-plugin-window-state = "2" thiserror = "2.0" time = { version = "0.3", features = ["formatting", "macros"] } tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] } tokio-util = "0.7" -tonic = "0.12" +tonic = { version = "0.14", default-features = false, features = [ + "codegen", + "gzip", + "router", + "tls-native-roots", + "tls-ring", + "transport", +] } +tonic-prost = "0.14" tracing = "0.1" tracing-appender = "0.2" tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } webbrowser = "1.0" x25519-dalek = { version = "2", features = [ - "getrandom", - "serde", - "static_secrets", + "getrandom", + "serde", + "static_secrets", ] } +tauri-plugin-opener = "2.5.0" +tauri-plugin-os = "2.3.1" +semver = "1.0.26" [target.'cfg(unix)'.dependencies] tokio-stream = "0.1" tower = "0.5" hyper-util = "0.1" +nix = { version = "0.30.1", features = ["user", "fs"] } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winsvc", "winerror"] } diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 04be1045..91455f0e 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -5,25 +5,24 @@ fn main() -> Result<(), Box> { let git2 = Git2Builder::default().branch(true).sha(true).build()?; Emitter::default().add_instructions(&git2)?.emit()?; - // compiling protos using path on build time - let mut config = prost_build::Config::new(); - // enable optional fields - config.protoc_arg("--experimental_allow_proto3_optional"); - // make sure empty DNS is deserialized correctly as None - config.type_attribute(".DeviceConfig", "#[serde_as]"); - config.field_attribute( - ".DeviceConfig.dns", - "#[serde_as(deserialize_as = \"NoneAsEmptyString\")]", - ); - // Make all messages serde-serializable - config.type_attribute(".", "#[derive(serde::Serialize,serde::Deserialize)]"); - tonic_build::configure().compile_protos_with_config( - config, - &["proto/client/client.proto", "proto/core/proxy.proto"], - &["proto/client", "proto/core"], - )?; + tonic_prost_build::configure() + // Enable optional fields. + .protoc_arg("--experimental_allow_proto3_optional") + // Make sure empty DNS is deserialized correctly as `None`. + .type_attribute(".DeviceConfig", "#[serde_as]") + .field_attribute( + ".DeviceConfig.dns", + "#[serde_as(deserialize_as = \"NoneAsEmptyString\")]", + ) + // Make all messages serde-serializable. + .type_attribute(".", "#[derive(serde::Serialize,serde::Deserialize)]") + .compile_protos( + &["proto/client/client.proto", "proto/core/proxy.proto"], + &["proto/client", "proto/core"], + )?; tauri_build::build(); + println!("cargo:rerun-if-changed=proto"); Ok(()) } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 00000000..c171d2b6 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,77 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "main-capability", + "description": "Capability for the main window", + "local": true, + "windows": ["main"], + "permissions": [ + "core:default", + "core:window:allow-create", + "core:window:allow-center", + "core:window:allow-request-user-attention", + "core:window:allow-set-resizable", + "core:window:allow-set-maximizable", + "core:window:allow-set-minimizable", + "core:window:allow-set-closable", + "core:window:allow-set-title", + "core:window:allow-maximize", + "core:window:allow-unmaximize", + "core:window:allow-minimize", + "core:window:allow-unminimize", + "core:window:allow-show", + "core:window:allow-hide", + "core:window:allow-close", + "core:window:allow-set-decorations", + "core:window:allow-set-always-on-top", + "core:window:allow-set-content-protected", + "core:window:allow-set-size", + "core:window:allow-set-min-size", + "core:window:allow-set-max-size", + "core:window:allow-set-position", + "core:window:allow-set-fullscreen", + "core:window:allow-set-focus", + "core:window:allow-set-icon", + "core:window:allow-set-skip-taskbar", + "core:window:allow-set-cursor-grab", + "core:window:allow-set-cursor-visible", + "core:window:allow-set-cursor-icon", + "core:window:allow-set-cursor-position", + "core:window:allow-set-ignore-cursor-events", + "core:window:allow-start-dragging", + "core:webview:allow-print", + "deep-link:default", + "fs:default", + "fs:allow-write-text-file", + "log:default", + "notification:default", + "os:default", + "os:allow-hostname", + "dialog:default", + "clipboard-manager:allow-write-text", + { + "identifier": "http:default", + "allow": [ + { + "url": "https://**" + }, + { + "url": "http://**" + }, + { + "url": "https://*:*" + }, + { + "url": "http://*:*" + } + ] + }, + { + "identifier": "opener:allow-open-url", + "allow": [ + { + "url": "https://*" + } + ] + } + ] +} diff --git a/src-tauri/cli/Cargo.toml b/src-tauri/cli/Cargo.toml index 44ddaeca..b8c6c080 100644 --- a/src-tauri/cli/Cargo.toml +++ b/src-tauri/cli/Cargo.toml @@ -8,14 +8,14 @@ rust-version.workspace = true version.workspace = true [build-dependencies] -prost-build = "0.13" +prost-build = "0.14" [dependencies] clap = { version = "4.5", features = ["cargo", "derive", "env"] } common = { path = "../common" } defguard_wireguard_rs = { workspace = true, features = ["check_dependencies"] } dirs-next = "2.0" -prost = "0.13" +prost = "0.14" reqwest = { version = "0.12", features = ["cookies", "json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src-tauri/cli/src/bin/dg.rs b/src-tauri/cli/src/bin/dg.rs index 036f56ad..3f94f339 100644 --- a/src-tauri/cli/src/bin/dg.rs +++ b/src-tauri/cli/src/bin/dg.rs @@ -1,7 +1,7 @@ -use core::fmt; #[cfg(not(windows))] use std::os::unix::fs::PermissionsExt; use std::{ + fmt, fs::{create_dir, OpenOptions}, net::IpAddr, path::{Path, PathBuf}, @@ -94,7 +94,7 @@ impl CliConfig { }; #[cfg(not(windows))] { - debug!("Setting config file permissions..."); + debug!("Setting config file permissions."); match file.metadata() { Ok(meta) => { let mut perms = meta.permissions(); @@ -138,8 +138,6 @@ enum CliError { TooManyDevices(usize), #[error(transparent)] WireGuard(#[from] WireguardInterfaceError), - #[error("Invalid address")] - InvalidAddress, #[error("Couldn't open CLI configuration at path: \"{0}\".")] ConfigNotFound(String), #[error("Couldn't parse CLI configuration at \"{0}\". Error details: {1}")] @@ -148,14 +146,15 @@ enum CliError { EnterpriseDisabled, #[error("Failed to save configuration at {0}: {1}")] ConfigSave(String, String), + #[error("Failed to find free TCP port")] + FreeTCPPort, } -async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError> { +/// Connect to Defguard Gateway. +async fn connect(config: CliConfig, ifname: String, trigger: Arc) -> Result<(), CliError> { let network_name = config.device_config.network_name.clone(); - debug!("Connecting to network {network_name}..."); - let ifname = get_interface_name(&config.device.name); + debug!("Connecting to network {network_name}."); - // let wgapi = setup_wgapi(&ifname).expect("Failed to setup WireGuard API"); #[cfg(not(target_os = "macos"))] let wgapi = WGApi::::new(ifname.to_string()).expect("Failed to setup WireGuard API"); #[cfg(target_os = "macos")] @@ -163,7 +162,7 @@ async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError #[cfg(not(windows))] { - // create new interface + // Create new interface. debug!("Creating new interface {ifname}"); wgapi .create_interface() @@ -183,7 +182,10 @@ async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError search_domains.push(entry); } } - debug!("DNS configuration for interface {ifname}: DNS: {dns:?}, Search domains: {search_domains:?}"); + debug!( + "DNS configuration for interface {ifname}: DNS: {dns:?}, Search domains: \ + {search_domains:?}" + ); let peer_key = Key::from_str(&config.device_config.pubkey).unwrap(); let mut peer = Peer::new(peer_key); @@ -197,7 +199,18 @@ async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError // peer.preshared_key = Some(peer_psk); // } - let allowed_ips: Vec<&str> = + let ip_addr_parser = |addr: &str| { + let ipaddrmask = addr.parse::(); + if let Err(err) = &ipaddrmask { + error!( + "Error parsing IP address `{addr}` while setting up interface: {err}. \ + Trying to parse the remaining addresses if any." + ); + } + ipaddrmask.ok() + }; + + peer.allowed_ips = //if location.route_all_traffic { // eprintln!("Using all traffic routing for location {location}: {DEFAULT_ROUTE_IPV4} {DEFAULT_ROUTE_IPV6}"); // vec![DEFAULT_ROUTE_IPV4.into(), DEFAULT_ROUTE_IPV6.into()] @@ -206,31 +219,23 @@ async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError .device_config .allowed_ips .split(',') - .collect(); - for allowed_ip in allowed_ips { - match IpAddrMask::from_str(allowed_ip) { - Ok(addr) => { - peer.allowed_ips.push(addr); - } - Err(err) => { - error!( - "Error parsing IP address `{allowed_ip}` while setting up interface: {err}. \ - Trying to parse the remaining addresses if any." - ); - } - } - } + .filter_map(ip_addr_parser) + .collect::>(); debug!("Parsed allowed IPs: {:?}", peer.allowed_ips); - let Ok(address) = config.device_config.assigned_ip.parse() else { - error!("Invalid assigned IP address in device configuration"); - return Err(CliError::InvalidAddress); - }; + + let addresses = config + .device_config + .assigned_ip + .split(',') + .filter_map(ip_addr_parser) + .collect::>(); + debug!("Parsed assigned IPs: {:?}", addresses); let config = InterfaceConfiguration { name: config.instance_info.name.clone(), prvkey: config.private_key.to_string(), - addresses: vec![address], - port: u32::from(find_free_tcp_port().unwrap()), + addresses, + port: u32::from(find_free_tcp_port().ok_or(CliError::FreeTCPPort)?), peers: vec![peer.clone()], mtu: None, }; @@ -253,7 +258,10 @@ async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError "No DNS configuration provided for interface {ifname}, skipping DNS configuration" ); } else { - debug!("The following DNS servers will be set: {dns:?}, search domains: {search_domains:?}"); + debug!( + "The following DNS servers will be set: {dns:?}, search domains: \ + {search_domains:?}" + ); wgapi .configure_dns(&dns, &search_domains) .expect("Failed to configure DNS for WireGuard interface"); @@ -265,10 +273,13 @@ async fn connect(config: CliConfig, trigger: Arc) -> Result<(), CliError trigger.notified().await; debug!( - "Closing the interface {ifname} for network {network_name} because of a received signal..." + "Closing the interface {ifname} for network {network_name} because of a received signal." ); if let Err(err) = wgapi.remove_interface() { - error!("Failed to close the interface {ifname} for network {network_name}: {err}. The interface may've been already closed or it's not available."); + error!( + "Failed to close the interface {ifname} for network {network_name}: {err}. The \ + interface may've been already closed or it's not available." + ); } else { info!("Connection to the network {network_name} has been terminated."); } @@ -285,7 +296,7 @@ struct ApiError { /// Enroll device. async fn enroll(base_url: &Url, token: String) -> Result { - debug!("Starting enrollment through the proxy at {base_url}..."); + debug!("Starting enrollment through Defguard Proxy at {base_url}."); let client = Client::builder().cookie_store(true).build()?; let mut url = base_url.clone(); url.set_path("/api/v1/enrollment/start"); @@ -297,8 +308,11 @@ async fn enroll(base_url: &Url, token: String) -> Result { let response: proto::EnrollmentStartResponse = if result.status() == StatusCode::OK { let result = result.json().await?; - debug!("Enrollment start request has been successfully sent to the proxy. Received a response, proceeding with the device configuration."); - trace!("Received response: {:?}", result); + debug!( + "Enrollment start request has been successfully sent to Defguard Proxy. Received a \ + response, proceeding with the device configuration." + ); + trace!("Received response: {result:?}"); result } else { let error: ApiError = result.json().await?; @@ -307,10 +321,7 @@ async fn enroll(base_url: &Url, token: String) -> Result { }; if response.instance.is_none() { - error!( - "InstanceInfo is missing from the received enrollment start response: {:?}", - response - ); + error!("InstanceInfo is missing from the received enrollment start response: {response:?}"); return Err(CliError::MissingData); } @@ -333,7 +344,10 @@ async fn enroll(base_url: &Url, token: String) -> Result { let response: proto::DeviceConfigResponse = if result.status() == StatusCode::OK { let result = result.json().await?; - debug!("The device public key has been successfully sent to the proxy. The device should be now configured on the server's end."); + debug!( + "The device public key has been successfully sent to Defguard Proxy. The device should \ + be now configured on the server's end." + ); result } else { let error: ApiError = result.json().await?; @@ -348,11 +362,11 @@ async fn enroll(base_url: &Url, token: String) -> Result { return Err(CliError::TooManyDevices(count)); } let Some(instance_info) = response.instance else { - error!("Missing InstanceInfo in the configuration received from the proxy."); + error!("Missing InstanceInfo in the configuration received from Defguard Proxy."); return Err(CliError::MissingData); }; let Some(device) = response.device else { - error!("Missing Device in the configuration received from the proxy."); + error!("Missing Device in the configuration received from Defguard Proxy."); return Err(CliError::MissingData); }; @@ -363,7 +377,7 @@ async fn enroll(base_url: &Url, token: String) -> Result { instance_info, token: response.token, }; - debug!("Enrollment done, returning the received configuration..."); + debug!("Enrollment done, returning the received configuration."); Ok(config) } @@ -425,10 +439,13 @@ async fn fetch_config( /// Poll configuration from Defguard proxy in regular intervals. /// Exit when `DeviceConfig` differs from the current one. async fn poll_config(config: &mut CliConfig) { - debug!("Starting the configuration polling task..."); + debug!("Starting the configuration polling task."); // sanity check let Some(token) = config.clone().token else { - debug!("No polling token found in the CLI configuration. Make sure you are using the latest Defguard version. Exiting..."); + debug!( + "No polling token found in the CLI configuration. Make sure you are using the latest \ + Defguard version. Exiting." + ); return; }; let client = match Client::builder().cookie_store(true).build() { @@ -449,31 +466,33 @@ async fn poll_config(config: &mut CliConfig) { } }; url.set_path("/api/v1/poll"); - debug!("Config polling setup done, starting the polling loop..."); + debug!("Config polling setup done, starting the polling loop."); let mut interval = interval(INTERVAL_SECONDS); loop { interval.tick().await; - debug!("Polling network configuration from proxy..."); + debug!("Polling network configuration from proxy."); match fetch_config(&client, url.clone(), token.clone()).await { Ok(device_config) => { if config.device_config != device_config { - debug!("Network configuration has changed, re-configuring..."); + debug!("Network configuration has changed, re-configuring."); trace!( - "Old configuration: {:?}. New configuration: {:?}.", + "Old configuration: {:?}. New configuration: {device_config:?}.", config.device_config, - device_config ); config.device_config = device_config; debug!("New configuration has been successfully applied."); break; } - debug!("Network configuration has not changed. Continuing..."); + debug!("Network configuration has not changed. Continuing."); } Err(CliError::EnterpriseDisabled) => { - debug!("Enterprise features are disabled on this Defguard instance. Skipping..."); + debug!("Enterprise features are disabled on this Defguard instance. Skipping."); } Err(CliError::Reqwest(err)) => { - warn!("Failed to make network request to proxy ({url}): {err}. Check your network connection."); + warn!( + "Failed to make network request to proxy ({url}): {err}. Check your network \ + connection." + ); } Err(err) => { warn!("Failed to fetch configuration from proxy ({url}): {err}"); @@ -545,7 +564,10 @@ async fn main() { .subcommand_required(false) .subcommand( Command::new("enroll") - .about("Perform the enrollment and configuration. Use this first to set up the device.") + .about( + "Perform the enrollment and configuration. Use this first to set up the \ + device.", + ) .arg(token_opt) .arg(url_opt), ) @@ -570,8 +592,8 @@ async fn main() { .with(tracing_subscriber::fmt::layer()) .init(); - debug!("Starting CLI..."); - debug!("Getting configuration path..."); + debug!("Starting CLI."); + debug!("Getting configuration path."); // Obtain configuration file path. let config_path = match matches.get_one::("config") { Some(path) => path.clone(), @@ -587,7 +609,10 @@ async fn main() { path.push("config.json"); path } else { - error!("Default configuration path is not available on this platform. Please, specify it explicitly."); + error!( + "Default configuration path is not available on this platform. Please, \ + specify it explicitly." + ); return; } } @@ -595,7 +620,7 @@ async fn main() { debug!("The following configuration will be used: {config_path:?}"); if let Some(("enroll", submatches)) = matches.subcommand() { - debug!("Enrollment command has been selected, starting enrollment..."); + debug!("Enrollment command has been selected, starting enrollment."); let token = submatches .get_one::("token") .expect("No enrollment token was provided or it's invalid") @@ -607,48 +632,57 @@ async fn main() { let config = enroll(url, token) .await .expect("The enrollment process has failed"); - debug!("Successfully enrolled the device, saving the configuration..."); + debug!("Successfully enrolled the device, saving the configuration."); if let Err(err) = config.save(&config_path) { error!("{err}"); return; } - info!("Device has been successfully enrolled and the CLI configuration has been saved to {config_path:?}"); + info!( + "Device has been successfully enrolled and the CLI configuration has been saved to \ + {config_path:?}" + ); } else { - debug!("No command has been selected, trying to proceed with establishing a connection..."); + debug!("No command has been selected, trying to proceed with establishing a connection."); let mut config = match CliConfig::load(&config_path) { Ok(config) => config, - Err(err) => match err { - CliError::ConfigNotFound(path) => { - error!("No CLI confioguration file found at \"{path}\". \ - Proceed with enrollment first using \"dg enroll -t -u \" or pass a valid configuration file path using the \"--config\" option. Use \"dg --help\" to display all options."); - return; - } - _ => { - error!("Failed to load CLI configuration: {err}"); + Err(err) => { + if let CliError::ConfigNotFound(path) = err { + error!( + "No CLI configuration file found at \"{path}\". Proceed with \ + enrollment first using \"dg enroll -t -u \" or pass a valid \ + configuration file path using the \"--config\" option. Use \"dg --help\" \ + to display all options." + ); return; } - }, + error!("Failed to load CLI configuration: {err}"); + return; + } }; info!("Using the following CLI configuration: {config_path:?}"); debug!("Successfully loaded CLI configuration"); - trace!("CLI configuration: {:?}", config); + trace!("CLI configuration: {config:?}"); let trigger = Arc::new(Notify::new()); let mut perpetuum = true; - debug!("Starting the main CLI loop..."); + + // Network interface name should not change in the loop below. + let ifname = get_interface_name(&config.device.name); + + debug!("Starting the main CLI loop."); while perpetuum { - debug!("Starting the connection task..."); + debug!("Starting the connection task."); // Must be spawned as a separate task, otherwise trigger won't reach it. - let task = tokio::spawn(connect(config.clone(), trigger.clone())); + let task = tokio::spawn(connect(config.clone(), ifname.clone(), trigger.clone())); debug!("Connection task has been spawned."); // After cancelling the connection a given task should wait for cleanup confirmation. select! { biased; () = wait_for_hangup() => { - info!("Re-configuring..."); + info!("Re-configuring."); trigger.notify_one(); match CliConfig::load(&config_path) { Ok(new_config) => { - info!("Configuration has been reloaded, resetting the connection..."); + info!("Configuration has been reloaded, resetting the connection."); config = new_config; } Err(err) => { @@ -660,12 +694,13 @@ async fn main() { }, _ = ctrl_c() => { trigger.notify_one(); - debug!("Quitting and shutting down the connection..."); + debug!("Quitting and shutting down the connection."); perpetuum = false; trigger.notified().await; }, () = poll_config(&mut config), if config.token.is_some() => { - info!("Location configuration has changed, re-configuring and resetting the connection..."); + info!("Location configuration has changed, re-configuring and resetting the \ + connection."); trigger.notify_one(); trigger.notified().await; }, diff --git a/src-tauri/deny.toml b/src-tauri/deny.toml index cfb4730b..f0d3f368 100644 --- a/src-tauri/deny.toml +++ b/src-tauri/deny.toml @@ -72,22 +72,21 @@ feature-depth = 1 ignore = [ { id = "RUSTSEC-2024-0429", reason = "https://github.com/tauri-apps/tauri/issues/12048" }, { id = "RUSTSEC-2024-0436", reason = "Unmaintained netlink dependency" }, - - # The "unmaintained" advisories below stem from our use of Tauri v1, which + # The "unmaintained" advisories below stem from our use of Tauri v2, which # depends on GTK3 bindings that are no longer maintained. - # These issues are expected to be resolved once we migrate to Tauri v2. - { id = "RUSTSEC-2024-0415", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0370", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0384", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0419", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0420", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0411", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0418", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0412", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0388", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0416", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0413", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2024-0414", reason = "Tauri v1 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0370", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0411", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0412", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0413", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0415", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0416", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0414", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0417", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0418", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0419", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2024-0420", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2025-0052", reason = "Discontinued, but dark-light v2.0.0 needs it" }, + { id = "RUSTSEC-2025-0057", reason = "Tauri needs it" }, ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. @@ -109,7 +108,7 @@ allow = [ "MPL-2.0", "BSD-3-Clause", "Unicode-3.0", - "Unicode-DFS-2016", # unicode-ident + "Unicode-DFS-2016", # unicode-ident "Zlib", "ISC", "BSL-1.0", @@ -126,8 +125,12 @@ confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ - { allow = ["AGPL-3.0"], crate = "common" }, - { allow = ["AGPL-3.0"], crate = "defguard-client" }, + { allow = [ + "AGPL-3.0-only", + ], crate = "common" }, + { allow = [ + "AGPL-3.0-only", + ], crate = "defguard-client" }, ] # Some crates don't have (easily) machine readable licensing information, @@ -247,11 +250,6 @@ unknown-git = "warn" # List of URLs for allowed crate registries. Defaults to the crates.io index # if not specified. If it is specified but empty, no registries are allowed. allow-registry = ["https://github.com/rust-lang/crates.io-index"] -# List of URLs for allowed Git repositories -allow-git = [ - "https://github.com/DefGuard/wireguard-rs", - "https://github.com/tauri-apps/plugins-workspace", -] [sources.allow-org] # github.com organizations to allow git sources for diff --git a/src-tauri/migrations/20250716095940_add_location_mfa_settings.sql b/src-tauri/migrations/20250716095940_add_location_mfa_settings.sql new file mode 100644 index 00000000..f636a5c4 --- /dev/null +++ b/src-tauri/migrations/20250716095940_add_location_mfa_settings.sql @@ -0,0 +1,20 @@ +-- add nullable column to `location` table +-- since SQLite does not support native enums we'll store them as integers +-- 1 - MFA disabled +-- 2 - internal MFA +-- 3 - external MFA +ALTER TABLE location ADD COLUMN location_mfa_mode INTEGER NOT NULL DEFAULT 1; + +-- populate new column based on value in `mfa_enabled` column +-- previously only internal MFA was available +UPDATE location +SET location_mfa_mode = CASE + WHEN mfa_enabled = true THEN 2 + ELSE 1 +END; + +-- drop the `mfa_enabled` column since it's no longer needed +ALTER TABLE location DROP COLUMN mfa_enabled; + +-- remove `use_openid_for_mfa` setting +ALTER TABLE instance DROP COLUMN use_openid_for_mfa; diff --git a/src-tauri/proto b/src-tauri/proto index c0aef683..fa9c14ef 160000 --- a/src-tauri/proto +++ b/src-tauri/proto @@ -1 +1 @@ -Subproject commit c0aef68395720f46a7f038b6766de3bb30e02930 +Subproject commit fa9c14efd121182ec39c8716370e1250c77fa652 diff --git a/src-tauri/resources-macos/resources/net.defguard.plist b/src-tauri/resources-macos/resources/net.defguard.plist index 696d5cd5..479b9737 100644 --- a/src-tauri/resources-macos/resources/net.defguard.plist +++ b/src-tauri/resources-macos/resources/net.defguard.plist @@ -16,5 +16,7 @@ http://www.apple.com/DTDs/PropertyList-1.0.dtd > RunAtLoad + GroupName + staff diff --git a/src-tauri/resources-windows/binaries/.gitkeep b/src-tauri/resources-windows/binaries/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src-tauri/resources-windows/main.wxs b/src-tauri/resources-windows/main.wxs deleted file mode 100644 index 768e57ce..00000000 --- a/src-tauri/resources-windows/main.wxs +++ /dev/null @@ -1,321 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - {{#if allow_downgrades}} - - {{else}} - - {{/if}} - - - Installed AND NOT UPGRADINGPRODUCTCODE - - - - - {{#if banner_path}} - - {{/if}} - {{#if dialog_image_path}} - - {{/if}} - {{#if license}} - - {{/if}} - - - - - - - - - - - - - - - - NOT REMOVE - - - - - - - - - - - WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed - - - - {{#unless license}} - - 1 - 1 - {{/unless}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{#each binaries as |bin| ~}} - - - - {{/each~}} - {{#if enable_elevated_update_task}} - - - - - - - - - - {{/if}} - {{resources}} - - - - - - - - - - - - - - - - - - - - - {{#each merge_modules as |msm| ~}} - - - - - - - - {{/each~}} - - - - - - {{#each resource_file_ids as |resource_file_id| ~}} - - {{/each~}} - - {{#if enable_elevated_update_task}} - - - - {{/if}} - - - - - - - - - - - {{#each binaries as |bin| ~}} - - {{/each~}} - - - - - {{#each component_group_refs as |id| ~}} - - {{/each~}} - {{#each component_refs as |id| ~}} - - {{/each~}} - {{#each feature_group_refs as |id| ~}} - - {{/each~}} - {{#each feature_refs as |id| ~}} - - {{/each~}} - {{#each merge_refs as |id| ~}} - - {{/each~}} - - - {{#if install_webview}} - - - - - - - {{#if download_bootstrapper}} - - - - - - - {{/if}} - - - {{#if webview2_bootstrapper_path}} - - - - - - - - {{/if}} - - - {{#if webview2_installer_path}} - - - - - - - - {{/if}} - - {{/if}} - - {{#if enable_elevated_update_task}} - - - - - NOT(REMOVE) - - - - - - - (REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE - - - {{/if}} - - - - diff --git a/src-tauri/resources/icons/tray-32x32-black-active.png b/src-tauri/resources/icons/tray-32x32-black-active.png new file mode 100644 index 00000000..69e37b71 Binary files /dev/null and b/src-tauri/resources/icons/tray-32x32-black-active.png differ diff --git a/src-tauri/resources/icons/tray-32x32-color-active.png b/src-tauri/resources/icons/tray-32x32-color-active.png new file mode 100644 index 00000000..5602c620 Binary files /dev/null and b/src-tauri/resources/icons/tray-32x32-color-active.png differ diff --git a/src-tauri/resources/icons/tray-32x32-gray-active.png b/src-tauri/resources/icons/tray-32x32-gray-active.png new file mode 100644 index 00000000..9ab5bd44 Binary files /dev/null and b/src-tauri/resources/icons/tray-32x32-gray-active.png differ diff --git a/src-tauri/resources/icons/tray-32x32-white-active.png b/src-tauri/resources/icons/tray-32x32-white-active.png new file mode 100644 index 00000000..f674c43b Binary files /dev/null and b/src-tauri/resources/icons/tray-32x32-white-active.png differ diff --git a/src-tauri/src/active_connections.rs b/src-tauri/src/active_connections.rs new file mode 100644 index 00000000..ed0ef4b2 --- /dev/null +++ b/src-tauri/src/active_connections.rs @@ -0,0 +1,97 @@ +use std::{collections::HashSet, sync::LazyLock}; + +use tokio::sync::Mutex; + +use crate::{ + database::{ + models::{connection::ActiveConnection, instance::Instance, location::Location, Id}, + DB_POOL, + }, + error::Error, + utils::disconnect_interface, + ConnectionType, +}; + +pub(crate) static ACTIVE_CONNECTIONS: LazyLock>> = + LazyLock::new(|| Mutex::new(Vec::new())); + +pub(crate) async fn get_connection_id_by_type(connection_type: ConnectionType) -> Vec { + let active_connections = ACTIVE_CONNECTIONS.lock().await; + + let connection_ids = active_connections + .iter() + .filter_map(|con| { + if con.connection_type == connection_type { + Some(con.location_id) + } else { + None + } + }) + .collect(); + + connection_ids +} + +pub async fn close_all_connections() -> Result<(), Error> { + debug!("Closing all active connections"); + let active_connections = ACTIVE_CONNECTIONS.lock().await; + let active_connections_count = active_connections.len(); + debug!("Found {active_connections_count} active connections"); + for connection in active_connections.iter() { + debug!( + "Found active connection with location {}", + connection.location_id + ); + trace!("Connection: {connection:#?}"); + debug!("Removing interface {}", connection.interface_name); + disconnect_interface(connection).await?; + } + if active_connections_count > 0 { + info!("All active connections ({active_connections_count}) have been closed."); + } else { + debug!("There were no active connections to close, nothing to do."); + } + Ok(()) +} + +pub(crate) async fn find_connection( + id: Id, + connection_type: ConnectionType, +) -> Option { + let connections = ACTIVE_CONNECTIONS.lock().await; + trace!( + "Checking for active connection with ID {id}, type {connection_type} in active connections." + ); + + if let Some(connection) = connections + .iter() + .find(|conn| conn.location_id == id && conn.connection_type == connection_type) + { + // 'connection' now contains the first element with the specified id and connection_type + trace!("Found connection: {connection:?}"); + Some(connection.to_owned()) + } else { + debug!( + "Couldn't find connection with ID {id}, type: {connection_type} in active connections." + ); + None + } +} + +/// Returns active connections for a given instance. +pub(crate) async fn active_connections( + instance: &Instance, +) -> Result, Error> { + let locations: HashSet = Location::find_by_instance_id(&*DB_POOL, instance.id) + .await? + .iter() + .map(|location| location.id) + .collect(); + Ok(ACTIVE_CONNECTIONS + .lock() + .await + .iter() + .filter(|connection| locations.contains(&connection.location_id)) + .cloned() + .collect()) +} diff --git a/src-tauri/src/app_config.rs b/src-tauri/src/app_config.rs index d4b49d2a..d949d67f 100644 --- a/src-tauri/src/app_config.rs +++ b/src-tauri/src/app_config.rs @@ -7,13 +7,13 @@ use log::LevelFilter; use serde::{Deserialize, Serialize}; use struct_patch::Patch; use strum::{Display, EnumString}; -use tauri::AppHandle; +use tauri::{AppHandle, Manager}; static APP_CONFIG_FILE_NAME: &str = "config.json"; fn get_config_file_path(app: &AppHandle) -> PathBuf { let mut config_file_path = app - .path_resolver() + .path() .app_data_dir() .expect("Failed to access app data"); if !config_file_path.exists() { @@ -42,7 +42,7 @@ pub enum AppTheme { Dark, } -#[derive(Clone, Debug, Deserialize, Display, EnumString, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Display, EnumString, PartialEq, Serialize)] #[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] pub enum AppTrayTheme { @@ -99,7 +99,7 @@ impl AppConfig { Ok(patch) => { app_config.apply(patch); } - // if deserialization failed, remove file and return default + // If deserialization fails, remove file and return the default. Err(err) => { eprintln!( "Failed to deserialize application configuration file: {err}. Using defaults." diff --git a/src-tauri/src/appstate.rs b/src-tauri/src/appstate.rs index 6435d8d8..87d6496b 100644 --- a/src-tauri/src/appstate.rs +++ b/src-tauri/src/appstate.rs @@ -1,47 +1,33 @@ -use std::collections::{HashMap, HashSet}; +use std::{collections::HashMap, sync::Mutex}; -use tauri::{ - async_runtime::{spawn, JoinHandle}, - AppHandle, -}; -use tokio::sync::Mutex; +use tauri::async_runtime::{spawn, JoinHandle}; use tokio_util::sync::CancellationToken; -use tonic::transport::Channel; use crate::{ + active_connections::ACTIVE_CONNECTIONS, app_config::AppConfig, database::{ - init_db, - models::{connection::ActiveConnection, instance::Instance, location::Location, Id}, - DbPool, - }, - error::Error, - service::{ - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, utils::setup_client, + models::{connection::ActiveConnection, Id}, + DB_POOL, }, - utils::{disconnect_interface, stats_handler}, + service::utils::DAEMON_CLIENT, + utils::stats_handler, ConnectionType, }; pub struct AppState { - pub db: DbPool, - pub active_connections: Mutex>, - pub client: DesktopDaemonServiceClient, - pub log_watchers: std::sync::Mutex>, - pub app_config: std::sync::Mutex, - stat_threads: std::sync::Mutex>>, // location ID is the key + pub log_watchers: Mutex>, + pub app_config: Mutex, + stat_threads: Mutex>>, // location ID is the key } impl AppState { #[must_use] pub fn new(config: AppConfig) -> Self { AppState { - db: init_db().expect("Failed to initalize database"), - active_connections: Mutex::new(Vec::new()), - client: setup_client().expect("Failed to setup gRPC client"), - log_watchers: std::sync::Mutex::new(HashMap::new()), - app_config: std::sync::Mutex::new(config), - stat_threads: std::sync::Mutex::new(HashMap::new()), + log_watchers: Mutex::new(HashMap::new()), + app_config: Mutex::new(config), + stat_threads: Mutex::new(HashMap::new()), } } @@ -54,17 +40,17 @@ impl AppState { let ifname = interface_name.into(); let connection = ActiveConnection::new(location_id, ifname.clone(), connection_type); debug!("Adding active connection for location ID: {location_id}"); - let mut connections = self.active_connections.lock().await; + let mut connections = ACTIVE_CONNECTIONS.lock().await; connections.push(connection); trace!("Current active connections: {connections:?}"); drop(connections); debug!("Spawning thread for network statistics for location ID {location_id}"); let handle = spawn(stats_handler( - self.db.clone(), + DB_POOL.clone(), ifname, connection_type, - self.client.clone(), + DAEMON_CLIENT.clone(), )); let Some(old_handle) = self .stat_threads @@ -104,7 +90,7 @@ impl AppState { } } - let mut connections = self.active_connections.lock().await; + let mut connections = ACTIVE_CONNECTIONS.lock().await; if let Some(index) = connections.iter().position(|conn| { conn.location_id == location_id && conn.connection_type == connection_type }) { @@ -117,99 +103,4 @@ impl AppState { None } } - - pub(crate) async fn get_connection_id_by_type( - &self, - connection_type: ConnectionType, - ) -> Vec { - let active_connections = self.active_connections.lock().await; - - let connection_ids = active_connections - .iter() - .filter_map(|con| { - if con.connection_type == connection_type { - Some(con.location_id) - } else { - None - } - }) - .collect(); - - connection_ids - } - - pub(crate) async fn close_all_connections(&self) -> Result<(), crate::error::Error> { - debug!("Closing all active connections"); - let active_connections = self.active_connections.lock().await; - let active_connections_count = active_connections.len(); - debug!("Found {active_connections_count} active connections"); - for connection in active_connections.iter() { - debug!( - "Found active connection with location {}", - connection.location_id - ); - trace!("Connection: {connection:#?}"); - debug!("Removing interface {}", connection.interface_name); - disconnect_interface(connection, self).await?; - } - if active_connections_count > 0 { - info!("All active connections ({active_connections_count}) have been closed."); - } else { - debug!("There were no active connections to close, nothing to do."); - } - Ok(()) - } - - pub(crate) async fn find_connection( - &self, - id: Id, - connection_type: ConnectionType, - ) -> Option { - let connections = self.active_connections.lock().await; - trace!( - "Checking for active connection with ID {id}, type {connection_type} in active connections." - ); - - if let Some(connection) = connections - .iter() - .find(|conn| conn.location_id == id && conn.connection_type == connection_type) - { - // 'connection' now contains the first element with the specified id and connection_type - trace!("Found connection: {connection:?}"); - Some(connection.to_owned()) - } else { - debug!("Couldn't find connection with ID {id}, type: {connection_type} in active connections."); - None - } - } - - /// Returns active connections for a given instance. - pub(crate) async fn active_connections( - &self, - instance: &Instance, - ) -> Result, Error> { - let locations: HashSet = Location::find_by_instance_id(&self.db, instance.id) - .await? - .iter() - .map(|location| location.id) - .collect(); - Ok(self - .active_connections - .lock() - .await - .iter() - .filter(|connection| locations.contains(&connection.location_id)) - .cloned() - .collect()) - } - - /// Close all connections, then terminate the application. - pub fn quit(&self, app_handle: &AppHandle) { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - let _ = self.close_all_connections().await; - app_handle.exit(0); - }); - }); - } } diff --git a/src-tauri/src/bin/defguard-client.rs b/src-tauri/src/bin/defguard-client.rs index 35f6c878..3f0699db 100644 --- a/src-tauri/src/bin/defguard-client.rs +++ b/src-tauri/src/bin/defguard-client.rs @@ -8,41 +8,109 @@ use std::{env, str::FromStr, sync::LazyLock}; #[cfg(target_os = "windows")] use defguard_client::utils::sync_connections; use defguard_client::{ + active_connections::close_all_connections, app_config::AppConfig, appstate::AppState, commands::*, - database::models::{location_stats::LocationStats, tunnel::TunnelStats}, - events::SINGLE_INSTANCE, + database::{ + models::{location_stats::LocationStats, tunnel::TunnelStats}, + DB_POOL, + }, periodic::run_periodic_tasks, service, - tray::{configure_tray_icon, handle_tray_event, reload_tray_menu}, + tray::{configure_tray_icon, setup_tray, show_main_window}, utils::load_log_targets, VERSION, }; use log::{Level, LevelFilter}; #[cfg(target_os = "macos")] -use tauri::{api::process, Env}; -use tauri::{Builder, Manager, RunEvent, State, SystemTray, WindowEvent}; -use tauri_plugin_log::LogTarget; - -#[derive(Clone, serde::Serialize)] -struct Payload { - args: Vec, - cwd: String, -} +use tauri::{process, Env}; +use tauri::{AppHandle, Builder, Manager, RunEvent, WindowEvent}; +use tauri_plugin_log::{Target, TargetKind}; #[macro_use] extern crate log; -// for tauri log plugin -const LOG_TARGETS: [LogTarget; 2] = [LogTarget::Stdout, LogTarget::LogDir]; -// if found in metadata target name it will ignore the log if it was below info level +// For tauri logging plugin: +// if found in metadata target name it will ignore the log if it was below info level. const LOGGING_TARGET_IGNORE_LIST: [&str; 5] = ["tauri", "sqlx", "hyper", "h2", "tower"]; static LOG_INCLUDES: LazyLock> = LazyLock::new(load_log_targets); -#[tokio::main] -async fn main() { +async fn startup(app_handle: &AppHandle) { + debug!("Running database migrations, if there are any."); + sqlx::migrate!() + .run(&*DB_POOL) + .await + .expect("Failed to apply database migrations."); + debug!("Applied all database migrations that were pending. If any."); + debug!("Database setup has been completed successfully."); + + debug!("Purging old stats from the database."); + if let Err(err) = LocationStats::purge(&*DB_POOL).await { + error!("Failed to purge location stats: {err}"); + } else { + debug!("Old location stats have been purged successfully."); + } + if let Err(err) = TunnelStats::purge(&*DB_POOL).await { + error!("Failed to purge tunnel stats: {err}"); + } else { + debug!("Old tunnel stats have been purged successfully."); + } + + // Sync already active connections on windows. + // When windows is restarted, the app doesn't close the active connections + // and they are still running after the restart. We sync them here to + // reflect the real system's state. + // TODO: Find a way to intercept the shutdown event and close all connections + #[cfg(target_os = "windows")] + { + match sync_connections(app_handle).await { + Ok(_) => { + info!( + "Synchronized application's active connections with the connections \ + already open on the system, if there were any." + ) + } + Err(err) => { + warn!( + "Failed to synchronize application's active connections with the connections \ + already open on the system. \ + The connections' state in the application may not reflect system's state. \ + Reconnect manually to reset them. Error: {err}" + ) + } + }; + } + + // Run periodic tasks. + let periodic_tasks_handle = app_handle.clone(); + tauri::async_runtime::spawn(async move { + run_periodic_tasks(&periodic_tasks_handle).await; + // One of the tasks exited, so something went wrong, quit the app + error!("One of the periodic tasks has stopped unexpectedly. Exiting the application."); + periodic_tasks_handle.exit(0); + }); + debug!("Periodic tasks have been started."); + + // Load tray menu after database initialization, so all instance and locations can be shown. + debug!( + "Re-generating tray menu to show all available instances and locations as we have \ + connected to the database." + ); + if let Err(err) = setup_tray(app_handle).await { + error!("Failed to setup system tray: {err}"); + } + let state = app_handle.state::(); + let theme = state.app_config.lock().unwrap().tray_theme; + match configure_tray_icon(app_handle, theme).await { + Ok(()) => info!("System tray configured."), + Err(err) => error!("Failed to configure system tray: {err}"), + } + debug!("Tray menu has been re-generated successfully."); +} + +fn main() { // add bundled `wireguard-go` binary to PATH #[cfg(target_os = "macos")] { @@ -57,7 +125,7 @@ async fn main() { "PATH", format!("{current_path}:{}", current_bin_dir.to_str().unwrap()), ); - debug!("Added binary dir {current_bin_dir:?} to PATH"); + debug!("Added binary dir {} to PATH", current_bin_dir.display()); } let app = Builder::default() @@ -88,215 +156,172 @@ async fn main() { command_get_app_config, command_set_app_config ]) - .on_window_event(|event| { - if let WindowEvent::CloseRequested { api, .. } = event.event() { + .on_window_event(|window, event| { + if let WindowEvent::CloseRequested { api, .. } = event { #[cfg(not(target_os = "macos"))] - let _ = event.window().hide(); + let _ = window.hide(); #[cfg(target_os = "macos")] - let _ = tauri::AppHandle::hide(&event.window().app_handle()); + let _ = tauri::AppHandle::hide(window.app_handle()); api.prevent_close(); } }) - .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { - let _ = app.emit_all(SINGLE_INSTANCE, Payload { args: argv, cwd }); + // Initialize plugins here, except for `tauri_plugin_log` which is handled in `setup()`. + // Single instance plugin should always be the first to register. + .plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| { + // Running instance might be hidden, so show it. + show_main_window(app); })) + .plugin(tauri_plugin_deep_link::init()) + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_clipboard_manager::init()) + .plugin(tauri_plugin_fs::init()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_notification::init()) + .plugin(tauri_plugin_window_state::Builder::new().build()) + .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_os::init()) .setup(|app| { - // prepare config - let config = AppConfig::new(&app.app_handle()); + // Register for linux and dev windows builds + #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] + { + use tauri_plugin_deep_link::DeepLinkExt; + app.deep_link().register_all()?; + } + + let app_handle = app.app_handle(); + + // Prepare `AppConfig`. + let config = AppConfig::new(app_handle); + + // Setup logging. - // setup logging - // use config default if deriving from env value fails so that env can override config file + // If deriving from env value fails, use config default (env overrides config file). let config_log_level = config.log_level; let log_level = match &env::var("DEFGUARD_CLIENT_LOG_LEVEL") { Ok(env_value) => LevelFilter::from_str(env_value).unwrap_or(config_log_level), Err(_) => config_log_level, }; - - // Sets the time format. Service's logs have a subsecond part, so we also need to include it here, - // otherwise the logs couldn't be sorted correctly when displayed together in the UI. - let format = time::macros::format_description!( - "[[[year]-[month]-[day]][[[hour]:[minute]:[second].[subsecond]]" - ); - - app.handle() - .plugin( - tauri_plugin_log::Builder::default() - .format(move |out, message, record| { - out.finish(format_args!( - "{}[{}][{}] {}", - tauri_plugin_log::TimezoneStrategy::UseUtc - .get_now() - .format(&format) - .unwrap(), - record.level(), - record.target(), - message - )); - }) - .targets(LOG_TARGETS) - .level(log_level) - .filter(|metadata| { - if metadata.level() == Level::Error { - return true; - } - if !LOG_INCLUDES.is_empty() { - for target in &*LOG_INCLUDES { - if metadata.target().contains(target) { - return true; - } - } - return false; - } - true - }) - .filter(|metadata| { - // Log all errors, warnings and infos - if metadata.level() == LevelFilter::Error - || metadata.level() == LevelFilter::Warn - || metadata.level() == LevelFilter::Info - { - return true; - } - // Otherwise do not log the following targets - for target in &LOGGING_TARGET_IGNORE_LIST { + app_handle.plugin( + tauri_plugin_log::Builder::new() + .format(move |out, message, record| { + out.finish(format_args!( + "{}[{}][{}] {}", + tauri_plugin_log::TimezoneStrategy::UseUtc + .get_now() + // Sets the time format. Service's logs have a subsecond part, so we + // also need to include it here, otherwise the logs couldn't be sorted + // correctly when displayed together in the UI. + .format(&time::macros::format_description!( + "[[[year]-[month]-[day]][[[hour]:[minute]:[second].[subsecond]]" + )) + .unwrap(), + record.level(), + record.target(), + message + )); + }) + .targets([ + Target::new(TargetKind::Stdout), + Target::new(TargetKind::LogDir { file_name: None }), + ]) + .level(log_level) + .filter(|metadata| { + if metadata.level() == Level::Error { + return true; + } + if !LOG_INCLUDES.is_empty() { + for target in &*LOG_INCLUDES { if metadata.target().contains(target) { - return false; + return true; } } - true - }) - .build(), - ) - .unwrap(); + return false; + } + true + }) + .filter(|metadata| { + // Log all errors, warnings and infos. + let level = metadata.level(); + if level == LevelFilter::Error + || level == LevelFilter::Warn + || level == LevelFilter::Info + { + return true; + } + // Otherwise do not log these targets. + for target in &LOGGING_TARGET_IGNORE_LIST { + if metadata.target().contains(target) { + return false; + } + } + true + }) + .build(), + )?; - { - let state = AppState::new(config); - app.manage(state); - } + let state = AppState::new(config); + app.manage(state); info!("App setup completed, log level: {log_level}"); - Ok(()) }) - .system_tray(SystemTray::new()) - .on_system_tray_event(handle_tray_event) - .plugin(tauri_plugin_window_state::Builder::default().build()) .build(tauri::generate_context!()) - .expect("error while building tauri application"); + .expect("Failed to build Tauri application"); info!("Starting Defguard client version {VERSION}"); - // initialize database - let app_handle = app.handle(); - - info!( - "The application data (database file) will be stored in: {:?} \ - and the application logs in: {:?}. Logs of the background defguard service responsible for \ - managing the VPN connections at the network level will be stored in: {}.", - // display the path to the app data directory, convert option to option<&str> - app_handle - .path_resolver() - .app_data_dir() - .unwrap_or_else(|| "UNDEFINED DATA DIRECTORY".into()), - app_handle - .path_resolver() - .app_log_dir() - .unwrap_or_else(|| "UNDEFINED LOG DIRECTORY".into()), - service::config::DEFAULT_LOG_DIR - ); - - debug!("Running database migrations, if there are any."); - let app_state: State = app_handle.state(); - sqlx::migrate!() - .run(&app_state.db) - .await - .expect("Failed to apply database migrations"); - debug!("Applied all database migrations that were pending. If any."); - debug!("Database setup has been completed successfully."); - - debug!("Purging old stats from the database..."); - if let Err(err) = LocationStats::purge(&app_state.db).await { - error!("Failed to purge location stats: {err}"); - } else { - debug!("Old location stats have been purged successfully."); - } - if let Err(err) = TunnelStats::purge(&app_state.db).await { - error!("Failed to purge tunnel stats: {err}"); - } else { - debug!("Old tunnel stats have been purged successfully."); - } - - // Sync already active connections on windows. - // When windows is restarted, the app doesn't close the active connections - // and they are still running after the restart. We sync them here to - // reflect the real system's state. - // TODO: Find a way to intercept the shutdown event and close all connections - #[cfg(target_os = "windows")] - { - match sync_connections(&app_handle).await { - Ok(_) => { - info!("Synchronized application's active connections with the connections already open on the system, if there were any.") - } - Err(err) => { - warn!( - "Failed to synchronize application's active connections with the connections already open on the system. \ - The connections' state in the application may not reflect system's state. Reconnect manually to reset them. Error: {err}" - ) - } - }; - } - // configure tray - debug!("Configuring tray icon..."); - if let Ok(app_config) = &app_state.app_config.lock() { - let _ = configure_tray_icon(&app_handle, &app_config.tray_theme); - debug!("Tray icon has been configured successfully"); - } else { - error!("Could not lock app config guard for tray configuration during app init."); - } - - // run periodic tasks - let periodic_tasks_handle = app_handle.clone(); - tauri::async_runtime::spawn(async move { - run_periodic_tasks(&periodic_tasks_handle).await; - // One of the tasks exited, so something went wrong, quit the app - error!("One of the periodic tasks has stopped unexpectedly. Exiting the application."); - let app_state: State = periodic_tasks_handle.state(); - app_state.quit(&periodic_tasks_handle); - }); - debug!("Periodic tasks have been started"); - - // load tray menu after database initialization to show all instance and locations - debug!("Re-generating tray menu to show all available instances and locations as we have connected to the database..."); - reload_tray_menu(&app_handle).await; - debug!("Tray menu has been re-generated successfully"); - - // Handle Ctrl-C - debug!("Setting up Ctrl-C handler..."); - tauri::async_runtime::spawn(async move { - tokio::signal::ctrl_c() - .await - .expect("Signal handler failure"); - debug!("Ctrl-C handler: quitting the app"); - let app_state: State = app_handle.state(); - app_state.quit(&app_handle); - }); - debug!("Ctrl-C handler has been set up successfully"); - - // run app - debug!("Starting the main application event loop..."); + // Run application. + debug!("Starting the main application event loop."); app.run(|app_handle, event| match event { - // prevent shutdown on window close - RunEvent::ExitRequested { api, .. } => { + // Startup tasks + RunEvent::Ready => { + info!( + "Application data (database file) will be stored in: {} and application logs in: {}. \ + Logs of the background Defguard service responsible for managing VPN connections at the \ + network level will be stored in: {}.", + // display the path to the app data directory, convert option to option<&str> + app_handle + .path() + .app_data_dir() + .unwrap_or_else(|_| "UNDEFINED DATA DIRECTORY".into()).display(), + app_handle + .path() + .app_log_dir() + .unwrap_or_else(|_| "UNDEFINED LOG DIRECTORY".into()).display(), + service::config::DEFAULT_LOG_DIR + ); + tauri::async_runtime::block_on(startup(app_handle)); + + // Handle Ctrl-C. + debug!("Setting up Ctrl-C handler."); + let app_handle_clone = app_handle.clone(); + tauri::async_runtime::spawn(async move { + tokio::signal::ctrl_c() + .await + .expect("Signal handler failure"); + debug!("Ctrl-C handler: quitting the app"); + app_handle_clone.exit(0); + }); + debug!("Ctrl-C handler has been set up successfully"); + } + RunEvent::ExitRequested { code, api, .. } => { debug!("Received exit request"); - api.prevent_exit(); + // `code` is `None` when the exit is requested by user interaction. + if code.is_none() { + // Prevent shutdown on window close. + api.prevent_exit(); + } } - // handle shutdown + // Handle shutdown. RunEvent::Exit => { - debug!("Exiting the application's main event loop..."); - let app_state: State = app_handle.state(); - app_state.quit(app_handle); + debug!("Exiting the application's main event loop."); + tauri::async_runtime::block_on(async { + let _ = close_all_connections().await; + // This will clean the database file, pruning write-ahead log. + DB_POOL.close().await; + }); } _ => { trace!("Received event: {event:?}"); diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index ad222e3f..c638e105 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -9,31 +9,35 @@ use chrono::{DateTime, Duration, NaiveDateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::{Sqlite, Transaction}; use struct_patch::Patch; -use tauri::{AppHandle, Manager, State}; +use tauri::{AppHandle, Emitter, Manager, State}; -static UPDATE_URL: &str = "https://pkgs.defguard.net/api/update/check"; +const UPDATE_URL: &str = "https://pkgs.defguard.net/api/update/check"; use crate::{ + active_connections::{find_connection, get_connection_id_by_type}, app_config::{AppConfig, AppConfigPatch}, appstate::AppState, - database::models::{ - connection::{ActiveConnection, Connection, ConnectionInfo}, - instance::{Instance, InstanceInfo}, - location::Location, - location_stats::LocationStats, - tunnel::{Tunnel, TunnelConnection, TunnelConnectionInfo, TunnelStats}, - wireguard_keys::WireguardKeys, - Id, NoId, + database::{ + models::{ + connection::{ActiveConnection, Connection, ConnectionInfo}, + instance::{Instance, InstanceInfo}, + location::{Location, LocationMfaMode}, + location_stats::LocationStats, + tunnel::{Tunnel, TunnelConnection, TunnelConnectionInfo, TunnelStats}, + wireguard_keys::WireguardKeys, + Id, NoId, + }, + DB_POOL, }, enterprise::periodic::config::poll_instance, error::Error, - events::{APPLICATION_CONFIG_CHANGED, CONNECTION_CHANGED, INSTANCE_UPDATE, LOCATION_UPDATE}, + events::EventKey, log_watcher::{ global_log_watcher::{spawn_global_log_watcher_task, stop_global_log_watcher_task}, service_log_watcher::stop_log_watcher_task, }, proto::DeviceConfigResponse, - service::proto::RemoveInterfaceRequest, + service::{proto::RemoveInterfaceRequest, utils::DAEMON_CLIENT}, tray::{configure_tray_icon, reload_tray_menu}, utils::{ disconnect_interface, execute_command, get_location_interface_details, @@ -44,11 +48,6 @@ use crate::{ CommonConnection, CommonConnectionInfo, CommonLocationStats, ConnectionType, }; -#[derive(Clone, Serialize)] -pub(crate) struct Payload { - pub message: String, -} - // Create new WireGuard interface #[tauri::command(async)] pub async fn connect( @@ -58,21 +57,23 @@ pub async fn connect( handle: AppHandle, ) -> Result<(), Error> { debug!("Received a command to connect to a {connection_type} with ID {location_id}"); - let state = handle.state::(); if connection_type == ConnectionType::Location { - if let Some(location) = Location::find_by_id(&state.db, location_id).await? { + if let Some(location) = Location::find_by_id(&*DB_POOL, location_id).await? { debug!( - "Identified location with ID {location_id} as \"{}\", handling connection...", + "Identified location with ID {location_id} as \"{}\", handling connection.", location.name ); handle_connection_for_location(&location, preshared_key, handle.clone()).await?; reload_tray_menu(&handle).await; info!("Connected to location {location}"); } else { - error!("Location with ID {location_id} not found in the database, aborting connection attempt"); + error!( + "Location with ID {location_id} not found in the database, aborting connection \ + attempt" + ); return Err(Error::NotFound); } - } else if let Some(tunnel) = Tunnel::find_by_id(&state.db, location_id).await? { + } else if let Some(tunnel) = Tunnel::find_by_id(&*DB_POOL, location_id).await? { debug!( "Identified tunnel with ID {location_id} as \"{}\", handling connection...", tunnel.name @@ -83,6 +84,12 @@ pub async fn connect( error!("Tunnel {location_id} not found"); return Err(Error::NotFound); } + + // Update tray icon to reflect connection state. + let app_state: State = handle.state(); + let theme = { app_state.app_config.lock().unwrap().tray_theme }; + configure_tray_icon(&handle, theme).await?; + Ok(()) } @@ -107,26 +114,30 @@ pub async fn disconnect( handle: AppHandle, ) -> Result<(), Error> { let state = handle.state::(); - let name = get_tunnel_or_location_name(location_id, connection_type, &state).await; + let name = get_tunnel_or_location_name(location_id, connection_type).await; debug!("Received a command to disconnect from the {connection_type} {name}({location_id})"); - debug!("Removing active connection for {connection_type} {name}({location_id}) from the application's state, if it exists..."); + debug!( + "Removing active connection for {connection_type} {name}({location_id}) from the \ + application's state, if it exists..." + ); if let Some(connection) = state.remove_connection(location_id, connection_type).await { - debug!("Found and removed active connection from the application's state for {connection_type} {name}({location_id})"); + debug!( + "Found and removed active connection from the application's state for \ + {connection_type} {name}({location_id})" + ); trace!("Connection: {connection:?}"); - disconnect_interface(&connection, &state).await?; - debug!("Emitting the event informing the frontend about the disconnection from {connection_type} {name}({location_id})"); - handle.emit_all( - CONNECTION_CHANGED, - Payload { - message: "Created new connection".into(), - }, - )?; + disconnect_interface(&connection).await?; + debug!( + "Emitting the event informing the frontend about the disconnection from \ + {connection_type} {name}({location_id})" + ); + handle.emit(EventKey::ConnectionChanged.into(), ())?; debug!("Event emitted successfully"); stop_log_watcher_task(&handle, &connection.interface_name)?; reload_tray_menu(&handle).await; if connection_type == ConnectionType::Location { - let name = get_tunnel_or_location_name(location_id, connection_type, &state).await; + let name = get_tunnel_or_location_name(location_id, connection_type).await; if let Err(err) = maybe_update_instance_config(location_id, &handle).await { match err { Error::CoreNotEnterprise => { @@ -153,6 +164,12 @@ pub async fn disconnect( }; } info!("Disconnected from {connection_type} {name}(ID: {location_id})"); + + // Update tray icon to reflect connection state. + let app_state: State = handle.state(); + let theme = { app_state.app_config.lock().unwrap().tray_theme }; + configure_tray_icon(&handle, theme).await?; + Ok(()) } else { warn!( @@ -166,8 +183,7 @@ pub async fn disconnect( /// Triggers poll on location's instance config. Config will be updated if there are no more active /// connections for this instance. async fn maybe_update_instance_config(location_id: Id, handle: &AppHandle) -> Result<(), Error> { - let state: State<'_, AppState> = handle.state(); - let mut transaction = state.db.begin().await?; + let mut transaction = DB_POOL.begin().await?; let Some(location) = Location::find_by_id(&mut *transaction, location_id).await? else { error!("Location {location_id} not found, skipping config update check"); return Err(Error::NotFound); @@ -182,7 +198,7 @@ async fn maybe_update_instance_config(location_id: Id, handle: &AppHandle) -> Re }; poll_instance(&mut transaction, &mut instance, handle).await?; transaction.commit().await?; - handle.emit_all(INSTANCE_UPDATE, ())?; + handle.emit(EventKey::InstanceUpdate.into(), ())?; Ok(()) } @@ -213,21 +229,24 @@ pub struct SaveDeviceConfigResponse { pub async fn save_device_config( private_key: String, response: DeviceConfigResponse, - app_state: State<'_, AppState>, handle: AppHandle, ) -> Result { debug!("Saving device configuration: {response:#?}."); - let mut transaction = app_state.db.begin().await?; + let mut transaction = DB_POOL.begin().await?; let instance_info = response .instance .expect("Missing instance info in device config response"); let mut instance: Instance = instance_info.into(); if response.token.is_some() { - debug!("The newly saved device config has a polling token, automatic configuration polling will be possible if the core has an enterprise license."); + debug!( + "The newly saved device config has a polling token, automatic configuration \ + polling will be possible if the core has an enterprise license." + ); } else { warn!( - "Missing polling token for instance {}, core and/or proxy services may need an update, configuration polling won't work", + "Missing polling token for instance {}, core and/or proxy services may need an \ + update, configuration polling won't work", instance.name, ); } @@ -265,9 +284,9 @@ pub async fn save_device_config( transaction.commit().await?; info!("New instance {instance} created."); trace!("Created following instance: {instance:#?}"); - let locations = Location::find_by_instance_id(&app_state.db, instance.id).await?; + let locations = Location::find_by_instance_id(&*DB_POOL, instance.id).await?; trace!("Created following locations: {locations:#?}"); - handle.emit_all(INSTANCE_UPDATE, ())?; + handle.emit(EventKey::InstanceUpdate.into(), ())?; let res: SaveDeviceConfigResponse = SaveDeviceConfigResponse { locations, instance, @@ -277,25 +296,23 @@ pub async fn save_device_config( } #[tauri::command(async)] -pub async fn all_instances(app_state: State<'_, AppState>) -> Result>, Error> { +pub async fn all_instances() -> Result>, Error> { debug!("Getting information about all instances."); - let instances = Instance::all(&app_state.db).await?; + let instances = Instance::all(&*DB_POOL).await?; trace!( "Found {} instances to return information about.", instances.len() ); trace!("Instances found: {instances:#?}"); let mut instance_info = Vec::new(); - let connection_ids = app_state - .get_connection_id_by_type(ConnectionType::Location) - .await; + let connection_ids = get_connection_id_by_type(ConnectionType::Location).await; for instance in instances { - let locations = Location::find_by_instance_id(&app_state.db, instance.id).await?; + let locations = Location::find_by_instance_id(&*DB_POOL, instance.id).await?; let location_ids: Vec = locations.iter().map(|location| location.id).collect(); let connected = connection_ids .iter() .any(|item1| location_ids.iter().any(|item2| item1 == item2)); - let keys = WireguardKeys::find_by_instance_id(&app_state.db, instance.id) + let keys = WireguardKeys::find_by_instance_id(&*DB_POOL, instance.id) .await? .ok_or(Error::NotFound)?; instance_info.push(InstanceInfo { @@ -308,7 +325,6 @@ pub async fn all_instances(app_state: State<'_, AppState>) -> Result String { + format!( + "{}: {}", + if self.active { "Disconnect" } else { "Connect" }, + self.name + ) + } } impl fmt::Display for LocationInfo { @@ -342,26 +369,24 @@ impl fmt::Display for LocationInfo { } #[tauri::command(async)] -pub async fn all_locations( - instance_id: Id, - app_state: State<'_, AppState>, -) -> Result, Error> { - let Some(instance) = Instance::find_by_id(&app_state.db, instance_id).await? else { - error!("Tried to get all locations for the instance with ID {instance_id}, but the instance was not found."); +pub async fn all_locations(instance_id: Id) -> Result, Error> { + let Some(instance) = Instance::find_by_id(&*DB_POOL, instance_id).await? else { + error!( + "Tried to get all locations for the instance with ID {instance_id}, but the \ + instance was not found." + ); return Err(Error::NotFound); }; trace!( "Getting information about all locations for instance {}.", instance.name ); - let locations = Location::find_by_instance_id(&app_state.db, instance_id).await?; + let locations = Location::find_by_instance_id(&*DB_POOL, instance_id).await?; trace!( "Found {} locations for instance {instance} to return information about.", locations.len() ); - let active_locations_ids = app_state - .get_connection_id_by_type(ConnectionType::Location) - .await; + let active_locations_ids = get_connection_id_by_type(ConnectionType::Location).await; let mut location_info = Vec::new(); for location in locations { let info = LocationInfo { @@ -374,8 +399,8 @@ pub async fn all_locations( route_all_traffic: location.route_all_traffic, connection_type: ConnectionType::Location, pubkey: location.pubkey, - mfa_enabled: location.mfa_enabled, network_id: location.network_id, + location_mfa_mode: location.location_mfa_mode, }; location_info.push(info); } @@ -409,13 +434,10 @@ pub struct LocationInterfaceDetails { pub async fn location_interface_details( location_id: Id, connection_type: ConnectionType, - app_state: State<'_, AppState>, ) -> Result { match connection_type { - ConnectionType::Location => { - get_location_interface_details(location_id, &app_state.db).await - } - ConnectionType::Tunnel => get_tunnel_interface_details(location_id, &app_state.db).await, + ConnectionType::Location => get_location_interface_details(location_id, &DB_POOL).await, + ConnectionType::Tunnel => get_tunnel_interface_details(location_id, &DB_POOL).await, } } @@ -423,18 +445,17 @@ pub async fn location_interface_details( pub async fn update_instance( instance_id: Id, response: DeviceConfigResponse, - app_state: State<'_, AppState>, app_handle: AppHandle, ) -> Result<(), Error> { debug!("Received command to update instance with id {instance_id}"); trace!("Processing following response:\n {response:#?}"); - if let Some(mut instance) = Instance::find_by_id(&app_state.db, instance_id).await? { + if let Some(mut instance) = Instance::find_by_id(&*DB_POOL, instance_id).await? { debug!("The instance with id {instance_id} to update was found: {instance}"); - let mut transaction = app_state.db.begin().await?; + let mut transaction = DB_POOL.begin().await?; do_update_instance(&mut transaction, &mut instance, response).await?; transaction.commit().await?; - app_handle.emit_all(INSTANCE_UPDATE, ())?; + app_handle.emit(EventKey::InstanceUpdate.into(), ())?; reload_tray_menu(&app_handle).await; Ok(()) } else { @@ -494,8 +515,8 @@ pub(crate) async fn do_update_instance( } instance.disable_all_traffic = instance_info.disable_all_traffic; instance.enterprise_enabled = instance_info.enterprise_enabled; - instance.use_openid_for_mfa = instance_info.use_openid_for_mfa; instance.openid_display_name = instance_info.openid_display_name; + instance.uuid = instance_info.id; // Token may be empty if it was not issued // This happens during polling, as core doesn't issue a new token for polling request if response.token.is_some() { @@ -543,9 +564,9 @@ pub(crate) async fn do_update_instance( current_location.pubkey = new_location.pubkey; current_location.endpoint = new_location.endpoint; current_location.allowed_ips = new_location.allowed_ips; - current_location.mfa_enabled = new_location.mfa_enabled; current_location.keepalive_interval = new_location.keepalive_interval; current_location.dns = new_location.dns; + current_location.location_mfa_mode = new_location.location_mfa_mode; current_location.save(transaction.as_mut()).await?; info!("Location {current_location} configuration updated for instance {instance}"); } else { @@ -614,21 +635,20 @@ pub async fn location_stats( location_id: Id, connection_type: ConnectionType, from: Option, - app_state: State<'_, AppState>, ) -> Result>, Error> { trace!("Location stats command received"); let from = parse_timestamp(from)?.naive_utc(); let aggregation = get_aggregation(from)?; let stats = match connection_type { ConnectionType::Location => { - LocationStats::all_by_location_id(&app_state.db, location_id, &from, &aggregation, None) + LocationStats::all_by_location_id(&*DB_POOL, location_id, &from, &aggregation, None) .await? .into_iter() .map(Into::into) .collect() } ConnectionType::Tunnel => { - TunnelStats::all_by_tunnel_id(&app_state.db, location_id, &from, &aggregation) + TunnelStats::all_by_tunnel_id(&*DB_POOL, location_id, &from, &aggregation) .await? .into_iter() .map(Into::into) @@ -643,22 +663,19 @@ pub async fn location_stats( pub async fn all_connections( location_id: Id, connection_type: ConnectionType, - app_state: State<'_, AppState>, ) -> Result, Error> { debug!("Retrieving connections for location {location_id}"); let connections: Vec = match connection_type { - ConnectionType::Location => ConnectionInfo::all_by_location_id(&app_state.db, location_id) + ConnectionType::Location => ConnectionInfo::all_by_location_id(&*DB_POOL, location_id) + .await? + .into_iter() + .map(Into::into) + .collect(), + ConnectionType::Tunnel => TunnelConnectionInfo::all_by_tunnel_id(&*DB_POOL, location_id) .await? .into_iter() .map(Into::into) .collect(), - ConnectionType::Tunnel => { - TunnelConnectionInfo::all_by_tunnel_id(&app_state.db, location_id) - .await? - .into_iter() - .map(Into::into) - .collect() - } }; debug!("Connections retrieved({})", connections.len()); trace!("Connections found:\n{connections:#?}"); @@ -666,12 +683,9 @@ pub async fn all_connections( } #[tauri::command(async)] -pub async fn all_tunnel_connections( - location_id: Id, - app_state: State<'_, AppState>, -) -> Result, Error> { +pub async fn all_tunnel_connections(location_id: Id) -> Result, Error> { debug!("Retrieving connections for location {location_id}"); - let connections = TunnelConnectionInfo::all_by_tunnel_id(&app_state.db, location_id).await?; + let connections = TunnelConnectionInfo::all_by_tunnel_id(&*DB_POOL, location_id).await?; debug!("Tunnel connections retrieved({})", connections.len()); trace!("Connections found:\n{connections:#?}"); Ok(connections) @@ -681,18 +695,17 @@ pub async fn all_tunnel_connections( pub async fn active_connection( location_id: Id, connection_type: ConnectionType, - handle: AppHandle, ) -> Result, Error> { - let state = handle.state::(); - let name = get_tunnel_or_location_name(location_id, connection_type, &state).await; + let name = get_tunnel_or_location_name(location_id, connection_type).await; debug!("Checking if there is an active connection for location {name}(ID: {location_id})"); - let connection = state.find_connection(location_id, connection_type).await; + let connection = find_connection(location_id, connection_type).await; if connection.is_some() { debug!("Found active connection for location {name}(ID: {location_id})"); } trace!("Connection retrieved:\n{connection:#?}"); debug!( - "Active connection information for location {name}(ID: {location_id}) has been found, returning connection information", + "Active connection information for location {name}(ID: {location_id}) has been found, \ + returning connection information", ); Ok(connection) } @@ -701,17 +714,14 @@ pub async fn active_connection( pub async fn last_connection( location_id: Id, connection_type: ConnectionType, - app_state: State<'_, AppState>, ) -> Result>, Error> { - let name = get_tunnel_or_location_name(location_id, connection_type, &app_state).await; + let name = get_tunnel_or_location_name(location_id, connection_type).await; debug!( "Retrieving last connection information for {connection_type} {name}(ID: {location_id})" ); if connection_type == ConnectionType::Location { - if let Some(connection) = - Connection::latest_by_location_id(&app_state.db, location_id).await? - { + if let Some(connection) = Connection::latest_by_location_id(&*DB_POOL, location_id).await? { debug!( "Last connection to {connection_type} {name} has been made at {}", connection.end @@ -722,7 +732,7 @@ pub async fn last_connection( Ok(None) } } else if let Some(connection) = - TunnelConnection::latest_by_tunnel_id(&app_state.db, location_id).await? + TunnelConnection::latest_by_tunnel_id(&*DB_POOL, location_id).await? { debug!( "Last connection to {connection_type} {name} has been made at {}", @@ -742,20 +752,21 @@ pub async fn update_location_routing( connection_type: ConnectionType, handle: AppHandle, ) -> Result<(), Error> { - let app_state = handle.state::(); debug!("Updating location routing {location_id} with {connection_type}"); - let name = get_tunnel_or_location_name(location_id, connection_type, &app_state).await; + let name = get_tunnel_or_location_name(location_id, connection_type).await; match connection_type { ConnectionType::Location => { - if let Some(mut location) = Location::find_by_id(&app_state.db, location_id).await? { + if let Some(mut location) = Location::find_by_id(&*DB_POOL, location_id).await? { // Check if the instance has route_all_traffic disabled - let instance = Instance::find_by_id(&app_state.db, location.instance_id) + let instance = Instance::find_by_id(&*DB_POOL, location.instance_id) .await? .ok_or(Error::NotFound)?; if instance.disable_all_traffic && route_all_traffic { error!( - "Couldn't update location routing: instance with id {} has route_all_traffic disabled.", instance.id + "Couldn't update location routing: instance with id {} has \ + route_all_traffic disabled.", + instance.id ); return Err(Error::InternalError( "Instance has route_all_traffic disabled".into(), @@ -763,14 +774,9 @@ pub async fn update_location_routing( } location.route_all_traffic = route_all_traffic; - location.save(&app_state.db).await?; + location.save(&*DB_POOL).await?; debug!("Location routing updated for location {name}(ID: {location_id})"); - handle.emit_all( - LOCATION_UPDATE, - Payload { - message: "Location routing updated".into(), - }, - )?; + handle.emit(EventKey::LocationUpdate.into(), ())?; Ok(()) } else { error!( @@ -780,16 +786,11 @@ pub async fn update_location_routing( } } ConnectionType::Tunnel => { - if let Some(mut tunnel) = Tunnel::find_by_id(&app_state.db, location_id).await? { + if let Some(mut tunnel) = Tunnel::find_by_id(&*DB_POOL, location_id).await? { tunnel.route_all_traffic = route_all_traffic; - tunnel.save(&app_state.db).await?; + tunnel.save(&*DB_POOL).await?; info!("Tunnel routing updated for tunnel {location_id}"); - handle.emit_all( - LOCATION_UPDATE, - Payload { - message: "Tunnel routing updated".into(), - }, - )?; + handle.emit(EventKey::LocationUpdate.into(), ())?; Ok(()) } else { error!("Couldn't update tunnel routing: tunnel with id {location_id} not found."); @@ -803,8 +804,8 @@ pub async fn update_location_routing( pub async fn delete_instance(instance_id: Id, handle: AppHandle) -> Result<(), Error> { debug!("Deleting instance with ID {instance_id}"); let app_state = handle.state::(); - let mut client = app_state.client.clone(); - let mut transaction = app_state.db.begin().await?; + let mut client = DAEMON_CLIENT.clone(); + let mut transaction = DB_POOL.begin().await?; let Some(instance) = Instance::find_by_id(&mut *transaction, instance_id).await? else { error!("Couldn't delete instance: instance with ID {instance_id} could not be found."); @@ -815,7 +816,7 @@ pub async fn delete_instance(instance_id: Id, handle: AppHandle) -> Result<(), E let instance_locations = Location::find_by_instance_id(&mut *transaction, instance_id).await?; if !instance_locations.is_empty() { debug!( - "Found locations associated with the instance {instance}, closing their connections..." + "Found locations associated with the instance {instance}, closing their connections." ); } for location in instance_locations { @@ -829,12 +830,21 @@ pub async fn delete_instance(instance_id: Id, handle: AppHandle) -> Result<(), E endpoint: location.endpoint.clone(), }; client.remove_interface(request).await.map_err(|status| { - error!("Error occurred while removing interface {} for location {location}, status: {status}", connection.interface_name); - Error::InternalError(format!( - "There was an error while removing interface for location {location}, error message: {}. Check logs for more details.", status.message() - )) - })?; - info!("The connection to location {location} has been closed, as it was associated with the instance {instance} that is being deleted."); + error!( + "Error occurred while removing interface {} for location {location}, \ + status: {status}", + connection.interface_name + ); + Error::InternalError(format!( + "There was an error while removing interface for location {location}, \ + error message: {}. Check logs for more details.", + status.message() + )) + })?; + info!( + "The connection to location {location} has been closed, as it was associated \ + with the instance {instance} that is being deleted." + ); } } instance.delete(&mut *transaction).await?; @@ -843,7 +853,7 @@ pub async fn delete_instance(instance_id: Id, handle: AppHandle) -> Result<(), E reload_tray_menu(&handle).await; - handle.emit_all(INSTANCE_UPDATE, ())?; + handle.emit(EventKey::InstanceUpdate.into(), ())?; info!("Successfully deleted instance {instance}."); Ok(()) } @@ -861,31 +871,19 @@ pub fn parse_tunnel_config(config: &str) -> Result { #[tauri::command(async)] pub async fn update_tunnel(mut tunnel: Tunnel, handle: AppHandle) -> Result<(), Error> { - let app_state = handle.state::(); debug!("Received tunnel configuration to update: {tunnel:?}"); - tunnel.save(&app_state.db).await?; + tunnel.save(&*DB_POOL).await?; info!("The tunnel {tunnel} configuration has been updated."); - handle.emit_all( - LOCATION_UPDATE, - Payload { - message: "Tunnel saved".into(), - }, - )?; + handle.emit(EventKey::LocationUpdate.into(), ())?; Ok(()) } #[tauri::command(async)] pub async fn save_tunnel(tunnel: Tunnel, handle: AppHandle) -> Result<(), Error> { - let app_state = handle.state::(); debug!("Received tunnel configuration to save: {tunnel:?}"); - let tunnel = tunnel.save(&app_state.db).await?; + let tunnel = tunnel.save(&*DB_POOL).await?; info!("The tunnel {tunnel} configuration has been saved."); - handle.emit_all( - LOCATION_UPDATE, - Payload { - message: "Tunnel saved".into(), - }, - )?; + handle.emit(EventKey::LocationUpdate.into(), ())?; Ok(()) } @@ -901,16 +899,14 @@ pub struct TunnelInfo { } #[tauri::command(async)] -pub async fn all_tunnels(app_state: State<'_, AppState>) -> Result>, Error> { +pub async fn all_tunnels() -> Result>, Error> { trace!("Getting information about all tunnels"); - let tunnels = Tunnel::all(&app_state.db).await?; + let tunnels = Tunnel::all(&*DB_POOL).await?; trace!("Found ({}) tunnels to get information about", tunnels.len()); trace!("Tunnels found: {tunnels:#?}"); let mut tunnel_info = Vec::new(); - let active_tunnel_ids = app_state - .get_connection_id_by_type(ConnectionType::Tunnel) - .await; + let active_tunnel_ids = get_connection_id_by_type(ConnectionType::Tunnel).await; for tunnel in tunnels { tunnel_info.push(TunnelInfo { @@ -932,13 +928,10 @@ pub async fn all_tunnels(app_state: State<'_, AppState>) -> Result, -) -> Result, Error> { +pub async fn tunnel_details(tunnel_id: Id) -> Result, Error> { debug!("Retrieving details about tunnel with ID {tunnel_id}."); - if let Some(tunnel) = Tunnel::find_by_id(&app_state.db, tunnel_id).await? { + if let Some(tunnel) = Tunnel::find_by_id(&*DB_POOL, tunnel_id).await? { debug!("The tunnel {tunnel} has been found, returning its details."); Ok(tunnel) } else { @@ -951,8 +944,8 @@ pub async fn tunnel_details( pub async fn delete_tunnel(tunnel_id: Id, handle: AppHandle) -> Result<(), Error> { debug!("Deleting tunnel with ID {tunnel_id}"); let app_state = handle.state::(); - let mut client = app_state.client.clone(); - let mut transaction = app_state.db.begin().await?; + let mut client = DAEMON_CLIENT.clone(); + let mut transaction = DB_POOL.begin().await?; let Some(tunnel) = Tunnel::find_by_id(&mut *transaction, tunnel_id).await? else { error!("The tunnel to delete with ID {tunnel_id} could not be found, cannot delete."); @@ -1044,7 +1037,7 @@ pub struct AppVersionInfo { pub update_url: String, } -static PRODUCT_NAME: &str = "defguard-client"; +const PRODUCT_NAME: &str = "defguard-client"; #[tauri::command(async)] pub async fn get_latest_app_version(handle: AppHandle) -> Result { @@ -1056,7 +1049,10 @@ pub async fn get_latest_app_version(handle: AppHandle) -> Result Result) -> Result debug!("Tray updated upon config change"), Err(err) => error!("Tray change failed. Reason: {err}"), } } if emit_event { - match app_handle.emit_all(APPLICATION_CONFIG_CHANGED, ()) { + match app_handle.emit(EventKey::ApplicationConfigChanged.into(), ()) { Ok(()) => debug!("Config changed event emitted successfully"), Err(err) => { error!("Failed to emit config change event. Reason: {err}"); diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index db4525dc..e2a03b6a 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -4,41 +4,45 @@ use std::{ env, fs::{create_dir_all, File}, str::FromStr, + sync::LazyLock, }; -use sqlx::sqlite::SqliteConnectOptions; +use sqlx::sqlite::{SqliteAutoVacuum, SqliteConnectOptions, SqliteJournalMode, SqlitePool}; use crate::{app_data_dir, error::Error}; const DB_NAME: &str = "defguard.db"; -pub(crate) type DbPool = sqlx::SqlitePool; +pub(crate) type DbPool = SqlitePool; -/// Initializes the database -pub fn init_db() -> Result { - let db_url = prepare_db_url()?; - let opts = SqliteConnectOptions::from_str(&db_url)? - .auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental) - .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal); +pub static DB_POOL: LazyLock = LazyLock::new(|| { + let db_url = prepare_db_url().expect("Wrong database URL."); + let opts = SqliteConnectOptions::from_str(&db_url) + .expect("Failed to set database connenction options.") + .create_if_missing(true) + .auto_vacuum(SqliteAutoVacuum::Incremental) + .journal_mode(SqliteJournalMode::Wal); debug!("Connecting to database: {db_url} with options: {opts:?}"); - let pool = DbPool::connect_lazy_with(opts); + SqlitePool::connect_lazy_with(opts) +}); - Ok(pool) -} - -/// Returns database url. Checks for custom url in `DATABASE_URL` env variable. +/// Returns database URL. Checks for custom URL in `DATABASE_URL` environment variable. /// Handles creating appropriate directories if they don't exist. fn prepare_db_url() -> Result { if let Ok(url) = env::var("DATABASE_URL") { - info!("The default database location has been just overridden by the DATABASE_URL environment variable. The application will use the database located at: {url}"); + info!( + "The default database location has been just overridden by the DATABASE_URL \ + environment variable. The application will use the database located at: {url}" + ); Ok(url) } else { debug!("A production database will be used as no custom DATABASE_URL was provided."); // Check if database directory and file exists, create if they don't. - let app_dir = app_data_dir() - .ok_or(Error::Config( - "Application data directory is not defined. Cannot proceed. Is the application running on a supported platform?".to_string() - ))?; + let app_dir = app_data_dir().ok_or(Error::Config( + "Application data directory is not defined. Cannot proceed. Is the application \ + running on a supported platform?" + .to_string(), + ))?; if app_dir.exists() { debug!( "Application data directory already exists at: {}, skipping its creation.", @@ -68,7 +72,8 @@ fn prepare_db_url() -> Result { ); File::create(&db_path)?; info!( - "A new, empty database file has been created at: {} as no previous database file was found. This file will be used to store application data.", + "A new, empty database file has been created at: {} as no previous database file \ + was found. This file will be used to store application data.", db_path.to_string_lossy() ); } diff --git a/src-tauri/src/database/models/instance.rs b/src-tauri/src/database/models/instance.rs index 5cfe8368..a9aac5b6 100644 --- a/src-tauri/src/database/models/instance.rs +++ b/src-tauri/src/database/models/instance.rs @@ -17,7 +17,6 @@ pub struct Instance { pub token: Option, pub disable_all_traffic: bool, pub enterprise_enabled: bool, - pub use_openid_for_mfa: bool, pub openid_display_name: Option, } @@ -39,7 +38,6 @@ impl From for Instance { token: None, disable_all_traffic: instance_info.disable_all_traffic, enterprise_enabled: instance_info.enterprise_enabled, - use_openid_for_mfa: instance_info.use_openid_for_mfa, openid_display_name: instance_info.openid_display_name, } } @@ -52,7 +50,7 @@ impl Instance { { query!( "UPDATE instance SET name = $1, uuid = $2, url = $3, proxy_url = $4, username = $5, \ - disable_all_traffic = $6, enterprise_enabled = $7, token = $8, use_openid_for_mfa = $9, openid_display_name = $10 WHERE id = $11;", + disable_all_traffic = $6, enterprise_enabled = $7, token = $8, openid_display_name = $9 WHERE id = $10;", self.name, self.uuid, self.url, @@ -61,7 +59,6 @@ impl Instance { self.disable_all_traffic, self.enterprise_enabled, self.token, - self.use_openid_for_mfa, self.openid_display_name, self.id ) @@ -77,7 +74,7 @@ impl Instance { let instances = query_as!( Self, "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token \"token?\", \ - disable_all_traffic, enterprise_enabled, use_openid_for_mfa, openid_display_name FROM instance;" + disable_all_traffic, enterprise_enabled, openid_display_name FROM instance ORDER BY name ASC;" ) .fetch_all(executor) .await?; @@ -91,7 +88,7 @@ impl Instance { let instance = query_as!( Self, "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token \"token?\", \ - disable_all_traffic, enterprise_enabled, use_openid_for_mfa, openid_display_name FROM instance WHERE id = $1;", + disable_all_traffic, enterprise_enabled, openid_display_name FROM instance WHERE id = $1;", id ) .fetch_optional(executor) @@ -125,8 +122,8 @@ impl Instance { let instances = query_as!( Self, "SELECT id \"id: _\", name, uuid, url, proxy_url, username, token, \ - disable_all_traffic, enterprise_enabled, use_openid_for_mfa, openid_display_name FROM instance - WHERE token IS NOT NULL;" + disable_all_traffic, enterprise_enabled, openid_display_name FROM instance + WHERE token IS NOT NULL ORDER BY name ASC;" ) .fetch_all(executor) .await?; @@ -144,7 +141,6 @@ impl PartialEq for Instance { && self.username == other.username && self.disable_all_traffic == other.disable_all_traffic && self.enterprise_enabled == other.enterprise_enabled - && self.use_openid_for_mfa == other.use_openid_for_mfa && self.openid_display_name == other.openid_display_name } } @@ -181,7 +177,6 @@ impl Instance { token: self.token, disable_all_traffic: self.disable_all_traffic, enterprise_enabled: self.enterprise_enabled, - use_openid_for_mfa: self.use_openid_for_mfa, openid_display_name: self.openid_display_name, }) } @@ -198,7 +193,6 @@ pub struct InstanceInfo { pub pubkey: String, pub disable_all_traffic: bool, pub enterprise_enabled: bool, - pub use_openid_for_mfa: bool, pub openid_display_name: Option, } diff --git a/src-tauri/src/database/models/location.rs b/src-tauri/src/database/models/location.rs index 3bd1b5f3..97d20dc6 100644 --- a/src-tauri/src/database/models/location.rs +++ b/src-tauri/src/database/models/location.rs @@ -1,10 +1,31 @@ use std::fmt; use serde::{Deserialize, Serialize}; -use sqlx::{query, query_as, query_scalar, Error as SqlxError, SqliteExecutor}; +use sqlx::{prelude::Type, query, query_as, query_scalar, Error as SqlxError, SqliteExecutor}; use super::{Id, NoId}; -use crate::error::Error; +use crate::{error::Error, proto::LocationMfaMode as ProtoLocationMfaMode}; + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Type)] +#[repr(u32)] +#[serde(rename_all = "lowercase")] +pub enum LocationMfaMode { + Disabled = 1, + Internal = 2, + External = 3, +} + +impl From for LocationMfaMode { + fn from(value: ProtoLocationMfaMode) -> Self { + match value { + ProtoLocationMfaMode::Unspecified | ProtoLocationMfaMode::Disabled => { + LocationMfaMode::Disabled + } + ProtoLocationMfaMode::Internal => LocationMfaMode::Internal, + ProtoLocationMfaMode::External => LocationMfaMode::External, + } + } +} #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct Location { @@ -19,8 +40,8 @@ pub struct Location { pub allowed_ips: String, pub dns: Option, pub route_all_traffic: bool, - pub mfa_enabled: bool, pub keepalive_interval: i64, + pub location_mfa_mode: LocationMfaMode, } impl fmt::Display for Location { @@ -44,8 +65,9 @@ impl Location { query_as!( Self, "SELECT id, instance_id, name, address, pubkey, endpoint, allowed_ips, dns, network_id,\ - route_all_traffic, mfa_enabled, keepalive_interval \ - FROM location;" + route_all_traffic, keepalive_interval, \ + location_mfa_mode \"location_mfa_mode: LocationMfaMode\" \ + FROM location ORDER BY name ASC;" ) .fetch_all(executor) .await @@ -59,7 +81,7 @@ impl Location { query!( "UPDATE location SET instance_id = $1, name = $2, address = $3, pubkey = $4, \ endpoint = $5, allowed_ips = $6, dns = $7, network_id = $8, route_all_traffic = $9, \ - mfa_enabled = $10, keepalive_interval = $11 WHERE id = $12", + keepalive_interval = $10, location_mfa_mode = $11 WHERE id = $12", self.instance_id, self.name, self.address, @@ -69,8 +91,8 @@ impl Location { self.dns, self.network_id, self.route_all_traffic, - self.mfa_enabled, self.keepalive_interval, + self.location_mfa_mode, self.id, ) .execute(executor) @@ -89,7 +111,8 @@ impl Location { query_as!( Self, "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, \ - network_id, route_all_traffic, mfa_enabled, keepalive_interval \ + network_id, route_all_traffic, keepalive_interval, \ + location_mfa_mode \"location_mfa_mode: LocationMfaMode\" \ FROM location WHERE id = $1", location_id ) @@ -107,8 +130,8 @@ impl Location { query_as!( Self, "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, \ - network_id, route_all_traffic, mfa_enabled, keepalive_interval \ - FROM location WHERE instance_id = $1", + network_id, route_all_traffic, keepalive_interval, location_mfa_mode \"location_mfa_mode: LocationMfaMode\" \ + FROM location WHERE instance_id = $1 ORDER BY name ASC", instance_id ) .fetch_all(executor) @@ -125,7 +148,7 @@ impl Location { query_as!( Self, "SELECT id \"id: _\", instance_id, name, address, pubkey, endpoint, allowed_ips, dns, \ - network_id, route_all_traffic, mfa_enabled, keepalive_interval \ + network_id, route_all_traffic, keepalive_interval, location_mfa_mode \"location_mfa_mode: LocationMfaMode\" \ FROM location WHERE pubkey = $1;", pubkey ) @@ -159,6 +182,13 @@ impl Location { .await?; Ok(()) } + + pub(crate) fn mfa_enabled(&self) -> bool { + match self.location_mfa_mode { + LocationMfaMode::Disabled => false, + LocationMfaMode::Internal | LocationMfaMode::External => true, + } + } } impl Location { @@ -169,7 +199,7 @@ impl Location { // Insert a new record when there is no ID let id = query_scalar!( "INSERT INTO location (instance_id, name, address, pubkey, endpoint, allowed_ips, \ - dns, network_id, route_all_traffic, mfa_enabled, keepalive_interval) \ + dns, network_id, route_all_traffic, keepalive_interval, location_mfa_mode) \ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) \ RETURNING id \"id!\"", self.instance_id, @@ -181,8 +211,8 @@ impl Location { self.dns, self.network_id, self.route_all_traffic, - self.mfa_enabled, - self.keepalive_interval + self.keepalive_interval, + self.location_mfa_mode ) .fetch_one(executor) .await?; @@ -198,8 +228,8 @@ impl Location { dns: self.dns, network_id: self.network_id, route_all_traffic: self.route_all_traffic, - mfa_enabled: self.mfa_enabled, keepalive_interval: self.keepalive_interval, + location_mfa_mode: self.location_mfa_mode, }) } } @@ -217,8 +247,8 @@ impl From> for Location { allowed_ips: location.allowed_ips, dns: location.dns, route_all_traffic: location.route_all_traffic, - mfa_enabled: location.mfa_enabled, keepalive_interval: location.keepalive_interval, + location_mfa_mode: location.location_mfa_mode, } } } diff --git a/src-tauri/src/database/models/settings.rs b/src-tauri/src/database/models/settings.rs index 51a7fa60..4c44466d 100644 --- a/src-tauri/src/database/models/settings.rs +++ b/src-tauri/src/database/models/settings.rs @@ -156,7 +156,7 @@ impl Settings { .await?; } else { debug!("Found previous app settings, using them."); - debug!("Found settings: {:?}", current_config); + debug!("Found settings: {current_config:?}"); } Ok(()) } diff --git a/src-tauri/src/database/models/tunnel.rs b/src-tauri/src/database/models/tunnel.rs index 71f3a6f3..974861c7 100644 --- a/src-tauri/src/database/models/tunnel.rs +++ b/src-tauri/src/database/models/tunnel.rs @@ -120,7 +120,7 @@ impl Tunnel { "SELECT id \"id: _\", name, pubkey, prvkey, address, server_pubkey, preshared_key, \ allowed_ips, endpoint, dns, persistent_keep_alive, route_all_traffic, pre_up, \ post_up, pre_down, post_down \ - FROM tunnel;" + FROM tunnel ORDER BY name ASC;" ) .fetch_all(executor) .await?; diff --git a/src-tauri/src/enterprise/periodic/config.rs b/src-tauri/src/enterprise/periodic/config.rs index e88d749f..46b87acf 100644 --- a/src-tauri/src/enterprise/periodic/config.rs +++ b/src-tauri/src/enterprise/periodic/config.rs @@ -1,17 +1,28 @@ -use std::{str::FromStr, time::Duration}; +use std::{ + cmp::Ordering, + collections::HashSet, + str::FromStr, + sync::{LazyLock, Mutex}, + time::Duration, +}; use reqwest::{Client, StatusCode}; +use serde::Serialize; use sqlx::{Sqlite, Transaction}; -use tauri::{AppHandle, Manager, State, Url}; +use tauri::{AppHandle, Emitter, Url}; use tokio::time::sleep; use crate::{ - appstate::AppState, + active_connections::active_connections, commands::{do_update_instance, locations_changed}, - database::models::{instance::Instance, Id}, + database::{ + models::{instance::Instance, Id}, + DB_POOL, + }, error::Error, - events::{CONFIG_CHANGED, INSTANCE_UPDATE}, + events::EventKey, proto::{DeviceConfigResponse, InstanceInfoRequest, InstanceInfoResponse}, + MIN_CORE_VERSION, MIN_PROXY_VERSION, }; const INTERVAL_SECONDS: Duration = Duration::from_secs(30); @@ -22,10 +33,11 @@ static POLLING_ENDPOINT: &str = "/api/v1/poll"; /// Updates are only performed if no connections are established to the [`Instance`], /// otherwise event is emmited and UI message is displayed. pub async fn poll_config(handle: AppHandle) { - debug!("Starting the configuration polling loop..."); - let state: State = handle.state(); + debug!("Starting the configuration polling loop."); + // Polling starts sooner than app's frontend may load in dev builds, causing events (toasts) to be lost, + // you may want to wait here before starting if you want to debug it. loop { - let Ok(mut transaction) = state.db.begin().await else { + let Ok(mut transaction) = DB_POOL.begin().await else { error!( "Failed to begin database transaction for config polling, retrying in {}s", INTERVAL_SECONDS.as_secs() @@ -44,7 +56,7 @@ pub async fn poll_config(handle: AppHandle) { }; debug!( "Found {} instances with a config polling token, proceeding with polling their \ - configuration...", + configuration.", instances.len() ); let mut config_retrieved = 0; @@ -79,9 +91,12 @@ pub async fn poll_config(handle: AppHandle) { } } if let Err(err) = transaction.commit().await { - error!("Failed to commit config polling transaction, configuration won't be updated: {err}"); + error!( + "Failed to commit config polling transaction, configuration won't be updated: \ + {err}" + ); } - if let Err(err) = handle.emit_all(INSTANCE_UPDATE, ()) { + if let Err(err) = handle.emit(EventKey::InstanceUpdate.into(), ()) { error!("Failed to emit instance update event to the frontend: {err}"); } if config_retrieved > 0 { @@ -136,15 +151,19 @@ pub async fn poll_instance( instance.name ); + check_min_version(&response, instance, handle)?; + // Return early if the enterprise features are disabled in the core if response.status() == StatusCode::PAYMENT_REQUIRED { debug!( - "Instance {}({}) has enterprise features disabled, checking if this state is reflected on our end...", + "Instance {}({}) has enterprise features disabled, checking if this state is reflected \ + on our end.", instance.name, instance.id ); if instance.enterprise_enabled { info!( - "Instance {}({}) has enterprise features disabled, but we have them enabled, disabling...", + "Instance {}({}) has enterprise features disabled, but we have them enabled, \ + disabling.", instance.name, instance.id ); instance @@ -152,7 +171,8 @@ pub async fn poll_instance( .await?; } else { debug!( - "Instance {}({}) has enterprise features disabled, and we have them disabled as well, no action needed", + "Instance {}({}) has enterprise features disabled, and we have them disabled as \ + well, no action needed", instance.name, instance.id ); } @@ -161,7 +181,7 @@ pub async fn poll_instance( // Parse the response debug!( - "Parsing the config response for instance {}...", + "Parsing the config response for instance {}.", instance.name ); let response: InstanceInfoResponse = response.json().await.map_err(|err| { @@ -193,8 +213,8 @@ pub async fn poll_instance( // Config changed. If there are no active connections for this instance, update the database. // Otherwise just display a message to reconnect. - let state: State<'_, AppState> = handle.state(); - if state.active_connections(instance).await?.is_empty() { + // + if active_connections(instance).await?.is_empty() { debug!( "Updating instance {}({}) configuration: {device_config:?}", instance.name, instance.id, @@ -209,7 +229,7 @@ pub async fn poll_instance( "Emitting config-changed event for instance {}({})", instance.name, instance.id, ); - let _ = handle.emit_all(CONFIG_CHANGED, &instance.name); + let _ = handle.emit(EventKey::ConfigChanged.into(), &instance.name); info!( "Emitted config-changed event for instance {}({})", instance.name, instance.id, @@ -248,3 +268,168 @@ fn build_request(instance: &Instance) -> Result token: (*token).to_string(), }) } + +/// Tracks instance IDs that for which we already sent notification about version mismatches +/// to prevent duplicate notifications in the app's lifetime. +static NOTIFIED_INSTANCES: LazyLock>> = + LazyLock::new(|| Mutex::new(HashSet::new())); + +const CORE_VERSION_HEADER: &str = "defguard-core-version"; +const CORE_CONNECTED_HEADER: &str = "defguard-core-connected"; +const PROXY_VERSION_HEADER: &str = "defguard-component-version"; + +#[derive(Clone, Serialize)] +struct VersionMismatchPayload { + instance_name: String, + instance_id: Id, + core_version: String, + proxy_version: String, + core_required_version: String, + proxy_required_version: String, + core_compatible: bool, + proxy_compatible: bool, +} + +fn check_min_version( + response: &reqwest::Response, + instance: &Instance, + handle: &AppHandle, +) -> Result<(), Error> { + let mut notified_instances = NOTIFIED_INSTANCES.lock().unwrap(); + if notified_instances.contains(&instance.id) { + debug!( + "Instance {}({}) already notified about version mismatch, skipping", + instance.name, instance.id + ); + return Ok(()); + } + + let detected_core_version: String; + let detected_proxy_version: String; + let defguard_core_connected: Option = response + .headers() + .get(CORE_CONNECTED_HEADER) + .and_then(|v| { + debug!( + "Defguard core connection status header for instance {}({}): {v:?}", + instance.name, instance.id + ); + v.to_str().ok() + }) + .and_then(|s| s.parse().ok()); + + let core_compatible = if let Some(core_version) = response.headers().get(CORE_VERSION_HEADER) { + if let Ok(core_version) = core_version.to_str() { + if let Ok(core_version) = semver::Version::from_str(core_version) { + detected_core_version = core_version.to_string(); + core_version.cmp_precedence(&MIN_CORE_VERSION) != Ordering::Less + } else { + warn!( + "Core version header not a valid semver string in response for instance {}({}): \ + '{core_version}'", + instance.name, instance.id + ); + detected_core_version = core_version.to_string(); + false + } + } else { + warn!( + "Core version header not a valid string in response for instance {}({}): \ + '{core_version:?}'", + instance.name, instance.id + ); + detected_core_version = "unknown".to_string(); + false + } + } else { + warn!( + "Core version header not present in response for instance {}({})", + instance.name, instance.id + ); + detected_core_version = "unknown".to_string(); + false + }; + + let proxy_compatible = if let Some(proxy_version) = response.headers().get(PROXY_VERSION_HEADER) + { + if let Ok(proxy_version) = proxy_version.to_str() { + if let Ok(proxy_version) = semver::Version::from_str(proxy_version) { + detected_proxy_version = proxy_version.to_string(); + proxy_version.cmp_precedence(&MIN_PROXY_VERSION) != Ordering::Less + } else { + warn!( + "Proxy version header not a valid semver string in response for instance {}({}): \ + '{proxy_version}'", + instance.name, instance.id + ); + detected_proxy_version = proxy_version.to_string(); + false + } + } else { + warn!( + "Proxy version header not a valid string in response for instance {}({}): \ + '{proxy_version:?}'", + instance.name, instance.id + ); + detected_proxy_version = "unknown".to_string(); + false + } + } else { + warn!( + "Proxy version header not present in response for instance {}({})", + instance.name, instance.id + ); + detected_proxy_version = "unknown".to_string(); + false + }; + + let should_inform = match defguard_core_connected { + Some(true) => { + debug!( + "Defguard core is connected for instance {}({})", + instance.name, instance.id + ); + true + } + Some(false) => { + info!( + "Defguard core is not connected for instance {}({})", + instance.name, instance.id + ); + false + } + None => { + debug!( + "Defguard core connection status unknown for instance {}({})", + instance.name, instance.id + ); + true + } + }; + + if should_inform && (!core_compatible || !proxy_compatible) { + warn!( + "Instance {} is running incompatible versions: core {detected_core_version}, proxy {detected_proxy_version}. Required \ + versions: core >= {MIN_CORE_VERSION}, proxy >= {MIN_PROXY_VERSION}", + instance.name, + ); + + let payload = VersionMismatchPayload { + instance_name: instance.name.clone(), + instance_id: instance.id, + core_version: detected_core_version, + proxy_version: detected_proxy_version, + core_required_version: MIN_CORE_VERSION.to_string(), + proxy_required_version: MIN_PROXY_VERSION.to_string(), + core_compatible, + proxy_compatible, + }; + if let Err(err) = handle.emit(EventKey::VersionMismatch.into(), payload) { + error!("Failed to emit version mismatch event to the frontend: {err}"); + } else { + notified_instances.insert(instance.id); + } + } + + Ok(()) +} diff --git a/src-tauri/src/events.rs b/src-tauri/src/events.rs index 857b53bd..68be593b 100644 --- a/src-tauri/src/events.rs +++ b/src-tauri/src/events.rs @@ -1,21 +1,47 @@ use serde::Serialize; -use tauri::{api::notification::Notification, AppHandle, Manager}; +use tauri::{AppHandle, Emitter, Url}; +use tauri_plugin_notification::NotificationExt; -use crate::ConnectionType; +use crate::{tray::show_main_window, ConnectionType}; -// Keep list of events on top -pub static SINGLE_INSTANCE: &str = "single-instance"; -pub static CONNECTION_CHANGED: &str = "connection-changed"; -pub static INSTANCE_UPDATE: &str = "instance-update"; -pub static LOCATION_UPDATE: &str = "location-update"; -pub static APP_VERSION_FETCH: &str = "app-version-fetch"; -pub static CONFIG_CHANGED: &str = "config-changed"; -pub static DEAD_CONNECTION_DROPPED: &str = "dead-connection-dropped"; -pub static DEAD_CONNECTION_RECONNECTED: &str = "dead-connection-reconnected"; -pub static APPLICATION_CONFIG_CHANGED: &str = "application-config-changed"; +// Match src/page/client/types.ts. +#[non_exhaustive] +pub enum EventKey { + ConnectionChanged, + InstanceUpdate, + LocationUpdate, + AppVersionFetch, + ConfigChanged, + DeadConnectionDropped, + DeadConnectionReconnected, + ApplicationConfigChanged, + AddInstance, + MfaTrigger, + VersionMismatch, + UuidMismatch, +} + +impl From for &'static str { + fn from(key: EventKey) -> &'static str { + match key { + EventKey::ConnectionChanged => "connection-changed", + EventKey::InstanceUpdate => "instance-update", + EventKey::LocationUpdate => "location-update", + EventKey::AppVersionFetch => "app-version-fetch", + EventKey::ConfigChanged => "config-changed", + EventKey::DeadConnectionDropped => "dead-connection-dropped", + EventKey::DeadConnectionReconnected => "dead-connection-reconnected", + EventKey::ApplicationConfigChanged => "application-config-changed", + EventKey::AddInstance => "add-instance", + EventKey::MfaTrigger => "mfa-trigger", + EventKey::VersionMismatch => "version-mismatch", + EventKey::UuidMismatch => "uuid-mismatch", + } + } +} /// Used as payload for [`DEAD_CONNECTION_DROPPED`] event -#[derive(Serialize, Clone, Debug)] +#[derive(Clone, Serialize)] pub struct DeadConnDroppedOut { pub(crate) name: String, pub(crate) con_type: ConnectionType, @@ -25,21 +51,24 @@ pub struct DeadConnDroppedOut { impl DeadConnDroppedOut { /// Emits [`DEAD_CONNECTION_DROPPED`] event with corresponding side effects. pub(crate) fn emit(self, app_handle: &AppHandle) { - if let Err(err) = Notification::new(&app_handle.config().tauri.bundle.identifier) + if let Err(err) = app_handle + .notification() + .builder() + // .id(&app_handle.config().identifier) .title(format!("{} {} disconnected", self.con_type, self.name)) - .body("Connection activity timeout") + .body("Connection activity timeout.") .show() { warn!("Dead connection dropped notification not shown. Reason: {err}"); } - if let Err(err) = app_handle.emit_all(DEAD_CONNECTION_DROPPED, self) { + if let Err(err) = app_handle.emit(EventKey::DeadConnectionDropped.into(), self) { error!("Event Dead Connection Dropped was not emitted. Reason: {err}"); } } } /// Used as payload for [`DEAD_CONNECTION_RECONNECTED`] event -#[derive(Serialize, Clone, Debug)] +#[derive(Clone, Serialize)] pub struct DeadConnReconnected { pub(crate) name: String, pub(crate) con_type: ConnectionType, @@ -49,15 +78,52 @@ pub struct DeadConnReconnected { impl DeadConnReconnected { /// Emits [`DEAD_CONNECTION_RECONNECTED`] event with corresponding side effects. pub(crate) fn emit(self, app_handle: &AppHandle) { - if let Err(err) = Notification::new(&app_handle.config().tauri.bundle.identifier) + if let Err(err) = app_handle + .notification() + .builder() + // .id(&app_handle.config().identifier) .title(format!("{} {} reconnected", self.con_type, self.name)) - .body("Connection activity timeout") + .body("Connection activity timeout.") .show() { warn!("Dead connection reconnected notification not shown. Reason: {err}"); } - if let Err(err) = app_handle.emit_all(DEAD_CONNECTION_RECONNECTED, self) { + if let Err(err) = app_handle.emit(EventKey::DeadConnectionReconnected.into(), self) { error!("Event Dead Connection Reconnected was not emitted. Reason: {err}"); } } } + +#[derive(Clone, Serialize)] +struct AddInstancePayload<'a> { + token: &'a str, + url: &'a str, +} + +/// Handle deep-link URLs. +pub fn handle_deep_link(app_handle: &AppHandle, urls: &[Url]) { + for link in urls { + if link.path() == "/addinstance" { + let mut token = None; + let mut url = None; + for (key, value) in link.query_pairs() { + if key == "token" { + token = Some(value.clone()); + } + if key == "url" { + url = Some(value.clone()); + } + } + if let (Some(token), Some(url)) = (token, url) { + show_main_window(app_handle); + let _ = app_handle.emit( + EventKey::AddInstance.into(), + AddInstancePayload { + token: &token, + url: &url, + }, + ); + } + } + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4069d3d7..e57ddd2c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,9 +3,12 @@ use std::{fmt, path::PathBuf}; use chrono::NaiveDateTime; -use database::models::NoId; +use semver::Version; use serde::{Deserialize, Serialize}; +use self::database::models::{Id, NoId}; + +pub mod active_connections; pub mod app_config; pub mod appstate; pub mod commands; @@ -21,13 +24,30 @@ pub mod utils; pub mod wg_config; pub mod proto { - use crate::database::models::{location::Location, Id, NoId}; + use crate::database::models::{ + location::{Location, LocationMfaMode as MfaMode}, + Id, NoId, + }; tonic::include_proto!("defguard.proxy"); impl DeviceConfig { #[must_use] pub(crate) fn into_location(self, instance_id: Id) -> Location { + let location_mfa_mode = match self.location_mfa_mode { + Some(_location_mfa_mode) => self.location_mfa_mode().into(), + None => { + // handle legacy core response + // DEPRECATED(1.5): superseeded by location_mfa_mode + #[allow(deprecated)] + if self.mfa_enabled { + MfaMode::Internal + } else { + MfaMode::Disabled + } + } + }; + Location { id: NoId, instance_id, @@ -39,16 +59,18 @@ pub mod proto { allowed_ips: self.allowed_ips, dns: self.dns, route_all_traffic: false, - mfa_enabled: self.mfa_enabled, keepalive_interval: self.keepalive_interval.into(), + location_mfa_mode, } } } } pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "-", env!("VERGEN_GIT_SHA")); +pub const MIN_CORE_VERSION: Version = Version::new(1, 5, 0); +pub const MIN_PROXY_VERSION: Version = Version::new(1, 5, 0); // This must match tauri.bundle.identifier from tauri.conf.json. -static BUNDLE_IDENTIFIER: &str = "net.defguard"; +const BUNDLE_IDENTIFIER: &str = "net.defguard"; // Returns the path to the user’s data directory. #[must_use] pub fn app_data_dir() -> Option { @@ -74,13 +96,11 @@ impl fmt::Display for ConnectionType { #[macro_use] extern crate log; -use self::database::models::Id; - /// Common fields for Tunnel and Location #[derive(Debug, Serialize, Deserialize)] pub struct CommonWireguardFields { pub instance_id: Id, - // Native id of network from defguard + // Native network ID from Defguard Core. pub network_id: Id, pub name: String, pub address: String, diff --git a/src-tauri/src/log_watcher/global_log_watcher.rs b/src-tauri/src/log_watcher/global_log_watcher.rs index 58f01195..38ca7b41 100644 --- a/src-tauri/src/log_watcher/global_log_watcher.rs +++ b/src-tauri/src/log_watcher/global_log_watcher.rs @@ -13,7 +13,7 @@ use std::{ use chrono::{DateTime, NaiveDate, NaiveDateTime, TimeZone, Utc}; use regex::Regex; -use tauri::{async_runtime::TokioJoinHandle, AppHandle, Manager}; +use tauri::{async_runtime::JoinHandle, AppHandle, Emitter, Manager}; use tokio_util::sync::CancellationToken; use tracing::Level; @@ -40,16 +40,12 @@ impl LogDirs { pub fn new(handle: &AppHandle) -> Result { debug!("Getting log directories for service and client to watch."); let service_log_dir = get_service_log_dir().to_path_buf(); - let client_log_dir = - handle - .path_resolver() - .app_log_dir() - .ok_or(LogWatcherError::LogPathError( - "Path to client logs directory is empty.".to_string(), - ))?; + let client_log_dir = handle.path().app_log_dir().map_err(|_| { + LogWatcherError::LogPathError("Path to client logs directory is empty.".to_string()) + })?; debug!( "Log directories of service and client have been identified by the global log watcher: \ - {service_log_dir:?} and {client_log_dir:?}" + {} and {}", service_log_dir.display(), client_log_dir.display() ); Ok(Self { @@ -65,8 +61,8 @@ impl LogDirs { /// with the last 10 characters specifying a date (e.g. `2023-12-15`). fn get_latest_log_file(&self) -> Result, LogWatcherError> { trace!( - "Getting latest log file from directory: {:?}", - self.service_log_dir + "Getting latest log file from directory: {}", + self.service_log_dir.display() ); let entries = read_dir(&self.service_log_dir)?; @@ -111,15 +107,15 @@ impl LogDirs { fn get_client_file(&self) -> Result { trace!( - "Opening the log file for the client, using directory: {:?}", - self.client_log_dir + "Opening the log file for the client, using directory: {}", + self.client_log_dir.display() ); let dir_str = self .client_log_dir .to_str() .ok_or(LogWatcherError::LogPathError(format!( - "Couldn't convert the client log directory path ({:?}) to a string slice", - self.client_log_dir + "Couldn't convert the client log directory path ({}) to a string slice", + self.client_log_dir.display() )))?; let path = format!("{dir_str}/defguard-client.log"); trace!("Constructed client log file path: {path}"); @@ -189,8 +185,9 @@ impl GlobalLogWatcher { trace!("Checking if log files are available"); if service_reader.is_none() && client_reader.is_none() { warn!( - "Couldn't read files at {:?} and {:?}, there will be no logs reported in the client.", - self.log_dirs.current_service_log_file, self.log_dirs.client_log_dir + "Couldn't read files at {:?} and {}, there will be no logs reported in the client.", + self.log_dirs.current_service_log_file, + self.log_dirs.client_log_dir.display() ); // Wait for logs to appear. sleep(DELAY); @@ -280,7 +277,7 @@ impl GlobalLogWatcher { if !parsed_lines.is_empty() { parsed_lines.sort_by(|a, b| a.timestamp.cmp(&b.timestamp)); trace!("Emitting parsed lines for the frontend"); - self.handle.emit_all(&self.event_topic, &parsed_lines)?; + self.handle.emit(&self.event_topic, &parsed_lines)?; trace!("Emitted {} lines to the frontend", parsed_lines.len()); parsed_lines.clear(); } @@ -415,10 +412,12 @@ pub async fn spawn_global_log_watcher_task( let token_clone = token.clone(); // spawn the task - let _join_handle: TokioJoinHandle> = tokio::spawn(async move { - GlobalLogWatcher::new(handle_clone, token_clone, topic_clone, log_level, from)?.run()?; - Ok(()) - }); + let _join_handle: JoinHandle> = + tauri::async_runtime::spawn(async move { + GlobalLogWatcher::new(handle_clone, token_clone, topic_clone, log_level, from)? + .run()?; + Ok(()) + }); // store `CancellationToken` to manually stop watcher thread let mut log_watchers = app_state diff --git a/src-tauri/src/log_watcher/service_log_watcher.rs b/src-tauri/src/log_watcher/service_log_watcher.rs index f5573f18..94bd9a57 100644 --- a/src-tauri/src/log_watcher/service_log_watcher.rs +++ b/src-tauri/src/log_watcher/service_log_watcher.rs @@ -14,7 +14,7 @@ use std::{ }; use chrono::{DateTime, NaiveDate, Utc}; -use tauri::{async_runtime::TokioJoinHandle, AppHandle, Manager}; +use tauri::{async_runtime::JoinHandle, AppHandle, Emitter, Manager}; use tokio_util::sync::CancellationToken; use tracing::Level; @@ -105,7 +105,7 @@ impl<'a> ServiceLogWatcher<'a> { // emit event with all relevant log lines if !parsed_lines.is_empty() { trace!("Emitting {} log lines for the frontend", parsed_lines.len()); - self.handle.emit_all(&self.event_topic, &parsed_lines)?; + self.handle.emit(&self.event_topic, &parsed_lines)?; } parsed_lines.clear(); @@ -181,7 +181,10 @@ impl<'a> ServiceLogWatcher<'a> { /// Log files are rotated daily and have a knows naming format, /// with the last 10 characters specifying a date (e.g. `2023-12-15`). fn get_latest_log_file(&self) -> Result, LogWatcherError> { - trace!("Getting latest log file from directory: {:?}", self.log_dir); + trace!( + "Getting latest log file from directory: {}", + self.log_dir.display() + ); let entries = read_dir(self.log_dir)?; let mut latest_log = None; @@ -255,10 +258,11 @@ pub async fn spawn_log_watcher_task( ); // spawn task - let _join_handle: TokioJoinHandle> = tokio::spawn(async move { - log_watcher.run()?; - Ok(()) - }); + let _join_handle: JoinHandle> = + tauri::async_runtime::spawn(async move { + log_watcher.run()?; + Ok(()) + }); // store `CancellationToken` to manually stop watcher thread // keep this in a block as we .await later, which should not be done while holding a lock like this @@ -274,7 +278,7 @@ pub async fn spawn_log_watcher_task( } } - let name = get_tunnel_or_location_name(location_id, connection_type, &app_state).await; + let name = get_tunnel_or_location_name(location_id, connection_type).await; info!( "A background task has been spawned to watch the defguard service log file for \ {connection_type} {name} (interface {interface_name}), location's specific collected logs \ diff --git a/src-tauri/src/periodic/connection.rs b/src-tauri/src/periodic/connection.rs index 6a08b668..fd20254c 100644 --- a/src-tauri/src/periodic/connection.rs +++ b/src-tauri/src/periodic/connection.rs @@ -5,13 +5,17 @@ use tauri::{AppHandle, Manager}; use tokio::time::interval; use crate::{ + active_connections::ACTIVE_CONNECTIONS, appstate::AppState, commands::{connect, disconnect}, - database::models::{ - location::Location, - location_stats::LocationStats, - tunnel::{Tunnel, TunnelStats}, - Id, + database::{ + models::{ + location::Location, + location_stats::LocationStats, + tunnel::{Tunnel, TunnelStats}, + Id, + }, + DB_POOL, }, events::{DeadConnDroppedOut, DeadConnReconnected}, ConnectionType, @@ -95,7 +99,7 @@ async fn disconnect_dead_connection( /// Only the download change is verified, as the upload change doesn't guarantee that packets are being received from the gateway. pub async fn verify_active_connections(app_handle: AppHandle) { let app_state = app_handle.state::(); - let pool = &app_state.db; + let pool = &*DB_POOL; debug!("Active connections verification started."); // Both vectors contain (ID, allow_reconnect) tuples. @@ -108,7 +112,7 @@ pub async fn verify_active_connections(app_handle: AppHandle) { loop { interval.tick().await; - let connections = app_state.active_connections.lock().await; + let connections = ACTIVE_CONNECTIONS.lock().await; let connection_count = connections.len(); if connection_count == 0 { debug!( @@ -155,7 +159,7 @@ pub async fn verify_active_connections(app_handle: AppHandle) { "There wasn't any activity for Location {} since its \ connection at {}; The amount of time passed since the connection \ is {time_since_connection}, the connection will be terminated when it reaches \ - {peer_alive_period}", + {peer_alive_period}", con.location_id, con.start); } } else { @@ -270,7 +274,7 @@ pub async fn verify_active_connections(app_handle: AppHandle) { .await; } else if // only try to reconnect when location is not protected behind MFA - location.mfa_enabled { + location.mfa_enabled() { warn!( "Automatic reconnect for location {}({}) is not possible due to \ enabled MFA. Interface will be disconnected.", diff --git a/src-tauri/src/periodic/mod.rs b/src-tauri/src/periodic/mod.rs index 239da3e2..0b4fab97 100644 --- a/src-tauri/src/periodic/mod.rs +++ b/src-tauri/src/periodic/mod.rs @@ -25,7 +25,7 @@ pub async fn run_periodic_tasks(app_handle: &AppHandle) { () = verify_active_connections(app_handle.clone()) => { error!("Active connection verification task has stopped unexpectedly"); } - () = purge_stats(app_handle.clone()) => { + () = purge_stats() => { error!("Stats purging task has stopped unexpectedly"); } }; diff --git a/src-tauri/src/periodic/purge_stats.rs b/src-tauri/src/periodic/purge_stats.rs index 84f0613b..81fafd06 100644 --- a/src-tauri/src/periodic/purge_stats.rs +++ b/src-tauri/src/periodic/purge_stats.rs @@ -1,11 +1,10 @@ use std::time::Duration; -use tauri::{AppHandle, Manager, State}; use tokio::time::interval; -use crate::{ - appstate::AppState, - database::models::{location_stats::LocationStats, tunnel::TunnelStats}, +use crate::database::{ + models::{location_stats::LocationStats, tunnel::TunnelStats}, + DB_POOL, }; // 12 hours @@ -15,11 +14,8 @@ const PURGE_INTERVAL: Duration = Duration::from_secs(12 * 60 * 60); /// /// By design this happens infrequently to not overload the DB connection. /// There is a separate purge done at client startup. -pub async fn purge_stats(handle: AppHandle) { - debug!("Starting the stats purging loop..."); - let app_state: State = handle.state(); - let pool = &app_state.db; - +pub async fn purge_stats() { + debug!("Starting the stats purging loop."); let mut interval = interval(PURGE_INTERVAL); loop { @@ -27,7 +23,7 @@ pub async fn purge_stats(handle: AppHandle) { interval.tick().await; // begin transaction - let Ok(mut transaction) = pool.begin().await else { + let Ok(mut transaction) = DB_POOL.begin().await else { error!( "Failed to begin database transaction for stats purging, retrying in {}h", PURGE_INTERVAL.as_secs() / 3600 @@ -52,7 +48,7 @@ pub async fn purge_stats(handle: AppHandle) { error!( "Failed to commit database transaction for stats purging: {err}. Retrying in {}h", PURGE_INTERVAL.as_secs() / 3600 - ) + ); } } } diff --git a/src-tauri/src/periodic/version.rs b/src-tauri/src/periodic/version.rs index 390c0353..9b8b9c10 100644 --- a/src-tauri/src/periodic/version.rs +++ b/src-tauri/src/periodic/version.rs @@ -1,14 +1,14 @@ use std::time::Duration; -use tauri::{AppHandle, Manager}; +use tauri::{AppHandle, Emitter, Manager}; use tokio::time::interval; -use crate::{appstate::AppState, commands::get_latest_app_version, events::APP_VERSION_FETCH}; +use crate::{appstate::AppState, commands::get_latest_app_version, events::EventKey}; const INTERVAL_IN_SECONDS: Duration = Duration::from_secs(12 * 60 * 60); // 12 hours pub async fn poll_version(app_handle: AppHandle) { - debug!("Starting the latest application version polling loop..."); + debug!("Starting the latest application version polling loop."); let state = app_handle.state::(); let mut interval = interval(INTERVAL_IN_SECONDS); @@ -20,7 +20,8 @@ pub async fn poll_version(app_handle: AppHandle) { Ok(guard) => Some(guard.clone()), Err(err) => { warn!( - "Check for updates: Could not lock app config mutex guard. Reason: {err}. Waiting for next loop." + "Check for updates: Could not lock app config mutex guard. Reason: {err}. \ + Waiting for next loop." ); None } @@ -30,13 +31,16 @@ pub async fn poll_version(app_handle: AppHandle) { let response = get_latest_app_version(app_handle.clone()).await; if let Ok(result) = response { debug!("Fetched latest application version info: {result:?}"); - let _ = app_handle.emit_all(APP_VERSION_FETCH, &result); + let _ = app_handle.emit(EventKey::AppVersionFetch.into(), &result); } else { let err = response.err().unwrap(); error!("Error while fetching latest application version: {err}"); } } else { - debug!("Checking for updates is turned off. Skipping latest application version fetch."); + debug!( + "Checking for updates is turned off. Skipping latest application version \ + fetch." + ); } } } diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 41814c49..80fd275b 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -15,6 +15,8 @@ use std::{ str::FromStr, time::{Duration, SystemTime, UNIX_EPOCH}, }; +#[cfg(unix)] +use std::{fs, os::unix::fs::PermissionsExt, path::Path}; #[cfg(not(target_os = "macos"))] use defguard_wireguard_rs::Kernel; @@ -27,12 +29,12 @@ use defguard_wireguard_rs::{ net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, }; +#[cfg(unix)] +use nix::unistd::{chown, Group}; use proto::{ desktop_daemon_service_server::{DesktopDaemonService, DesktopDaemonServiceServer}, CreateInterfaceRequest, InterfaceData, ReadInterfaceDataRequest, RemoveInterfaceRequest, }; -#[cfg(unix)] -use std::{fs, os::unix::fs::PermissionsExt, path::Path}; use thiserror::Error; #[cfg(unix)] use tokio::net::UnixListener; @@ -48,14 +50,22 @@ use tracing::{debug, error, info, info_span, Instrument}; use self::config::Config; use super::VERSION; +use crate::error::Error; #[cfg(windows)] const DAEMON_HTTP_PORT: u16 = 54127; #[cfg(windows)] pub(super) const DAEMON_BASE_URL: &str = "http://localhost:54127"; +#[cfg(unix)] pub(super) const DAEMON_SOCKET_PATH: &str = "/var/run/defguard.socket"; +#[cfg(target_os = "macos")] +pub(super) const DAEMON_SOCKET_GROUP: &str = "staff"; + +#[cfg(target_os = "linux")] +pub(super) const DAEMON_SOCKET_GROUP: &str = "defguard"; + #[derive(Error, Debug)] pub enum DaemonError { #[error(transparent)] @@ -348,9 +358,19 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { let uds = UnixListener::bind(DAEMON_SOCKET_PATH)?; + // change owner group for socket file + // get the group ID by name + let group = Group::from_name(DAEMON_SOCKET_GROUP)?.ok_or_else(|| { + error!("Group '{}' not found", DAEMON_SOCKET_GROUP); + Error::InternalError(format!("Group '{DAEMON_SOCKET_GROUP}' not found")) + })?; + + // change ownership - keep current user, change group + chown(DAEMON_SOCKET_PATH, None, Some(group.gid))?; + // Set socket permissions to allow client access - // 0o666 allows read/write for owner, group, and others - fs::set_permissions(DAEMON_SOCKET_PATH, fs::Permissions::from_mode(0o666))?; + // 0o660 allows read/write for owner and group only + fs::set_permissions(DAEMON_SOCKET_PATH, fs::Permissions::from_mode(0o660))?; let uds_stream = UnixListenerStream::new(uds); diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 3ca741ce..3d35d868 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,9 +1,5 @@ -use std::io::stdout; +use std::{io::stdout, sync::LazyLock}; -#[cfg(windows)] -use crate::service::DAEMON_BASE_URL; -#[cfg(unix)] -use crate::service::DAEMON_SOCKET_PATH; #[cfg(unix)] use hyper_util::rt::TokioIo; #[cfg(unix)] @@ -20,30 +16,35 @@ use tracing_subscriber::{ Layer, }; -use crate::service::{ - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DaemonError, -}; +use crate::service::proto::desktop_daemon_service_client::DesktopDaemonServiceClient; +#[cfg(windows)] +use crate::service::DAEMON_BASE_URL; +#[cfg(unix)] +use crate::service::DAEMON_SOCKET_PATH; #[cfg(unix)] -pub fn setup_client() -> Result, DaemonError> { - debug!("Setting up gRPC client"); - let endpoint = Endpoint::try_from("http://[::]:50051")?; - let channel = endpoint.connect_with_connector_lazy(service_fn(|_: Uri| async { - // Connect to a Uds socket - Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(DAEMON_SOCKET_PATH).await?)) - })); - let client = DesktopDaemonServiceClient::new(channel); - Ok(client) -} +pub(crate) static DAEMON_CLIENT: LazyLock> = + LazyLock::new(|| { + debug!("Setting up gRPC client"); + let endpoint = Endpoint::try_from("http://[::]:50051").unwrap(); // Should not panic. + let channel = endpoint.connect_with_connector_lazy(service_fn(|_: Uri| async { + // Connect to a Unix domain socket. + let stream = UnixStream::connect(DAEMON_SOCKET_PATH) + .await + .expect("Failed to connect to Unix domain socket."); + Ok::<_, std::io::Error>(TokioIo::new(stream)) + })); + DesktopDaemonServiceClient::new(channel) + }); #[cfg(windows)] -pub fn setup_client() -> Result, DaemonError> { - debug!("Setting up gRPC client"); - let endpoint = Endpoint::from_shared(DAEMON_BASE_URL)?; - let channel = endpoint.connect_lazy(); - let client = DesktopDaemonServiceClient::new(channel); - Ok(client) -} +pub(crate) static DAEMON_CLIENT: LazyLock> = + LazyLock::new(|| { + debug!("Setting up gRPC client"); + let endpoint = Endpoint::from_shared(DAEMON_BASE_URL).unwrap(); // Should not panic. + let channel = endpoint.connect_lazy(); + DesktopDaemonServiceClient::new(channel) + }); pub fn logging_setup(log_dir: &str, log_level: &str) -> WorkerGuard { // prepare log file appender diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index 2768fc06..de27923e 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -1,147 +1,245 @@ use tauri::{ - AppHandle, CustomMenuItem, Icon, Manager, State, SystemTrayEvent, SystemTrayMenu, - SystemTrayMenuItem, SystemTraySubmenu, + image::Image, + menu::{Menu, MenuBuilder, MenuEvent, MenuItem, SubmenuBuilder}, + path::BaseDirectory, + tray::TrayIconBuilder, + AppHandle, Emitter, Manager, Runtime, }; use crate::{ + active_connections::{get_connection_id_by_type, ACTIVE_CONNECTIONS}, app_config::AppTrayTheme, - appstate::AppState, commands::{all_instances, all_locations, connect, disconnect}, - database::models::location::Location, + database::{models::location::Location, DB_POOL}, error::Error, + events::EventKey, ConnectionType, }; -static SUBSCRIBE_UPDATES_LINK: &str = "https://defguard.net/newsletter"; -static JOIN_COMMUNITY_LINK: &str = "https://matrix.to/#/#defguard:teonite.com"; -static FOLLOW_US_LINK: &str = "https://floss.social/@defguard"; - -pub async fn generate_tray_menu(app_state: State<'_, AppState>) -> Result { - debug!("Generating tray menu..."); - let quit = CustomMenuItem::new("quit", "Quit"); - let show = CustomMenuItem::new("show", "Show"); - let hide = CustomMenuItem::new("hide", "Hide"); - let subscribe_updates = CustomMenuItem::new("subscribe_updates", "Subscribe for updates"); - let join_community = CustomMenuItem::new("join_community", "Join our Community"); - let follow_us = CustomMenuItem::new("follow_us", "Follow us"); - let mut tray_menu = SystemTrayMenu::new(); - - // INSTANCE SECTION +const SUBSCRIBE_UPDATES_LINK: &str = "https://defguard.net/newsletter"; +const JOIN_COMMUNITY_LINK: &str = "https://matrix.to/#/#defguard:teonite.com"; +const FOLLOW_US_LINK: &str = "https://floss.social/@defguard"; + +const MAIN_WINDOW_ID: &str = "main"; + +const TRAY_ICON_ID: &str = "tray"; + +const TRAY_EVENT_QUIT: &str = "quit"; +const TRAY_EVENT_SHOW: &str = "show"; +const TRAY_EVENT_HIDE: &str = "hide"; +const TRAY_EVENT_UPDATES: &str = "updates"; +const TRAY_EVENT_COMMINITY: &str = "community"; +const TRAY_EVENT_FOLLOW: &str = "follow"; + +/// Generate contents of system tray menu. +async fn generate_tray_menu(app: &AppHandle) -> Result, Error> { + debug!("Generating tray menu."); + let quit = MenuItem::with_id(app, TRAY_EVENT_QUIT, "Quit", true, None::<&str>)?; + let show = MenuItem::with_id(app, TRAY_EVENT_SHOW, "Show", true, None::<&str>)?; + let hide = MenuItem::with_id(app, TRAY_EVENT_HIDE, "Hide", true, None::<&str>)?; + let subscribe_updates = MenuItem::with_id( + app, + TRAY_EVENT_UPDATES, + "Subscribe for updates", + true, + None::<&str>, + )?; + let join_community = MenuItem::with_id( + app, + TRAY_EVENT_COMMINITY, + "Join our community", + true, + None::<&str>, + )?; + let follow_us = MenuItem::with_id(app, TRAY_EVENT_FOLLOW, "Follow us", true, None::<&str>)?; + + let mut menu = MenuBuilder::new(app); debug!("Getting all instances information for the tray menu"); - let all_instances = all_instances(app_state.clone()).await; - if let Ok(instances) = all_instances { - let instance_count = instances.len(); - debug!("Got {instance_count} instances to display in the tray menu"); - for instance in instances { - let mut instance_menu = SystemTrayMenu::new(); - let all_locations = all_locations(instance.id, app_state.clone()).await.unwrap(); - debug!( - "Found {} locations for the {} instance to display in the tray menu", - all_locations.len(), - instance - ); - - // TODO: apply icons instead of Connect/Disconnect when defguard utilizes tauri v2 - for location in all_locations { - let item_name = if location.active { - format!("Disconnect: {}", location.name) - } else { - format!("Connect: {}", location.name) - }; - instance_menu = - instance_menu.add_item(CustomMenuItem::new(location.id.to_string(), item_name)); - debug!("Added new tray menu item (instance {instance}) for location: {location}"); + match all_instances().await { + Ok(instances) => { + let instance_count = instances.len(); + debug!("Got {instance_count} instances to display in the tray menu"); + + // One instance omits sub-menu. + if instance_count == 1 { + let instance = &instances[0]; + let all_locations = all_locations(instance.id).await?; + debug!( + "Found {} locations for the {instance} instance to display in the tray menu", + all_locations.len(), + ); + // TODO: Use icons instead of Connect/Disconnect when Defguard utilizes tauri v2. + for location in all_locations { + let menu_item = MenuItem::with_id( + app, + location.id.to_string(), + location.menu_label(), + true, + None::<&str>, + )?; + menu = menu.item(&menu_item); + } + } else { + for instance in instances { + let mut instance_menu = SubmenuBuilder::new(app, &instance.name); + let all_locations = all_locations(instance.id).await?; + debug!( + "Found {} locations for the {instance} instance to display in the tray menu", + all_locations.len(), + ); + + // TODO: Use icons instead of Connect/Disconnect when Defguard utilizes tauri v2. + for location in all_locations { + let menu_item = MenuItem::with_id( + app, + location.id.to_string(), + location.menu_label(), + true, + None::<&str>, + )?; + instance_menu = instance_menu.item(&menu_item); + } + let submenu = instance_menu.build()?; + menu = menu.item(&submenu); + } } - tray_menu = tray_menu.add_submenu(SystemTraySubmenu::new(instance.name, instance_menu)); } - } else if let Err(err) = all_instances { - warn!("Cannot load instance menu: {err:?}"); + Err(err) => { + warn!("Cannot load instance menu: {err:?}"); + } } - // Load rest of tray menu options - tray_menu = tray_menu - .add_native_item(SystemTrayMenuItem::Separator) - .add_item(show) - .add_item(hide) - .add_native_item(SystemTrayMenuItem::Separator) - .add_item(subscribe_updates) - .add_item(join_community) - .add_item(follow_us) - .add_native_item(SystemTrayMenuItem::Separator) - .add_item(quit); - - debug!("Successfully generated tray menu"); - Ok(tray_menu) + Ok(menu + .separator() + .items(&[&show, &hide]) + .separator() + .items(&[&subscribe_updates, &join_community, &follow_us]) + .separator() + .item(&quit) + .build()?) +} + +/// Setup system tray. +/// This function should only be called once. +pub async fn setup_tray(app: &AppHandle) -> Result<(), Error> { + let tray_menu = generate_tray_menu(app).await?; + + // On macOS, always show menu under system tray icon. + #[cfg(target_os = "macos")] + TrayIconBuilder::with_id(TRAY_ICON_ID) + .menu(&tray_menu) + .show_menu_on_left_click(true) + .on_menu_event(handle_tray_menu_event) + .build(app)?; + // On other systems (especially Windows), system tray menu is on right-click, + // and double-click shows the main window. + #[cfg(not(target_os = "macos"))] + TrayIconBuilder::with_id(TRAY_ICON_ID) + .menu(&tray_menu) + .show_menu_on_left_click(false) + .on_tray_icon_event(|icon, event| { + if let tauri::tray::TrayIconEvent::DoubleClick { .. } = event { + show_main_window(icon.app_handle()) + } + }) + .on_menu_event(handle_tray_menu_event) + .build(app)?; + + debug!("Tray menu successfully generated"); + Ok(()) +} + +/// Reload menu contents in system tray. +pub(crate) async fn reload_tray_menu(app: &AppHandle) { + let Some(tray) = app.tray_by_id(TRAY_ICON_ID) else { + error!("System tray menu not initialized."); + return; + }; + + let menu = generate_tray_menu(app).await.ok(); + match tray.set_menu(menu) { + Ok(()) => debug!("System tray menu re-generarted."), + Err(err) => error!("Failed to re-generate system tray menu: {err}"), + } } -pub async fn reload_tray_menu(app_handle: &AppHandle) { - let system_menu = generate_tray_menu(app_handle.state::()) - .await - .unwrap(); - if let Err(err) = app_handle.tray_handle().set_menu(system_menu) { - warn!("Unable to update tray menu {err:?}"); +fn hide_main_window(app: &AppHandle) { + #[cfg(target_os = "macos")] + if let Err(err) = app.hide() { + warn!("Failed to hide application: {err}"); + } + #[cfg(not(target_os = "macos"))] + if let Some(main_window) = app.get_webview_window(MAIN_WINDOW_ID) { + if let Err(err) = main_window.hide() { + warn!("Failed to hide main window: {err}"); + } } } -fn show_main_window(app: &AppHandle) { - if let Some(main_window) = app.get_window("main") { - // if this fails tauri has a problem - let minimized = main_window.is_minimizable().unwrap_or_default(); - let visible = main_window.is_visible().unwrap_or_default(); - if minimized { - let _ = main_window.unminimize(); - let _ = main_window.set_focus(); +pub fn show_main_window(app: &AppHandle) { + if let Some(main_window) = app.get_webview_window(MAIN_WINDOW_ID) { + if let Err(err) = main_window.unminimize() { + warn!("Failed to unminimize main window: {err}"); } - if !visible { - let _ = main_window.show(); - let _ = main_window.set_focus(); + #[cfg(target_os = "macos")] + if let Err(err) = app.show() { + warn!("Failed to show application: {err}"); } + #[cfg(not(target_os = "macos"))] + { + if let Err(err) = main_window.show() { + warn!("Failed to show main window: {err}"); + } + } + let _ = main_window.set_focus(); } } -// handle tray actions -pub fn handle_tray_event(app: &AppHandle, event: SystemTrayEvent) { +/// Handle tray actions. +pub fn handle_tray_menu_event(app: &AppHandle, event: MenuEvent) { let handle = app.clone(); - if let SystemTrayEvent::MenuItemClick { id, .. } = event { - match id.as_str() { - "quit" => { - info!("Received QUIT request. Initiating shutdown..."); - let app_state: State = app.state(); - app_state.quit(app); - } - "show" => show_main_window(app), - "hide" => { - if let Some(main_window) = app.get_window("main") { - if main_window.is_visible().unwrap_or_default() { - let _ = main_window.hide(); - } - } - } - "subscribe_updates" => { - let _ = webbrowser::open(SUBSCRIBE_UPDATES_LINK); - } - "join_community" => { - let _ = webbrowser::open(JOIN_COMMUNITY_LINK); - } - "follow_us" => { - let _ = webbrowser::open(FOLLOW_US_LINK); - } - _ if id.chars().all(char::is_numeric) => { - tauri::async_runtime::spawn(async move { - handle_location_tray_menu(id, &handle).await; - }); - } - _ => {} + match event.id.as_ref() { + TRAY_EVENT_QUIT => { + info!("Received QUIT request. Initiating shutdown..."); + handle.exit(0); + } + TRAY_EVENT_SHOW => show_main_window(app), + TRAY_EVENT_HIDE => hide_main_window(app), + TRAY_EVENT_UPDATES => { + let _ = webbrowser::open(SUBSCRIBE_UPDATES_LINK); + } + TRAY_EVENT_COMMINITY => { + let _ = webbrowser::open(JOIN_COMMUNITY_LINK); + } + TRAY_EVENT_FOLLOW => { + let _ = webbrowser::open(FOLLOW_US_LINK); } + id if id.chars().all(char::is_numeric) => { + tauri::async_runtime::spawn(async move { + handle_location_tray_menu(event.id.0, &handle).await; + }); + } + _ => {} } } -pub fn configure_tray_icon(app: &AppHandle, theme: &AppTrayTheme) -> Result<(), Error> { - let resource_str = format!("resources/icons/tray-32x32-{theme}.png"); +pub async fn configure_tray_icon(app: &AppHandle, theme: AppTrayTheme) -> Result<(), Error> { + let Some(tray_icon) = app.tray_by_id(TRAY_ICON_ID) else { + error!("System tray menu not initialized."); + return Ok(()); + }; + + let mut resource_str = String::from("resources/icons/tray-32x32-"); + resource_str.push_str(&theme.to_string()); + let active_connections = ACTIVE_CONNECTIONS.lock().await; + if !active_connections.is_empty() { + resource_str.push_str("-active"); + } + resource_str.push_str(".png"); debug!("Trying to load the tray icon from {resource_str}"); - if let Some(icon_path) = app.path_resolver().resolve_resource(&resource_str) { - let icon = Icon::File(icon_path); - app.tray_handle().set_icon(icon)?; + if let Ok(icon_path) = app.path().resolve(&resource_str, BaseDirectory::Resource) { + let icon = Image::from_path(icon_path)?; + tray_icon.set_icon(Some(icon))?; debug!("Tray icon set to {resource_str} successfully."); Ok(()) } else { @@ -150,56 +248,42 @@ pub fn configure_tray_icon(app: &AppHandle, theme: &AppTrayTheme) -> Result<(), } } -#[derive(Clone, serde::Serialize)] -struct Payload { - message: String, -} - -async fn handle_location_tray_menu(id: String, handle: &AppHandle) { +async fn handle_location_tray_menu(id: String, app: &AppHandle) { match id.parse::() { Ok(location_id) => { - match Location::find_by_id(&handle.state::().db, location_id).await { + match Location::find_by_id(&*DB_POOL, location_id).await { Ok(Some(location)) => { - let active_locations_ids = handle - .state::() - .get_connection_id_by_type(ConnectionType::Location) - .await; + let active_locations_ids = + get_connection_id_by_type(ConnectionType::Location).await; if active_locations_ids.contains(&location_id) { - info!("Disconnect location with id {id}"); + info!("Disconnect location with ID {id}"); let _ = - disconnect(location_id, ConnectionType::Location, handle.clone()).await; + disconnect(location_id, ConnectionType::Location, app.clone()).await; } else { - info!("Connect location with id {id}"); - // check is mfa enabled and trigger modal on frontend - if location.mfa_enabled { + info!("Connect location with ID {id}"); + // Check if MFA is enabled. If so, trigger modal on frontend. + if location.mfa_enabled() { info!( - "mfa enabled for location with id {:?}, trigger mfa modal", + "MFA enabled for location with ID {:?}, trigger MFA modal", location.id ); - handle - .emit_all( - "mfa-trigger", - Payload { - message: "Trigger mfa event".into(), - }, - ) - .unwrap(); - } else if let Err(e) = - connect(location_id, ConnectionType::Location, None, handle.clone()) - .await + show_main_window(app); + let _ = app.emit(EventKey::MfaTrigger.into(), &location); + } else if let Err(err) = + connect(location_id, ConnectionType::Location, None, app.clone()).await { info!( - "Unable to connect location with id {}, error: {e:?}", + "Unable to connect location with ID {}, error: {err:?}", location.id ); } } } Ok(None) => warn!("Location does not exist"), - Err(e) => warn!("Unable to find location: {e:?}"), + Err(err) => warn!("Unable to find location: {err:?}"), }; } - Err(e) => warn!("Can't handle event due to: {e:?}"), + Err(err) => warn!("Can't handle event due to: {err:?}"), } } diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index c5a89032..ccc92135 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -3,7 +3,7 @@ use std::{env, path::Path, process::Command, str::FromStr}; use common::{find_free_tcp_port, get_interface_name}; use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration}; use sqlx::query; -use tauri::{AppHandle, Manager}; +use tauri::{AppHandle, Emitter, Manager}; use tonic::{transport::Channel, Code}; use tracing::Level; #[cfg(target_os = "windows")] @@ -16,7 +16,7 @@ use windows_service::{ use crate::{ appstate::AppState, - commands::{LocationInterfaceDetails, Payload}, + commands::LocationInterfaceDetails, database::{ models::{ connection::{ActiveConnection, Connection}, @@ -26,18 +26,24 @@ use crate::{ wireguard_keys::WireguardKeys, Id, }, - DbPool, + DbPool, DB_POOL, }, error::Error, - events::CONNECTION_CHANGED, + events::EventKey, log_watcher::service_log_watcher::spawn_log_watcher_task, - service::proto::{ - desktop_daemon_service_client::DesktopDaemonServiceClient, CreateInterfaceRequest, - ReadInterfaceDataRequest, RemoveInterfaceRequest, + service::{ + proto::{ + desktop_daemon_service_client::DesktopDaemonServiceClient, CreateInterfaceRequest, + ReadInterfaceDataRequest, RemoveInterfaceRequest, + }, + utils::DAEMON_CLIENT, }, ConnectionType, }; +#[cfg(target_os = "windows")] +use crate::active_connections::find_connection; + pub(crate) static DEFAULT_ROUTE_IPV4: &str = "0.0.0.0/0"; pub(crate) static DEFAULT_ROUTE_IPV6: &str = "::/0"; @@ -296,8 +302,9 @@ pub(crate) async fn stats_handler( // commit transaction if let Err(err) = transaction.commit().await { error!( - "Failed to commit database transaction for saving location/tunnel stats: {err}", - ) + "Failed to commit database transaction for saving location/tunnel stats: \ + {err}", + ); } } Ok(None) => { @@ -622,8 +629,8 @@ pub(crate) async fn handle_connection_for_location( location, interface_name.clone(), preshared_key, - &state.db, - state.client.clone(), + &DB_POOL, + DAEMON_CLIENT.clone(), ) .await?; state @@ -631,12 +638,7 @@ pub(crate) async fn handle_connection_for_location( .await; debug!("Sending event informing the frontend that a new connection has been created."); - handle.emit_all( - CONNECTION_CHANGED, - Payload { - message: "Created new connection".into(), - }, - )?; + handle.emit(EventKey::ConnectionChanged.into(), ())?; debug!("Event informing the frontend that a new connection has been created sent."); // spawn log watcher @@ -662,18 +664,13 @@ pub(crate) async fn handle_connection_for_tunnel( debug!("Setting up the connection for tunnel: {}", tunnel.name); let state = handle.state::(); let interface_name = get_interface_name(&tunnel.name); - setup_interface_tunnel(tunnel, interface_name.clone(), state.client.clone()).await?; + setup_interface_tunnel(tunnel, interface_name.clone(), DAEMON_CLIENT.clone()).await?; state .add_connection(tunnel.id, &interface_name, ConnectionType::Tunnel) .await; debug!("Sending event informing the frontend that a new connection has been created."); - handle.emit_all( - CONNECTION_CHANGED, - Payload { - message: "Created new connection".into(), - }, - )?; + handle.emit(EventKey::ConnectionChanged.into(), ())?; debug!("Event informing the frontend that a new connection has been created sent."); // spawn log watcher @@ -718,19 +715,18 @@ pub fn execute_command(command: &str) -> Result<(), Error> { /// Helper function to remove interface and close connection pub(crate) async fn disconnect_interface( active_connection: &ActiveConnection, - state: &AppState, ) -> Result<(), Error> { debug!( - "Disconnecting interface {}...", + "Disconnecting interface {}.", active_connection.interface_name ); - let mut client = state.client.clone(); + let mut client = DAEMON_CLIENT.clone(); let location_id = active_connection.location_id; let interface_name = active_connection.interface_name.clone(); match active_connection.connection_type { ConnectionType::Location => { - let Some(location) = Location::find_by_id(&state.db, location_id).await? else { + let Some(location) = Location::find_by_id(&*DB_POOL, location_id).await? else { error!( "Error while disconnecting interface {interface_name}, location with ID \ {location_id} not found" @@ -764,7 +760,7 @@ pub(crate) async fn disconnect_interface( return Err(Error::InternalError(msg)); } let connection: Connection = active_connection.into(); - let connection = connection.save(&state.db).await?; + let connection = connection.save(&*DB_POOL).await?; debug!( "Saved location {} new connection status in the database", location.name @@ -777,7 +773,7 @@ pub(crate) async fn disconnect_interface( debug!("Finished disconnecting from location {}", location.name); } ConnectionType::Tunnel => { - let Some(tunnel) = Tunnel::find_by_id(&state.db, location_id).await? else { + let Some(tunnel) = Tunnel::find_by_id(&*DB_POOL, location_id).await? else { error!( "Error while disconnecting interface {interface_name}, tunnel with ID \ {location_id} not found" @@ -825,7 +821,7 @@ pub(crate) async fn disconnect_interface( ); } let connection: TunnelConnection = active_connection.into(); - let connection = connection.save(&state.db).await?; + let connection = connection.save(&*DB_POOL).await?; debug!( "Saved new tunnel {} connection status in the database", tunnel.name @@ -845,17 +841,13 @@ pub(crate) async fn disconnect_interface( /// Helper function to get the name of a tunnel or location by its ID /// Returns the name of the tunnel or location if it exists, otherwise "UNKNOWN" /// This is for logging purposes. -pub async fn get_tunnel_or_location_name( - id: Id, - connection_type: ConnectionType, - appstate: &AppState, -) -> String { +pub async fn get_tunnel_or_location_name(id: Id, connection_type: ConnectionType) -> String { let name = match connection_type { - ConnectionType::Location => Location::find_by_id(&appstate.db, id) + ConnectionType::Location => Location::find_by_id(&*DB_POOL, id) .await .ok() .and_then(|l| l.map(|l| l.name)), - ConnectionType::Tunnel => Tunnel::find_by_id(&appstate.db, id) + ConnectionType::Tunnel => Tunnel::find_by_id(&*DB_POOL, id) .await .ok() .and_then(|t| t.map(|t| t.name)), @@ -881,11 +873,11 @@ async fn check_connection( id: Id, name: &str, connection_type: ConnectionType, - app_handle: AppHandle, + app_handle: &AppHandle, ) -> Result<(), Error> { let appstate = app_handle.state::(); let interface_name = get_interface_name(name); - let service_name = format!("WireGuardTunnel${}", interface_name); + let service_name = format!("WireGuardTunnel${interface_name}"); let service = match service_manager.open_service(&service_name, ServiceAccess::QUERY_STATUS) { Ok(service) => service, Err(windows_service::Error::Winapi(err)) @@ -928,11 +920,7 @@ async fn check_connection( } } - if appstate - .find_connection(id, connection_type) - .await - .is_some() - { + if find_connection(id, connection_type).await.is_some() { debug!("{connection_type} {name} has already a connected state, skipping synchronization"); return Ok(()); } @@ -942,12 +930,7 @@ async fn check_connection( .await; debug!("Sending event informing the frontend that a new connection has been created."); - app_handle.emit_all( - CONNECTION_CHANGED, - Payload { - message: "Created new connection".into(), - }, - )?; + app_handle.emit(EventKey::ConnectionChanged.into(), ())?; debug!("Event informing the frontend that a new connection has been created sent."); debug!("Spawning service log watcher for {connection_type} {name}..."); @@ -971,18 +954,17 @@ async fn check_connection( #[cfg(target_os = "windows")] pub async fn sync_connections(app_handle: &AppHandle) -> Result<(), Error> { debug!("Synchronizing active connections with the systems' state..."); - let appstate = app_handle.state::(); - let all_locations = Location::all(&appstate.db).await?; + let all_locations = Location::all(&*DB_POOL).await?; let service_manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT).map_err( |err| { error!( - "Failed to open service control manager while trying to sync client's connections \ - with the host state: {err}" - ); + "Failed to open service control manager while trying to sync client's \ + connections with the host state: {err}" + ); Error::InternalError( - "Failed to open service control manager while trying to sync client's - connections with the host state" + "Failed to open service control manager while trying to sync client's \ + connections with the host state" .to_string(), ) }, @@ -998,20 +980,20 @@ pub async fn sync_connections(app_handle: &AppHandle) -> Result<(), Error> { location.id, &location.name, ConnectionType::Location, - app_handle.clone(), + app_handle, ) .await?; } debug!("Synchronizing active connections for tunnels..."); // Do the same for tunnels - for tunnel in Tunnel::all(&appstate.db).await? { + for tunnel in Tunnel::all(&*DB_POOL).await? { check_connection( &service_manager, tunnel.id, &tunnel.name, ConnectionType::Tunnel, - app_handle.clone(), + app_handle, ) .await?; } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 656350be..6ab63153 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,48 +1,53 @@ { - "$schema": "../node_modules/@tauri-apps/cli/schema.json", + "$schema": "https://schema.tauri.app/config/2", "build": { "beforeBuildCommand": "pnpm build", "beforeDevCommand": "pnpm dev", - "devPath": "http://localhost:3001", - "distDir": "../dist" + "frontendDist": "../dist", + "devUrl": "http://localhost:3001" }, - "package": { - "productName": "defguard-client", - "version": "1.5.0" - }, - "tauri": { - "systemTray": { - "iconPath": "resources/icons/tray-32x32-color.png", - "iconAsTemplate": false - }, - "allowlist": { - "all": false, - "window": { - "all": true - }, - "http": { - "all": true, - "request": true, - "scope": ["https://**", "http://**"] - }, - "fs": { - "all": true, - "scope": ["$RESOURCE/*", "$APPDATA/*"] - }, - "clipboard": { - "all": true - }, - "dialog": { - "all": true - }, - "notification": { - "all": true + "bundle": { + "active": true, + "category": "Utility", + "copyright": "Defguard", + "targets": [ + "deb", + "app" + ], + "externalBin": [], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "", + "wix": { + "fragmentPaths": [ + "./resources-windows/service-fragment.wxs" + ], + "componentRefs": [ + "DefGuardServiceFragment" + ] } }, - "bundle": { - "active": true, - "category": "Utility", - "copyright": "teonite", + "macOS": { + "entitlements": null, + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, + "resources": [ + "resources/icons/*" + ], + "shortDescription": "Defguard desktop client", + "longDescription": "Defguard desktop client", + "linux": { "deb": { "files": { "/usr/sbin/defguard-service": "target/release/defguard-service", @@ -53,43 +58,28 @@ "../control/postrm": "../resources-linux/postrm" } }, - "externalBin": [], - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "identifier": "net.defguard", - "longDescription": "Defguard desktop client.", - "macOS": { - "entitlements": null, - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null - }, - "resources": ["resources/*"], - "shortDescription": "", - "targets": ["deb", "app"], - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "", - "wix": { - "template": "./resources-windows/main.wxs", - "fragmentPaths": ["./resources-windows/service-fragment.wxs"], - "componentRefs": ["DefGuardServiceFragment"] - } + "rpm": { + "files": { + "/usr/sbin/defguard-service": "target/release/defguard-service", + "/lib/systemd/system/defguard-service.service": "../resources-linux/defguard-service.service" + }, + "postInstallScript": "../resources-linux/postinst", + "preRemoveScript": "../resources-linux/prerm", + "postRemoveScript": "../resources-linux/postrm" } - }, + } + }, + "productName": "defguard-client", + "mainBinaryName": "defguard-client", + "identifier": "net.defguard", + "version": "1.5.0", + "app": { "security": { + "capabilities": [ + "main-capability" + ], "csp": null }, - "updater": { - "active": false - }, "windows": [ { "fullscreen": false, @@ -103,8 +93,18 @@ "title": "Defguard", "width": 992, "minWidth": 650, - "minHeight": 450 + "minHeight": 450, + "useHttpsScheme": true } ] + }, + "plugins": { + "deep-link": { + "desktop": { + "schemes": [ + "defguard" + ] + } + } } -} +} \ No newline at end of file diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json new file mode 100644 index 00000000..1ceacbf7 --- /dev/null +++ b/src-tauri/tauri.linux.conf.json @@ -0,0 +1,5 @@ +{ + "bundle": { + "longDescription": "IMPORTANT: Reboot or Re-login Required\nOn initial install the user is added to the defguard group.\nA reboot or logging out and back in is required for group membership changes to take effect.\nThis is not required on subsequent updates." + } +} \ No newline at end of file diff --git a/src-tauri/tauri.macos.conf.json b/src-tauri/tauri.macos.conf.json index 22f1d767..bb2a7dfc 100644 --- a/src-tauri/tauri.macos.conf.json +++ b/src-tauri/tauri.macos.conf.json @@ -1,13 +1,11 @@ { - "tauri": { - "bundle": { - "externalBin": [ - "resources-macos/binaries/*" - ], - "resources": [ - "resources-macos/resources/*", - "resources/*" - ] - } - } + "bundle": { + "externalBin": [ + "resources-macos/binaries/*" + ], + "resources": [ + "resources-macos/resources/*", + "resources/icons/*" + ] + } } diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json index 8bd799eb..5027918d 100644 --- a/src-tauri/tauri.windows.conf.json +++ b/src-tauri/tauri.windows.conf.json @@ -1,11 +1,9 @@ { - "tauri": { - "bundle": { - "targets": ["msi"], - "resources": [ - "resources-windows/binaries/*", - "resources/*" - ] - } - } + "bundle": { + "targets": ["msi"], + "resources": [ + "resources-windows/binaries/*", + "resources/icons/*" + ] + } } diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index dbad4d85..5bc83de6 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -4,6 +4,8 @@ import '../../shared/scss/index.scss'; import { QueryClient } from '@tanstack/query-core'; import { QueryClientProvider } from '@tanstack/react-query'; +import { debug } from '@tauri-apps/plugin-log'; +import { openUrl } from '@tauri-apps/plugin-opener'; import dayjs from 'dayjs'; import customParseData from 'dayjs/plugin/customParseFormat'; import duration from 'dayjs/plugin/duration'; @@ -14,14 +16,12 @@ import updateLocale from 'dayjs/plugin/updateLocale'; import utc from 'dayjs/plugin/utc'; import { useEffect, useMemo, useRef, useState } from 'react'; import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom'; -import { debug } from 'tauri-plugin-log-api'; import { localStorageDetector } from 'typesafe-i18n/detectors'; - import TypesafeI18n from '../../i18n/i18n-react'; import { detectLocale } from '../../i18n/i18n-util'; import { loadLocaleAsync } from '../../i18n/i18n-util.async'; -import { clientApi } from '../../pages/client/clientAPI/clientApi'; import { ClientPage } from '../../pages/client/ClientPage'; +import { clientApi } from '../../pages/client/clientAPI/clientApi'; import { useClientStore } from '../../pages/client/hooks/useClientStore'; import { CarouselPage } from '../../pages/client/pages/CarouselPage/CarouselPage'; import { ClientAddedPage } from '../../pages/client/pages/ClientAddedPage/ClientAddedPage'; @@ -30,7 +30,7 @@ import { ClientAddTunnelPage } from '../../pages/client/pages/ClientAddTunnelPag import { ClientEditTunnelPage } from '../../pages/client/pages/ClientEditTunnelPage/ClientEditTunnelPage'; import { ClientInstancePage } from '../../pages/client/pages/ClientInstancePage/ClientInstancePage'; import { ClientSettingsPage } from '../../pages/client/pages/ClientSettingsPage/ClientSettingsPage'; -import { WireguardInstanceType } from '../../pages/client/types'; +import { ClientConnectionType } from '../../pages/client/types'; import { EnrollmentPage } from '../../pages/enrollment/EnrollmentPage'; import { SessionTimeoutPage } from '../../pages/sessionTimeout/SessionTimeoutPage'; import { ToastManager } from '../../shared/defguard-ui/components/Layout/ToastManager/ToastManager'; @@ -87,7 +87,7 @@ const router = createBrowserRouter([ }, { path: '/client/instance-created', - element: , + element: , }, { path: '/client/add-tunnel', @@ -95,7 +95,7 @@ const router = createBrowserRouter([ }, { path: '/client/tunnel-created', - element: , + element: , }, { path: '/client/edit-tunnel', @@ -120,7 +120,7 @@ const router = createBrowserRouter([ const detectedLocale = detectLocale(localStorageDetector); export const App = () => { - //workaround ensure effect once in dev mode, thanks react :3 + // Workaround: ensure effect once in dev mode, thanks react :3 const tauriInitLoadRef = useRef(false); const localeLoadRef = useRef(false); const [localeLoaded, setWasLoaded] = useState(false); @@ -146,12 +146,13 @@ export const App = () => { } }, []); - // load settings from tauri first time + // Load settings from Tauri for the first time. + // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater useEffect(() => { if (!tauriInitLoadRef.current) { tauriInitLoadRef.current = true; const loadTauriState = async () => { - debug('App init state from tauri'); + debug('App init state from Tauri'); const appConfig = await getAppConfig(); const instances = await getInstances(); const tunnels = await getTunnels(); @@ -165,6 +166,26 @@ export const App = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + const handler = (e: MouseEvent) => { + const target = e.target as HTMLElement | undefined; + if (target) { + const link = target.closest('a'); + if ( + link instanceof HTMLAnchorElement && + link.target === '_blank' && + link.href.startsWith('https') + ) { + void openUrl(link.href); + } + } + }; + document.addEventListener('click', handler); + return () => { + document.removeEventListener('click', handler); + }; + }, []); + if (!appLoaded) return null; return ( diff --git a/src/components/ApplicationUpdateManager/ApplicationUpdateManager.tsx b/src/components/ApplicationUpdateManager/ApplicationUpdateManager.tsx index 594e1c67..0a756419 100644 --- a/src/components/ApplicationUpdateManager/ApplicationUpdateManager.tsx +++ b/src/components/ApplicationUpdateManager/ApplicationUpdateManager.tsx @@ -1,13 +1,13 @@ import { getVersion } from '@tauri-apps/api/app'; -import { listen, UnlistenFn } from '@tauri-apps/api/event'; +import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { useEffect, useState } from 'react'; import { clientApi } from '../../pages/client/clientAPI/clientApi.ts'; import { useClientStore } from '../../pages/client/hooks/useClientStore'; import { TauriEventKey } from '../../pages/client/types'; -import { NewApplicationVersionInfo } from '../../shared/hooks/api/types'; +import type { NewApplicationVersionInfo } from '../../shared/hooks/api/types'; import { - ApplicationUpdateStore, + type ApplicationUpdateStore, useApplicationUpdateStore, } from './useApplicationUpdateStore'; @@ -37,7 +37,9 @@ export const ApplicationUpdateManager = () => { // Stop listening if "check for updates" setting has been turned off. if (!checkForUpdates) { - subs.forEach((sub) => sub()); + subs.forEach((sub) => { + sub(); + }); return; } @@ -56,7 +58,9 @@ export const ApplicationUpdateManager = () => { }); return () => { - subs.forEach((sub) => sub()); + subs.forEach((sub) => { + sub(); + }); }; }, [checkForUpdates, setApplicationUpdateData]); diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index 59616332..2dca9b4f 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -55,6 +55,18 @@ const en = { deadConDropped: 'Detected that the {con_type: string} {interface_name: string} has disconnected, trying to reconnect...', noCookie: 'No defguard_proxy set-cookie received', + insecureContext: 'Context is not secure.', + clipboard: { + error: 'Clipboard is not accessible.', + success: 'Content copied to clipboard.', + }, + versionMismatch: + 'Your Defguard instance "{instance_name: string}" version is not supported by your Defguard Client version. \ + Defguard Core version: {core_version: string} (required: {core_required_version: string}), Defguard Proxy version: {proxy_version: string} (required: {proxy_required_version: string}). \ + Please contact your administrator.', + uuidMismatch: + 'The identifier (UUID) of the remote Defguard instance "{instance_name: string}" does not match the one stored locally. \ + Because of this, some features may not work correctly. To resolve this issue, remove the instance and add it again, or contact your administrator.', }, }, components: { @@ -485,6 +497,10 @@ If this will not change, please contact your administrator.`, password: 'Create password', vpn: 'Configure VPN', finish: 'Finish', + mfa: 'Configure MFA', + mfaChoice: 'Choose method', + mfaSetup: 'Complete method', + mfaRecovery: 'Recovery codes', }, appVersion: 'Application version', }, diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 265a1cd4..088f2ae0 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -170,6 +170,34 @@ type RootTranslation = { * N​o​ ​d​e​f​g​u​a​r​d​_​p​r​o​x​y​ ​s​e​t​-​c​o​o​k​i​e​ ​r​e​c​e​i​v​e​d */ noCookie: string + /** + * C​o​n​t​e​x​t​ ​i​s​ ​n​o​t​ ​s​e​c​u​r​e​. + */ + insecureContext: string + clipboard: { + /** + * C​l​i​p​b​o​a​r​d​ ​i​s​ ​n​o​t​ ​a​c​c​e​s​s​i​b​l​e​. + */ + error: string + /** + * C​o​n​t​e​n​t​ ​c​o​p​i​e​d​ ​t​o​ ​c​l​i​p​b​o​a​r​d​. + */ + success: string + } + /** + * Y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​i​n​s​t​a​n​c​e​ ​"​{​i​n​s​t​a​n​c​e​_​n​a​m​e​}​"​ ​v​e​r​s​i​o​n​ ​i​s​ ​n​o​t​ ​s​u​p​p​o​r​t​e​d​ ​b​y​ ​y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​C​l​i​e​n​t​ ​v​e​r​s​i​o​n​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​e​f​g​u​a​r​d​ ​C​o​r​e​ ​v​e​r​s​i​o​n​:​ ​{​c​o​r​e​_​v​e​r​s​i​o​n​}​ ​(​r​e​q​u​i​r​e​d​:​ ​{​c​o​r​e​_​r​e​q​u​i​r​e​d​_​v​e​r​s​i​o​n​}​)​,​ ​D​e​f​g​u​a​r​d​ ​P​r​o​x​y​ ​v​e​r​s​i​o​n​:​ ​{​p​r​o​x​y​_​v​e​r​s​i​o​n​}​ ​(​r​e​q​u​i​r​e​d​:​ ​{​p​r​o​x​y​_​r​e​q​u​i​r​e​d​_​v​e​r​s​i​o​n​}​)​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​P​l​e​a​s​e​ ​c​o​n​t​a​c​t​ ​y​o​u​r​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​. + * @param {string} core_required_version + * @param {string} core_version + * @param {string} instance_name + * @param {string} proxy_required_version + * @param {string} proxy_version + */ + versionMismatch: RequiredParams<'core_required_version' | 'core_version' | 'instance_name' | 'proxy_required_version' | 'proxy_version'> + /** + * T​h​e​ ​i​d​e​n​t​i​f​i​e​r​ ​(​U​U​I​D​)​ ​o​f​ ​t​h​e​ ​r​e​m​o​t​e​ ​D​e​f​g​u​a​r​d​ ​i​n​s​t​a​n​c​e​ ​"​{​i​n​s​t​a​n​c​e​_​n​a​m​e​}​"​ ​d​o​e​s​ ​n​o​t​ ​m​a​t​c​h​ ​t​h​e​ ​o​n​e​ ​s​t​o​r​e​d​ ​l​o​c​a​l​l​y​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​e​c​a​u​s​e​ ​o​f​ ​t​h​i​s​,​ ​s​o​m​e​ ​f​e​a​t​u​r​e​s​ ​m​a​y​ ​n​o​t​ ​w​o​r​k​ ​c​o​r​r​e​c​t​l​y​.​ ​T​o​ ​r​e​s​o​l​v​e​ ​t​h​i​s​ ​i​s​s​u​e​,​ ​r​e​m​o​v​e​ ​t​h​e​ ​i​n​s​t​a​n​c​e​ ​a​n​d​ ​a​d​d​ ​i​t​ ​a​g​a​i​n​,​ ​o​r​ ​c​o​n​t​a​c​t​ ​y​o​u​r​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​. + * @param {string} instance_name + */ + uuidMismatch: RequiredParams<'instance_name'> } } components: { @@ -1112,6 +1140,22 @@ type RootTranslation = { * F​i​n​i​s​h */ finish: string + /** + * C​o​n​f​i​g​u​r​e​ ​M​F​A + */ + mfa: string + /** + * C​h​o​o​s​e​ ​m​e​t​h​o​d + */ + mfaChoice: string + /** + * C​o​m​p​l​e​t​e​ ​m​e​t​h​o​d + */ + mfaSetup: string + /** + * R​e​c​o​v​e​r​y​ ​c​o​d​e​s + */ + mfaRecovery: string } /** * A​p​p​l​i​c​a​t​i​o​n​ ​v​e​r​s​i​o​n @@ -1827,6 +1871,28 @@ export type TranslationFunctions = { * No defguard_proxy set-cookie received */ noCookie: () => LocalizedString + /** + * Context is not secure. + */ + insecureContext: () => LocalizedString + clipboard: { + /** + * Clipboard is not accessible. + */ + error: () => LocalizedString + /** + * Content copied to clipboard. + */ + success: () => LocalizedString + } + /** + * Your Defguard instance "{instance_name}" version is not supported by your Defguard Client version. Defguard Core version: {core_version} (required: {core_required_version}), Defguard Proxy version: {proxy_version} (required: {proxy_required_version}). Please contact your administrator. + */ + versionMismatch: (arg: { core_required_version: string, core_version: string, instance_name: string, proxy_required_version: string, proxy_version: string }) => LocalizedString + /** + * The identifier (UUID) of the remote Defguard instance "{instance_name}" does not match the one stored locally. Because of this, some features may not work correctly. To resolve this issue, remove the instance and add it again, or contact your administrator. + */ + uuidMismatch: (arg: { instance_name: string }) => LocalizedString } } components: { @@ -2762,6 +2828,22 @@ export type TranslationFunctions = { * Finish */ finish: () => LocalizedString + /** + * Configure MFA + */ + mfa: () => LocalizedString + /** + * Choose method + */ + mfaChoice: () => LocalizedString + /** + * Complete method + */ + mfaSetup: () => LocalizedString + /** + * Recovery codes + */ + mfaRecovery: () => LocalizedString } /** * Application version diff --git a/src/pages/client/ClientPage.tsx b/src/pages/client/ClientPage.tsx index ff8b49a1..74b04bd4 100644 --- a/src/pages/client/ClientPage.tsx +++ b/src/pages/client/ClientPage.tsx @@ -7,16 +7,24 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import { shallow } from 'zustand/shallow'; import { useI18nContext } from '../../i18n/i18n-react'; +import { DeepLinkProvider } from '../../shared/components/providers/DeepLinkProvider'; import { useToaster } from '../../shared/defguard-ui/hooks/toasts/useToaster'; import { routes } from '../../shared/routes'; import { clientApi } from './clientAPI/clientApi'; import { ClientSideBar } from './components/ClientSideBar/ClientSideBar'; +import { MfaModalProvider } from './components/MfaModalProvider'; import { DeadConDroppedModal } from './components/modals/DeadConDroppedModal/DeadConDroppedModal'; import { useDeadConDroppedModal } from './components/modals/DeadConDroppedModal/store'; import { useClientFlags } from './hooks/useClientFlags'; import { useClientStore } from './hooks/useClientStore'; +import { useMFAModal } from './pages/ClientInstancePage/components/LocationsList/modals/MFAModal/useMFAModal'; import { clientQueryKeys } from './query'; -import { DeadConDroppedPayload, DeadConReconnectedPayload, TauriEventKey } from './types'; +import { + type CommonWireguardFields, + type DeadConDroppedPayload, + TauriEventKey, + ClientConnectionType, +} from './types'; const { getInstances, getTunnels, getAppConfig } = clientApi; @@ -35,6 +43,7 @@ export const ClientPage = () => { const location = useLocation(); const toaster = useToaster(); const openDeadConDroppedModal = useDeadConDroppedModal((s) => s.open); + const openMFAModal = useMFAModal((state) => state.open); const { LL } = useI18nContext(); const { data: instances } = useQuery({ @@ -58,6 +67,7 @@ export const ClientPage = () => { refetchOnWindowFocus: false, }); + // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater useEffect(() => { const appConfigChanged = listen(TauriEventKey.APPLICATION_CONFIG_CHANGED, () => { queryClient.invalidateQueries({ @@ -70,20 +80,57 @@ export const ClientPage = () => { clientQueryKeys.getLocations, clientQueryKeys.getTunnels, ]; - invalidate.forEach((key) => + invalidate.forEach((key) => { queryClient.invalidateQueries({ queryKey: [key], + }); + }); + }); + + const verionMismatch = listen(TauriEventKey.VERSION_MISMATCH, (data) => { + const payload = data.payload as { + instance_name: string; + instance_id: number; + core_version: string; + proxy_version: string; + core_required_version: string; + proxy_required_version: string; + core_compatible: boolean; + proxy_compatible: boolean; + }; + toaster.error( + LL.common.messages.versionMismatch({ + instance_name: payload.instance_name, + core_version: payload.core_version, + proxy_version: payload.proxy_version, + core_required_version: payload.core_required_version, + proxy_required_version: payload.proxy_required_version, }), + { lifetime: -1 }, + ); + }); + + const uuidMismatch = listen<{ + instance_name: string; + local_uuid: string; + core_uuid: string; + }>(TauriEventKey.UUID_MISMATCH, (data) => { + const payload = data.payload; + toaster.error( + LL.common.messages.uuidMismatch({ + instance_name: payload.instance_name, + }), + { lifetime: -1 }, ); }); const locationUpdate = listen(TauriEventKey.LOCATION_UPDATE, () => { const invalidate = [clientQueryKeys.getLocations, clientQueryKeys.getTunnels]; - invalidate.forEach((key) => + invalidate.forEach((key) => { queryClient.invalidateQueries({ queryKey: [key], - }), - ); + }); + }); }); const connectionChanged = listen(TauriEventKey.CONNECTION_CHANGED, () => { @@ -96,11 +143,11 @@ export const ClientPage = () => { clientQueryKeys.getInstances, clientQueryKeys.getTunnels, ]; - invalidate.forEach((key) => + invalidate.forEach((key) => { queryClient.invalidateQueries({ queryKey: [key], - }), - ); + }); + }); }); const configChanged = listen(TauriEventKey.CONFIG_CHANGED, (data) => { @@ -115,7 +162,7 @@ export const ClientPage = () => { }, ); - const deadConnectionReconnected = listen( + const deadConnectionReconnected = listen( TauriEventKey.DEAD_CONNECTION_RECONNECTED, (data) => { toaster.warning( @@ -130,6 +177,15 @@ export const ClientPage = () => { }, ); + const mfaTrigger = listen( + TauriEventKey.MFA_TRIGGER, + (data) => { + // Set connection type, as it is not transferred from Rust and MFA is only for locations. + data.payload.connection_type = ClientConnectionType.LOCATION; + openMFAModal(data.payload); + }, + ); + return () => { deadConnectionDropped.then((cleanup) => cleanup()); deadConnectionReconnected.then((cleanup) => cleanup()); @@ -137,7 +193,10 @@ export const ClientPage = () => { connectionChanged.then((cleanup) => cleanup()); instanceUpdate.then((cleanup) => cleanup()); locationUpdate.then((cleanup) => cleanup()); - appConfigChanged.then((c) => c()); + appConfigChanged.then((cleanup) => cleanup()); + mfaTrigger.then((cleanup) => cleanup()); + verionMismatch.then((cleanup) => cleanup()); + uuidMismatch.then((cleanup) => cleanup()); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -152,15 +211,9 @@ export const ClientPage = () => { setListChecked(true); setTunnels(tunnels); } - }, [ - instances, - setInstances, - tunnels, - setTunnels, - setListChecked, - openDeadConDroppedModal, - ]); + }, [instances, setInstances, tunnels, setTunnels, setListChecked]); + // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater useEffect(() => { if (appConfig) { setClientState({ appConfig }); @@ -182,10 +235,12 @@ export const ClientPage = () => { }, [navigate, listChecked, instances, tunnels]); return ( - <> - + + + + - + ); }; diff --git a/src/pages/client/clientAPI/clientApi.ts b/src/pages/client/clientAPI/clientApi.ts index 8389c031..b689e1aa 100644 --- a/src/pages/client/clientAPI/clientApi.ts +++ b/src/pages/client/clientAPI/clientApi.ts @@ -1,17 +1,17 @@ -import { invoke } from '@tauri-apps/api'; -import { InvokeArgs } from '@tauri-apps/api/tauri'; +import type { InvokeArgs } from '@tauri-apps/api/core'; +import { invoke } from '@tauri-apps/api/core'; +import { debug, error, trace } from '@tauri-apps/plugin-log'; import pTimeout, { TimeoutError } from 'p-timeout'; -import { debug, error, trace } from 'tauri-plugin-log-api'; -import { NewApplicationVersionInfo } from '../../../shared/hooks/api/types'; -import { +import type { NewApplicationVersionInfo } from '../../../shared/hooks/api/types'; +import type { CommonWireguardFields, Connection, DefguardInstance, LocationStats, Tunnel, } from '../types'; -import { +import type { AppConfig, ConnectionRequest, GetLocationsRequest, diff --git a/src/pages/client/clientAPI/types.ts b/src/pages/client/clientAPI/types.ts index c28a126a..39d3ad1b 100644 --- a/src/pages/client/clientAPI/types.ts +++ b/src/pages/client/clientAPI/types.ts @@ -1,6 +1,6 @@ -import { ThemeKey } from '../../../shared/defguard-ui/hooks/theme/types'; -import { CreateDeviceResponse } from '../../../shared/hooks/api/types'; -import { DefguardInstance, DefguardLocation, WireguardInstanceType } from '../types'; +import type { ThemeKey } from '../../../shared/defguard-ui/hooks/theme/types'; +import type { CreateDeviceResponse } from '../../../shared/hooks/api/types'; +import type { DefguardInstance, DefguardLocation, ClientConnectionType } from '../types'; export type GetLocationsRequest = { instanceId: number; @@ -8,19 +8,19 @@ export type GetLocationsRequest = { export type ConnectionRequest = { locationId: number; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; presharedKey?: string; }; export type RoutingRequest = { locationId: number; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; routeAllTraffic?: boolean; }; export type StatsRequest = { locationId: number; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; from?: string; }; @@ -114,7 +114,7 @@ export type TunnelRequest = { export type LocationDetailsRequest = { locationId: number; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; }; export type TauriCommandKey = diff --git a/src/pages/client/components/ClientSideBar/ClientSideBar.tsx b/src/pages/client/components/ClientSideBar/ClientSideBar.tsx index 9e7161c5..36a5efb5 100644 --- a/src/pages/client/components/ClientSideBar/ClientSideBar.tsx +++ b/src/pages/client/components/ClientSideBar/ClientSideBar.tsx @@ -16,14 +16,12 @@ import { IconContainer } from '../../../../shared/defguard-ui/components/Layout/ import SvgIconPlus from '../../../../shared/defguard-ui/components/svg/IconPlus'; import SvgIconSettings from '../../../../shared/defguard-ui/components/svg/IconSettings'; import { routes } from '../../../../shared/routes'; -import { clientApi } from '../../clientAPI/clientApi'; import { useClientStore } from '../../hooks/useClientStore'; -import { WireguardInstanceType } from '../../types'; +import { useAddInstanceStore } from '../../pages/ClientAddInstancePage/hooks/useAddInstanceStore'; +import { ClientConnectionType } from '../../types'; import { ClientBarItem } from './components/ClientBarItem/ClientBarItem'; import { NewApplicationVersionAvailableInfo } from './components/NewApplicationVersionAvailableInfo/NewApplicationVersionAvailableInfo'; -const { openLink } = clientApi; - export const ClientSideBar = () => { const navigate = useNavigate(); const { LL } = useI18nContext(); @@ -32,7 +30,7 @@ export const ClientSideBar = () => { ); const tunnelPathActive = selectedInstance?.id === undefined && - selectedInstance?.type === WireguardInstanceType.TUNNEL; + selectedInstance?.type === ClientConnectionType.TUNNEL; return (
@@ -62,12 +60,11 @@ export const ClientSideBar = () => {
{instances.map((instance) => ( ))} @@ -84,7 +81,7 @@ export const ClientSideBar = () => { setClientStore({ selectedInstance: { id: undefined, - type: WireguardInstanceType.TUNNEL, + type: ClientConnectionType.TUNNEL, }, }); navigate(routes.client.base, { replace: true }); @@ -97,9 +94,9 @@ export const ClientSideBar = () => { ))} @@ -133,13 +130,19 @@ const FooterApplicationInfo = () => { ); @@ -168,11 +171,13 @@ const SettingsNav = () => { const AddInstance = () => { const { LL } = useI18nContext(); const navigate = useNavigate(); + const resetStore = useAddInstanceStore((s) => s.reset); return (
{ + resetStore(); navigate(routes.client.addInstance, { replace: true }); }} > diff --git a/src/pages/client/components/ClientSideBar/components/ClientBarItem/ClientBarItem.tsx b/src/pages/client/components/ClientSideBar/components/ClientBarItem/ClientBarItem.tsx index 4bd2a052..59492fb9 100644 --- a/src/pages/client/components/ClientSideBar/components/ClientBarItem/ClientBarItem.tsx +++ b/src/pages/client/components/ClientSideBar/components/ClientBarItem/ClientBarItem.tsx @@ -7,10 +7,10 @@ import { useMatch, useNavigate } from 'react-router-dom'; import SvgIconConnection from '../../../../../../shared/defguard-ui/components/svg/IconConnection'; import { routes } from '../../../../../../shared/routes'; import { useClientStore } from '../../../../hooks/useClientStore'; -import { SelectedInstance, WireguardInstanceType } from '../../../../types'; +import type { ClientConnectionType } from '../../../../types'; type Props = { - itemType: WireguardInstanceType; + itemType: ClientConnectionType; itemId: number; label: string; active?: boolean; @@ -47,26 +47,12 @@ export const ClientBarItem = ({ itemType, itemId, label, active = false }: Props className={cn} ref={refs.setReference} onClick={() => { - switch (itemType) { - case WireguardInstanceType.DEFGUARD_INSTANCE: { - const _selectedInstance: SelectedInstance = { - id: itemId, - type: WireguardInstanceType.DEFGUARD_INSTANCE, - }; - setClientStore({ - selectedInstance: _selectedInstance, - }); - break; - } - case WireguardInstanceType.TUNNEL: - setClientStore({ - selectedInstance: { - id: itemId, - type: WireguardInstanceType.TUNNEL, - }, - }); - break; - } + setClientStore({ + selectedInstance: { + id: itemId, + type: itemType, + }, + }); if (!instancePage) { navigate(routes.client.instancePage, { replace: true }); } diff --git a/src/pages/client/components/ClientSideBar/style.scss b/src/pages/client/components/ClientSideBar/style.scss index 8f6aacb2..ad97b195 100644 --- a/src/pages/client/components/ClientSideBar/style.scss +++ b/src/pages/client/components/ClientSideBar/style.scss @@ -281,7 +281,8 @@ padding-right: 5px; } - & > span { + & > a { + color: inherit; cursor: pointer; } } diff --git a/src/pages/client/components/MfaModalProvider.tsx b/src/pages/client/components/MfaModalProvider.tsx new file mode 100644 index 00000000..680100c5 --- /dev/null +++ b/src/pages/client/components/MfaModalProvider.tsx @@ -0,0 +1,40 @@ +import { listen, type UnlistenFn } from '@tauri-apps/api/event'; +import { type PropsWithChildren, useEffect } from 'react'; +import { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; +import { MFAModal } from '../pages/ClientInstancePage/components/LocationsList/modals/MFAModal/MFAModal'; +import { useMFAModal } from '../pages/ClientInstancePage/components/LocationsList/modals/MFAModal/useMFAModal'; +import type { CommonWireguardFields } from '../types'; + +type Props = PropsWithChildren; + +type Payload = { + location?: CommonWireguardFields; +}; + +export const MfaModalProvider = ({ children }: Props) => { + const openMFAModal = useMFAModal((state) => state.open); + // listen for rust backend requesting MFA + + useEffect(() => { + let unlisten: UnlistenFn; + + (async () => { + unlisten = await listen('mfa-trigger', ({ payload: { location } }) => { + if (isPresent(location)) { + openMFAModal(location); + } + }); + })(); + + return () => { + unlisten?.(); + }; + }, [openMFAModal]); + + return ( + <> + {children} + + + ); +}; diff --git a/src/pages/client/components/modals/DeadConDroppedModal/store.tsx b/src/pages/client/components/modals/DeadConDroppedModal/store.tsx index 0162250e..f794d851 100644 --- a/src/pages/client/components/modals/DeadConDroppedModal/store.tsx +++ b/src/pages/client/components/modals/DeadConDroppedModal/store.tsx @@ -1,6 +1,6 @@ import { createWithEqualityFn } from 'zustand/traditional'; -import { DeadConDroppedPayload } from '../../../types'; +import type { DeadConDroppedPayload } from '../../../types'; const defaultValues: StoreValues = { visible: false, diff --git a/src/pages/client/hooks/useClientStore.tsx b/src/pages/client/hooks/useClientStore.tsx index 604573df..6dad3356 100644 --- a/src/pages/client/hooks/useClientStore.tsx +++ b/src/pages/client/hooks/useClientStore.tsx @@ -3,12 +3,12 @@ import { createJSONStorage, persist } from 'zustand/middleware'; import { createWithEqualityFn } from 'zustand/traditional'; import { clientApi } from '../clientAPI/clientApi'; -import { AppConfig, ClientView } from '../clientAPI/types'; +import type { AppConfig, ClientView } from '../clientAPI/types'; import { - CommonWireguardFields, - DefguardInstance, - SelectedInstance, - WireguardInstanceType, + type CommonWireguardFields, + type DefguardInstance, + type SelectedInstance, + ClientConnectionType, } from '../types'; const { getInstances, setAppConfig } = clientApi; @@ -49,7 +49,7 @@ export const useClientStore = createWithEqualityFn()( instances: values, selectedInstance: { id: values[0]?.id, - type: WireguardInstanceType.DEFGUARD_INSTANCE, + type: ClientConnectionType.LOCATION, }, }); } @@ -59,7 +59,7 @@ export const useClientStore = createWithEqualityFn()( if (isUndefined(get().selectedInstance)) { return set({ tunnels: values, - selectedInstance: { id: values[0]?.id, type: WireguardInstanceType.TUNNEL }, + selectedInstance: { id: values[0]?.id, type: ClientConnectionType.TUNNEL }, }); } return set({ tunnels: values }); @@ -73,11 +73,11 @@ export const useClientStore = createWithEqualityFn()( // check if currently selected instances is in updated instances if (!isUndefined(selected) && res.length && selected.id) { if (!res.map((i) => i.id).includes(selected.id)) { - selected = { id: res[0].id, type: WireguardInstanceType.DEFGUARD_INSTANCE }; + selected = { id: res[0].id, type: ClientConnectionType.LOCATION }; } } if (isUndefined(selected) && res.length) { - selected = { id: res[0].id, type: WireguardInstanceType.DEFGUARD_INSTANCE }; + selected = { id: res[0].id, type: ClientConnectionType.LOCATION }; } set({ instances: res, selectedInstance: selected }); }, diff --git a/src/pages/client/pages/CarouselPage/CarouselPage.tsx b/src/pages/client/pages/CarouselPage/CarouselPage.tsx index 1a823276..c98a6ecc 100644 --- a/src/pages/client/pages/CarouselPage/CarouselPage.tsx +++ b/src/pages/client/pages/CarouselPage/CarouselPage.tsx @@ -11,7 +11,7 @@ import { WelcomeCardSlide, } from './cards/CarouselCards'; import { CardCarousel } from './components/CardCarousel/CardCarousel'; -import { CarouselItem } from './components/CardCarousel/types'; +import type { CarouselItem } from './components/CardCarousel/types'; const slides: CarouselItem[] = [ { diff --git a/src/pages/client/pages/CarouselPage/components/CardCarousel/CardCarousel.tsx b/src/pages/client/pages/CarouselPage/components/CardCarousel/CardCarousel.tsx index c2a50014..934dff2d 100644 --- a/src/pages/client/pages/CarouselPage/components/CardCarousel/CardCarousel.tsx +++ b/src/pages/client/pages/CarouselPage/components/CardCarousel/CardCarousel.tsx @@ -1,13 +1,13 @@ import './style.scss'; import classNames from 'classnames'; -import { AnimatePresence, motion } from 'framer-motion'; import { isUndefined } from 'lodash-es'; -import { HTMLProps, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { AnimatePresence, motion } from 'motion/react'; +import { type HTMLProps, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { interval } from 'rxjs'; import { CarouselControls } from './components/CarouselControl/CarouselControl'; -import { CarouselItem } from './types'; +import type { CarouselItem } from './types'; type Props = { cards: CarouselItem[]; @@ -51,7 +51,7 @@ export const CardCarousel = ({ } return currentIndex + 1; }); - }, [setInternalIndex, cardsCount]); + }, [cardsCount]); useEffect(() => { if (autoSlide) { diff --git a/src/pages/client/pages/CarouselPage/components/CardCarousel/types.ts b/src/pages/client/pages/CarouselPage/components/CardCarousel/types.ts index c836137a..bde2adf3 100644 --- a/src/pages/client/pages/CarouselPage/components/CardCarousel/types.ts +++ b/src/pages/client/pages/CarouselPage/components/CardCarousel/types.ts @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; export type CarouselItem = { key: string; diff --git a/src/pages/client/pages/ClientAddInstancePage/ClientAddInstnacePage.tsx b/src/pages/client/pages/ClientAddInstancePage/ClientAddInstnacePage.tsx index 6998af33..e42d6f0f 100644 --- a/src/pages/client/pages/ClientAddInstancePage/ClientAddInstnacePage.tsx +++ b/src/pages/client/pages/ClientAddInstancePage/ClientAddInstnacePage.tsx @@ -7,7 +7,7 @@ import { AddInstanceGuide } from './components/AddInstanceGuide/AddInstanceGuide export const ClientAddInstancePage = () => { const { LL } = useI18nContext(); return ( -
+

{LL.pages.client.pages.addInstancePage.title()}

diff --git a/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/AddInstanceFormCard.tsx b/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/AddInstanceFormCard.tsx index 387a5e3b..48e4f19f 100644 --- a/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/AddInstanceFormCard.tsx +++ b/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/AddInstanceFormCard.tsx @@ -1,26 +1,15 @@ -import { useState } from 'react'; - import { Card } from '../../../../../../shared/defguard-ui/components/Layout/Card/Card'; +import { AddInstanceFormStep } from '../../hooks/types'; +import { useAddInstanceStore } from '../../hooks/useAddInstanceStore'; import { AddInstanceDeviceForm } from './components/AddInstanceDeviceForm/AddInstanceDeviceForm'; import { AddInstanceInitForm } from './components/AddInstanceInitForm/AddInstanceInitForm'; -import { AddInstanceInitResponse } from './types'; export const AddInstanceFormCard = () => { - const [currentStep, setCurrentStep] = useState(0); - const [response, setResponse] = useState( - undefined, - ); + const currentStep = useAddInstanceStore((s) => s.step); return ( - {currentStep === 0 && ( - { - setResponse(data); - setCurrentStep((step) => step + 1); - }} - /> - )} - {currentStep === 1 && response && } + {currentStep === AddInstanceFormStep.INIT && } + {currentStep === AddInstanceFormStep.DEVICE && } ); }; diff --git a/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceDeviceForm/AddInstanceDeviceForm.tsx b/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceDeviceForm/AddInstanceDeviceForm.tsx index bb54b08f..db8199cf 100644 --- a/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceDeviceForm/AddInstanceDeviceForm.tsx +++ b/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceDeviceForm/AddInstanceDeviceForm.tsx @@ -1,13 +1,14 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Body, fetch } from '@tauri-apps/api/http'; -import { useMemo, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { useQuery } from '@tanstack/react-query'; +import { fetch } from '@tauri-apps/plugin-http'; +import { error } from '@tauri-apps/plugin-log'; +import { hostname } from '@tauri-apps/plugin-os'; +import { useCallback, useMemo, useState } from 'react'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; -import { error } from 'tauri-plugin-log-api'; import { z } from 'zod'; - import { useI18nContext } from '../../../../../../../../i18n/i18n-react'; import { FormInput } from '../../../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; import { Button } from '../../../../../../../../shared/defguard-ui/components/Layout/Button/Button'; @@ -16,7 +17,8 @@ import { ButtonStyleVariant, } from '../../../../../../../../shared/defguard-ui/components/Layout/Button/types'; import { useToaster } from '../../../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; -import { +import { isPresent } from '../../../../../../../../shared/defguard-ui/utils/isPresent'; +import type { CreateDeviceRequest, CreateDeviceResponse, } from '../../../../../../../../shared/hooks/api/types'; @@ -24,37 +26,34 @@ import { routes } from '../../../../../../../../shared/routes'; import { generateWGKeys } from '../../../../../../../../shared/utils/generateWGKeys'; import { clientApi } from '../../../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../../../hooks/useClientStore'; -import { SelectedInstance, WireguardInstanceType } from '../../../../../../types'; -import { AddInstanceInitResponse } from '../../types'; +import { type SelectedInstance, ClientConnectionType } from '../../../../../../types'; +import { useAddInstanceStore } from '../../../../hooks/useAddInstanceStore'; +import type { AddInstanceInitResponse } from '../../types'; const { getInstances, saveConfig } = clientApi; -type Props = { - response: AddInstanceInitResponse; -}; - -type FormFields = { - name: string; -}; - type ErrorData = { error: string; }; -const defaultValues: FormFields = { - name: '', -}; - -export const AddInstanceDeviceForm = ({ response }: Props) => { +export const AddInstanceDeviceForm = () => { const { LL } = useI18nContext(); const localLL = LL.pages.client.pages.addInstancePage.forms.device; const toaster = useToaster(); const setClientStore = useClientStore((state) => state.setState); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); + const response = useAddInstanceStore((s) => s.response as AddInstanceInitResponse); + const resetAddInstanceStore = useAddInstanceStore((s) => s.reset); const { url: proxyUrl, cookie, device_names } = response; + const { data: instancesCount } = useQuery({ + queryFn: getInstances, + queryKey: ['instance', 'count'], + select: (data) => data.length, + }); + const schema = useMemo( () => z.object({ @@ -69,6 +68,15 @@ export const AddInstanceDeviceForm = ({ response }: Props) => { [LL.form.errors, device_names], ); + type FormFields = z.infer; + + const defaultValues = useCallback(async (): Promise => { + const name = await hostname(); + return { + name: name ?? '', + }; + }, []); + const { control, handleSubmit } = useForm({ defaultValues: defaultValues, resolver: zodResolver(schema), @@ -90,20 +98,19 @@ export const AddInstanceDeviceForm = ({ response }: Props) => { try { await fetch(`${proxyUrl}/enrollment/create_device`, { headers, - body: Body.json(data), + body: JSON.stringify(data), method: 'POST', - }).then((r) => { + }).then(async (r) => { if (!r.ok) { setIsLoading(false); - const details = `${ - (r.data as ErrorData)?.error ? (r.data as ErrorData).error + ', ' : '' - }`; + const data = (await r.json()) as ErrorData; + const details = `${data?.error ? `${data.error}, ` : ''}`; error( - `Failed to create device check enrollment and defguard logs, details: ${details} Error status code: ${r.status}`, + `Failed to create device. Check enrollment and Defguard logs, details: ${details}. Error status code: ${r.status}`, ); - throw Error(`Failed to create device, details: ${details}`); + throw Error(`Failed to create device, details: ${details} `); } - const deviceResp = r.data as CreateDeviceResponse; + const deviceResp = (await r.json()) as CreateDeviceResponse; saveConfig({ privateKey: privateKey, response: deviceResp, @@ -112,14 +119,14 @@ export const AddInstanceDeviceForm = ({ response }: Props) => { setIsLoading(false); toaster.success(localLL.messages.addSuccess()); const instances = await getInstances(); - const _selectedInstance: SelectedInstance = { + const selectedInstance: SelectedInstance = { id: res.instance.id, - type: WireguardInstanceType.DEFGUARD_INSTANCE, + type: ClientConnectionType.LOCATION, }; - setClientStore({ - selectedInstance: _selectedInstance, - instances, - }); + setClientStore({ selectedInstance, instances }); + setTimeout(() => { + resetAddInstanceStore(); + }, 250); navigate(routes.client.instancePage, { replace: true }); }) .catch((e) => { @@ -161,20 +168,26 @@ export const AddInstanceDeviceForm = ({ response }: Props) => {
-
diff --git a/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceInitForm/AddInstanceInitForm.tsx b/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceInitForm/AddInstanceInitForm.tsx index 20ae704b..f5b0a39b 100644 --- a/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceInitForm/AddInstanceInitForm.tsx +++ b/src/pages/client/pages/ClientAddInstancePage/components/AddInstanceFormCard/components/AddInstanceInitForm/AddInstanceInitForm.tsx @@ -1,13 +1,13 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Body, fetch, Response } from '@tauri-apps/api/http'; -import { invoke } from '@tauri-apps/api/tauri'; +import { invoke } from '@tauri-apps/api/core'; +import { fetch } from '@tauri-apps/plugin-http'; +import { debug, error, info } from '@tauri-apps/plugin-log'; import dayjs from 'dayjs'; import { useMemo, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; -import { debug, error, info } from 'tauri-plugin-log-api'; import { z } from 'zod'; import { useI18nContext } from '../../../../../../../../i18n/i18n-react'; @@ -18,7 +18,7 @@ import { ButtonStyleVariant, } from '../../../../../../../../shared/defguard-ui/components/Layout/Button/types'; import { useToaster } from '../../../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; -import { +import type { CreateDeviceResponse, EnrollmentError, EnrollmentStartResponse, @@ -27,24 +27,12 @@ import { routes } from '../../../../../../../../shared/routes'; import { useEnrollmentStore } from '../../../../../../../enrollment/hooks/store/useEnrollmentStore'; import { clientApi } from '../../../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../../../hooks/useClientStore'; -import { SelectedInstance, WireguardInstanceType } from '../../../../../../types'; -import { AddInstanceInitResponse } from '../../types'; - -type Props = { - nextStep: (data: AddInstanceInitResponse) => void; -}; +import { type SelectedInstance, ClientConnectionType } from '../../../../../../types'; +import { AddInstanceFormStep } from '../../../../hooks/types'; +import { useAddInstanceStore } from '../../../../hooks/useAddInstanceStore'; -type FormFields = { - url: string; - token: string; -}; - -const defaultValues: FormFields = { - url: '', - token: '', -}; - -export const AddInstanceInitForm = ({ nextStep }: Props) => { +export const AddInstanceInitForm = () => { + const setPageState = useAddInstanceStore((s) => s.setState); const toaster = useToaster(); const navigate = useNavigate(); const { LL } = useI18nContext(); @@ -66,14 +54,18 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => { [LL.form.errors], ); + type FormFields = z.infer; + const { handleSubmit, control } = useForm({ resolver: zodResolver(schema), - defaultValues, + defaultValues: { + url: '', + token: '', + }, mode: 'all', }); const handleValidSubmit: SubmitHandler = async (values) => { - debug('Sending token to proxy'); const url = () => { const endpoint = '/api/v1/enrollment/start'; let base: string; @@ -96,17 +88,16 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => { }; setIsLoading(true); - fetch(endpointUrl, { + fetch(endpointUrl, { method: 'POST', headers, - body: Body.json(data), + body: JSON.stringify(data), }) - .then(async (res: Response) => { + .then(async (res: Response) => { if (!res.ok) { setIsLoading(false); - error(JSON.stringify(res.data)); error(JSON.stringify(res.status)); - const errorMessage = (res.data as EnrollmentError).error; + const errorMessage = ((await res.json()) as EnrollmentError).error; switch (errorMessage) { case 'token expired': { @@ -123,9 +114,9 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => { } // There may be other set-cookies, set by e.g. a proxy // Get only the defguard_proxy cookie - const authCookie = res.rawHeaders['set-cookie'].find((cookie) => - cookie.startsWith('defguard_proxy='), - ); + const authCookie = res.headers + .getSetCookie() + .find((cookie) => cookie.startsWith('defguard_proxy=')); if (!authCookie) { setIsLoading(false); error( @@ -140,31 +131,33 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => { ); } debug('Response received with status OK'); - const r = res.data as EnrollmentStartResponse; + const startResponse = (await res.json()) as EnrollmentStartResponse; // get client registered instances const clientInstances = await clientApi.getInstances(); - const instance = clientInstances.find((i) => i.uuid === r.instance.id); + const instance = clientInstances.find( + (i) => i.uuid === startResponse.instance.id, + ); let proxy_api_url = values.url; if (proxy_api_url[proxy_api_url.length - 1] === '/') { proxy_api_url = proxy_api_url.slice(0, -1); } - proxy_api_url = proxy_api_url + '/api/v1'; + proxy_api_url = `${proxy_api_url}/api/v1`; setIsLoading(false); if (instance) { debug('Instance already exists, fetching update'); // update already registered instance instead - headers['Cookie'] = authCookie; - fetch(`${proxy_api_url}/enrollment/network_info`, { + headers.Cookie = authCookie; + fetch(`${proxy_api_url}/enrollment/network_info`, { method: 'POST', headers, - body: Body.json({ + body: JSON.stringify({ pubkey: instance.pubkey, }), }).then(async (res) => { invoke('update_instance', { instanceId: instance.id, - response: res.data, + response: (await res.json()) as CreateDeviceResponse, }) .then(() => { info('Configured device'); @@ -173,7 +166,7 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => { ); const _selectedInstance: SelectedInstance = { id: instance.id, - type: WireguardInstanceType.DEFGUARD_INSTANCE, + type: ClientConnectionType.LOCATION, }; setClientState({ selectedInstance: _selectedInstance, @@ -192,24 +185,32 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => { } // register new instance // is user in need of full enrollment ? - if (r.user.enrolled) { + if (startResponse.user.enrolled) { //no, only create new device for desktop client debug('User already active, adding device only.'); - nextStep({ - url: proxy_api_url, - cookie: authCookie, - device_names: r.user.device_names, + setPageState({ + step: AddInstanceFormStep.DEVICE, + response: { + url: proxy_api_url, + cookie: authCookie, + device_names: startResponse.user.device_names, + }, }); } else { // yes, enroll the user debug('User is not active. Starting enrollment.'); - const sessionEnd = dayjs.unix(r.deadline_timestamp).utc().local().format(); + const sessionEnd = dayjs + .unix(startResponse.deadline_timestamp) + .utc() + .local() + .format(); const sessionStart = dayjs().local().format(); initEnrollment({ - userInfo: r.user, - adminInfo: r.admin, - endContent: r.final_page_content, + userInfo: startResponse.user, + adminInfo: startResponse.admin, + endContent: startResponse.final_page_content, proxy_url: proxy_api_url, + enrollmentSettings: startResponse.settings, sessionEnd, sessionStart, cookie: authCookie, diff --git a/src/pages/client/pages/ClientAddInstancePage/hooks/types.ts b/src/pages/client/pages/ClientAddInstancePage/hooks/types.ts new file mode 100644 index 00000000..29bd4099 --- /dev/null +++ b/src/pages/client/pages/ClientAddInstancePage/hooks/types.ts @@ -0,0 +1,4 @@ +export enum AddInstanceFormStep { + INIT, + DEVICE, +} diff --git a/src/pages/client/pages/ClientAddInstancePage/hooks/useAddInstanceStore.tsx b/src/pages/client/pages/ClientAddInstancePage/hooks/useAddInstanceStore.tsx new file mode 100644 index 00000000..52e7a68f --- /dev/null +++ b/src/pages/client/pages/ClientAddInstancePage/hooks/useAddInstanceStore.tsx @@ -0,0 +1,26 @@ +import { createWithEqualityFn } from 'zustand/traditional'; +import type { AddInstanceInitResponse } from '../components/AddInstanceFormCard/types'; +import { AddInstanceFormStep } from './types'; + +const defaults: StoreValues = { + step: AddInstanceFormStep.INIT, + response: undefined, +}; + +export const useAddInstanceStore = createWithEqualityFn((set) => ({ + ...defaults, + setState: (values) => set((old) => ({ ...old, ...values })), + reset: () => set(defaults), +})); + +type Store = StoreMethods & StoreValues; + +type StoreMethods = { + setState: (values: Partial) => void; + reset: () => void; +}; + +type StoreValues = { + step: AddInstanceFormStep; + response?: AddInstanceInitResponse; +}; diff --git a/src/pages/client/pages/ClientAddInstancePage/style.scss b/src/pages/client/pages/ClientAddInstancePage/style.scss index 176eba55..37cc5ecb 100644 --- a/src/pages/client/pages/ClientAddInstancePage/style.scss +++ b/src/pages/client/pages/ClientAddInstancePage/style.scss @@ -1,4 +1,4 @@ -#client-add-instnace-page { +#client-add-instance-page { h1 { @include typography(app-title); color: var(--text-body-primary); diff --git a/src/pages/client/pages/ClientAddTunnelPage/components/AddTunnelFormCard/AddTunnelFormCard.tsx b/src/pages/client/pages/ClientAddTunnelPage/components/AddTunnelFormCard/AddTunnelFormCard.tsx index 4c112adc..4b9ce224 100644 --- a/src/pages/client/pages/ClientAddTunnelPage/components/AddTunnelFormCard/AddTunnelFormCard.tsx +++ b/src/pages/client/pages/ClientAddTunnelPage/components/AddTunnelFormCard/AddTunnelFormCard.tsx @@ -3,7 +3,7 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; import { pickBy } from 'lodash-es'; import { useEffect, useMemo, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { z } from 'zod'; @@ -132,7 +132,7 @@ export const AddTunnelFormCard = () => { dns: z .string() .refine((value) => { - if (value && value.length != 0) { + if (value && value.length !== 0) { return validateIpOrDomainList(value, ',', true); } return true; @@ -211,9 +211,9 @@ export const AddTunnelFormCard = () => { }; useEffect(() => { - // eslint-disable-next-line - const onPrvKeyChange = (e: any) => { - if (generatedKeys && e.target.value !== defaultValues.prvkey) { + const onPrvKeyChange = (e: Event) => { + const input = e.target as HTMLInputElement; + if (generatedKeys && input.value !== defaultValues.prvkey) { setGeneratedKeys(false); } }; diff --git a/src/pages/client/pages/ClientAddedPage/ClientAddedPage.tsx b/src/pages/client/pages/ClientAddedPage/ClientAddedPage.tsx index 51792cca..65ef7ab3 100644 --- a/src/pages/client/pages/ClientAddedPage/ClientAddedPage.tsx +++ b/src/pages/client/pages/ClientAddedPage/ClientAddedPage.tsx @@ -11,17 +11,17 @@ import { } from '../../../../shared/defguard-ui/components/Layout/Button/types'; import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; import { routes } from '../../../../shared/routes'; -import { WireguardInstanceType } from '../../types'; +import { ClientConnectionType } from '../../types'; type Props = { - pageType: WireguardInstanceType; + pageType: ClientConnectionType; }; export const ClientAddedPage = ({ pageType }: Props) => { const { LL } = useI18nContext(); const navigate = useNavigate(); const [localLL, navigateRoute] = - pageType === WireguardInstanceType.TUNNEL + pageType === ClientConnectionType.TUNNEL ? [LL.pages.client.pages.createdPage.tunnel, routes.client.addTunnel] : [LL.pages.client.pages.createdPage.instance, routes.client.addInstance]; diff --git a/src/pages/client/pages/ClientEditTunnelPage/ClientEditTunnelPage.tsx b/src/pages/client/pages/ClientEditTunnelPage/ClientEditTunnelPage.tsx index e59be110..ab36b147 100644 --- a/src/pages/client/pages/ClientEditTunnelPage/ClientEditTunnelPage.tsx +++ b/src/pages/client/pages/ClientEditTunnelPage/ClientEditTunnelPage.tsx @@ -15,7 +15,7 @@ import { routes } from '../../../../shared/routes'; import { clientApi } from '../../clientAPI/clientApi'; import { useClientStore } from '../../hooks/useClientStore'; import { clientQueryKeys } from '../../query'; -import { WireguardInstanceType } from '../../types'; +import { ClientConnectionType } from '../../types'; import { EditTunnelFormCard } from './components/EditTunnelFormCard'; import { DeleteTunnelModal } from './modals/DeleteTunnelModal/DeleteTunnelModal'; import { useDeleteTunnelModal } from './modals/DeleteTunnelModal/useDeleteTunnelModal'; @@ -31,7 +31,7 @@ export const ClientEditTunnelPage = () => { useEffect(() => { if ( selectedInstance?.id === undefined || - selectedInstance.type !== WireguardInstanceType.TUNNEL + selectedInstance.type !== ClientConnectionType.TUNNEL ) { navigate(routes.client.base, { replace: true }); } diff --git a/src/pages/client/pages/ClientEditTunnelPage/components/EditTunnelFormCard.tsx b/src/pages/client/pages/ClientEditTunnelPage/components/EditTunnelFormCard.tsx index 8b53fa0c..15e66114 100644 --- a/src/pages/client/pages/ClientEditTunnelPage/components/EditTunnelFormCard.tsx +++ b/src/pages/client/pages/ClientEditTunnelPage/components/EditTunnelFormCard.tsx @@ -1,6 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { z } from 'zod'; @@ -25,7 +25,7 @@ import { import { routes } from '../../../../../shared/routes'; import { validateIpOrDomainList } from '../../../../../shared/validators/tunnel'; import { clientApi } from '../../../clientAPI/clientApi'; -import { Tunnel } from '../../../types'; +import type { Tunnel } from '../../../types'; type Props = { tunnel: Tunnel; @@ -162,7 +162,7 @@ export const EditTunnelFormCard = ({ tunnel, submitRef }: Props) => { dns: z .string() .refine((value) => { - if (value && value.length != 0) { + if (value && value.length !== 0) { return validateIpOrDomainList(value, ',', true); } return true; @@ -209,109 +209,105 @@ export const EditTunnelFormCard = ({ tunnel, submitRef }: Props) => { }; return ( - <> -
- -
-

Tunnel Configuration

-
-
-
- {localLL.helpers.name()}} - /> - {localLL.helpers.prvkey()}} - /> - {localLL.helpers.pubkey()}} - /> - {localLL.helpers.address()}} - /> -
-
- -

{localLL.sections.vpnServer()}

+ + +
+

Tunnel Configuration

+
+
+
{localLL.helpers.serverPubkey()}} + controller={{ control, name: 'name' }} + label={localLL.labels.name()} + labelExtras={{localLL.helpers.name()}} /> {localLL.helpers.presharedKey()}} + controller={{ control, name: 'prvkey' }} + label={localLL.labels.privateKey()} + labelExtras={{localLL.helpers.prvkey()}} /> {localLL.helpers.endpoint()}} + controller={{ control, name: 'pubkey' }} + label={localLL.labels.publicKey()} + labelExtras={{localLL.helpers.pubkey()}} /> {localLL.helpers.dns()}} + controller={{ control, name: 'address' }} + label={localLL.labels.address()} + labelExtras={{localLL.helpers.address()}} /> +
+
+ +

{localLL.sections.vpnServer()}

+ {localLL.helpers.serverPubkey()}} + /> + {localLL.helpers.presharedKey()}} + /> + {localLL.helpers.endpoint()}} + /> + {localLL.helpers.dns()}} + /> + {localLL.helpers.allowedIps()}} + /> + + {localLL.helpers.persistentKeepAlive()}} + /> +
+

{localLL.sections.advancedOptions()}

+ {localLL.helpers.advancedOptions()} +
+ +
+
{localLL.helpers.allowedIps()}} + controller={{ control, name: 'pre_up' }} + label={localLL.labels.preUp()} + labelExtras={{localLL.helpers.preUp()}} /> - {localLL.helpers.persistentKeepAlive()}} + controller={{ control, name: 'post_up' }} + label={localLL.labels.postUp()} + labelExtras={{localLL.helpers.postUp()}} /> -
-

{localLL.sections.advancedOptions()}

- {localLL.helpers.advancedOptions()} -
- -
-
- {localLL.helpers.preUp()}} - /> - {localLL.helpers.postUp()}} - /> - {localLL.helpers.preDown()}} - /> - {localLL.helpers.postDown()}} - /> -
- - - - + {localLL.helpers.preDown()}} + /> + {localLL.helpers.postDown()}} + /> +
+
+ + ); }; diff --git a/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/DeleteTunnelModal.tsx b/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/DeleteTunnelModal.tsx index 228e521d..aa314248 100644 --- a/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/DeleteTunnelModal.tsx +++ b/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/DeleteTunnelModal.tsx @@ -11,7 +11,7 @@ import { routes } from '../../../../../../shared/routes'; import { clientApi } from '../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../hooks/useClientStore'; import { clientQueryKeys } from '../../../../query'; -import { WireguardInstanceType } from '../../../../types'; +import { ClientConnectionType } from '../../../../types'; import { useDeleteTunnelModal } from './useDeleteTunnelModal'; const { deleteTunnel } = clientApi; @@ -48,7 +48,7 @@ export const DeleteTunnelModal = () => { setClientStore({ selectedInstance: { id: undefined, - type: WireguardInstanceType.TUNNEL, + type: ClientConnectionType.TUNNEL, }, }); navigate(routes.client.base, { replace: true }); diff --git a/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/useDeleteTunnelModal.ts b/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/useDeleteTunnelModal.ts index 213b89d6..60a0a05f 100644 --- a/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/useDeleteTunnelModal.ts +++ b/src/pages/client/pages/ClientEditTunnelPage/modals/DeleteTunnelModal/useDeleteTunnelModal.ts @@ -1,6 +1,6 @@ import { createWithEqualityFn } from 'zustand/traditional'; -import { Tunnel } from '../../../../types'; +import type { Tunnel } from '../../../../types'; const defaultValues: StoreValues = { isOpen: false, diff --git a/src/pages/client/pages/ClientInstancePage/ClientInstancePage.tsx b/src/pages/client/pages/ClientInstancePage/ClientInstancePage.tsx index 861de1cb..34fc9b34 100644 --- a/src/pages/client/pages/ClientInstancePage/ClientInstancePage.tsx +++ b/src/pages/client/pages/ClientInstancePage/ClientInstancePage.tsx @@ -12,7 +12,7 @@ import { routes } from '../../../../shared/routes'; import { clientApi } from '../../clientAPI/clientApi'; import { useClientStore } from '../../hooks/useClientStore'; import { clientQueryKeys } from '../../query'; -import { DefguardInstance, WireguardInstanceType } from '../../types'; +import { type DefguardInstance, ClientConnectionType } from '../../types'; import { LocationsList } from './components/LocationsList/LocationsList'; import { StatsFilterSelect } from './components/StatsFilterSelect/StatsFilterSelect'; import { StatsLayoutSelect } from './components/StatsLayoutSelect/StatsLayoutSelect'; @@ -37,7 +37,7 @@ export const ClientInstancePage = () => { if ( !isUndefined(selectedInstanceId) && selectedInstanceType && - selectedInstanceType === WireguardInstanceType.DEFGUARD_INSTANCE + selectedInstanceType === ClientConnectionType.LOCATION ) { return instances.find((i) => i.id === selectedInstanceId); } @@ -45,12 +45,12 @@ export const ClientInstancePage = () => { const navigate = useNavigate(); - const isLocationPage = selectedInstanceType === WireguardInstanceType.DEFGUARD_INSTANCE; + const isLocationPage = selectedInstanceType === ClientConnectionType.LOCATION; const openUpdateInstanceModal = useUpdateInstanceModal((state) => state.open); const queryKey = useMemo(() => { - if (selectedInstanceType === WireguardInstanceType.DEFGUARD_INSTANCE) { + if (selectedInstanceType === ClientConnectionType.LOCATION) { return [clientQueryKeys.getLocations, selectedInstanceId as number]; } else { return [clientQueryKeys.getTunnels]; @@ -58,7 +58,7 @@ export const ClientInstancePage = () => { }, [selectedInstanceId, selectedInstanceType]); const queryFn = useCallback(() => { - if (selectedInstanceType === WireguardInstanceType.DEFGUARD_INSTANCE) { + if (selectedInstanceType === ClientConnectionType.LOCATION) { return getLocations({ instanceId: selectedInstanceId as number }); } else { return getTunnels(); @@ -73,8 +73,8 @@ export const ClientInstancePage = () => { useEffect(() => { const isDefguardInstance = - selectedInstanceType === WireguardInstanceType.DEFGUARD_INSTANCE; - const isTunnelInstance = selectedInstanceType === WireguardInstanceType.TUNNEL; + selectedInstanceType === ClientConnectionType.LOCATION; + const isTunnelInstance = selectedInstanceType === ClientConnectionType.TUNNEL; if (isDefguardInstance && !selectedInstance) { navigate(routes.client.addInstance, { replace: true }); diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationUsageChart/LocationUsageChart.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationUsageChart/LocationUsageChart.tsx index 01e77e26..a21d79e2 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationUsageChart/LocationUsageChart.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationUsageChart/LocationUsageChart.tsx @@ -9,7 +9,7 @@ import { Bar, BarChart, Line, LineChart, XAxis, YAxis } from 'recharts'; import { NetworkSpeed } from '../../../../../../shared/defguard-ui/components/Layout/NetworkSpeed/NetworkSpeed'; import { NetworkDirection } from '../../../../../../shared/defguard-ui/components/Layout/NetworkSpeed/types'; import { useTheme } from '../../../../../../shared/defguard-ui/hooks/theme/useTheme'; -import { LocationStats } from '../../../../types'; +import type { LocationStats } from '../../../../types'; import { LocationUsageChartType } from './types'; type ChartBoxSpacing = { diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/LocationsList.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/LocationsList.tsx index 00350251..c708b45e 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/LocationsList.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/LocationsList.tsx @@ -9,13 +9,12 @@ import { useToaster } from '../../../../../../shared/defguard-ui/hooks/toasts/us import { routes } from '../../../../../../shared/routes'; import { useClientStore } from '../../../../hooks/useClientStore'; import { - CommonWireguardFields, - DefguardInstance, - WireguardInstanceType, + type CommonWireguardFields, + type DefguardInstance, + ClientConnectionType, } from '../../../../types'; import { LocationsDetailView } from './components/LocationsDetailView/LocationsDetailView'; import { LocationsGridView } from './components/LocationsGridView/LocationsGridView'; -import { MFAModal } from './modals/MFAModal/MFAModal'; interface LocationsListProps { locations: CommonWireguardFields[] | undefined; @@ -35,7 +34,7 @@ export const LocationsList = ({ const toaster = useToaster(); const navigate = useNavigate(); - const isTunnelType = selectedInstance?.type === WireguardInstanceType.TUNNEL; + const isTunnelType = selectedInstance?.type === ClientConnectionType.TUNNEL; useEffect(() => { if (isError) { @@ -46,12 +45,14 @@ export const LocationsList = ({ useEffect(() => { if ( locations?.length === 0 && - selectedInstance?.type === WireguardInstanceType.TUNNEL + selectedInstance?.type === ClientConnectionType.TUNNEL ) { navigate(routes.client.addTunnel, { replace: true }); } }, [locations, navigate, selectedInstance]); + // Listen for rust requesting MFA for connection + // TODO: add loader or another placeholder view pointing to opening enter token modal if no instances are found / present if (!selectedInstance || !locations) return null; @@ -83,8 +84,6 @@ export const LocationsList = ({ selectedDefguardInstance={selectedDefguardInstance} /> )} - - ); }; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardConnectButton/LocationCardConnectButton.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardConnectButton/LocationCardConnectButton.tsx index cd0a8b91..88a14c84 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardConnectButton/LocationCardConnectButton.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardConnectButton/LocationCardConnectButton.tsx @@ -1,9 +1,8 @@ import './style.scss'; -import { listen } from '@tauri-apps/api/event'; +import { error } from '@tauri-apps/plugin-log'; import classNames from 'classnames'; -import { useEffect, useState } from 'react'; -import { error } from 'tauri-plugin-log-api'; +import { useState } from 'react'; import { useI18nContext } from '../../../../../../../../i18n/i18n-react'; import SvgIconCheckmarkSmall from '../../../../../../../../shared/components/svg/IconCheckmarkSmall'; @@ -15,7 +14,7 @@ import { import SvgIconX from '../../../../../../../../shared/defguard-ui/components/svg/IconX'; import { useToaster } from '../../../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; import { clientApi } from '../../../../../../clientAPI/clientApi'; -import { CommonWireguardFields } from '../../../../../../types'; +import { type CommonWireguardFields, LocationMfaType } from '../../../../../../types'; import { useMFAModal } from '../../modals/MFAModal/useMFAModal'; const { connect, disconnect } = clientApi; @@ -24,20 +23,21 @@ type Props = { location?: CommonWireguardFields; }; -type Payload = { - location?: CommonWireguardFields; -}; - export const LocationCardConnectButton = ({ location }: Props) => { + const openMFAModal = useMFAModal((state) => state.open); const toaster = useToaster(); const [isLoading, setIsLoading] = useState(false); const { LL } = useI18nContext(); - const openMFAModal = useMFAModal((state) => state.open); const cn = classNames('location-card-connect-button', { connected: location?.active, }); + const mfaEnabled = + location?.location_mfa_mode && + (location.location_mfa_mode === LocationMfaType.INTERNAL || + location.location_mfa_mode === LocationMfaType.EXTERNAL); + const handleClick = async () => { setIsLoading(true); try { @@ -48,7 +48,7 @@ export const LocationCardConnectButton = ({ location }: Props) => { connectionType: location.connection_type, }); } else { - if (location.mfa_enabled) { + if (mfaEnabled) { openMFAModal(location); } else { await connect({ @@ -71,17 +71,6 @@ export const LocationCardConnectButton = ({ location }: Props) => { } }; - useEffect(() => { - async function listenMFAEvent() { - await listen('mfa-trigger', () => { - if (location) { - openMFAModal(location); - } - }); - } - listenMFAEvent(); - }, [openMFAModal, location]); - return (
-

{location?.address}

+
); }; + +type AssignedIpProps = { + // comma-separated list of addresses + address: string; +}; + +const AssignedIp = ({ address }: AssignedIpProps) => { + // split into separate addreses to show in tooltip + const addresses = useMemo(() => address.split(','), [address]); + + return ( + + +
+

{address}

+
+
+ +
    + {addresses.map((d) => ( +
  • {d}
  • + ))} +
+
+
+ ); +}; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardInfo/style.scss b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardInfo/style.scss index 4dc78022..e3b77e15 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardInfo/style.scss +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardInfo/style.scss @@ -23,4 +23,32 @@ color: var(--text-body-primary); text-align: left; } + + & > .assigned-ip-container { + cursor: help; + + & > label { + text-align: right; + } + + & > p { + @include typography(app-button-xl); + + color: var(--text-body-primary); + text-align: right; + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + } + } +} + +.location-card-info-ip { + justify-items: flex-end; + justify-content: flex-end; + align-items: flex-end; + + & > label { + text-align: right; + } } diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardRoute/LocationCardRoute.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardRoute/LocationCardRoute.tsx index 05ca97f6..61cc8032 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardRoute/LocationCardRoute.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardRoute/LocationCardRoute.tsx @@ -1,13 +1,13 @@ import './style.scss'; +import { error } from '@tauri-apps/plugin-log'; import { useMemo } from 'react'; -import { error } from 'tauri-plugin-log-api'; import { useI18nContext } from '../../../../../../../../i18n/i18n-react'; import { Toggle } from '../../../../../../../../shared/defguard-ui/components/Layout/Toggle/Toggle'; -import { ToggleOption } from '../../../../../../../../shared/defguard-ui/components/Layout/Toggle/types'; +import type { ToggleOption } from '../../../../../../../../shared/defguard-ui/components/Layout/Toggle/types'; import { clientApi } from '../../../../../../clientAPI/clientApi'; -import { CommonWireguardFields, DefguardInstance } from '../../../../../../types'; +import type { CommonWireguardFields, DefguardInstance } from '../../../../../../types'; type Props = { location?: CommonWireguardFields; @@ -18,7 +18,7 @@ const { updateLocationRouting } = clientApi; export const LocationCardRoute = ({ location, selectedDefguardInstance }: Props) => { const handleChange = async (value: boolean) => { try { - if (location && location.connection_type) { + if (location?.connection_type) { await updateLocationRouting({ locationId: location?.id, connectionType: location.connection_type, diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/LocationCardTitle.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/LocationCardTitle.tsx index e2ca231b..2610ceb2 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/LocationCardTitle.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/LocationCardTitle.tsx @@ -1,11 +1,15 @@ import './style.scss'; import classNames from 'classnames'; +import { useMemo } from 'react'; import { Badge } from '../../../../../../../../shared/defguard-ui/components/Layout/Badge/Badge'; import { BadgeStyleVariant } from '../../../../../../../../shared/defguard-ui/components/Layout/Badge/types'; +import { FloatingMenu } from '../../../../../../../../shared/defguard-ui/components/Layout/FloatingMenu/FloatingMenu'; +import { FloatingMenuProvider } from '../../../../../../../../shared/defguard-ui/components/Layout/FloatingMenu/FloatingMenuProvider'; +import { FloatingMenuTrigger } from '../../../../../../../../shared/defguard-ui/components/Layout/FloatingMenu/FloatingMenuTrigger'; import SvgIconConnection from '../../../../../../../../shared/defguard-ui/components/svg/IconConnection'; -import { CommonWireguardFields } from '../../../../../../types'; +import type { CommonWireguardFields } from '../../../../../../types'; type Props = { location?: CommonWireguardFields; @@ -19,7 +23,40 @@ export const LocationCardTitle = ({ location }: Props) => {
{location?.name} - + +
); }; + +type AddressBadgeProps = { + // comma-separated list of addresses + address: string; +}; + +const AddressBadge = ({ address }: AddressBadgeProps) => { + // split into separate addreses to show in tooltip + const addresses = useMemo(() => address.split(','), [address]); + + return ( + + +
+ +
+
+ +
    + {addresses.map((d) => ( +
  • {d}
  • + ))} +
+
+
+ ); +}; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/style.scss b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/style.scss index abdd34ef..272e63f7 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/style.scss +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationCardTitle/style.scss @@ -30,4 +30,34 @@ } } } + + & > .addresses-badge-container { + cursor: help; + + & > .client-addresses { + max-width: 90px; + + & > span { + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} + +.list-addresses-floating { + list-style: none; + display: flex; + flex-flow: column; + row-gap: var(--spacing-xs); + max-height: 250px; + max-width: 100dvh; + overflow: auto; + padding: var(--spacing-xs); + margin: 0; + + li { + @include typography(app-modal-1); + color: var(--text-body-secondary); + } } diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/LocationsDetailView.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/LocationsDetailView.tsx index 709a6860..54dfc3fe 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/LocationsDetailView.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/LocationsDetailView.tsx @@ -7,15 +7,15 @@ import { useNavigate } from 'react-router-dom'; import { shallow } from 'zustand/shallow'; import { CardTabs } from '../../../../../../../../shared/defguard-ui/components/Layout/CardTabs/CardTabs'; -import { CardTabsData } from '../../../../../../../../shared/defguard-ui/components/Layout/CardTabs/types'; +import type { CardTabsData } from '../../../../../../../../shared/defguard-ui/components/Layout/CardTabs/types'; import { routes } from '../../../../../../../../shared/routes'; import { clientApi } from '../../../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../../../hooks/useClientStore'; import { clientQueryKeys } from '../../../../../../query'; import { - CommonWireguardFields, - DefguardInstance, - WireguardInstanceType, + type CommonWireguardFields, + type DefguardInstance, + ClientConnectionType, } from '../../../../../../types'; import { LocationConnectionHistory } from './components/LocationConnectionHistory/LocationConnectionHistory'; import { LocationDetailCard } from './components/LocationDetailCard/LocationDetailCard'; @@ -23,7 +23,7 @@ import { LocationDetails } from './components/LocationDetails/LocationDetails'; type Props = { locations: CommonWireguardFields[]; - connectionType?: WireguardInstanceType; + connectionType?: ClientConnectionType; selectedDefguardInstance?: DefguardInstance; }; @@ -36,7 +36,7 @@ const { getTunnels } = clientApi; export const LocationsDetailView = ({ locations, - connectionType = WireguardInstanceType.DEFGUARD_INSTANCE, + connectionType = ClientConnectionType.LOCATION, selectedDefguardInstance, }: Props) => { const selectedInstance = useClientStore((state) => state.selectedInstance); @@ -80,7 +80,7 @@ export const LocationsDetailView = ({ queryKey: [clientQueryKeys.getTunnels], queryFn: () => getTunnels(), enabled: !!( - selectedInstance?.id && selectedInstance?.type === WireguardInstanceType.TUNNEL + selectedInstance?.id && selectedInstance?.type === ClientConnectionType.TUNNEL ), }); @@ -97,7 +97,7 @@ export const LocationsDetailView = ({ return (
- {connectionType === WireguardInstanceType.DEFGUARD_INSTANCE && ( + {connectionType === ClientConnectionType.LOCATION && ( <> {activeLocation && ( @@ -122,7 +122,7 @@ export const LocationsDetailView = ({ )} )} - {connectionType === WireguardInstanceType.TUNNEL && ( + {connectionType === ClientConnectionType.TUNNEL && ( <> {tunnel && } {tunnel && ( diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationConnectionHistory.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationConnectionHistory.tsx index 5e5b9801..3e547527 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationConnectionHistory.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationConnectionHistory.tsx @@ -6,13 +6,16 @@ import { useI18nContext } from '../../../../../../../../../../i18n/i18n-react'; import { Card } from '../../../../../../../../../../shared/defguard-ui/components/Layout/Card/Card'; import { clientApi } from '../../../../../../../../clientAPI/clientApi'; import { clientQueryKeys } from '../../../../../../../../query'; -import { DefguardLocation, WireguardInstanceType } from '../../../../../../../../types'; +import type { + DefguardLocation, + ClientConnectionType, +} from '../../../../../../../../types'; import { LocationCardNeverConnected } from '../../../LocationCardNeverConnected/LocationCardNeverConnected'; import { LocationHistoryTable } from './LocationHistoryTable/LocationHistoryTable'; type Props = { locationId: DefguardLocation['id']; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; connected: boolean; }; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationHistoryTable/LocationHistoryTable.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationHistoryTable/LocationHistoryTable.tsx index bb9aeea0..81f717cd 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationHistoryTable/LocationHistoryTable.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationConnectionHistory/LocationHistoryTable/LocationHistoryTable.tsx @@ -2,16 +2,16 @@ import byteSize from 'byte-size'; import classNames from 'classnames'; import dayjs from 'dayjs'; import { floor, isUndefined } from 'lodash-es'; -import { ReactNode, useCallback, useEffect, useMemo, useRef } from 'react'; +import { type ReactNode, useCallback, useEffect, useMemo, useRef } from 'react'; import { useI18nContext } from '../../../../../../../../../../../i18n/i18n-react'; import { - ListHeader, - ListRowCell, + type ListHeader, + type ListRowCell, ListSortDirection, } from '../../../../../../../../../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; import { VirtualizedList } from '../../../../../../../../../../../shared/defguard-ui/components/Layout/VirtualizedList/VirtualizedList'; -import { Connection } from '../../../../../../../../../types'; +import type { Connection } from '../../../../../../../../../types'; type Props = { connections: Connection[]; @@ -35,6 +35,7 @@ export const LocationHistoryTable = ({ connections }: Props) => { const { LL } = useI18nContext(); const pageLL = LL.pages.client.pages.instancePage.detailView.history.headers; const connectionsLength = useRef(0); + // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater const listHeaders = useMemo((): ListHeader[] => { return [ { diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetailCard/LocationDetailCard.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetailCard/LocationDetailCard.tsx index 647d5d3a..45bdf5f5 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetailCard/LocationDetailCard.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetailCard/LocationDetailCard.tsx @@ -15,7 +15,10 @@ import { getStatsFilterValue } from '../../../../../../../../../../shared/utils/ import { clientApi } from '../../../../../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../../../../../hooks/useClientStore'; import { clientQueryKeys } from '../../../../../../../../query'; -import { CommonWireguardFields, DefguardInstance } from '../../../../../../../../types'; +import type { + CommonWireguardFields, + DefguardInstance, +} from '../../../../../../../../types'; import { LocationUsageChart } from '../../../../../LocationUsageChart/LocationUsageChart'; import { LocationUsageChartType } from '../../../../../LocationUsageChart/types'; import { LocationCardConnectButton } from '../../../LocationCardConnectButton/LocationCardConnectButton'; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetails/LocationDetails.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetails/LocationDetails.tsx index 3c7f9abb..94c33000 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetails/LocationDetails.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationDetails/LocationDetails.tsx @@ -10,12 +10,15 @@ import { Divider } from '../../../../../../../../../../shared/defguard-ui/compon import { Label } from '../../../../../../../../../../shared/defguard-ui/components/Layout/Label/Label'; import { clientApi } from '../../../../../../../../clientAPI/clientApi'; import { clientQueryKeys } from '../../../../../../../../query'; -import { DefguardLocation, WireguardInstanceType } from '../../../../../../../../types'; +import type { + DefguardLocation, + ClientConnectionType, +} from '../../../../../../../../types'; import { LocationLogs } from '../LocationLogs/LocationLogs'; type Props = { locationId: DefguardLocation['id']; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; }; const { getLocationDetails } = clientApi; @@ -90,14 +93,18 @@ const InfoSection = memo(({ locationId, connectionType }: Props) => {
- {data && data.allowed_ips.split(',').map((ip) =>

{ip}

)} + {data?.allowed_ips.split(',').map((ip) => ( +

{ip}

+ ))}

- {data && data.dns && data.dns.split(',').map((d) =>

{d}

)} + {data?.dns?.split(',').map((d) => ( +

{d}

+ ))}

diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogs.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogs.tsx index a137617c..94fd2f88 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogs.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogs.tsx @@ -1,24 +1,26 @@ import './style.scss'; -import { clipboard } from '@tauri-apps/api'; -import { save } from '@tauri-apps/api/dialog'; -import { listen, UnlistenFn } from '@tauri-apps/api/event'; -import { writeTextFile } from '@tauri-apps/api/fs'; +import { listen, type UnlistenFn } from '@tauri-apps/api/event'; +import * as clipboard from '@tauri-apps/plugin-clipboard-manager'; +import { save } from '@tauri-apps/plugin-dialog'; +import { writeTextFile } from '@tauri-apps/plugin-fs'; import { isUndefined } from 'lodash-es'; import { useCallback, useEffect, useRef } from 'react'; - import { useI18nContext } from '../../../../../../../../../../i18n/i18n-react'; import { ActionButton } from '../../../../../../../../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; import { ActionButtonVariant } from '../../../../../../../../../../shared/defguard-ui/components/Layout/ActionButton/types'; import { Card } from '../../../../../../../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { LogItem, LogLevel } from '../../../../../../../../clientAPI/types'; +import type { LogItem, LogLevel } from '../../../../../../../../clientAPI/types'; import { useClientStore } from '../../../../../../../../hooks/useClientStore'; -import { DefguardLocation, WireguardInstanceType } from '../../../../../../../../types'; +import type { + DefguardLocation, + ClientConnectionType, +} from '../../../../../../../../types'; import { LocationLogsSelect } from './LocationLogsSelect'; type Props = { locationId: DefguardLocation['id']; - connectionType: WireguardInstanceType; + connectionType: ClientConnectionType; }; export const LocationLogs = ({ locationId, connectionType }: Props) => { @@ -45,7 +47,7 @@ export const LocationLogs = ({ locationId, connectionType }: Props) => { // Clear logs when the component is unmounted or locationId changes useEffect(() => { return () => clearLogs(); - }, [clearLogs, locationId]); + }, [clearLogs]); // Listen to new logs useEffect(() => { @@ -64,7 +66,7 @@ export const LocationLogs = ({ locationId, connectionType }: Props) => { const element = createLogLineElement(messageString); const scrollAfterAppend = logsContainerElement.current.scrollHeight - - logsContainerElement.current.scrollTop === + logsContainerElement.current.scrollTop === logsContainerElement.current.clientHeight; logsContainerElement.current.appendChild(element); // auto scroll to bottom if user didn't scroll up @@ -87,14 +89,14 @@ export const LocationLogs = ({ locationId, connectionType }: Props) => { eventUnlisten?.(); }; //eslint-disable-next-line - }, [locationId]); + }, [locationId, connectionType]); const getAllLogs = () => { let logs = ''; if (logsContainerElement) { logsContainerElement.current?.childNodes.forEach((item) => { - logs += item.textContent + '\n'; + logs += `${item.textContent}\n`; }); } diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogsSelect.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogsSelect.tsx index d60ec352..4374930b 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogsSelect.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsDetailView/components/LocationLogs/LocationLogsSelect.tsx @@ -3,11 +3,11 @@ import { useCallback, useMemo, useState } from 'react'; import { useI18nContext } from '../../../../../../../../../../i18n/i18n-react'; import { Select } from '../../../../../../../../../../shared/defguard-ui/components/Layout/Select/Select'; import { - SelectOption, - SelectSelectedValue, + type SelectOption, + type SelectSelectedValue, SelectSizeVariant, } from '../../../../../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import { LogLevel } from '../../../../../../../../clientAPI/types'; +import type { LogLevel } from '../../../../../../../../clientAPI/types'; type Props = { initSelected: LogLevel; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsGridView/LocationsGridView.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsGridView/LocationsGridView.tsx index f5347319..926e94dc 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsGridView/LocationsGridView.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/components/LocationsGridView/LocationsGridView.tsx @@ -12,10 +12,10 @@ import { getStatsFilterValue } from '../../../../../../../../shared/utils/getSta import { clientApi } from '../../../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../../../hooks/useClientStore'; import { clientQueryKeys } from '../../../../../../query'; -import { +import type { CommonWireguardFields, DefguardInstance, - WireguardInstanceType, + ClientConnectionType, } from '../../../../../../types'; import { LocationUsageChart } from '../../../LocationUsageChart/LocationUsageChart'; import { LocationUsageChartType } from '../../../LocationUsageChart/types'; @@ -86,7 +86,7 @@ const GridItem = ({ location, selectedDefguardInstance }: GridItemProps) => { queryFn: () => getLocationStats({ locationId: location.id as number, - connectionType: location.connection_type as WireguardInstanceType, + connectionType: location.connection_type as ClientConnectionType, from: getStatsFilterValue(statsFilter), }), enabled: !!location.id, diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/MFAModal.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/MFAModal.tsx index b8aaca7d..032e03b0 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/MFAModal.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/MFAModal.tsx @@ -2,13 +2,13 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation } from '@tanstack/react-query'; -import { Body, fetch } from '@tauri-apps/api/http'; +import { fetch } from '@tauri-apps/plugin-http'; +import { error } from '@tauri-apps/plugin-log'; import { isUndefined } from 'lodash-es'; import { useCallback, useEffect, useMemo, useState } from 'react'; import AuthCode from 'react-auth-code-input'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import ReactMarkdown from 'react-markdown'; -import { error } from 'tauri-plugin-log-api'; import { z } from 'zod'; import { shallow } from 'zustand/shallow'; @@ -23,9 +23,11 @@ import { MessageBox } from '../../../../../../../../shared/defguard-ui/component import { MessageBoxType } from '../../../../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; import { ModalWithTitle } from '../../../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; import { useToaster } from '../../../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import { isPresent } from '../../../../../../../../shared/defguard-ui/utils/isPresent'; import { clientApi } from '../../../../../../clientAPI/clientApi'; import { useClientStore } from '../../../../../../hooks/useClientStore'; -import { DefguardInstance, WireguardInstanceType } from '../../../../../../types'; +import { type DefguardInstance, LocationMfaType } from '../../../../../../types'; +import { MfaMobileApprove } from './components/MfaMobileApprove/MfaMobileApprove'; import { BrowserErrorIcon, BrowserPendingIcon, GoToBrowserIcon } from './Icons'; import { useMFAModal } from './useMFAModal'; @@ -48,6 +50,7 @@ const defaultValues: FormFields = { type MFAStartResponse = { token: string; + challenge?: string; }; type Screen = @@ -56,132 +59,132 @@ type Screen = | 'email' | 'openid_login' | 'openid_pending' - | 'openid_unavailable'; + | 'openid_unavailable' + | 'mobile_approve'; export const MFAModal = () => { const { LL } = useI18nContext(); const toaster = useToaster(); - const [authMethod, setAuthMethod] = useState<0 | 1 | 2>(0); + const [authMethod, setAuthMethod] = useState(0); const [screen, setScreen] = useState('start'); - const [mfaToken, setMFAToken] = useState(''); const [proxyUrl, setProxyUrl] = useState(''); + const [startResponse, setStartResponse] = useState(); const localLL = LL.modals.mfa.authentication; - const isOpen = useMFAModal((state) => state.isOpen); - const location = useMFAModal((state) => state.instance); + const [isOpen, location] = useMFAModal((state) => [state.isOpen, state.instance]); const [close, reset] = useMFAModal((state) => [state.close, state.reset], shallow); - const [selectedInstanceId, selectedInstanceType] = useClientStore((state) => [ - state.selectedInstance?.id, - state.selectedInstance?.type, - ]); const instances = useClientStore((state) => state.instances); const selectedInstance = useMemo((): DefguardInstance | undefined => { - if ( - !isUndefined(selectedInstanceId) && - selectedInstanceType && - selectedInstanceType === WireguardInstanceType.DEFGUARD_INSTANCE - ) { - return instances.find((i) => i.id === selectedInstanceId); + const instanceId = location?.instance_id; + if (!isUndefined(instanceId)) { + return instances.find((i) => i.id === instanceId); } - }, [selectedInstanceId, selectedInstanceType, instances]); + }, [location, instances]); const resetState = () => { reset(); setScreen('start'); - setMFAToken(''); + setStartResponse(undefined); }; const resetAuthState = () => { setScreen('start'); - setMFAToken(''); + setStartResponse(undefined); }; - // selectedMethod: 0 = authenticator app, 1 = email, 2 = OpenID - const startMFA = async (selectedMethod: number) => { - if (!location) return toaster.error(localLL.errors.locationNotSpecified()); + // selectedMethod: 0 = authenticator app, 1 = email, 2 = OpenID, 3 = MobileApprove + const startMFA = useCallback( + async (method: number) => { + if (!location) return toaster.error(localLL.errors.locationNotSpecified()); - if (!selectedInstance) { - return toaster.error(localLL.errors.instanceNotFound()); - } + if (!selectedInstance) { + return toaster.error(localLL.errors.instanceNotFound()); + } - setProxyUrl(selectedInstance.proxy_url); - const mfaStartUrl = selectedInstance.proxy_url + CLIENT_MFA_ENDPOINT + '/start'; + setProxyUrl(selectedInstance.proxy_url); + const mfaStartUrl = `${selectedInstance.proxy_url + CLIENT_MFA_ENDPOINT}/start`; - const data = { - method: selectedMethod, - pubkey: selectedInstance.pubkey, - location_id: location.network_id, - }; + const data = { + method, + pubkey: selectedInstance.pubkey, + location_id: location.network_id, + }; - const response = await fetch(mfaStartUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: Body.json(data), - }); + const response = await fetch(mfaStartUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); - if (response.ok) { - const { token } = response.data; - - switch (selectedMethod) { - case 0: - setScreen('authenticator_app'); - break; - case 1: - setScreen('email'); - break; - case 2: - setScreen('openid_login'); - break; - default: - toaster.error(localLL.errors.mfaStartGeneric()); + if (response.ok) { + const data = (await response.json()) as MFAStartResponse; + + switch (method) { + case 0: + setScreen('authenticator_app'); + break; + case 1: + setScreen('email'); + break; + case 2: + setScreen('openid_login'); + break; + case 4: + // just to be safe + if (!isPresent(data.challenge)) { + toaster.error('Unsupported response from proxy'); + } + setScreen('mobile_approve'); + break; + default: + toaster.error(localLL.errors.mfaStartGeneric()); + return; + } + setStartResponse(data); + return data; + } else { + const errorData = ((await response.json()) as unknown as MFAError).error; + error(`MFA failed to start with the following error: ${errorData}`); + if (method === 2) { + setScreen('openid_unavailable'); return; - } - setMFAToken(token); + } - return response.data; - } else { - const errorData = (response.data as unknown as MFAError).error; - error('MFA failed to start with the following error: ' + errorData); - if (selectedMethod === 2) { - setScreen('openid_unavailable'); - return; - } + if (errorData === 'selected MFA method not available') { + toaster.error(localLL.errors.mfaNotConfigured()); + } else { + toaster.error(localLL.errors.mfaStartGeneric()); + } - if (errorData === 'selected MFA method not available') { - toaster.error(localLL.errors.mfaNotConfigured()); - } else { - toaster.error(localLL.errors.mfaStartGeneric()); + return; } - - return; - } - }; + }, + [ + localLL.errors.instanceNotFound, + localLL.errors.locationNotSpecified, + localLL.errors.mfaNotConfigured, + localLL.errors.mfaStartGeneric, + location, + selectedInstance, + toaster.error, + ], + ); const useOpenIDMFA = useMemo(() => { - return selectedInstance?.use_openid_for_mfa || false; - }, [selectedInstance]); + return location?.location_mfa_mode === LocationMfaType.EXTERNAL; + }, [location]); const { mutate, isPending } = useMutation({ mutationFn: startMFA, }); - const showEmailCodeForm = useCallback(() => { - setAuthMethod(1); - mutate(1); - }, [mutate]); - - const showAuthenticatorAppCodeForm = useCallback(() => { - setAuthMethod(0); - mutate(0); - }, [mutate]); - - const showOpenIDScreen = useCallback(() => { - setAuthMethod(2); - mutate(2); - }, [mutate]); + const handleMfaStart = (method: number) => { + setAuthMethod(method); + mutate(method); + }; return ( { afterClose={resetState} > {useOpenIDMFA && screen === 'start' && ( - + { + handleMfaStart(2); + }} + /> )} {useOpenIDMFA && screen === 'openid_unavailable' && ( @@ -201,39 +209,51 @@ export const MFAModal = () => { )} - {screen === 'openid_login' && ( + {screen === 'openid_login' && isPresent(startResponse) && ( )} - {screen === 'openid_pending' && ( + {screen === 'openid_pending' && isPresent(startResponse) && ( - )} - {(screen === 'authenticator_app' || screen === 'email') && ( - )} + {(screen === 'authenticator_app' || screen === 'email') && + isPresent(startResponse) && ( + + )} + {screen === 'mobile_approve' && + isPresent(startResponse) && + isPresent(selectedInstance) && ( + + )} ); }; @@ -241,9 +261,7 @@ export const MFAModal = () => { type MFAStartProps = { isPending: boolean; authMethod: number; - showAuthenticatorAppCodeForm: () => void; - showEmailCodeForm: () => void; - showOpenIDScreen: () => void; + startMfa: (method: number) => void; }; const OpenIDMFAUnavailable = ({ resetState }: { resetState: () => void }) => { @@ -289,12 +307,7 @@ const OpenIDMFAStart = ({ ); }; -const MFAStart = ({ - isPending, - authMethod, - showAuthenticatorAppCodeForm, - showEmailCodeForm, -}: MFAStartProps) => { +const MFAStart = ({ isPending, authMethod, startMfa }: MFAStartProps) => { const { LL } = useI18nContext(); const localLL = LL.modals.mfa.authentication; @@ -314,16 +327,32 @@ const MFAStart = ({ size={ButtonSize.LARGE} loading={isAuthenticatorAppPending} styleVariant={ButtonStyleVariant.STANDARD} + // biome-ignore lint/correctness/useHookAtTopLevel: not a hook text={isAuthenticatorAppPending ? '' : localLL.useAuthenticatorApp()} - onClick={showAuthenticatorAppCodeForm} + onClick={() => { + startMfa(0); + }} />
@@ -375,7 +404,7 @@ const OpenIDMFALogin = ({ styleVariant={ButtonStyleVariant.PRIMARY} text={localLL.openidLogin.buttonText({ provider: displayName })} onClick={() => { - const link = proxyUrl + 'openid/mfa?token=' + token; + const link = `${proxyUrl}openid/mfa?token=${token}`; openLink(link); setScreen('openid_pending'); }} @@ -401,7 +430,6 @@ const OpenIDMFAPending = ({ proxyUrl, token, resetState }: OpenIDMFAPendingProps useEffect(() => { const TIMEOUT_DURATION = 5 * 1000 * 60; // 5 minutes timeout - // eslint-disable-next-line prefer-const let timeoutId: NodeJS.Timeout; const pollMFAStatus = async () => { @@ -411,27 +439,24 @@ const OpenIDMFAPending = ({ proxyUrl, token, resetState }: OpenIDMFAPendingProps return; } - const data = { token }; - const response = await fetch( - proxyUrl + CLIENT_MFA_ENDPOINT + '/finish', - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: Body.json(data), + const body_token = { token }; + const response = await fetch(`${proxyUrl + CLIENT_MFA_ENDPOINT}/finish`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', }, - ); + body: JSON.stringify(body_token), + }); if (response.ok) { clearInterval(interval); clearTimeout(timeoutId); closeModal(); - + const data = (await response.json()) as MFAFinishResponse; await connect({ locationId: location?.id, connectionType: location.connection_type, - presharedKey: response.data.preshared_key, + presharedKey: data.preshared_key, }); return; } @@ -444,16 +469,17 @@ const OpenIDMFAPending = ({ proxyUrl, token, resetState }: OpenIDMFAPendingProps // Other errors: stop polling and handle clearInterval(interval); clearTimeout(timeoutId); - const { error: errorMessage } = response.data as unknown as MFAError; + const data = (await response.json()) as unknown as MFAError; + const { error: errorMessage } = data; if (errorMessage === 'invalid token') { - error(JSON.stringify(response.data, null, 2)); + error(JSON.stringify(data, null, 2)); setErrorMessage(localLL.errors.tokenExpired()); } else if (errorMessage === 'login session not found') { - error(JSON.stringify(response.data, null, 2)); + error(JSON.stringify(data, null, 2)); setErrorMessage(localLL.errors.sessionInvalidated()); } else { - error(JSON.stringify(response.data, null, 2)); + error(JSON.stringify(data, null, 2)); setErrorMessage(localLL.errors.mfaStartGeneric()); } }; @@ -471,7 +497,7 @@ const OpenIDMFAPending = ({ proxyUrl, token, resetState }: OpenIDMFAPendingProps clearInterval(interval); clearTimeout(timeoutId); }; - }, [proxyUrl, token, location, closeModal, resetState, localLL.errors, toaster]); + }, [proxyUrl, token, location, closeModal, localLL.errors, toaster]); return (
@@ -534,27 +560,26 @@ const MFACodeForm = ({ description, token, proxyUrl, resetState }: MFACodeForm) const data = { token, code: code }; - const response = await fetch( - proxyUrl + CLIENT_MFA_ENDPOINT + '/finish', - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: Body.json(data), + const response = await fetch(`${proxyUrl + CLIENT_MFA_ENDPOINT}/finish`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', }, - ); + body: JSON.stringify(data), + }); if (response.ok) { closeModal(); - + const data = (await response.json()) as MFAFinishResponse; + error(`ARSE ${location.connection_type}`); await connect({ locationId: location?.id, connectionType: location.connection_type, - presharedKey: response.data.preshared_key, + presharedKey: data.preshared_key, }); } else { - const { error: errorMessage } = response.data as unknown as MFAError; + const data = (await response.json()) as unknown as MFAError; + const { error: errorMessage } = data; let message = ''; if (errorMessage === 'Unauthorized') { @@ -563,17 +588,17 @@ const MFACodeForm = ({ description, token, proxyUrl, resetState }: MFACodeForm) errorMessage === 'invalid token' || errorMessage === 'login session not found' ) { - console.error(response.data); + console.error(data); toaster.error(localLL.errors.tokenExpired()); resetState(); - error(JSON.stringify(response.data)); + error(JSON.stringify(data)); return; } else { toaster.error(localLL.errors.mfaStartGeneric()); } setMFAError(message); - error(JSON.stringify(response.data)); + error(JSON.stringify(data)); return; } }; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/components/MfaMobileApprove/MfaMobileApprove.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/components/MfaMobileApprove/MfaMobileApprove.tsx new file mode 100644 index 00000000..e4870037 --- /dev/null +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/components/MfaMobileApprove/MfaMobileApprove.tsx @@ -0,0 +1,193 @@ +import { fromUint8Array } from 'js-base64'; +import { useEffect, useMemo } from 'react'; +import QrCode from 'react-qr-code'; +import useWebSocket from 'react-use-websocket'; +import z from 'zod'; +import { shallow } from 'zustand/shallow'; +import { useToaster } from '../../../../../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import { clientApi } from '../../../../../../../../clientAPI/clientApi'; +import type { CommonWireguardFields } from '../../../../../../../../types'; +import { useMFAModal } from '../../useMFAModal'; +import './style.scss'; +import { debug, error } from '@tauri-apps/plugin-log'; +import { Button } from '../../../../../../../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { MessageBox } from '../../../../../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; + +type MfaMobileQrData = { + token: string; + challenge: string; +}; + +type Props = { + data: MfaMobileQrData; + proxyUrl: string; + instanceUuid: string; + onCancel: () => void; +}; + +const { connect } = clientApi; + +const wsResponseSchema = z.object({ + type: z.string().min(1), + preshared_key: z.string().min(1), +}); + +export const MfaMobileApprove = ({ + data: { challenge, token }, + proxyUrl, + instanceUuid, + onCancel, +}: Props) => { + const toaster = useToaster(); + const [closeModal] = useMFAModal((s) => [s.close], shallow); + const location = useMFAModal((s) => s.instance as CommonWireguardFields); + + const wsUrl = useMemo( + () => + `${proxyUrl.replace('http', 'ws').replace('https', 'wss')}api/v1/client-mfa/remote`, + [proxyUrl], + ); + + const { lastMessage } = useWebSocket(wsUrl, { + queryParams: { + token, + }, + onClose: () => { + debug('WebSocket connection to proxy for mobile app MFA closed.'); + }, + onError: () => { + toaster.error('Unexpected error in WebSocket connection to proxy'); + error( + 'MFA auth using mobile app failed. Unexpected error in WebSocket connection to proxy.', + ); + // go back to previous step + onCancel(); + }, + }); + + const qrString = useMemo(() => { + const data = { + token, + challenge, + instance_id: instanceUuid, + }; + const jsonString = JSON.stringify(data); + const textEncoder = new TextEncoder(); + const encoded = textEncoder.encode(jsonString); + return fromUint8Array(encoded); + }, [token, challenge, instanceUuid]); + + // biome-ignore lint/correctness/useExhaustiveDependencies: Side effect + useEffect(() => { + if (lastMessage != null) { + const parsed = JSON.parse(lastMessage.data); + const schemaResult = wsResponseSchema.safeParse(parsed); + if (schemaResult.success) { + connect({ + connectionType: location.connection_type, + locationId: location.id, + presharedKey: schemaResult.data.preshared_key, + }) + .then(() => { + closeModal(); + toaster.success('Connection authorized.'); + }) + .catch((e) => { + console.error(e); + }); + } else { + // catch possible changes in api + toaster.error('Unknown response from proxy'); + } + } + }, [lastMessage]); + + return ( +
+ +

+ + {'Go to the mobile app, select this instance and click the Biometry button'} + + + {'in the botom right corner.'} +

+
+ +
+ ); +}; + +const ExampleButton = () => { + return ( +
+ +
+ ); +}; + +const QrIcon = () => { + return ( + + + + + + + + + + + + + + + + ); +}; diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/components/MfaMobileApprove/style.scss b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/components/MfaMobileApprove/style.scss new file mode 100644 index 00000000..82ebca66 --- /dev/null +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/components/MfaMobileApprove/style.scss @@ -0,0 +1,29 @@ +#mobile-approve-mfa { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + box-sizing: border-box; + padding: var(--spacing-m) var(--spacing-s); + gap: var(--spacing-m); + + .message-box { + p { + display: inline-flex; + flex-flow: row wrap; + column-gap: 10px; + align-items: center; + } + } + + .example-mobile-button { + border-radius: 15px; + background-color: var(--surface-main-primary); + display: inline-flex; + flex-flow: row nowrap; + align-items: center; + justify-content: center; + width: 42px; + height: 42px; + } +} diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/useMFAModal.ts b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/useMFAModal.ts index 49449d90..0947f745 100644 --- a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/useMFAModal.ts +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/useMFAModal.ts @@ -1,6 +1,6 @@ import { createWithEqualityFn } from 'zustand/traditional'; -import { CommonWireguardFields } from '../../../../../../types'; +import type { CommonWireguardFields } from '../../../../../../types'; const defaultValues: StoreValues = { isOpen: false, diff --git a/src/pages/client/pages/ClientInstancePage/components/StatsFilterSelect/StatsFilterSelect.tsx b/src/pages/client/pages/ClientInstancePage/components/StatsFilterSelect/StatsFilterSelect.tsx index 6093aef3..bb82b9dc 100644 --- a/src/pages/client/pages/ClientInstancePage/components/StatsFilterSelect/StatsFilterSelect.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/StatsFilterSelect/StatsFilterSelect.tsx @@ -2,7 +2,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; import { Select } from '../../../../../../shared/defguard-ui/components/Layout/Select/Select'; -import { +import type { SelectOption, SelectSelectedValue, } from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; diff --git a/src/pages/client/pages/ClientInstancePage/components/StatsLayoutSelect/StatsLayoutSelect.tsx b/src/pages/client/pages/ClientInstancePage/components/StatsLayoutSelect/StatsLayoutSelect.tsx index f3e333ba..89ae5d83 100644 --- a/src/pages/client/pages/ClientInstancePage/components/StatsLayoutSelect/StatsLayoutSelect.tsx +++ b/src/pages/client/pages/ClientInstancePage/components/StatsLayoutSelect/StatsLayoutSelect.tsx @@ -3,14 +3,14 @@ import { shallow } from 'zustand/shallow'; import { useI18nContext } from '../../../../../../i18n/i18n-react'; import { Select } from '../../../../../../shared/defguard-ui/components/Layout/Select/Select'; -import { +import type { SelectOption, SelectProps, SelectSelectedValue, } from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import { ClientView } from '../../../../clientAPI/types'; +import type { ClientView } from '../../../../clientAPI/types'; import { useClientStore } from '../../../../hooks/useClientStore'; -import { CommonWireguardFields } from '../../../../types'; +import type { CommonWireguardFields } from '../../../../types'; interface StatsLayoutSelect { locations: CommonWireguardFields[] | undefined; @@ -42,8 +42,8 @@ export const StatsLayoutSelect = ({ locations }: StatsLayoutSelect) => { key: selected.key, displayValue: selected.label, }; - } else if (selected == null && locations != undefined) { - if (locations.length == 1) { + } else if (selected == null && locations !== undefined) { + if (locations.length === 1) { return { key: 'detail', displayValue: localLL.header.filters.views.detail(), diff --git a/src/pages/client/pages/ClientInstancePage/modals/DeleteInstanceModal/DeleteInstanceModal.tsx b/src/pages/client/pages/ClientInstancePage/modals/DeleteInstanceModal/DeleteInstanceModal.tsx index 94feaadc..f743919c 100644 --- a/src/pages/client/pages/ClientInstancePage/modals/DeleteInstanceModal/DeleteInstanceModal.tsx +++ b/src/pages/client/pages/ClientInstancePage/modals/DeleteInstanceModal/DeleteInstanceModal.tsx @@ -67,7 +67,7 @@ export const DeleteInstanceModal = () => { useEffect(() => { reset(); // eslint-disable-next-line - }, []); + }, [reset]); return ( { ); const isDeleteOpen = useDeleteInstanceModal((state) => state.isOpen); - // reset state on page mount - useEffect(() => { + useEffectOnce(() => { reset(); - // eslint-disable-next-line - }, []); + }); return ( { token: values.token, }; - const res = await fetch(endpointUrl, { + const res = await fetch(endpointUrl, { method: 'POST', headers, - body: Body.json(data), + body: JSON.stringify(data), }); if (res.ok) { - const enrollmentData = res.data; + const enrollmentData = (await res.json()) as EnrollmentStartResponse; let proxy_api_url = values.url; if (proxy_api_url[proxy_api_url.length - 1] === '/') { proxy_api_url = proxy_api_url.slice(0, -1); } - proxy_api_url = proxy_api_url + '/api/v1'; + proxy_api_url = `${proxy_api_url}/api/v1`; const instance = clientInstances.find((i) => i.uuid === enrollmentData.instance.id); if (instance) { - const authCookie = res.rawHeaders['set-cookie'].find((cookie) => - cookie.startsWith('defguard_proxy='), - ); + const authCookie = res.headers + .getSetCookie() + .find((cookie) => cookie.startsWith('defguard_proxy=')); if (!authCookie) { toaster.error( LL.common.messages.errorWithMessage({ @@ -119,21 +119,22 @@ export const UpdateInstanceModalForm = () => { ); return; } - headers['Cookie'] = authCookie; - const instanceInfoResponse = await fetch( + headers.Cookie = authCookie; + const instanceInfoResponse = await fetch( `${proxy_api_url}/enrollment/network_info`, { method: 'POST', headers, - body: Body.json({ + body: JSON.stringify({ pubkey: instance.pubkey, }), }, ); if (instanceInfoResponse.ok) { + const data = (await instanceInfoResponse.json()) as CreateDeviceResponse; updateInstance({ instanceId: instance.id, - response: instanceInfoResponse.data, + response: data, }) .then(() => { invalidateOnSuccess.forEach((k) => { @@ -152,6 +153,20 @@ export const UpdateInstanceModalForm = () => { .catch(() => { toaster.error(LL.common.messages.error()); }); + } else { + // Device does not match used enrollment token. + toaster.error( + LL.common.messages.errorWithMessage({ + message: 'Token is not valid for this device', + }), + ); + setError( + 'token', + { + message: localLL.form.fieldErrors.token.rejected(), + }, + { shouldFocus: true }, + ); } } else { // Instance not found in client, use add instance. diff --git a/src/pages/client/pages/ClientInstancePage/modals/UpdateInstanceModal/useUpdateInstanceModal.ts b/src/pages/client/pages/ClientInstancePage/modals/UpdateInstanceModal/useUpdateInstanceModal.ts index 4f65d8b3..9bdcc3ce 100644 --- a/src/pages/client/pages/ClientInstancePage/modals/UpdateInstanceModal/useUpdateInstanceModal.ts +++ b/src/pages/client/pages/ClientInstancePage/modals/UpdateInstanceModal/useUpdateInstanceModal.ts @@ -1,6 +1,6 @@ import { createWithEqualityFn } from 'zustand/traditional'; -import { DefguardInstance } from '../../../../types'; +import type { DefguardInstance } from '../../../../types'; const defaultValues: StoreValues = { isOpen: false, diff --git a/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogs.tsx b/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogs.tsx index b144b961..c6f14053 100644 --- a/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogs.tsx +++ b/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogs.tsx @@ -1,18 +1,18 @@ import './style.scss'; -import { clipboard } from '@tauri-apps/api'; -import { save } from '@tauri-apps/api/dialog'; -import { listen, UnlistenFn } from '@tauri-apps/api/event'; -import { writeTextFile } from '@tauri-apps/api/fs'; +import { listen, type UnlistenFn } from '@tauri-apps/api/event'; +import * as clipboard from '@tauri-apps/plugin-clipboard-manager'; +import { save } from '@tauri-apps/plugin-dialog'; +import { writeTextFile } from '@tauri-apps/plugin-fs'; +import { error } from '@tauri-apps/plugin-log'; import { useCallback, useEffect, useRef } from 'react'; - import { useI18nContext } from '../../../../../../i18n/i18n-react'; import { ActionButton } from '../../../../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; import { ActionButtonVariant } from '../../../../../../shared/defguard-ui/components/Layout/ActionButton/types'; import { Card } from '../../../../../../shared/defguard-ui/components/Layout/Card/Card'; import { Helper } from '../../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; import { clientApi } from '../../../../clientAPI/clientApi'; -import { +import type { GlobalLogLevel, LogItem, LogLevel, @@ -30,10 +30,20 @@ export const GlobalLogs = () => { const { startGlobalLogWatcher, stopGlobalLogWatcher } = clientApi; const handleLogsDownload = async () => { - const path = await save({}); + const path = await save({ + filters: [ + { + name: 'Logs', + extensions: ['txt', 'log'], + }, + ], + }); + if (path) { const logs = getAllLogs(); await writeTextFile(path, logs); + } else { + error('Failed to save logs! Path was null'); } }; @@ -91,14 +101,14 @@ export const GlobalLogs = () => { eventUnlisten?.(); }; //eslint-disable-next-line - }, []); + }, [startGlobalLogWatcher, stopGlobalLogWatcher]); const getAllLogs = () => { let logs = ''; if (logsContainerElement) { logsContainerElement.current?.childNodes.forEach((item) => { - logs += item.textContent + '\n'; + logs += `${item.textContent}\n`; }); } diff --git a/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSelect.tsx b/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSelect.tsx index 2606f688..b635b12d 100644 --- a/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSelect.tsx +++ b/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSelect.tsx @@ -3,11 +3,11 @@ import { useCallback, useMemo, useState } from 'react'; import { useI18nContext } from '../../../../../../i18n/i18n-react'; import { Select } from '../../../../../../shared/defguard-ui/components/Layout/Select/Select'; import { - SelectOption, - SelectSelectedValue, + type SelectOption, + type SelectSelectedValue, SelectSizeVariant, } from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import { GlobalLogLevel } from '../../../../clientAPI/types'; +import type { GlobalLogLevel } from '../../../../clientAPI/types'; type Props = { initSelected: GlobalLogLevel; diff --git a/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSourceSelect.tsx b/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSourceSelect.tsx index 8af09f9b..04234b07 100644 --- a/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSourceSelect.tsx +++ b/src/pages/client/pages/ClientSettingsPage/components/GlobalLogs/GlobalLogsSourceSelect.tsx @@ -3,11 +3,11 @@ import { useCallback, useMemo, useState } from 'react'; import { useI18nContext } from '../../../../../../i18n/i18n-react'; import { Select } from '../../../../../../shared/defguard-ui/components/Layout/Select/Select'; import { - SelectOption, - SelectSelectedValue, + type SelectOption, + type SelectSelectedValue, SelectSizeVariant, } from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import { LogSource } from '../../../../clientAPI/types'; +import type { LogSource } from '../../../../clientAPI/types'; type Props = { initSelected: LogSource; diff --git a/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx b/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx index 23b5dfe1..4d6dea45 100644 --- a/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx +++ b/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx @@ -4,9 +4,9 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { - SubmitHandler, + type SubmitHandler, + type UseControllerProps, useController, - UseControllerProps, useForm, } from 'react-hook-form'; import { z } from 'zod'; @@ -23,20 +23,20 @@ import { } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; import { Helper } from '../../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; import { - SelectOption, - SelectSelectedValue, + type SelectOption, + type SelectSelectedValue, SelectSizeVariant, } from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; import { availableThemes, - ThemeKey, + type ThemeKey, } from '../../../../../../shared/defguard-ui/hooks/theme/types'; import { - AppConfig, + type AppConfig, availableLogLevels, availableTrayThemes, - LogLevel, - TrayIconTheme, + type LogLevel, + type TrayIconTheme, } from '../../../../clientAPI/types'; import { useClientStore } from '../../../../hooks/useClientStore'; diff --git a/src/pages/client/pages/ClientSettingsPage/style.scss b/src/pages/client/pages/ClientSettingsPage/style.scss index 8cf0ed29..d06da128 100644 --- a/src/pages/client/pages/ClientSettingsPage/style.scss +++ b/src/pages/client/pages/ClientSettingsPage/style.scss @@ -13,10 +13,6 @@ color: var(--text-body-primary); } - path { - fill: var(--surface-icon-secondary); - } - h1, h2, h3 { diff --git a/src/pages/client/types.ts b/src/pages/client/types.ts index 45532a2f..97136f0c 100644 --- a/src/pages/client/types.ts +++ b/src/pages/client/types.ts @@ -8,10 +8,15 @@ export type DefguardInstance = { active: boolean; pubkey: string; disable_all_traffic: boolean; - use_openid_for_mfa: boolean; openid_display_name?: string; }; +export enum LocationMfaType { + DISABLED = 'disabled', + INTERNAL = 'internal', + EXTERNAL = 'external', +} + export type DefguardLocation = { instance_id: number; } & CommonWireguardFields; @@ -58,22 +63,17 @@ export type CommonWireguardFields = { // Connected active: boolean; // Tunnel or Location - connection_type: WireguardInstanceType; + connection_type: ClientConnectionType; // Available in Location only - mfa_enabled: boolean | undefined; + location_mfa_mode?: LocationMfaType; pubkey: string; instance_id: number; network_id: number; }; -export enum WireguardInstanceType { - TUNNEL = 'Tunnel', - DEFGUARD_INSTANCE = 'Instance', -} - export type SelectedInstance = { id?: number; - type: WireguardInstanceType; + type: ClientConnectionType; }; export enum ClientConnectionType { @@ -92,14 +92,7 @@ export type DeadConDroppedPayload = { peer_alive_period: number; }; -export type DeadConReconnectedPayload = { - name: string; - con_type: ClientConnectionType; - peer_alive_period: number; -}; - export enum TauriEventKey { - SINGLE_INSTANCE = 'single-instance', CONNECTION_CHANGED = 'connection-changed', INSTANCE_UPDATE = 'instance-update', LOCATION_UPDATE = 'location-update', @@ -108,4 +101,7 @@ export enum TauriEventKey { DEAD_CONNECTION_DROPPED = 'dead-connection-dropped', DEAD_CONNECTION_RECONNECTED = 'dead-connection-reconnected', APPLICATION_CONFIG_CHANGED = 'application-config-changed', + MFA_TRIGGER = 'mfa-trigger', + VERSION_MISMATCH = 'version-mismatch', + UUID_MISMATCH = 'uuid-mismatch', } diff --git a/src/pages/enrollment/EnrollmentPage.tsx b/src/pages/enrollment/EnrollmentPage.tsx index 142c8ef5..64c472cb 100644 --- a/src/pages/enrollment/EnrollmentPage.tsx +++ b/src/pages/enrollment/EnrollmentPage.tsx @@ -1,9 +1,9 @@ import './style.scss'; +import { debug, error } from '@tauri-apps/plugin-log'; import dayjs from 'dayjs'; -import { ReactNode, useEffect, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; -import { debug, error } from 'tauri-plugin-log-api'; import { useBreakpoint } from 'use-breakpoint'; import { shallow } from 'zustand/shallow'; @@ -24,12 +24,8 @@ import { import { routes } from '../../shared/routes'; import { EnrollmentSideBar } from './components/EnrollmentSideBar/EnrollmentSideBar'; import { EnrollmentStepControls } from './components/EnrollmentStepControls/EnrollmentStepControls'; +import { EnrollmentStepKey, enrollmentSteps, flattenEnrollConf } from './const'; import { useEnrollmentStore } from './hooks/store/useEnrollmentStore'; -import { DataVerificationStep } from './steps/DataVerificationStep/DataVerificationStep'; -import { DeviceStep } from './steps/DeviceStep/DeviceStep'; -import { FinishStep } from './steps/FinishStep/FinishStep'; -import { PasswordStep } from './steps/PasswordStep/PasswordStep'; -import { WelcomeStep } from './steps/WelcomeStep/WelcomeStep'; export const EnrollmentPage = () => { const enrollmentFinished = useRef(false); @@ -38,23 +34,16 @@ export const EnrollmentPage = () => { const { breakpoint } = useBreakpoint(deviceBreakpoints); const sessionEnd = useEnrollmentStore((state) => state.sessionEnd); const currentStep = useEnrollmentStore((state) => state.step); - const stepsMax = useEnrollmentStore((state) => state.stepsMax); const loading = useEnrollmentStore((state) => state.loading); - const [setEnrollmentState, back, reset, nextSubject] = useEnrollmentStore( - (state) => [state.setState, state.perviousStep, state.reset, state.nextSubject], - shallow, - ); + const [back, next] = useEnrollmentStore((state) => [state.back, state.next], shallow); const controlsSize: ButtonSize = breakpoint !== 'desktop' ? ButtonSize.SMALL : ButtonSize.LARGE; - // ensure number of steps is correct - useEffect(() => { - if (stepsMax !== steps.length - 1) { - setEnrollmentState({ stepsMax: steps.length - 1 }); - } - }, [setEnrollmentState, stepsMax]); + const flatConf = useMemo(() => flattenEnrollConf(), []); + + const currentStepConfig = flatConf[currentStep]; useEffect(() => { if (!enrollmentFinished.current) { @@ -64,7 +53,7 @@ export const EnrollmentPage = () => { if (diff > 0) { const timeout = setTimeout(() => { if (!enrollmentFinished.current) { - debug('Enrollment session time ended, navigatig to timeout page.'); + debug('Enrollment session time ended, navigating to timeout page.'); navigate(routes.timeout, { replace: true }); } }, diff); @@ -72,19 +61,19 @@ export const EnrollmentPage = () => { clearTimeout(timeout); }; } else { - debug('Enrollment session time ended, navigatig to timeout page.'); + debug('Enrollment session time ended, navigating to timeout page.'); navigate(routes.timeout, { replace: true }); } } else { - error('Seesion end time not found, navigating to timeout page.'); + error('Session end time not found, navigating to timeout page.'); navigate(routes.timeout, { replace: true }); } } - }, [sessionEnd, navigate, reset]); + }, [sessionEnd, navigate]); useEffect(() => { - enrollmentFinished.current = stepsMax === currentStep; - }, [currentStep, stepsMax]); + enrollmentFinished.current = currentStep === EnrollmentStepKey.FINISH; + }, [currentStep]); return ( @@ -96,7 +85,7 @@ export const EnrollmentPage = () => { size={controlsSize} styleVariant={ButtonStyleVariant.STANDARD} onClick={() => back()} - disabled={(steps[currentStep].backDisabled ?? false) || loading} + disabled={!currentStepConfig?.backEnabled || loading} icon={ { />
- {steps.map((text, index) => ( - + {stepsData.map((step) => ( + + + {isPresent(step.children) && + step.children.map((child) => ( + + ))} + ))}
- {currentStep !== stepsMax && ( + {currentStep !== EnrollmentStepKey.FINISH && ( <> )} - {currentStep === stepsMax && } + {currentStep === EnrollmentStepKey.FINISH && }

- Copyright © 2023{' '} - - teonite + Copyright © {`${dayjs().year()} `} + + defguard

@@ -82,26 +142,24 @@ export const EnrollmentSideBar = () => { }; type StepProps = { - text: LocalizedString; - index: number; + data: SideBarItem; + child?: boolean; }; -const Step = ({ index, text }: StepProps) => { +const Step = ({ data, child = false }: StepProps) => { const currentStep = useEnrollmentStore((state) => state.step); - const active = currentStep === index; - - const cn = classNames('step', { - active, - }); + const active = data.activeKeys.includes(currentStep); return ( -

-

- {index + 1}.{' '} - {text} -

- {active &&
} +
+

{data.label}

+ {active && !isPresent(data.children) &&
}
); }; diff --git a/src/pages/enrollment/components/EnrollmentSideBar/style.scss b/src/pages/enrollment/components/EnrollmentSideBar/style.scss index fa1e5176..16e6d77f 100644 --- a/src/pages/enrollment/components/EnrollmentSideBar/style.scss +++ b/src/pages/enrollment/components/EnrollmentSideBar/style.scss @@ -81,9 +81,11 @@ align-items: center; justify-content: flex-start; box-sizing: border-box; - padding: 62px 0 0 0; position: relative; user-select: none; + flex-grow: 1; + padding: var(--spacing-s) 0; + justify-content: center; & > .step { position: relative; @@ -94,14 +96,17 @@ justify-content: flex-start; color: var(--text-body-tertiary); box-sizing: border-box; - padding: 15px 10px 10px 32px; - min-height: 58px; user-select: none; + padding: var(--spacing-xs) var(--spacing-s); transition-property: color; transition-timing-function: ease-in-out; transition-duration: 250ms; + &.child { + padding-left: calc(var(--spacing-s) * 2); + } + &.active { color: var(--text-body-primary); } @@ -127,7 +132,6 @@ } & > .time-left { - margin-top: auto; box-sizing: border-box; margin-bottom: 20px; padding: 0 40px; diff --git a/src/pages/enrollment/components/EnrollmentStepControls/EnrollmentStepControls.tsx b/src/pages/enrollment/components/EnrollmentStepControls/EnrollmentStepControls.tsx index 85ee8d64..c392faf2 100644 --- a/src/pages/enrollment/components/EnrollmentStepControls/EnrollmentStepControls.tsx +++ b/src/pages/enrollment/components/EnrollmentStepControls/EnrollmentStepControls.tsx @@ -1,7 +1,7 @@ import './style.scss'; import classNames from 'classnames'; -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; import { AdminInfo } from '../AdminInfo/AdminInfo'; import { TimeLeft } from '../TimeLeft/TimeLeft'; diff --git a/src/pages/enrollment/components/EnrollmentStepIndicator/EnrollmentStepIndicator.tsx b/src/pages/enrollment/components/EnrollmentStepIndicator/EnrollmentStepIndicator.tsx index 241e72f5..62398e6f 100644 --- a/src/pages/enrollment/components/EnrollmentStepIndicator/EnrollmentStepIndicator.tsx +++ b/src/pages/enrollment/components/EnrollmentStepIndicator/EnrollmentStepIndicator.tsx @@ -1,25 +1,23 @@ import './style.scss'; -import { shallow } from 'zustand/shallow'; - +import { useMemo } from 'react'; import { useI18nContext } from '../../../../i18n/i18n-react'; +import { flattenEnrollConf } from '../../const'; import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; export const EnrollmentStepIndicator = () => { const { LL } = useI18nContext(); - const [step, maxStep] = useEnrollmentStore( - (state) => [state.step, state.stepsMax], - shallow, - ); + const currentStepKey = useEnrollmentStore((state) => state.step); + const flatConf = useMemo(() => flattenEnrollConf(), []); + const currentStep = flatConf[currentStepKey]; return (

- {LL.pages.enrollment.stepsIndicator.step()} {step + 1}{' '} - - {LL.pages.enrollment.stepsIndicator.of()} {maxStep + 1} - + {LL.pages.enrollment.stepsIndicator.step()}{' '} + {currentStep.indicatorPrefix ?? currentStep.sideBarPrefix ?? ''}{' '} + {LL.pages.enrollment.stepsIndicator.of()} 6

); diff --git a/src/pages/enrollment/components/types.ts b/src/pages/enrollment/components/types.ts new file mode 100644 index 00000000..3ee16633 --- /dev/null +++ b/src/pages/enrollment/components/types.ts @@ -0,0 +1,5 @@ +export type EnrollmentSideBarData = { + stepIndex: number | number[]; + label: string; + stepDisplayNumber: number; +}; diff --git a/src/pages/enrollment/const.tsx b/src/pages/enrollment/const.tsx new file mode 100644 index 00000000..73425de4 --- /dev/null +++ b/src/pages/enrollment/const.tsx @@ -0,0 +1,119 @@ +import { cloneDeep } from 'lodash-es'; +import type { ReactNode } from 'react'; +import { ChooseMfaStep } from './steps/ChooseMfaStep/ChooseMfaStep'; +import { DataVerificationStep } from './steps/DataVerificationStep/DataVerificationStep'; +import { DeviceStep } from './steps/DeviceStep/DeviceStep'; +import { FinishStep } from './steps/FinishStep/FinishStep'; +import { MfaRecoveryCodesStep } from './steps/MfaRecoveryCodesStep/MfaRecoveryCodesStep'; +import { MfaSetupStep } from './steps/MfaSetupStep/MfaSetupStep'; +import { PasswordStep } from './steps/PasswordStep/PasswordStep'; +import { SendFinishStep } from './steps/SendFinishStep/SendFinishStep'; +import { WelcomeStep } from './steps/WelcomeStep/WelcomeStep'; + +export enum EnrollmentStepKey { + WELCOME = 'welcome', + DATA_VERIFICATION = 'data-verification', + PASSWORD = 'password', + DEVICE = 'device', + MFA = 'mfa', + MFA_CHOICE = 'mfa-choice', + MFA_SETUP = 'mfa-setup', + MFA_RECOVERY = 'mfa-recovery', + ACTIVATE_USER = 'activate', + FINISH = 'finish', +} + +export type EnrollmentStep = { + key: EnrollmentStepKey; + sideBarPrefix?: string; + indicatorPrefix?: string; + // enable back in navigation + backEnabled?: boolean; + nextDisabled?: boolean; + children?: EnrollmentStep[]; + // this means it's only rendered and it doesn't count as a step in UI + // meant for loading in-between steps like send finish + hidden?: boolean; +}; + +// this servers as configuration for side bar and steps indicator +// some steps are like in between loaders and mfa has sub steps that's why this needs to exist +// in side bar this serves as base for final config with translated labels +export const enrollmentStepsConfig: Record = { + [EnrollmentStepKey.WELCOME]: { + key: EnrollmentStepKey.WELCOME, + sideBarPrefix: '1', + }, + [EnrollmentStepKey.DATA_VERIFICATION]: { + key: EnrollmentStepKey.DATA_VERIFICATION, + sideBarPrefix: '2', + }, + [EnrollmentStepKey.PASSWORD]: { + key: EnrollmentStepKey.PASSWORD, + sideBarPrefix: '3', + backEnabled: true, + }, + [EnrollmentStepKey.DEVICE]: { + key: EnrollmentStepKey.DEVICE, + sideBarPrefix: '4', + backEnabled: true, + }, + [EnrollmentStepKey.MFA]: { + key: EnrollmentStepKey.MFA, + sideBarPrefix: '5', + children: [ + { + key: EnrollmentStepKey.MFA_CHOICE, + sideBarPrefix: 'a', + indicatorPrefix: '5a', + nextDisabled: true, + }, + { + key: EnrollmentStepKey.MFA_SETUP, + sideBarPrefix: 'b', + indicatorPrefix: '5b', + backEnabled: true, + }, + { + key: EnrollmentStepKey.MFA_RECOVERY, + sideBarPrefix: 'c', + indicatorPrefix: '5c', + }, + ], + }, + [EnrollmentStepKey.ACTIVATE_USER]: { + key: EnrollmentStepKey.ACTIVATE_USER, + hidden: true, + nextDisabled: true, + }, + [EnrollmentStepKey.FINISH]: { + sideBarPrefix: '6', + key: EnrollmentStepKey.FINISH, + }, +}; + +export const enrollmentSteps: Record = { + [EnrollmentStepKey.WELCOME]: , + [EnrollmentStepKey.DATA_VERIFICATION]: , + [EnrollmentStepKey.PASSWORD]: , + [EnrollmentStepKey.DEVICE]: , + // this will be skipped and is here only for TS + [EnrollmentStepKey.MFA]: null, + [EnrollmentStepKey.MFA_RECOVERY]: , + [EnrollmentStepKey.MFA_CHOICE]: , + [EnrollmentStepKey.MFA_SETUP]: , + [EnrollmentStepKey.ACTIVATE_USER]: , + [EnrollmentStepKey.FINISH]: , +}; + +export const flattenEnrollConf = () => { + const steps = cloneDeep(enrollmentStepsConfig); + Object.values(steps).forEach((step) => { + if (step.children) { + step.children.forEach((child) => { + steps[child.key] = child; + }); + } + }); + return steps; +}; diff --git a/src/pages/enrollment/hooks/store/useEnrollmentStore.tsx b/src/pages/enrollment/hooks/store/useEnrollmentStore.tsx index d9dc905a..ff404086 100644 --- a/src/pages/enrollment/hooks/store/useEnrollmentStore.tsx +++ b/src/pages/enrollment/hooks/store/useEnrollmentStore.tsx @@ -1,16 +1,33 @@ +import type { Dayjs } from 'dayjs'; import { pick } from 'lodash-es'; import { Subject } from 'rxjs'; -import { createJSONStorage, devtools, persist } from 'zustand/middleware'; +import { createJSONStorage, persist } from 'zustand/middleware'; import { createWithEqualityFn } from 'zustand/traditional'; - -import { AdminInfo, UserInfo } from '../../../../shared/hooks/api/types'; +import type { + AdminInfo, + CreateDeviceResponse, + EnrollmentSettings, + UserInfo, +} from '../../../../shared/hooks/api/types'; +import { MfaMethod } from '../../../../shared/types'; +import { EnrollmentStepKey } from '../../const'; +import { EnrollmentNavDirection } from '../types'; const defaultValues: StoreValues = { // assume default dev proxy_url: '/api/v1/', loading: false, - step: 0, - stepsMax: 4, + enrollmentSettings: { + admin_device_management: false, + mfa_required: false, + only_client_activation: false, + smtp_configured: false, + vpn_setup_optional: true, + }, + emailResendTimestamp: undefined, + step: EnrollmentStepKey.WELCOME, + mfaMethod: MfaMethod.TOTP, + recoveryCodes: [], sessionStart: undefined, sessionEnd: undefined, userInfo: undefined, @@ -18,52 +35,50 @@ const defaultValues: StoreValues = { vpnOptional: undefined, userPassword: undefined, cookie: undefined, - nextSubject: new Subject(), + nextSubject: new Subject(), + deviceKeys: undefined, + deviceResponse: undefined, }; const persistKeys: Array = [ 'step', + 'proxy_url', 'userInfo', 'userPassword', + 'recoveryCodes', + 'mfaMethod', 'sessionEnd', 'sessionStart', 'adminInfo', 'deviceName', 'endContent', 'vpnOptional', + 'deviceKeys', + 'deviceResponse', + 'cookie', + 'enrollmentSettings', ]; export const useEnrollmentStore = createWithEqualityFn()( - devtools( - persist( - (set, get) => ({ - ...defaultValues, - init: (values) => set({ ...defaultValues, ...values }), - setState: (newValues) => set((old) => ({ ...old, ...newValues })), - reset: () => set(defaultValues), - nextStep: () => { - const current = get().step; - const max = get().stepsMax; - - if (current < max) { - return set({ step: current + 1 }); - } - }, - perviousStep: () => { - const current = get().step; - - if (current > 0) { - return set({ step: current - 1 }); - } - }, - }), - { - name: 'enrollment-storage', - version: 0.1, - storage: createJSONStorage(() => sessionStorage), - partialize: (state) => pick(state, persistKeys), + persist( + (set, get) => ({ + ...defaultValues, + init: (values) => set({ ...defaultValues, ...values }), + setState: (newValues) => set((old) => ({ ...old, ...newValues })), + reset: () => set(defaultValues), + next: () => { + get().nextSubject.next(EnrollmentNavDirection.NEXT); }, - ), + back: () => { + get().nextSubject.next(EnrollmentNavDirection.BACK); + }, + }), + { + name: 'enrollment-storage', + version: 2, + storage: createJSONStorage(() => sessionStorage), + partialize: (state) => pick(state, persistKeys), + }, ), Object.is, ); @@ -71,13 +86,15 @@ export const useEnrollmentStore = createWithEqualityFn()( type Store = StoreValues & StoreMethods; type StoreValues = { - // next and back are disabled loading: boolean; - step: number; - stepsMax: number; - nextSubject: Subject; + enrollmentSettings: EnrollmentSettings; + step: EnrollmentStepKey; + mfaMethod: MfaMethod; + emailResendTimestamp?: Dayjs; + nextSubject: Subject; // Date proxy_url: string; + recoveryCodes: string[]; sessionStart?: string; sessionEnd?: string; userInfo?: UserInfo; @@ -88,12 +105,17 @@ type StoreValues = { endContent?: string; deviceName?: string; cookie?: string; + deviceKeys?: { + public: string; + private: string; + }; + deviceResponse?: CreateDeviceResponse; }; type StoreMethods = { setState: (values: Partial) => void; - reset: () => void; - nextStep: () => void; - perviousStep: () => void; init: (initValues: Partial) => void; + next: () => void; + back: () => void; + reset: () => void; }; diff --git a/src/pages/enrollment/hooks/types.ts b/src/pages/enrollment/hooks/types.ts new file mode 100644 index 00000000..8639ec39 --- /dev/null +++ b/src/pages/enrollment/hooks/types.ts @@ -0,0 +1,4 @@ +export enum EnrollmentNavDirection { + NEXT, + BACK, +} diff --git a/src/pages/enrollment/hooks/useEnrollmentApi.tsx b/src/pages/enrollment/hooks/useEnrollmentApi.tsx index 567bb8ff..1557d1f8 100644 --- a/src/pages/enrollment/hooks/useEnrollmentApi.tsx +++ b/src/pages/enrollment/hooks/useEnrollmentApi.tsx @@ -1,13 +1,7 @@ -import { Body, fetch } from '@tauri-apps/api/http'; +import { fetch } from '@tauri-apps/plugin-http'; import { useEnrollmentStore } from '../../../pages/enrollment/hooks/store/useEnrollmentStore'; -import { - ActivateUserResponse, - AppInfo, - CreateDeviceResponse, - EnrollmentStartResponse, - UseApi, -} from '../../../shared/hooks/api/types'; +import type { UseApi } from '../../../shared/hooks/api/types'; export const useEnrollmentApi = (): UseApi => { const [proxyUrl, cookie] = useEnrollmentStore((state) => [ @@ -15,60 +9,100 @@ export const useEnrollmentApi = (): UseApi => { state.cookie, ]); - const startEnrollment: UseApi['enrollment']['start'] = async (data) => { - const response = await fetch( - `${proxyUrl}/enrollment/start`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Cookie: cookie, - }, - body: Body.json(data), - }, - ); + const networkInfo: UseApi['enrollment']['networkInfo'] = async ( + data, + url?: string, + overrideCookie?: string, + ) => { + const response = await fetch(`${url ?? proxyUrl}/enrollment/network_info`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: overrideCookie ?? cookie, + } as Record, + body: JSON.stringify(data), + }); + return await response.json(); + }; + + const registerCodeMfaStart: UseApi['enrollment']['registerCodeMfaStart'] = async ( + method, + ) => { + const response = await fetch(`${proxyUrl}/enrollment/register-mfa/code/start`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: cookie, + } as Record, + body: JSON.stringify({ + method: method.valueOf(), + }), + }); + return await response.json(); + }; + + const registerCodeMfaFinish: UseApi['enrollment']['registerCodeMfaFinish'] = async ( + data, + ) => { + const response = await fetch(`${proxyUrl}/enrollment/register-mfa/code/finish`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: cookie, + } as Record, + body: JSON.stringify(data), + }); + if (!response.ok) throw Error('Register finish request failed'); + return await response.json(); + }; + + const start: UseApi['enrollment']['start'] = async (data) => { + const response = await fetch(`${data.proxyUrl ?? proxyUrl}/enrollment/start`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: cookie, + } as Record, + body: JSON.stringify({ + token: data.token, + }), + }); return response; }; const activateUser: UseApi['enrollment']['activateUser'] = async (data) => { - const response = await fetch( - `${proxyUrl}/enrollment/activate_user`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Cookie: cookie, - }, - body: Body.json(data), - }, - ); + const response = await fetch(`${proxyUrl}/enrollment/activate_user`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: cookie, + } as Record, + body: JSON.stringify(data), + }); return response; }; const createDevice: UseApi['enrollment']['createDevice'] = async (data) => { - const response = await fetch( - `${proxyUrl}/enrollment/create_device`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Cookie: cookie, - }, - body: Body.json(data), - }, - ); + const response = await fetch(`${proxyUrl}/enrollment/create_device`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: cookie, + } as Record, + body: JSON.stringify(data), + }); return response; }; const getAppInfo: UseApi['getAppInfo'] = async () => { - const response = await fetch(`${proxyUrl}/info`, { + const response = await fetch(`${proxyUrl}/info`, { method: 'GET', headers: { 'Content-Type': 'application/json', Cookie: cookie, - }, + } as Record, }); return response; @@ -76,9 +110,12 @@ export const useEnrollmentApi = (): UseApi => { return { enrollment: { - start: startEnrollment, + start, activateUser, createDevice, + registerCodeMfaStart, + registerCodeMfaFinish, + networkInfo, }, getAppInfo, }; diff --git a/src/pages/enrollment/steps/ChooseMfaStep/ChooseMfaStep.tsx b/src/pages/enrollment/steps/ChooseMfaStep/ChooseMfaStep.tsx new file mode 100644 index 00000000..21bbb261 --- /dev/null +++ b/src/pages/enrollment/steps/ChooseMfaStep/ChooseMfaStep.tsx @@ -0,0 +1,94 @@ +import './style.scss'; + +import { useEffect } from 'react'; +import { shallow } from 'zustand/shallow'; +import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { + ButtonSize, + ButtonStyleVariant, +} from '../../../../shared/defguard-ui/components/Layout/Button/types'; +import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; +import { LabeledRadio } from '../../../../shared/defguard-ui/components/Layout/LabeledRadio/LabeledRadio'; +import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; +import { MfaMethod } from '../../../../shared/types'; +import { EnrollmentStepIndicator } from '../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../const'; +import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; +import { EnrollmentNavDirection } from '../../hooks/types'; + +export const ChooseMfaStep = () => { + const selectedMethod = useEnrollmentStore((s) => s.mfaMethod); + const enrollmentSettings = useEnrollmentStore((s) => s.enrollmentSettings); + const [setStore, navSubject] = useEnrollmentStore( + (s) => [s.setState, s.nextSubject], + shallow, + ); + + // biome-ignore lint/correctness/useExhaustiveDependencies: rxjs + useEffect(() => { + const sub = navSubject.subscribe((dir) => { + if (dir === EnrollmentNavDirection.NEXT) { + setStore({ step: EnrollmentStepKey.MFA_SETUP }); + } + }); + return () => { + sub.unsubscribe(); + }; + }, [navSubject]); + + return ( + +
+ +

Enable Multi-Factor Authentication

+
+ +
+ { + setStore({ mfaMethod: MfaMethod.TOTP }); + }} + /> + { + setStore({ mfaMethod: MfaMethod.EMAIL }); + }} + /> +
+
+ {!enrollmentSettings.mfa_required && ( +
+
+ ); +}; diff --git a/src/pages/enrollment/steps/ChooseMfaStep/style.scss b/src/pages/enrollment/steps/ChooseMfaStep/style.scss new file mode 100644 index 00000000..17c87f34 --- /dev/null +++ b/src/pages/enrollment/steps/ChooseMfaStep/style.scss @@ -0,0 +1,30 @@ +#enrollment-choose-mfa-step { + width: 100%; + max-width: 650px; + padding: 50px 40px; + display: flex; + flex-flow: column; + row-gap: var(--spacing-m); + + .choices { + display: flex; + flex-flow: row wrap; + width: 100%; + align-items: start; + justify-content: space-between; + column-gap: var(--spacing-s); + row-gap: var(--spacing-s); + + & > div { + width: calc(50% - (var(--spacing-s) / 2)); + } + } + + .controls { + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: center; + gap: var(--spacing-s); + } +} diff --git a/src/pages/enrollment/steps/DataVerificationStep/DataVerificationStep.tsx b/src/pages/enrollment/steps/DataVerificationStep/DataVerificationStep.tsx index 256ce153..7139a90a 100644 --- a/src/pages/enrollment/steps/DataVerificationStep/DataVerificationStep.tsx +++ b/src/pages/enrollment/steps/DataVerificationStep/DataVerificationStep.tsx @@ -2,7 +2,7 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; import { useEffect, useMemo, useRef } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; import { shallow } from 'zustand/shallow'; @@ -12,6 +12,7 @@ import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; import { MessageBoxType } from '../../../../shared/defguard-ui/components/Layout/MessageBox/types'; import { EnrollmentStepIndicator } from '../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../const'; import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; const phonePattern = /^\+?[0-9]+( [0-9]+)?$/; @@ -28,10 +29,7 @@ export const DataVerificationStep = () => { const userInfo = useEnrollmentStore((state) => state.userInfo); - const [setEnrollment, next] = useEnrollmentStore( - (state) => [state.setState, state.nextStep], - shallow, - ); + const [setEnrollment] = useEnrollmentStore((state) => [state.setState], shallow); const pageLL = LL.pages.enrollment.steps.dataVerification; @@ -61,8 +59,8 @@ export const DataVerificationStep = () => { if (userInfo) { setEnrollment({ userInfo: { ...userInfo, phone_number: values.phone }, + step: EnrollmentStepKey.PASSWORD, }); - next(); } }; diff --git a/src/pages/enrollment/steps/DeviceStep/DeviceStep.tsx b/src/pages/enrollment/steps/DeviceStep/DeviceStep.tsx index ac3e9853..3d67e1ff 100644 --- a/src/pages/enrollment/steps/DeviceStep/DeviceStep.tsx +++ b/src/pages/enrollment/steps/DeviceStep/DeviceStep.tsx @@ -3,15 +3,17 @@ import './style.scss'; import classNames from 'classnames'; import { useEffect } from 'react'; import { shallow } from 'zustand/shallow'; - +import { EnrollmentStepKey } from '../../const'; import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; +import { EnrollmentNavDirection } from '../../hooks/types'; import { DesktopSetup } from './components/DesktopSetup/DesktopSetup'; export const DeviceStep = () => { const deviceName = useEnrollmentStore((state) => state.deviceName); const vpnOptional = useEnrollmentStore((state) => state.vpnOptional); - const [nextSubject, next] = useEnrollmentStore( - (state) => [state.nextSubject, state.nextStep], + + const [nextSubject, setStore] = useEnrollmentStore( + (state) => [state.nextSubject, state.setState], shallow, ); @@ -20,17 +22,24 @@ export const DeviceStep = () => { optional: vpnOptional, }); + // biome-ignore lint/correctness/useExhaustiveDependencies: jsx useEffect(() => { - const sub = nextSubject.subscribe(() => { - if (deviceName) { - next(); + const sub = nextSubject.subscribe((direction) => { + switch (direction) { + case EnrollmentNavDirection.BACK: + setStore({ step: EnrollmentStepKey.PASSWORD }); + break; + case EnrollmentNavDirection.NEXT: + if (deviceName) { + setStore({ step: EnrollmentStepKey.MFA_CHOICE }); + } + break; } }); - return () => { sub.unsubscribe(); }; - }, [nextSubject, next, deviceName]); + }, [nextSubject, deviceName]); return (
diff --git a/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx b/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx index 7d80154a..af01aef9 100644 --- a/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx +++ b/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx @@ -1,11 +1,11 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; +import { error } from '@tauri-apps/plugin-log'; import { isUndefined } from 'lodash-es'; import { useMemo, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; -import { debug, error, info } from 'tauri-plugin-log-api'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; import { useI18nContext } from '../../../../../../i18n/i18n-react'; @@ -17,26 +17,23 @@ import { } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; import { Card } from '../../../../../../shared/defguard-ui/components/Layout/Card/Card'; import { useToaster } from '../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; -import { CreateDeviceResponse } from '../../../../../../shared/hooks/api/types'; +import type { CreateDeviceResponse } from '../../../../../../shared/hooks/api/types'; import { generateWGKeys } from '../../../../../../shared/utils/generateWGKeys'; -import { clientApi } from '../../../../../client/clientAPI/clientApi'; -import { clientQueryKeys } from '../../../../../client/query'; +import { EnrollmentStepIndicator } from '../../../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../../../const'; import { useEnrollmentStore } from '../../../../hooks/store/useEnrollmentStore'; import { useEnrollmentApi } from '../../../../hooks/useEnrollmentApi'; -const { saveConfig } = clientApi; - type FormFields = { name: string; }; export const DesktopSetup = () => { - const queryClient = useQueryClient(); const { LL } = useI18nContext(); const toaster = useToaster(); const stepLL = LL.pages.enrollment.steps.deviceSetup; const { - enrollment: { createDevice, activateUser }, + enrollment: { createDevice }, } = useEnrollmentApi(); const deviceName = useEnrollmentStore((state) => state.deviceName); const [userInfo, userPassword] = useEnrollmentStore((state) => [ @@ -44,24 +41,8 @@ export const DesktopSetup = () => { state.userPassword, ]); const setEnrollmentStore = useEnrollmentStore((state) => state.setState); - const next = useEnrollmentStore((state) => state.nextStep); const [isLoading, setIsLoading] = useState(false); - const { mutateAsync: mutateUserActivation, isPending: activationPending } = useMutation( - { - mutationFn: activateUser, - onError: (e) => { - toaster.error( - LL.common.messages.errorWithMessage({ - message: String(e), - }), - ); - console.error(e); - error(String(e)); - }, - }, - ); - const { mutateAsync: createDeviceMutation, isPending: createDevicePending } = useMutation({ mutationFn: createDevice, @@ -89,73 +70,35 @@ export const DesktopSetup = () => { const handleValidSubmit: SubmitHandler = async (values) => { if (!userInfo || !userPassword) return; const { publicKey, privateKey } = generateWGKeys(); - const deviceResponse = await createDeviceMutation({ + const deviceResponse = (await createDeviceMutation({ name: values.name, pubkey: publicKey, - }).then((res) => { + }).then(async (res) => { if (!res.ok) { error( `Failed to create device during the enrollment. Error details: ${JSON.stringify( - res.data, - )} Error status code: ${res.status}`, + await res.json(), + )} Error status code: ${res.status} `, ); } - return res; - }); - mutateUserActivation({ - password: userPassword, - phone_number: userInfo.phone_number, - }).then((res) => { - if (!res.ok) { - error( - `Failed to activate user during the enrollment. Error details: ${JSON.stringify( - res.data, - )} Error status code: ${res.status}`, - ); - throw Error('Failed to activate user'); - } - info('User activated'); - setIsLoading(true); - debug('Invoking save_device_config'); - saveConfig({ - privateKey, - response: deviceResponse.data as CreateDeviceResponse, - }) - .then(() => { - debug('Config saved'); - setIsLoading(false); - setEnrollmentStore({ deviceName: values.name }); - toaster.success(stepLL.desktopSetup.messages.deviceConfigured()); - const invalidate = [clientQueryKeys.getInstances, clientQueryKeys.getLocations]; - invalidate.forEach((key) => { - queryClient.invalidateQueries({ - queryKey: [key], - }); - }); - next(); - }) - .catch((e) => { - setIsLoading(false); - - if (typeof e === 'string') { - if (e.includes('Network Error')) { - toaster.error(LL.common.messages.networkError()); - return; - } - toaster.error(LL.common.messages.errorWithMessage({ message: String(e) })); - } else { - toaster.error( - LL.common.messages.errorWithMessage({ - message: String(e), - }), - ); - } - }); + return await res.json(); + })) as CreateDeviceResponse; + toaster.success(stepLL.desktopSetup.messages.deviceConfigured()); + setEnrollmentStore({ + deviceResponse, + step: EnrollmentStepKey.MFA_CHOICE, + deviceName: values.name, + deviceKeys: { + private: privateKey, + public: publicKey, + }, }); + setIsLoading(false); }; return ( +

{stepLL.desktopSetup.title()}

{ : stepLL.desktopSetup.controls.create() } disabled={!isUndefined(deviceName)} - loading={isLoading || activationPending || createDevicePending} + loading={isLoading || createDevicePending} />
diff --git a/src/pages/enrollment/steps/MfaRecoveryCodesStep/MfaRecoveryCodesStep.tsx b/src/pages/enrollment/steps/MfaRecoveryCodesStep/MfaRecoveryCodesStep.tsx new file mode 100644 index 00000000..ad332097 --- /dev/null +++ b/src/pages/enrollment/steps/MfaRecoveryCodesStep/MfaRecoveryCodesStep.tsx @@ -0,0 +1,65 @@ +import './style.scss'; +import { useEffect } from 'react'; +import { shallow } from 'zustand/shallow'; +import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; +import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; +import { + MessageBoxStyleVariant, + MessageBoxType, +} from '../../../../shared/defguard-ui/components/Layout/MessageBox/types'; +import SvgIconCopy from '../../../../shared/defguard-ui/components/svg/IconCopy'; +import { useClipboard } from '../../../../shared/hooks/useClipboard'; +import { EnrollmentStepIndicator } from '../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../const'; +import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; +import { EnrollmentNavDirection } from '../../hooks/types'; + +export const MfaRecoveryCodesStep = () => { + const { writeToClipboard } = useClipboard(); + const [setStore, navSubject] = useEnrollmentStore( + (s) => [s.setState, s.nextSubject], + shallow, + ); + const codes = useEnrollmentStore((s) => s.recoveryCodes); + + // biome-ignore lint/correctness/useExhaustiveDependencies: rxjs + useEffect(() => { + const sub = navSubject.subscribe((dir) => { + if (dir === EnrollmentNavDirection.NEXT) { + setStore({ step: EnrollmentStepKey.ACTIVATE_USER }); + } + }); + return () => { + sub.unsubscribe(); + }; + }, [navSubject]); + + return ( + +
+ +

Recovery codes

+
+ +
+
    + {codes.map((code) => ( +
  • {code}
  • + ))} +
+
+
+ ); +}; diff --git a/src/pages/enrollment/steps/MfaRecoveryCodesStep/style.scss b/src/pages/enrollment/steps/MfaRecoveryCodesStep/style.scss new file mode 100644 index 00000000..78d373c5 --- /dev/null +++ b/src/pages/enrollment/steps/MfaRecoveryCodesStep/style.scss @@ -0,0 +1,27 @@ +#enrollment-mfa-recovery-codes-step { + width: 100%; + max-width: 650px; + padding: 50px 40px; + display: flex; + flex-flow: column; + gap: var(--spacing-m); + + & > .codes { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + gap: var(--spacing-s); + + ul { + li { + &:not(:last-child) { + margin-bottom: var(--spacing-xs); + } + + @include typography(app-code); + font-size: 14px; + } + } + } +} diff --git a/src/pages/enrollment/steps/MfaSetupStep/MfaSetupEmail.tsx b/src/pages/enrollment/steps/MfaSetupStep/MfaSetupEmail.tsx new file mode 100644 index 00000000..5d26be79 --- /dev/null +++ b/src/pages/enrollment/steps/MfaSetupStep/MfaSetupEmail.tsx @@ -0,0 +1,71 @@ +import dayjs, { type Dayjs } from 'dayjs'; +import { type ReactNode, useCallback, useEffect, useState } from 'react'; +import { interval } from 'rxjs'; +import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { + ButtonSize, + ButtonStyleVariant, +} from '../../../../shared/defguard-ui/components/Layout/Button/types'; +import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; +import { useToaster } from '../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; + +type Props = { + children: ReactNode; + userEmail: string; + refetch: () => void; +}; + +export const MfaSetupEmail = ({ children, userEmail, refetch }: Props) => { + const toaster = useToaster(); + const checkResend = useCallback((resend: Dayjs | undefined) => { + if (resend) { + const cd = resend.add(1, 'minute'); + return cd.isBefore(dayjs()); + } + return true; + }, []); + + const resendTimestamp = useEnrollmentStore((s) => s.emailResendTimestamp); + const setStore = useEnrollmentStore((s) => s.setState); + + const [canResent, setCanResend] = useState(checkResend(resendTimestamp)); + + useEffect(() => { + const sub = interval(1000).subscribe(() => { + if (resendTimestamp) { + setCanResend(checkResend(resendTimestamp)); + } + }); + return () => { + sub.unsubscribe(); + }; + }, [resendTimestamp, checkResend]); + + return ( +
+ +

+ To setup your MFA, enter the code that was sent to your account email: +
+ {userEmail} +

+
+ {children} +
+ ); +}; diff --git a/src/pages/enrollment/steps/MfaSetupStep/MfaSetupStep.tsx b/src/pages/enrollment/steps/MfaSetupStep/MfaSetupStep.tsx new file mode 100644 index 00000000..10f826c8 --- /dev/null +++ b/src/pages/enrollment/steps/MfaSetupStep/MfaSetupStep.tsx @@ -0,0 +1,165 @@ +import './style.scss'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { error } from '@tauri-apps/plugin-log'; +import { type Ref, useEffect, useRef } from 'react'; +import { type SubmitHandler, useForm } from 'react-hook-form'; +import z from 'zod'; +import { shallow } from 'zustand/shallow'; +import { FormInput } from '../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; +import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; +import { LoaderSpinner } from '../../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; +import { useToaster } from '../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import { isPresent } from '../../../../shared/defguard-ui/utils/isPresent'; +import { MfaMethod } from '../../../../shared/types'; +import { EnrollmentStepIndicator } from '../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../const'; +import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; +import { EnrollmentNavDirection } from '../../hooks/types'; +import { useEnrollmentApi } from '../../hooks/useEnrollmentApi'; +import { MfaSetupEmail } from './MfaSetupEmail'; +import { MfaSetupTotp } from './MfaSetupTotp'; + +const formSchema = z.object({ + code: z.string().trim().min(6, 'Enter valid code').max(6, 'Enter valid code'), +}); + +type FormFields = z.infer; + +export const MfaSetupStep = () => { + const submitRef = useRef(null); + const [userInfo, mfaMethod] = useEnrollmentStore((s) => [s.userInfo, s.mfaMethod]); + const [nextSubject, setStoreState] = useEnrollmentStore( + (s) => [s.nextSubject, s.setState], + shallow, + ); + + const { + enrollment: { registerCodeMfaStart }, + } = useEnrollmentApi(); + + const { + data: startData, + isLoading: startLoading, + refetch, + } = useQuery({ + queryFn: () => registerCodeMfaStart(mfaMethod), + queryKey: ['register-mfa', mfaMethod], + refetchOnWindowFocus: false, + enabled: isPresent(mfaMethod), + }); + + // biome-ignore lint/correctness/useExhaustiveDependencies: rxjs sub + useEffect(() => { + const sub = nextSubject.subscribe((direction) => { + switch (direction) { + case EnrollmentNavDirection.NEXT: + submitRef.current?.click(); + break; + case EnrollmentNavDirection.BACK: + setStoreState({ step: EnrollmentStepKey.MFA_CHOICE }); + break; + } + }); + return () => { + sub.unsubscribe(); + }; + }, [nextSubject]); + + return ( + +
+ +

Configure MFA

+
+ {startLoading && ( +
+ +
+ )} + {!startLoading && isPresent(userInfo) && ( + <> + {mfaMethod === MfaMethod.TOTP && isPresent(startData?.totp_secret) && ( + + + + )} + {mfaMethod === MfaMethod.EMAIL && ( + + + + )} + + )} +
+ ); +}; + +type CodeFormProps = { + inputRef: Ref; +}; + +const CodeForm = ({ inputRef }: CodeFormProps) => { + const toaster = useToaster(); + const mfaMethod = useEnrollmentStore((s) => s.mfaMethod); + const setStoreState = useEnrollmentStore((s) => s.setState); + const { + enrollment: { registerCodeMfaFinish }, + } = useEnrollmentApi(); + + const { handleSubmit, control, setError } = useForm({ + resolver: zodResolver(formSchema), + }); + + const { mutate, isPending } = useMutation({ + mutationFn: registerCodeMfaFinish, + onSuccess: (response) => { + toaster.success('MFA configured'); + setStoreState({ + loading: false, + step: EnrollmentStepKey.MFA_RECOVERY, + recoveryCodes: response.recovery_codes, + }); + }, + onError: (err) => { + setError( + 'code', + { + message: 'Enter valid code', + type: 'value', + }, + { + shouldFocus: false, + }, + ); + error(`MFA configuration failed! \nReason: ${err.message}`); + console.error(err); + }, + }); + + const submitHandler: SubmitHandler = (data) => { + const sendData = { + code: data.code, + method: mfaMethod, + }; + mutate(sendData); + }; + + // biome-ignore lint/correctness/useExhaustiveDependencies: sideEffect + useEffect(() => { + setStoreState({ + loading: isPending, + }); + }, [isPending]); + + return ( +
+ + + + ); +}; diff --git a/src/pages/enrollment/steps/MfaSetupStep/MfaSetupTotp.tsx b/src/pages/enrollment/steps/MfaSetupStep/MfaSetupTotp.tsx new file mode 100644 index 00000000..2c4f5073 --- /dev/null +++ b/src/pages/enrollment/steps/MfaSetupStep/MfaSetupTotp.tsx @@ -0,0 +1,49 @@ +import type { ReactNode } from 'react'; +import QRCode from 'react-qr-code'; +import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; +import SvgIconCopy from '../../../../shared/defguard-ui/components/svg/IconCopy'; +import { useClipboard } from '../../../../shared/hooks/useClipboard'; + +type Props = { + children: ReactNode; + email: string; + secret: string; +}; + +export const MfaSetupTotp = ({ children, email, secret }: Props) => { + return ( +
+ + + {children} +
+ ); +}; + +type TotpProps = { + email: string; + secret: string; +}; + +const TotpQr = ({ email, secret }: TotpProps) => { + const { writeToClipboard } = useClipboard(); + return ( +
+
+ +
+
+ ); +}; diff --git a/src/pages/enrollment/steps/MfaSetupStep/style.scss b/src/pages/enrollment/steps/MfaSetupStep/style.scss new file mode 100644 index 00000000..ad95f873 --- /dev/null +++ b/src/pages/enrollment/steps/MfaSetupStep/style.scss @@ -0,0 +1,72 @@ +#enrollment-totp-card { + width: 100%; + max-width: 650px; + padding: 50px 40px; + display: flex; + flex-flow: column; + row-gap: var(--spacing-m); + + .loading { + width: 100%; + display: flex; + flex-flow: row; + align-items: center; + justify-content: center; + min-height: 25dvh; + } + + .totp-info { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + row-gap: var(--spacing-s); + + .qr { + display: flex; + flex-flow: row; + align-items: center; + justify-content: center; + } + + .btn { + width: unset; + flex-grow: 0; + width: min-content; + } + } + + .btn.resend { + width: 100%; + } + + .totp-setup, + .email-setup { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + row-gap: var(--spacing-s); + } + + .resend { + width: 100%; + } + + form { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + row-gap: var(--spacing-m); + width: 100%; + + & > .input { + width: 100%; + } + + input[type='submit'] { + @include visually-hidden(); + } + } +} diff --git a/src/pages/enrollment/steps/PasswordStep/PasswordStep.tsx b/src/pages/enrollment/steps/PasswordStep/PasswordStep.tsx index 6608e71b..569dd69d 100644 --- a/src/pages/enrollment/steps/PasswordStep/PasswordStep.tsx +++ b/src/pages/enrollment/steps/PasswordStep/PasswordStep.tsx @@ -2,7 +2,7 @@ import './style.scss'; import { zodResolver } from '@hookform/resolvers/zod'; import { useEffect, useMemo, useRef } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; import { shallow } from 'zustand/shallow'; @@ -11,7 +11,9 @@ import { FormInput } from '../../../../shared/defguard-ui/components/Form/FormIn import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; import { passwordValidator } from '../../../../shared/validators/password'; import { EnrollmentStepIndicator } from '../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../const'; import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; +import { EnrollmentNavDirection } from '../../hooks/types'; type FormFields = { password: string; @@ -22,10 +24,9 @@ export const PasswordStep = () => { const submitRef = useRef(null); const { LL } = useI18nContext(); - const setStore = useEnrollmentStore((state) => state.setState); const userPassword = useEnrollmentStore((state) => state.userPassword); - const [nextSubject, next] = useEnrollmentStore( - (state) => [state.nextSubject, state.nextStep], + const [nextSubject, setStore] = useEnrollmentStore( + (state) => [state.nextSubject, state.setState], shallow, ); @@ -38,9 +39,14 @@ export const PasswordStep = () => { password: passwordValidator(LL), repeat: z.string().min(1, LL.form.errors.required()), }) - .refine((values) => values.password === values.repeat, { - message: pageLL.form.fields.repeat.errors.matching(), - path: ['repeat'], + .superRefine((values, ctx) => { + if (values.password !== values.repeat && values.repeat.length >= 1) { + ctx.addIssue({ + path: ['repeat'], + message: pageLL.form.fields.repeat.errors.matching(), + code: 'custom', + }); + } }), [LL, pageLL.form.fields.repeat.errors], ); @@ -56,24 +62,31 @@ export const PasswordStep = () => { }); const handleValidSubmit: SubmitHandler = (values) => { - setStore({ userPassword: values.password }); - next(); + setStore({ userPassword: values.password, step: EnrollmentStepKey.DEVICE }); }; + // biome-ignore lint/correctness/useExhaustiveDependencies: rxjs useEffect(() => { - const sub = nextSubject.subscribe(() => { - submitRef.current?.click(); + const sub = nextSubject.subscribe((direction) => { + switch (direction) { + case EnrollmentNavDirection.BACK: + setStore({ step: EnrollmentStepKey.DATA_VERIFICATION }); + break; + case EnrollmentNavDirection.NEXT: + submitRef.current?.click(); + break; + } }); return () => { sub.unsubscribe(); }; - }, [nextSubject, submitRef]); + }, [nextSubject]); useEffect(() => { reset(); //eslint-disable-next-line - }, []); + }, [reset]); return ( @@ -85,7 +98,13 @@ export const PasswordStep = () => { > { /> diff --git a/src/pages/enrollment/steps/SendFinishStep/SendFinishStep.tsx b/src/pages/enrollment/steps/SendFinishStep/SendFinishStep.tsx new file mode 100644 index 00000000..64a108f6 --- /dev/null +++ b/src/pages/enrollment/steps/SendFinishStep/SendFinishStep.tsx @@ -0,0 +1,126 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { debug, error, info } from '@tauri-apps/plugin-log'; +import { useCallback } from 'react'; +import { shallow } from 'zustand/shallow'; +import { useI18nContext } from '../../../../i18n/i18n-react'; +import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; +import { LoaderSpinner } from '../../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; +import { useToaster } from '../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import type { + ActivateUserRequest, + CreateDeviceResponse, +} from '../../../../shared/hooks/api/types'; +import { clientApi } from '../../../client/clientAPI/clientApi'; +import { clientQueryKeys } from '../../../client/query'; +import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; +import { useEnrollmentApi } from '../../hooks/useEnrollmentApi'; +import './style.scss'; +import useEffectOnce from '../../../../shared/defguard-ui/utils/useEffectOnce'; +import { EnrollmentStepKey } from '../../const'; + +const { saveConfig } = clientApi; + +export const SendFinishStep = () => { + const { LL } = useI18nContext(); + const toaster = useToaster(); + const { + enrollment: { activateUser }, + } = useEnrollmentApi(); + const queryClient = useQueryClient(); + + const finishData = useEnrollmentStore((s) => ({ + phone_number: s.userInfo?.phone_number as string, + password: s.userPassword as string, + })); + + const deviceKeys = useEnrollmentStore((s) => s.deviceKeys); + const deviceResponse = useEnrollmentStore((s) => s.deviceResponse); + + const [setEnrollmentStore] = useEnrollmentStore((state) => [state.setState], shallow); + + const queryFn = useCallback( + async ( + finishData: ActivateUserRequest, + deviceResponse: CreateDeviceResponse, + privateKey: string, + ) => { + await activateUser(finishData); + info('User activated'); + debug('Invoking save_device_config'); + saveConfig({ + privateKey, + response: deviceResponse, + }) + .then(() => { + debug('Config saved'); + setEnrollmentStore({ deviceName: deviceResponse.device.name }); + const invalidate = [clientQueryKeys.getInstances, clientQueryKeys.getLocations]; + invalidate.forEach((key) => { + queryClient.invalidateQueries({ + queryKey: [key], + }); + }); + }) + .catch((e) => { + if (typeof e === 'string') { + if (e.includes('Network Error')) { + toaster.error(LL.common.messages.networkError()); + return; + } + toaster.error(LL.common.messages.errorWithMessage({ message: String(e) })); + } else { + toaster.error( + LL.common.messages.errorWithMessage({ + message: String(e), + }), + ); + } + }); + }, + [ + LL.common.messages.errorWithMessage, + LL.common.messages.networkError, + activateUser, + queryClient.invalidateQueries, + setEnrollmentStore, + toaster.error, + ], + ); + + const { mutate } = useMutation({ + mutationFn: () => + queryFn( + finishData, + deviceResponse as CreateDeviceResponse, + deviceKeys?.private as string, + ), + onError: (e) => { + setEnrollmentStore({ loading: false }); + toaster.error( + LL.common.messages.errorWithMessage({ + message: String(e), + }), + ); + console.error(e); + error(String(e)); + }, + onSuccess: () => { + setEnrollmentStore({ loading: false, step: EnrollmentStepKey.FINISH }); + }, + }); + + useEffectOnce(() => { + setEnrollmentStore({ + loading: true, + }); + setTimeout(() => { + mutate(); + }, 250); + }); + + return ( + + + + ); +}; diff --git a/src/pages/enrollment/steps/SendFinishStep/style.scss b/src/pages/enrollment/steps/SendFinishStep/style.scss new file mode 100644 index 00000000..ff74a618 --- /dev/null +++ b/src/pages/enrollment/steps/SendFinishStep/style.scss @@ -0,0 +1,9 @@ +#enrollment-finish-request-step { + width: 100%; + max-width: 650px; + min-height: 25dvh; + display: flex; + flex-flow: row; + align-items: center; + justify-content: center; +} diff --git a/src/pages/enrollment/steps/WelcomeStep/WelcomeStep.tsx b/src/pages/enrollment/steps/WelcomeStep/WelcomeStep.tsx index a7428724..694b9acc 100644 --- a/src/pages/enrollment/steps/WelcomeStep/WelcomeStep.tsx +++ b/src/pages/enrollment/steps/WelcomeStep/WelcomeStep.tsx @@ -10,6 +10,7 @@ import { useI18nContext } from '../../../../i18n/i18n-react'; import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; import { AdminInfo } from '../../components/AdminInfo/AdminInfo'; import { EnrollmentStepIndicator } from '../../components/EnrollmentStepIndicator/EnrollmentStepIndicator'; +import { EnrollmentStepKey } from '../../const'; import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; export const WelcomeStep = () => { @@ -20,8 +21,8 @@ export const WelcomeStep = () => { ]); const userInfo = useEnrollmentStore((state) => state.userInfo); - const [nextSubject, next] = useEnrollmentStore( - (state) => [state.nextSubject, state.nextStep], + const [nextSubject, setStore] = useEnrollmentStore( + (state) => [state.nextSubject, state.setState], shallow, ); @@ -36,27 +37,26 @@ export const WelcomeStep = () => { }); }, [LL.pages.enrollment.steps.welcome, sessionEnd, sessionStart]); + // biome-ignore lint/correctness/useExhaustiveDependencies: rxjs sub useEffect(() => { const sub = nextSubject.subscribe(() => { - next(); + setStore({ step: EnrollmentStepKey.DATA_VERIFICATION }); }); return () => { sub.unsubscribe(); }; - }, [next, nextSubject]); + }, [nextSubject]); return ( - <> - - -

- {LL.pages.enrollment.steps.welcome.title({ name: `${userInfo?.first_name}` })} -

-
- {markdown} -
- -
- + + +

+ {LL.pages.enrollment.steps.welcome.title({ name: `${userInfo?.first_name}` })} +

+
+ {markdown} +
+ +
); }; diff --git a/src/pages/enrollment/style.scss b/src/pages/enrollment/style.scss index ad2fa9f8..5b391c08 100644 --- a/src/pages/enrollment/style.scss +++ b/src/pages/enrollment/style.scss @@ -16,7 +16,7 @@ text-align: left; } - & > h3 { + h3 { @include typography(app-body-1); color: var(--text-body-primary); diff --git a/src/shared/components/layout/PageContainer/PageContainer.tsx b/src/shared/components/layout/PageContainer/PageContainer.tsx index edb7ba89..34b4d9e0 100644 --- a/src/shared/components/layout/PageContainer/PageContainer.tsx +++ b/src/shared/components/layout/PageContainer/PageContainer.tsx @@ -1,7 +1,7 @@ import './style.scss'; import classNames from 'classnames'; -import { HtmlHTMLAttributes, ReactNode, useMemo } from 'react'; +import { type HtmlHTMLAttributes, type ReactNode, useMemo } from 'react'; type Props = HtmlHTMLAttributes & { children?: ReactNode; diff --git a/src/shared/components/providers/DeepLinkProvider.tsx b/src/shared/components/providers/DeepLinkProvider.tsx new file mode 100644 index 00000000..3f8792b5 --- /dev/null +++ b/src/shared/components/providers/DeepLinkProvider.tsx @@ -0,0 +1,232 @@ +import { invoke } from '@tauri-apps/api/core'; +import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link'; +import { debug, error } from '@tauri-apps/plugin-log'; +import dayjs from 'dayjs'; +import { type PropsWithChildren, useCallback, useEffect, useRef } from 'react'; +import { useNavigate } from 'react-router-dom'; +import z, { string } from 'zod'; +import { clientApi } from '../../../pages/client/clientAPI/clientApi'; +import { useClientStore } from '../../../pages/client/hooks/useClientStore'; +import { AddInstanceFormStep } from '../../../pages/client/pages/ClientAddInstancePage/hooks/types'; +import { useAddInstanceStore } from '../../../pages/client/pages/ClientAddInstancePage/hooks/useAddInstanceStore'; +import { ClientConnectionType } from '../../../pages/client/types'; +import { useEnrollmentStore } from '../../../pages/enrollment/hooks/store/useEnrollmentStore'; +import { useEnrollmentApi } from '../../../pages/enrollment/hooks/useEnrollmentApi'; +import type { EnrollmentStartResponse } from '../../hooks/api/types'; +import { routes } from '../../routes'; + +enum DeepLink { + AddInstance = 'addinstance', +} + +const linkStorageKey = 'lastSuccessfullyHandledDeepLink'; + +const storeLink = (value: string) => { + sessionStorage.setItem(linkStorageKey, value); +}; + +const readStoreLink = (): string | null => { + return sessionStorage.getItem(linkStorageKey); +}; + +const addInstanceLinkSchema = z.object({ + token: string().trim().min(1), + url: string().trim().min(1).url(), +}); + +const AddInstanceLink = z.object({ + link: z.literal(DeepLink.AddInstance), + data: addInstanceLinkSchema, +}); + +const validLinkPayload = z.discriminatedUnion('link', [AddInstanceLink]); + +type LinkPayload = z.infer; + +const linkIntoPayload = (link: URL | null): LinkPayload | null => { + if (link == null) return null; + + const searchData = Object.fromEntries(new URLSearchParams(link.search)); + const linkKey = [link.hostname, link.pathname] + .map((l) => l.trim().replaceAll('/', '')) + .filter((l) => l !== '')[0] as string; + const payload = { + link: linkKey, + data: searchData, + }; + const result = validLinkPayload.safeParse(payload); + if (result.success) { + return result.data; + } else { + error(`Link ${link} was rejected due to schema validation.`); + } + return null; +}; + +const prepareProxyUrl = (value: string) => { + let proxyUrl = value; + if (proxyUrl[proxyUrl.length - 1] === '/') { + proxyUrl = proxyUrl.slice(0, -1); + } + proxyUrl = `${proxyUrl}/api/v1`; + return proxyUrl; +}; + +export const DeepLinkProvider = ({ children }: PropsWithChildren) => { + const mounted = useRef(false); + + const { + enrollment: { start, networkInfo }, + } = useEnrollmentApi(); + + const setEnrollmentState = useEnrollmentStore((s) => s.init); + const setAddInstanceState = useAddInstanceStore((s) => s.setState); + const setClientState = useClientStore((s) => s.setState); + + const navigate = useNavigate(); + + // biome-ignore lint/correctness/useExhaustiveDependencies: should init once + const handleValidLink = useCallback(async (payload: LinkPayload, rawLink?: string) => { + const { data, link } = payload; + switch (link) { + case DeepLink.AddInstance: + await start({ + token: data.token, + proxyUrl: prepareProxyUrl(data.url), + }).then(async (response) => { + if (response.ok) { + const authCookie = response.headers + .getSetCookie() + .find((cookie) => cookie.startsWith('defguard_proxy=')); + if (authCookie === undefined) { + error('Failed to open deep link, auth cookie missing from proxy response.'); + return; + } + const respData = (await response.json()) as EnrollmentStartResponse; + const instances = await clientApi.getInstances(); + const proxy_api_url = prepareProxyUrl( + respData.instance.proxy_url ?? respData.instance.url, + ); + const existingInstance = instances.find( + (instance) => instance.uuid === respData.instance.id, + ); + if (existingInstance) { + // update existing instance instead + const networkInfoResp = await networkInfo( + { + pubkey: existingInstance.pubkey, + }, + proxy_api_url, + authCookie, + ); + await invoke('update_instance', { + instanceId: existingInstance.id, + response: networkInfoResp, + }); + setClientState({ + selectedInstance: { + type: ClientConnectionType.LOCATION, + id: existingInstance.id, + }, + }); + if (rawLink) { + storeLink(rawLink); + } + debug(`Updated ${existingInstance.name} via deep link`); + navigate(routes.client.base, { replace: true }); + return; + } + if (!respData.user.enrolled) { + // user needs full enrollment + const sessionEnd = dayjs + .unix(respData.deadline_timestamp) + .utc() + .local() + .format(); + const sessionStart = dayjs().local().format(); + // set enrollment + setEnrollmentState({ + enrollmentSettings: respData.settings, + proxy_url: proxy_api_url, + userInfo: respData.user, + adminInfo: respData.admin, + endContent: respData.final_page_content, + cookie: authCookie, + sessionEnd, + sessionStart, + }); + navigate('/enrollment', { replace: true }); + } else { + // only needs to register this device + setAddInstanceState({ + step: AddInstanceFormStep.DEVICE, + response: { + cookie: authCookie, + device_names: respData.user.device_names, + url: proxy_api_url, + }, + }); + navigate('/client/add-instance', { replace: true }); + } + } else { + error( + `Add instance from deep link failed! Proxy enrollment start request failed! status: ${response.status}`, + ); + } + }); + break; + } + if (rawLink) { + storeLink(rawLink); + } + }, []); + + // biome-ignore lint/correctness/useExhaustiveDependencies: only on mount + useEffect(() => { + if (!mounted.current) { + mounted.current = true; + + let unlisten: (() => void) | undefined; + (async () => { + const start = await getCurrent(); + if (start != null) { + const lastLink = readStoreLink(); + // if the link is exact as last successfully executed link + // this is only necessary bcs in dev mode window is hot reloaded causing the startup link to be handled multiple times over. + if (lastLink != null && lastLink === start[0]) { + return; + } + const payload = linkIntoPayload(new URL(start[0])); + if (payload != null) { + try { + handleValidLink(payload, start[0]); + } catch (e) { + error( + `Failed to handle valid deep link ${payload.link}!\n${JSON.stringify(e)}`, + ); + } + } + } + unlisten = await onOpenUrl((urls) => { + if (urls?.length) { + const link = urls[0]; + const payload = linkIntoPayload(new URL(link)); + if (payload != null) { + try { + handleValidLink(payload); + } catch (e) { + error(`Failed to handle valid deep link ${payload?.link} action!`); + error(JSON.stringify(e)); + } + } + } + }); + })(); + return () => { + unlisten?.(); + }; + } + }, []); + + return <>{children}; +}; diff --git a/src/shared/components/svg/DefguardLogoCollapsed.tsx b/src/shared/components/svg/DefguardLogoCollapsed.tsx index d7767eef..0126db6b 100644 --- a/src/shared/components/svg/DefguardLogoCollapsed.tsx +++ b/src/shared/components/svg/DefguardLogoCollapsed.tsx @@ -1,4 +1,5 @@ import type { SVGProps } from 'react'; + const SvgDefguardLogoCollapsed = (props: SVGProps) => ( ) => ( ) => ( ) => ( ) => ( ) => ( ) => ( ) => ( ) => ( ; @@ -23,15 +22,25 @@ export type UserInfo = { export type EnrollmentStartRequest = { token: string; + proxyUrl?: string; +}; + +export type EnrollmentSettings = { + admin_device_management: boolean; + mfa_required: boolean; + only_client_activation: boolean; + smtp_configured: boolean; + vpn_setup_optional: boolean; }; export type EnrollmentStartResponse = { admin: AdminInfo; user: UserInfo; + instance: EnrollmentInstanceInfo; deadline_timestamp: number; final_page_content: string; vpn_setup_optional: boolean; - instance: EnrollmentInstanceInfo; + settings: EnrollmentSettings; }; export type ActivateUserRequest = { @@ -95,6 +104,9 @@ export type EnrollmentInstanceInfo = { id: string; name: string; url: string; + proxy_url?: string; + username: string; + openid_display_name?: string; }; export type NewApplicationVersionInfo = { @@ -104,14 +116,36 @@ export type NewApplicationVersionInfo = { update_url: string; }; +export type RegisterCodeMfaFinishRequest = { + code: string; + method: MfaMethod; +}; + +export type RegisterCodeMfaStartResponse = { + totp_secret?: string; +}; + +export type RegisterCodeMfaFinishResponse = { + recovery_codes: string[]; +}; + // FIXME: strong types export type UseApi = { enrollment: { - start: (data: EnrollmentStartRequest) => Promise>; - activateUser: (data: ActivateUserRequest) => Promise>; - createDevice: (data: CreateDeviceRequest) => Promise>; + networkInfo: ( + data: { pubkey: string }, + proxyUrl?: string, + cookie?: string, + ) => Promise; + start: (data: EnrollmentStartRequest) => Promise; + activateUser: (data: ActivateUserRequest) => Promise; + createDevice: (data: CreateDeviceRequest) => Promise; + registerCodeMfaStart: (method: MfaMethod) => Promise; + registerCodeMfaFinish: ( + data: RegisterCodeMfaFinishRequest, + ) => Promise; }; - getAppInfo: () => Promise>; + getAppInfo: () => Promise; }; export type EnrollmentError = { diff --git a/src/shared/hooks/api/utils.ts b/src/shared/hooks/api/utils.ts index 50e9a9b7..6539474b 100644 --- a/src/shared/hooks/api/utils.ts +++ b/src/shared/hooks/api/utils.ts @@ -1,4 +1,4 @@ -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: intended export const removeNulls = (obj: any) => { return JSON.parse(JSON.stringify(obj), (_, value) => { if (value == null) return undefined; diff --git a/src/shared/hooks/useClipboard.ts b/src/shared/hooks/useClipboard.ts new file mode 100644 index 00000000..2db90825 --- /dev/null +++ b/src/shared/hooks/useClipboard.ts @@ -0,0 +1,32 @@ +import { writeText } from '@tauri-apps/plugin-clipboard-manager'; +import { useCallback } from 'react'; + +import { useI18nContext } from '../../i18n/i18n-react'; +import { useToaster } from '../defguard-ui/hooks/toasts/useToaster'; + +export const useClipboard = () => { + const { LL } = useI18nContext(); + + const toaster = useToaster(); + + const writeToClipboard = useCallback( + async (content: string, customMessage?: string) => { + try { + await writeText(content); + if (customMessage) { + toaster.success(customMessage); + } else { + toaster.success(LL.common.messages.clipboard.success()); + } + } catch (e) { + toaster.error(LL.common.messages.clipboard.error()); + console.error(e); + } + }, + [LL.common.messages, toaster], + ); + + return { + writeToClipboard, + }; +}; diff --git a/src/shared/patterns.ts b/src/shared/patterns.ts index b2e47252..0dc65fbb 100644 --- a/src/shared/patterns.ts +++ b/src/shared/patterns.ts @@ -65,7 +65,7 @@ export const patternValidUrl = new RegExp( ); export const patternValidDomain = - /^(?:(?:(?:[a-zA-z\-]+)\:\/{1,3})?(?:[a-zA-Z0-9])(?:[a-zA-Z0-9\-\.]){1,61}(?:\.[a-zA-Z]{2,})+|\[(?:(?:(?:[a-fA-F0-9]){1,4})(?::(?:[a-fA-F0-9]){1,4}){7}|::1|::)\]|(?:(?:[0-9]{1,3})(?:\.[0-9]{1,3}){3}))(?:\:[0-9]{1,5})?$/; + /^(?:(?:(?:[a-zA-z-]+):\/{1,3})?(?:[a-zA-Z0-9])(?:[a-zA-Z0-9\-.]){1,61}(?:\.[a-zA-Z]{2,})+|\[(?:(?:(?:[a-fA-F0-9]){1,4})(?::(?:[a-fA-F0-9]){1,4}){7}|::1|::)\]|(?:(?:[0-9]{1,3})(?:\.[0-9]{1,3}){3}))(?::[0-9]{1,5})?$/; export const patternValidIp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/32)?$/; diff --git a/src/shared/providers/ThemeProvider/ThemeProvider.tsx b/src/shared/providers/ThemeProvider/ThemeProvider.tsx index 14c09692..9f9b7470 100644 --- a/src/shared/providers/ThemeProvider/ThemeProvider.tsx +++ b/src/shared/providers/ThemeProvider/ThemeProvider.tsx @@ -1,7 +1,7 @@ -import { ReactNode, useEffect } from 'react'; +import { type ReactNode, useEffect } from 'react'; import { useClientStore } from '../../../pages/client/hooks/useClientStore'; -import { ThemeKey } from '../../defguard-ui/hooks/theme/types'; +import type { ThemeKey } from '../../defguard-ui/hooks/theme/types'; type Props = { children: ReactNode; @@ -13,7 +13,7 @@ export const ThemeProvider = ({ children }: Props) => { useEffect(() => { const current = document.documentElement.dataset.theme as ThemeKey; - if (currentTheme != current) { + if (currentTheme !== current) { document.documentElement.dataset.theme = currentTheme; } }, [currentTheme]); diff --git a/src/shared/types.ts b/src/shared/types.ts new file mode 100644 index 00000000..bee7e50f --- /dev/null +++ b/src/shared/types.ts @@ -0,0 +1,7 @@ +export enum MfaMethod { + TOTP = 'Totp', + EMAIL = 'Email', + OIDC = 'Oidc', + BIOMETRIC = 'Biometric', + MOBILE_APPROVE = 'MobileApprove', +} diff --git a/src/shared/utils/getStatsFilterValue.ts b/src/shared/utils/getStatsFilterValue.ts index 09bdca21..a063da3c 100644 --- a/src/shared/utils/getStatsFilterValue.ts +++ b/src/shared/utils/getStatsFilterValue.ts @@ -1,5 +1,6 @@ import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; + dayjs.extend(utc); export const getStatsFilterValue = (hours: number): string => diff --git a/src/shared/validators/password.ts b/src/shared/validators/password.ts index 4af42495..2088afcf 100644 --- a/src/shared/validators/password.ts +++ b/src/shared/validators/password.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import { TranslationFunctions } from '../../i18n/i18n-types'; +import type { TranslationFunctions } from '../../i18n/i18n-types'; import { patternAtLeastOneDigit, patternAtLeastOneLowerCaseChar, diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 00000000..cbcdbd82 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + "target": "ES2023", + "useDefineForClassFields": true, + "lib": [ + "ESNext", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "plugins": [ + { + "name": "typescript-eslint-language-service" + } + ], + "types": [ + "vite/client" + ] + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "dist", + "build" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 71027951..e14af76c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,4 +45,4 @@ "path": "./tsconfig.node.json" } ] -} \ No newline at end of file +} diff --git a/tsconfig.node.tsbuildinfo b/tsconfig.node.tsbuildinfo new file mode 100644 index 00000000..ca3935e3 --- /dev/null +++ b/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/buffer.buffer.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/utility.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/client-stats.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/h2c-client.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/mock-call-history.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/cache-interceptor.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@7.10.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/dom-events.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@24.2.1/node_modules/@types/node/index.d.ts","./node_modules/.pnpm/@swc+types@0.1.24/node_modules/@swc/types/assumptions.d.ts","./node_modules/.pnpm/@swc+types@0.1.24/node_modules/@swc/types/index.d.ts","./node_modules/.pnpm/@swc+core@1.13.3/node_modules/@swc/core/binding.d.ts","./node_modules/.pnpm/@swc+core@1.13.3/node_modules/@swc/core/spack.d.ts","./node_modules/.pnpm/@swc+core@1.13.3/node_modules/@swc/core/index.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/dist/node/modulerunnertransport-bwuzbvlx.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/customevent.d.ts","./node_modules/.pnpm/@types+estree@1.0.8/node_modules/@types/estree/index.d.ts","./node_modules/.pnpm/rollup@4.46.2/node_modules/rollup/dist/rollup.d.ts","./node_modules/.pnpm/rollup@4.46.2/node_modules/rollup/dist/parseast.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/hot.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/dist/node/module-runner.d.ts","./node_modules/.pnpm/esbuild@0.25.8/node_modules/esbuild/lib/main.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/internal/terseroptions.d.ts","./node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/previous-map.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/input.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/declaration.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/root.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/warning.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/processor.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/result.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/document.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/rule.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/node.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/comment.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/container.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/at-rule.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/list.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/postcss.d.ts","./node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/postcss.d.mts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/internal/lightningcssoptions.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/util/promise_or.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/importer.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/logger/source_location.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/logger/source_span.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/logger/index.d.ts","./node_modules/.pnpm/immutable@4.3.7/node_modules/immutable/dist/immutable.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/boolean.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/calculation.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/color.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/function.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/list.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/map.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/mixin.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/number.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/string.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/argument_list.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/value/index.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/options.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/compile.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/exception.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/legacy/exception.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/legacy/plugin_this.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/legacy/function.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/legacy/importer.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/legacy/options.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/legacy/render.d.ts","./node_modules/.pnpm/sass@1.70.0/node_modules/sass/types/index.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/internal/csspreprocessoroptions.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/importglob.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/types/metadata.d.ts","./node_modules/.pnpm/vite@7.1.1_@types+node@24.2.1_sass@1.70.0/node_modules/vite/dist/node/index.d.ts","./node_modules/.pnpm/@vitejs+plugin-react-swc@3.11.0_vite@7.1.1_@types+node@24.2.1_sass@1.70.0_/node_modules/@vitejs/plugin-react-swc/index.d.ts","./node_modules/.pnpm/browserslist@4.25.2/node_modules/browserslist/index.d.ts","./node_modules/.pnpm/autoprefixer@10.4.21_postcss@8.5.6/node_modules/autoprefixer/lib/autoprefixer.d.ts","./vite.config.ts","./node_modules/.pnpm/@types+byte-size@8.1.2/node_modules/@types/byte-size/index.d.ts","./node_modules/.pnpm/@types+file-saver@2.0.7/node_modules/@types/file-saver/index.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/common.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/array.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/collection.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/date.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/function.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/lang.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/math.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/number.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/object.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/seq.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/string.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/common/util.d.ts","./node_modules/.pnpm/@types+lodash@4.17.20/node_modules/@types/lodash/index.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/add.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/after.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ary.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assign.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assignin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assigninwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assignwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/at.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/attempt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/before.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/bind.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/bindall.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/bindkey.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/camelcase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/capitalize.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/castarray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ceil.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/chain.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/chunk.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clamp.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clone.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clonedeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clonedeepwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clonewith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/compact.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/concat.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/cond.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/conforms.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/conformsto.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/constant.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/countby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/create.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/curry.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/curryright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/debounce.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/deburr.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defaults.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defaultsdeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defaultto.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defer.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/delay.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/difference.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/differenceby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/differencewith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/divide.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/drop.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/dropright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/droprightwhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/dropwhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/each.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/eachright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/endswith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/entries.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/entriesin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/eq.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/escape.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/escaperegexp.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/every.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/extend.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/extendwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/fill.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/filter.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/find.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findindex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findkey.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findlast.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findlastindex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findlastkey.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/first.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatmap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatmapdeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatmapdepth.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatten.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flattendeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flattendepth.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flip.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/floor.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flow.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flowright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/foreach.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/foreachright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forinright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forown.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forownright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/frompairs.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/functions.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/functionsin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/get.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/groupby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/gt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/gte.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/has.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/hasin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/head.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/identity.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/includes.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/indexof.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/initial.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/inrange.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/intersection.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/intersectionby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/intersectionwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invert.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invertby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invoke.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invokemap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isarguments.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isarray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isarraybuffer.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isarraylike.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isarraylikeobject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isboolean.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isbuffer.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isdate.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/iselement.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isempty.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isequal.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isequalwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/iserror.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isfinite.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isfunction.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isinteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/islength.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ismap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ismatch.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ismatchwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isnan.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isnative.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isnil.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isnull.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isnumber.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isobject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isobjectlike.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isplainobject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isregexp.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/issafeinteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isset.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isstring.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/issymbol.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/istypedarray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isundefined.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isweakmap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isweakset.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/iteratee.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/join.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/kebabcase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/keyby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/keys.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/keysin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/last.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lastindexof.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lowercase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lowerfirst.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lte.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/map.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mapkeys.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mapvalues.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/matches.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/matchesproperty.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/max.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/maxby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mean.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/meanby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/memoize.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/merge.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mergewith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/method.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/methodof.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/min.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/minby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mixin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/multiply.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/negate.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/noop.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/now.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/nth.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ntharg.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/omit.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/omitby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/once.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/orderby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/over.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/overargs.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/overevery.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/oversome.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pad.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/padend.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/padstart.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/parseint.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/partial.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/partialright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/partition.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pick.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pickby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/property.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/propertyof.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pull.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullall.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullallby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullallwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullat.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/random.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/range.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/rangeright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/rearg.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reduce.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reduceright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/remove.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/repeat.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/replace.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/rest.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/result.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reverse.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/round.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sample.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/samplesize.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/set.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/setwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/shuffle.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/size.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/slice.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/snakecase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/some.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedindex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedindexby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedindexof.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedlastindex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedlastindexby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedlastindexof.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sorteduniq.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sorteduniqby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/split.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/spread.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/startcase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/startswith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubarray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubfalse.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubobject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubstring.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubtrue.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/subtract.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sum.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sumby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tail.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/take.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/takeright.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/takerightwhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/takewhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/template.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/templatesettings.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/throttle.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/thru.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/times.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toarray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tofinite.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tointeger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tolength.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tolower.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tonumber.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/topairs.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/topairsin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/topath.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toplainobject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tosafeinteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tostring.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toupper.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/transform.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/trim.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/trimend.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/trimstart.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/truncate.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unary.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unescape.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/union.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unionby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unionwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniq.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniqby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniqueid.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniqwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unset.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unzip.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unzipwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/update.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/updatewith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uppercase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/upperfirst.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/values.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/valuesin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/without.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/words.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/wrap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/xor.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/xorby.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/xorwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zip.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zipobject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zipobjectdeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zipwith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/index.d.ts","./node_modules/.pnpm/@types+react@18.3.23/node_modules/@types/react/global.d.ts","./node_modules/.pnpm/csstype@3.1.3/node_modules/csstype/index.d.ts","./node_modules/.pnpm/@types+prop-types@15.7.15/node_modules/@types/prop-types/index.d.ts","./node_modules/.pnpm/@types+react@18.3.23/node_modules/@types/react/index.d.ts","./node_modules/.pnpm/@types+react-dom@18.3.7_@types+react@18.3.23/node_modules/@types/react-dom/index.d.ts"],"fileIdsList":[[54,99],[54,99,149,151,152,153],[54,99,151],[54,99,150],[54,99,235],[54,99,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539],[54,99,223,225,226,227,228,229,230,231,232,233,234,235],[54,99,223,224,226,227,228,229,230,231,232,233,234,235],[54,99,224,225,226,227,228,229,230,231,232,233,234,235],[54,99,223,224,225,227,228,229,230,231,232,233,234,235],[54,99,223,224,225,226,228,229,230,231,232,233,234,235],[54,99,223,224,225,226,227,229,230,231,232,233,234,235],[54,99,223,224,225,226,227,228,230,231,232,233,234,235],[54,99,223,224,225,226,227,228,229,231,232,233,234,235],[54,99,223,224,225,226,227,228,229,230,232,233,234,235],[54,99,223,224,225,226,227,228,229,230,231,233,234,235],[54,99,223,224,225,226,227,228,229,230,231,232,234,235],[54,99,223,224,225,226,227,228,229,230,231,232,233,235],[54,99,223,224,225,226,227,228,229,230,231,232,233,234],[54,96,99],[54,98,99],[99],[54,99,104,134],[54,99,100,105,111,112,119,131,142],[54,99,100,101,111,119],[54,99,102,143],[54,99,103,104,112,120],[54,99,104,131,139],[54,99,105,107,111,119],[54,98,99,106],[54,99,107,108],[54,99,109,111],[54,98,99,111],[54,99,111,112,113,131,142],[54,99,111,112,113,126,131,134],[54,94,99],[54,94,99,107,111,114,119,131,142],[54,99,111,112,114,115,119,131,139,142],[54,99,114,116,131,139,142],[52,53,54,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148],[54,99,111,117],[54,99,118,142],[54,99,107,111,119,131],[54,99,120],[54,99,121],[54,98,99,122],[54,96,97,98,99,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148],[54,99,124],[54,99,125],[54,99,111,126,127],[54,99,126,128,143,145],[54,99,111,131,132,134],[54,99,133,134],[54,99,131,132],[54,99,134],[54,99,135],[54,96,99,131,136],[54,99,111,137,138],[54,99,137,138],[54,99,104,119,131,139],[54,99,140],[54,99,119,141],[54,99,114,125,142],[54,99,104,143],[54,99,131,144],[54,99,118,145],[54,99,146],[54,99,111,113,122,131,134,142,144,145,147],[54,99,131,148],[54,99,544],[54,99,541,542,543],[54,99,154,216],[54,99,184,218],[54,99,180],[54,99,178,180],[54,99,169,177,178,179,181,183],[54,99,167],[54,99,170,175,180,183],[54,99,166,183],[54,99,170,171,174,175,176,183],[54,99,170,171,172,174,175,183],[54,99,167,168,169,170,171,175,176,177,179,180,181,183],[54,99,183],[54,99,165,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182],[54,99,165,183],[54,99,170,172,173,175,176,183],[54,99,174,183],[54,99,175,176,180,183],[54,99,168,178],[54,99,159,215,216],[54,99,158,159],[54,99,165,203],[54,99,190],[54,99,186,203],[54,99,186,187,190,202,203,204,205,206,207,208,209,210,211],[54,99,207],[54,99,190,208,209],[54,99,206,210],[54,99,188,189],[54,99,188],[54,99,186,187,190,202],[54,99,191,196,202],[54,99,202],[54,99,191,202],[54,99,191,192,193,194,195,196,197,198,199,200,201],[54,61,64,67,68,99,142],[54,64,99,131,142],[54,64,68,99,142],[54,99,131],[54,58,99],[54,62,99],[54,60,61,64,99,142],[54,99,119,139],[54,99,149],[54,58,99,149],[54,60,64,99,119,142],[54,55,56,57,59,63,99,111,131,142],[54,64,72,99],[54,56,62,99],[54,64,88,89,99],[54,56,59,64,99,134,142,149],[54,64,99],[54,60,64,99,142],[54,55,99],[54,58,59,60,62,63,64,65,66,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,89,90,91,92,93,99],[54,64,81,84,99,107],[54,64,72,73,74,99],[54,62,64,73,75,99],[54,63,99],[54,56,58,64,99],[54,64,68,73,75,99],[54,68,99],[54,62,64,67,99,142],[54,56,60,64,72,99],[54,64,81,99],[54,58,64,88,99,134,147,149],[54,99,111,112,114,115,116,119,131,139,142,148,149,155,156,157,159,160,162,163,164,184,185,213,214,215,216],[54,99,155,156,157,161],[54,99,155],[54,99,157],[54,99,212],[54,99,159,216],[54,99,121,216,217,219]],"fileInfos":[{"version":"a7297ff837fcdf174a9524925966429eb8e5feecc2cc55cc06574e6b092c1eaa","impliedFormat":1},{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"4245fee526a7d1754529d19227ecbf3be066ff79ebb6a380d78e41648f2f224d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0671b50bb99cc7ad46e9c68fa0e7f15ba4bc898b59c31a17ea4611fab5095da","affectsGlobalScope":true,"impliedFormat":1},{"version":"d802f0e6b5188646d307f070d83512e8eb94651858de8a82d1e47f60fb6da4e2","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"487b694c3de27ddf4ad107d4007ad304d29effccf9800c8ae23c2093638d906a","impliedFormat":1},{"version":"e525f9e67f5ddba7b5548430211cae2479070b70ef1fd93550c96c10529457bd","impliedFormat":1},{"version":"ccf4552357ce3c159ef75f0f0114e80401702228f1898bdc9402214c9499e8c0","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"4bc0794175abedf989547e628949888c1085b1efcd93fc482bccd77ee27f8b7c","impliedFormat":1},{"version":"3c8e93af4d6ce21eb4c8d005ad6dc02e7b5e6781f429d52a35290210f495a674","impliedFormat":1},{"version":"2c9875466123715464539bfd69bcaccb8ff6f3e217809428e0d7bd6323416d01","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"33e981bf6376e939f99bd7f89abec757c64897d33c005036b9a10d9587d80187","impliedFormat":1},{"version":"6c8e442ba33b07892169a14f7757321e49ab0f1032d676d321a1fdab8a67d40c","impliedFormat":1},{"version":"b41767d372275c154c7ea6c9d5449d9a741b8ce080f640155cc88ba1763e35b3","impliedFormat":1},{"version":"1cd673d367293fc5cb31cd7bf03d598eb368e4f31f39cf2b908abbaf120ab85a","impliedFormat":1},{"version":"af13e99445f37022c730bfcafcdc1761e9382ce1ea02afb678e3130b01ce5676","impliedFormat":1},{"version":"3825bf209f1662dfd039010a27747b73d0ef379f79970b1d05601ec8e8a4249f","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"da52342062e70c77213e45107921100ba9f9b3a30dd019444cf349e5fb3470c4","impliedFormat":1},{"version":"e9ace91946385d29192766bf783b8460c7dbcbfc63284aa3c9cae6de5155c8bc","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"1e30c045732e7db8f7a82cf90b516ebe693d2f499ce2250a977ec0d12e44a529","impliedFormat":1},{"version":"84b736594d8760f43400202859cda55607663090a43445a078963031d47e25e7","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"54c3e2371e3d016469ad959697fd257e5621e16296fa67082c2575d0bf8eced0","impliedFormat":1},{"version":"beb8233b2c220cfa0feea31fbe9218d89fa02faa81ef744be8dce5acb89bb1fd","impliedFormat":1},{"version":"78b29846349d4dfdd88bd6650cc5d2baaa67f2e89dc8a80c8e26ef7995386583","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"e38d4fdf79e1eadd92ed7844c331dbaa40f29f21541cfee4e1acff4db09cda33","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"7c10a32ae6f3962672e6869ee2c794e8055d8225ef35c91c0228e354b4e5d2d3","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"99f569b42ea7e7c5fe404b2848c0893f3e1a56e0547c1cd0f74d5dbb9a9de27e","impliedFormat":1},{"version":"f4b4faedc57701ae727d78ba4a83e466a6e3bdcbe40efbf913b17e860642897c","affectsGlobalScope":true,"impliedFormat":1},{"version":"bbcfd9cd76d92c3ee70475270156755346c9086391e1b9cb643d072e0cf576b8","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"72c1f5e0a28e473026074817561d1bc9647909cf253c8d56c41d1df8d95b85f7","impliedFormat":1},{"version":"003ec918ec442c3a4db2c36dc0c9c766977ea1c8bcc1ca7c2085868727c3d3f6","affectsGlobalScope":true,"impliedFormat":1},{"version":"938f94db8400d0b479626b9006245a833d50ce8337f391085fad4af540279567","impliedFormat":1},{"version":"c4e8e8031808b158cfb5ac5c4b38d4a26659aec4b57b6a7e2ba0a141439c208c","impliedFormat":1},{"version":"2c91d8366ff2506296191c26fd97cc1990bab3ee22576275d28b654a21261a44","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"db39d9a16e4ddcd8a8f2b7b3292b362cc5392f92ad7ccd76f00bccf6838ac7de","affectsGlobalScope":true,"impliedFormat":1},{"version":"289e9894a4668c61b5ffed09e196c1f0c2f87ca81efcaebdf6357cfb198dac14","impliedFormat":1},{"version":"25a1105595236f09f5bce42398be9f9ededc8d538c258579ab662d509aa3b98e","impliedFormat":1},{"version":"5078cd62dbdf91ae8b1dc90b1384dec71a9c0932d62bdafb1a811d2a8e26bef2","impliedFormat":1},{"version":"a2e2bbde231b65c53c764c12313897ffdfb6c49183dd31823ee2405f2f7b5378","impliedFormat":1},{"version":"ad1cc0ed328f3f708771272021be61ab146b32ecf2b78f3224959ff1e2cd2a5c","impliedFormat":1},{"version":"71450bbc2d82821d24ca05699a533e72758964e9852062c53b30f31c36978ab8","affectsGlobalScope":true,"impliedFormat":1},{"version":"62f572306e0b173cc5dfc4c583471151f16ef3779cf27ab96922c92ec82a3bc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"067bdd82d9768baddbdc8df51d85f7b96387c47176bf7f895d2e21e2b6b2f1f4","impliedFormat":1},{"version":"42d30e7d04915facc3ded22b4127c9f517973b4c2b1326e56c10ff70daf6800a","impliedFormat":1},{"version":"bd8b644c5861b94926687618ec2c9e60ad054d334d6b7eb4517f23f53cb11f91","impliedFormat":1},{"version":"bcbabfaca3f6b8a76cb2739e57710daf70ab5c9479ab70f5351c9b4932abf6bd","impliedFormat":1},{"version":"77fced47f495f4ff29bb49c52c605c5e73cd9b47d50080133783032769a9d8a6","impliedFormat":1},{"version":"55f370475031b3d36af8dd47fb3934dff02e0f1330d13f1977c9e676af5c2e70","impliedFormat":1},{"version":"c54f0b30a787b3df16280f4675bd3d9d17bf983ae3cd40087409476bc50b922d","affectsGlobalScope":true,"impliedFormat":1},{"version":"0f5cda0282e1d18198e2887387eb2f026372ebc4e11c4e4516fef8a19ee4d514","impliedFormat":1},{"version":"e99b0e71f07128fc32583e88ccd509a1aaa9524c290efb2f48c22f9bf8ba83b1","impliedFormat":1},{"version":"76957a6d92b94b9e2852cf527fea32ad2dc0ef50f67fe2b14bd027c9ceef2d86","impliedFormat":1},{"version":"5e9f8c1e042b0f598a9be018fc8c3cb670fe579e9f2e18e3388b63327544fe16","affectsGlobalScope":true,"impliedFormat":1},{"version":"a8a99a5e6ed33c4a951b67cc1fd5b64fd6ad719f5747845c165ca12f6c21ba16","affectsGlobalScope":true,"impliedFormat":1},{"version":"a58a15da4c5ba3df60c910a043281256fa52d36a0fcdef9b9100c646282e88dd","impliedFormat":1},{"version":"b36beffbf8acdc3ebc58c8bb4b75574b31a2169869c70fc03f82895b93950a12","impliedFormat":1},{"version":"de263f0089aefbfd73c89562fb7254a7468b1f33b61839aafc3f035d60766cb4","impliedFormat":1},{"version":"70b57b5529051497e9f6482b76d91c0dcbb103d9ead8a0549f5bab8f65e5d031","impliedFormat":1},{"version":"8c81fd4a110490c43d7c578e8c6f69b3af01717189196899a6a44f93daa57a3a","impliedFormat":1},{"version":"1013eb2e2547ad8c100aca52ef9df8c3f209edee32bb387121bb3227f7c00088","impliedFormat":1},{"version":"e07c573ac1971ea89e2c56ff5fd096f6f7bba2e6dbcd5681d39257c8d954d4a8","impliedFormat":1},{"version":"363eedb495912790e867da6ff96e81bf792c8cfe386321e8163b71823a35719a","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"125d792ec6c0c0f657d758055c494301cc5fdb327d9d9d5960b3f129aff76093","impliedFormat":1},{"version":"dba28a419aec76ed864ef43e5f577a5c99a010c32e5949fe4e17a4d57c58dd11","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea713aa14a670b1ea0fbaaca4fd204e645f71ca7653a834a8ec07ee889c45de6","impliedFormat":1},{"version":"1e080418e53f9b7a05db81ab517c4e1d71b7194ee26ddd54016bcef3ac474bd4","impliedFormat":1},{"version":"9705cd157ffbb91c5cab48bdd2de5a437a372e63f870f8a8472e72ff634d47c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ae86f30d5d10e4f75ce8dcb6e1bd3a12ecec3d071a21e8f462c5c85c678efb41","impliedFormat":1},{"version":"982efeb2573605d4e6d5df4dc7e40846bda8b9e678e058fc99522ab6165c479e","impliedFormat":1},{"version":"e03460fe72b259f6d25ad029f085e4bedc3f90477da4401d8fbc1efa9793230e","impliedFormat":1},{"version":"4286a3a6619514fca656089aee160bb6f2e77f4dd53dc5a96b26a0b4fc778055","impliedFormat":1},{"version":"3b63610eaabadf26aadf51a563e4b2a8bf56eeaab1094f2a2b21509008eaef0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"2d5d50cd0667d9710d4d2f6e077cc4e0f9dc75e106cccaea59999b36873c5a0d","affectsGlobalScope":true,"impliedFormat":1},{"version":"784490137935e1e38c49b9289110e74a1622baf8a8907888dcbe9e476d7c5e44","impliedFormat":1},{"version":"42180b657831d1b8fead051698618b31da623fb71ff37f002cb9d932cfa775f1","impliedFormat":1},{"version":"4f98d6fb4fe7cbeaa04635c6eaa119d966285d4d39f0eb55b2654187b0b27446","impliedFormat":1},{"version":"f8529fe0645fd9af7441191a4961497cc7638f75a777a56248eac6a079bb275d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4445f6ce6289c5b2220398138da23752fd84152c5c95bb8b58dedefc1758c036","impliedFormat":1},{"version":"a51f786b9f3c297668f8f322a6c58f85d84948ef69ade32069d5d63ec917221c","impliedFormat":1},{"version":"7a0b3e902cabef41f2d37e5eb4dab644c5b8470594318810434df7cc547b0cf8","impliedFormat":1},{"version":"e32c3ff360bf74a4257a289b9ed4f67196e7b66233942752b13781b23499c0db","impliedFormat":1},{"version":"d7d1b49e0462eb979fd506c9667f1d4afbb0d39940ec9da5ef4473d1b952b0b6","impliedFormat":1},{"version":"136ac2fb228b2c64ad2d039eb4de311212505a20a91b9ba632bd6cfdc3b4126f","impliedFormat":1},{"version":"7d98e7acbe7ffe68b699bf7656af842f5d5efecd1df67800b92ed71ed60f2287","impliedFormat":1},{"version":"a7ca8df4f2931bef2aa4118078584d84a0b16539598eaadf7dce9104dfaa381c","impliedFormat":1},{"version":"5c31dea483b64cbb341ea8a7073c457720d1574f87837e71cccb70ce91196211","impliedFormat":99},{"version":"11443a1dcfaaa404c68d53368b5b818712b95dd19f188cab1669c39bee8b84b3","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"ffb518fc55181aefd066c690dbc0f8fa6a1533c8ddac595469c8c5f7fda2d756","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"36977c14a7f7bfc8c0426ae4343875689949fb699f3f84ecbe5b300ebf9a2c55","impliedFormat":1},{"version":"217d7b67dacf8438f0be82b846f933981a1e6527e63c082c56adaf4782d62ab4","impliedFormat":99},{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"8885cf05f3e2abf117590bbb951dcf6359e3e5ac462af1c901cfd24c6a6472e2","impliedFormat":1},{"version":"333caa2bfff7f06017f114de738050dd99a765c7eb16571c6d25a38c0d5365dc","impliedFormat":1},{"version":"e61df3640a38d535fd4bc9f4a53aef17c296b58dc4b6394fd576b808dd2fe5e6","impliedFormat":1},{"version":"459920181700cec8cbdf2a5faca127f3f17fd8dd9d9e577ed3f5f3af5d12a2e4","impliedFormat":1},{"version":"4719c209b9c00b579553859407a7e5dcfaa1c472994bd62aa5dd3cc0757eb077","impliedFormat":1},{"version":"7ec359bbc29b69d4063fe7dad0baaf35f1856f914db16b3f4f6e3e1bca4099fa","impliedFormat":1},{"version":"70790a7f0040993ca66ab8a07a059a0f8256e7bb57d968ae945f696cbff4ac7a","impliedFormat":1},{"version":"d1b9a81e99a0050ca7f2d98d7eedc6cda768f0eb9fa90b602e7107433e64c04c","impliedFormat":1},{"version":"a022503e75d6953d0e82c2c564508a5c7f8556fad5d7f971372d2d40479e4034","impliedFormat":1},{"version":"b215c4f0096f108020f666ffcc1f072c81e9f2f95464e894a5d5f34c5ea2a8b1","impliedFormat":1},{"version":"644491cde678bd462bb922c1d0cfab8f17d626b195ccb7f008612dc31f445d2d","impliedFormat":1},{"version":"dfe54dab1fa4961a6bcfba68c4ca955f8b5bbeb5f2ab3c915aa7adaa2eabc03a","impliedFormat":1},{"version":"1251d53755b03cde02466064260bb88fd83c30006a46395b7d9167340bc59b73","impliedFormat":1},{"version":"47865c5e695a382a916b1eedda1b6523145426e48a2eae4647e96b3b5e52024f","impliedFormat":1},{"version":"4cdf27e29feae6c7826cdd5c91751cc35559125e8304f9e7aed8faef97dcf572","impliedFormat":1},{"version":"331b8f71bfae1df25d564f5ea9ee65a0d847c4a94baa45925b6f38c55c7039bf","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"0146fd6262c3fd3da51cb0254bb6b9a4e42931eb2f56329edd4c199cb9aaf804","impliedFormat":1},{"version":"183f480885db5caa5a8acb833c2be04f98056bdcc5fb29e969ff86e07efe57ab","impliedFormat":99},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":1},{"version":"71b110829b8f5e7653352a132544ece2b9a10e93ba1c77453187673bd46f13ee","impliedFormat":1},{"version":"a93ee856675c0eb60f270383d10eb4fa49ef9637a962661897e8565d682c1372","impliedFormat":1},{"version":"1223780c318ef42fd33ac772996335ed92d57cf7c0fc73178acab5e154971aab","impliedFormat":1},{"version":"0d04cbe88c8a25c2debd2eef03ec5674563e23ca9323fa82ede3577822653bd2","impliedFormat":1},{"version":"57c8cbb9afab16efe0f85d5508cb46b09bd3678b1d509102e6d1a61c245d303a","impliedFormat":1},{"version":"4ace083580c1b77eb8ddf4ea915cde605af1a96e426c4c04b897feef1acdb534","impliedFormat":1},{"version":"daf07c1ca8ccfb21ad958833546a4f414c418fe096dcebdbb90b02e12aa5c3a2","impliedFormat":1},{"version":"89ac5224feeb2de76fc52fc2a91c5f6448a98dbe4e8d726ecb1730fa64cd2d30","impliedFormat":1},{"version":"e259db66999453467359d0ea0a125866edaa851c580a5e840f4cac96ce5b035a","impliedFormat":1},{"version":"acf00cfabe8c4de18bea655754ea39c4d04140257556bbf283255b695d00e36f","impliedFormat":1},{"version":"39b70d5f131fcfdeba404ee63aba25f26d8376a73bacd8275fb5a9f06219ac77","impliedFormat":1},{"version":"cdae26c737cf4534eeec210e42eab2d5f0c3855240d8dde3be4aee9194e4e781","impliedFormat":1},{"version":"5aa0c50083d0d9a423a46afaef78c7f42420759cfa038ad40e8b9e6cafc38831","impliedFormat":1},{"version":"10d6a49a99a593678ba4ea6073d53d005adfc383df24a9e93f86bf47de6ed857","impliedFormat":1},{"version":"1b7ea32849a7982047c2e5d372300a4c92338683864c9ab0f5bbd1acadae83a3","impliedFormat":1},{"version":"224083e6fcec1d300229da3d1dafc678c642863996cbfed7290df20954435a55","impliedFormat":1},{"version":"51e0c0d376d59425f8a2c5d775c34cc3eb87da4a423e12ec097fc40432aa8c16","impliedFormat":1},{"version":"522a2ab83e7c08e8bde2d3f2b689529aa3919eb2b544232d8f044803ddadd7b3","impliedFormat":1},{"version":"ca055d26105248f745ea6259b4c498ebeed18c9b772e7f2b3a16f50226ff9078","impliedFormat":1},{"version":"ea6b2badb951d6dfa24bb7d7eb733327e5f9a15fc994d6dc1c54b2c7a83b6a0b","impliedFormat":1},{"version":"03fdf8dba650d830388b9985750d770dd435f95634717f41cea814863a9ac98b","impliedFormat":1},{"version":"6fd08e3ef1568cd0dc735c9015f6765e25143a4a0331d004a29c51b50eec402a","impliedFormat":1},{"version":"2e988cd4d24edac4936449630581c79686c8adac10357eb0cdb410c24f47c7f0","impliedFormat":1},{"version":"b813f62a37886ed986b0f6f8c5bf323b3fcae32c1952b71d75741e74ea9353cf","impliedFormat":1},{"version":"7e5e80eababa97e96a0d4e60cd26c096c5d235b78b584faf2929feb286195170","impliedFormat":1},{"version":"83fe1053701101ac6d25364696fea50d2ceb2f81d1456bc11e682a20aaeac52e","impliedFormat":1},{"version":"10a1a3731b2f8b224b4d7a4d0c7340d32fbd034679fa562f55141b668a326386","impliedFormat":1},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e4af494f7a14b226bbe732e9c130d8811f8c7025911d7c58dd97121a85519715","impliedFormat":1},{"version":"a5d643092d2f16cb861872357b12ab844f33fc1181f7c5aed447b3424b4f5668","impliedFormat":99},{"version":"064569cccad7d2ee3b9ec0e306919ca0c1bab8169de19a0784adaf798125fd49","impliedFormat":99},{"version":"88623aa2028777d8f073c61590feb7f3abde4513918329d868c8c0cb38d2d000","affectsGlobalScope":true,"impliedFormat":1},{"version":"4f21bd6e22ec4af7e666f033455090f6cc26a692c1524acb113526a38f07d48e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fe3e24828f33dd7ef2fa2a169109cf97ac06515d8d2005f3eab7353863631ebb","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"596d8f74336c5aa7955e53e166bd2ca68aecca5c2ff50bc9d16579c36430ecf1","impliedFormat":1},{"version":"ba854883a418fca4343b51cb93718d481770f3b81e978bbf6378a2385264e55c","impliedFormat":1},{"version":"380b919bfa0516118edaf25b99e45f855e7bc3fd75ce4163a1cfe4a666388804","impliedFormat":1},{"version":"0b24a72109c8dd1b41f94abfe1bb296ba01b3734b8ac632db2c48ffc5dccaf01","impliedFormat":1},{"version":"fcf79300e5257a23ed3bacaa6861d7c645139c6f7ece134d15e6669447e5e6db","impliedFormat":1},{"version":"187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","impliedFormat":1},{"version":"aa2c18a1b5a086bbcaae10a4efba409cc95ba7287d8cf8f2591b53704fea3dea","impliedFormat":1},{"version":"b88749bdb18fc1398370e33aa72bc4f88274118f4960e61ce26605f9b33c5ba2","impliedFormat":1},{"version":"0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","impliedFormat":1},{"version":"00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","impliedFormat":1},{"version":"a873c50d3e47c21aa09fbe1e2023d9a44efb07cc0cb8c72f418bf301b0771fd3","impliedFormat":1},{"version":"7c14ccd2eaa82619fffc1bfa877eb68a012e9fb723d07ee98db451fadb618906","impliedFormat":1},{"version":"49c36529ee09ea9ce19525af5bb84985ea8e782cb7ee8c493d9e36d027a3d019","impliedFormat":1},{"version":"df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","impliedFormat":1},{"version":"4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd","impliedFormat":1},{"version":"cf93e7b09b66e142429611c27ba2cbf330826057e3c793e1e2861e976fae3940","impliedFormat":99},{"version":"90e727d145feb03695693fdc9f165a4dc10684713ee5f6aa81e97a6086faa0f8","impliedFormat":99},{"version":"ee2c6ec73c636c9da5ab4ce9227e5197f55a57241d66ea5828f94b69a4a09a2d","impliedFormat":99},{"version":"afaf64477630c7297e3733765046c95640ab1c63f0dfb3c624691c8445bc3b08","impliedFormat":99},{"version":"5aa03223a53ad03171988820b81a6cae9647eabcebcb987d1284799de978d8e3","impliedFormat":99},{"version":"7f50c8914983009c2b940923d891e621db624ba32968a51db46e0bf480e4e1cb","impliedFormat":99},{"version":"90fc18234b7d2e19d18ac026361aaf2f49d27c98dc30d9f01e033a9c2b01c765","impliedFormat":99},{"version":"a980e4d46239f344eb4d5442b69dcf1d46bd2acac8d908574b5a507181f7e2a1","impliedFormat":99},{"version":"bbbfa4c51cdaa6e2ef7f7be3ae199b319de6b31e3b5afa7e5a2229c14bb2568a","impliedFormat":99},{"version":"bc7bfe8f48fa3067deb3b37d4b511588b01831ba123a785ea81320fe74dd9540","impliedFormat":99},{"version":"fd60c0aaf7c52115f0e7f367d794657ac18dbb257255777406829ab65ca85746","impliedFormat":99},{"version":"15c17866d58a19f4a01a125f3f511567bd1c22235b4fd77bf90c793bf28388c3","impliedFormat":99},{"version":"51301a76264b1e1b4046f803bda44307fba403183bc274fe9e7227252d7315cb","impliedFormat":99},{"version":"ddef23e8ace6c2b2ddf8d8092d30b1dd313743f7ff47b2cbb43f36c395896008","impliedFormat":99},{"version":"9e42df47111429042b5e22561849a512ad5871668097664b8fb06a11640140ac","impliedFormat":99},{"version":"391fcc749c6f94c6c4b7f017c6a6f63296c1c9ae03fa639f99337dddb9cc33fe","impliedFormat":99},{"version":"ac4706eb1fb167b19f336a93989763ab175cd7cc6227b0dcbfa6a7824c6ba59a","impliedFormat":99},{"version":"633220dc1e1a5d0ccf11d3c3e8cadc9124daf80fef468f2ff8186a2775229de3","impliedFormat":99},{"version":"6de22ad73e332e513454f0292275155d6cb77f2f695b73f0744928c4ebb3a128","impliedFormat":99},{"version":"ebe0e3c77f5114b656d857213698fade968cff1b3a681d1868f3cfdd09d63b75","impliedFormat":99},{"version":"22c27a87488a0625657b52b9750122814c2f5582cac971484cda0dcd7a46dc3b","impliedFormat":99},{"version":"7e7a817c8ec57035b2b74df8d5dbcc376a4a60ad870b27ec35463536158e1156","impliedFormat":99},{"version":"0e2061f86ca739f34feae42fd7cce27cc171788d251a587215b33eaec456e786","impliedFormat":99},{"version":"91659b2b090cadffdb593736210910508fc5b77046d4ce180b52580b14b075ec","impliedFormat":99},{"version":"d0f6c657c45faaf576ca1a1dc64484534a8dc74ada36fd57008edc1aab65a02b","impliedFormat":99},{"version":"ce0c52b1ebc023b71d3c1fe974804a2422cf1d85d4af74bb1bced36ff3bff8b5","impliedFormat":99},{"version":"9c6acb4a388887f9a5552eda68987ee5d607152163d72f123193a984c48157c9","impliedFormat":99},{"version":"90d0a9968cbb7048015736299f96a0cceb01cf583fd2e9a9edbc632ac4c81b01","impliedFormat":99},{"version":"49abec0571c941ab6f095885a76828d50498511c03bb326eec62a852e58000c5","impliedFormat":99},{"version":"8eeb4a4ff94460051173d561749539bca870422a6400108903af2fb7a1ffe3d7","impliedFormat":99},{"version":"49e39b284b87452fed1e27ac0748ba698f5a27debe05084bc5066b3ecf4ed762","impliedFormat":99},{"version":"59dcf835762f8df90fba5a3f8ba87941467604041cf127fb456543c793b71456","impliedFormat":99},{"version":"33e0c4c683dcaeb66bedf5bb6cc35798d00ac58d7f3bc82aadb50fa475781d60","impliedFormat":99},{"version":"605839abb6d150b0d83ed3712e1b3ffbeb309e382770e7754085d36bc2d84a4c","impliedFormat":99},{"version":"a862dcb740371257e3dae1ab379b0859edcb5119484f8359a5e6fb405db9e12e","impliedFormat":99},{"version":"0f0a16a0e8037c17e28f537028215e87db047eba52281bd33484d5395402f3c1","impliedFormat":99},{"version":"cf533aed4c455b526ddccbb10dae7cc77e9269c3d7862f9e5cedbd4f5c92e05e","impliedFormat":99},{"version":"f8a60ca31702a0209ef217f8f3b4b32f498813927df2304787ac968c78d8560d","impliedFormat":99},{"version":"530192961885d3ddad87bf9c4390e12689fa29ff515df57f17a57c9125fc77c3","impliedFormat":99},{"version":"165ba9e775dd769749e2177c383d24578e3b212e4774b0a72ad0f6faee103b68","impliedFormat":99},{"version":"61448f238fdfa94e5ccce1f43a7cced5e548b1ea2d957bec5259a6e719378381","impliedFormat":99},{"version":"69fa523e48131ced0a52ab1af36c3a922c5fd7a25e474d82117329fe051f5b85","impliedFormat":99},{"version":"fa10b79cd06f5dd03435e184fb05cc5f0d02713bfb4ee9d343db527501be334c","impliedFormat":99},{"version":"c6fb591e363ee4dea2b102bb721c0921485459df23a2d2171af8354cacef4bce","impliedFormat":99},{"version":"ea7e1f1097c2e61ed6e56fa04a9d7beae9d276d87ac6edb0cd39a3ee649cddfe","impliedFormat":99},{"version":"e8cf2659d87462aae9c7647e2a256ac7dcaf2a565a9681bfb49328a8a52861e8","impliedFormat":99},{"version":"7e374cb98b705d35369b3c15444ef2ff5ff983bd2fbb77a287f7e3240abf208c","impliedFormat":99},{"version":"ca75ba1519f9a426b8c512046ebbad58231d8627678d054008c93c51bc0f3fa5","impliedFormat":99},{"version":"ff63760147d7a60dcfc4ac16e40aa2696d016b9ffe27e296b43655dfa869d66b","impliedFormat":99},{"version":"4d434123b16f46b290982907a4d24675442eb651ca95a5e98e4c274be16f1220","impliedFormat":99},{"version":"57263d6ba38046e85f499f3c0ab518cfaf0a5f5d4f53bdae896d045209ab4aff","impliedFormat":99},{"version":"d3a535f2cd5d17f12b1abf0b19a64e816b90c8c10a030b58f308c0f7f2acfe2c","impliedFormat":99},{"version":"be26d49bb713c13bd737d00ae8a61aa394f0b76bc2d5a1c93c74f59402eb8db3","impliedFormat":99},{"version":"c7012003ac0c9e6c9d3a6418128ddebf6219d904095180d4502b19c42f46a186","impliedFormat":99},{"version":"d58c55750756bcf73f474344e6b4a9376e5381e4ba7d834dc352264b491423b6","impliedFormat":99},{"version":"01e2aabfabe22b4bf6d715fc54d72d32fa860a3bd1faa8974e0d672c4b565dfe","impliedFormat":99},{"version":"ba2c489bb2566c16d28f0500b3d98013917e471c40a4417c03991460cb248e88","impliedFormat":99},{"version":"39f94b619f0844c454a6f912e5d6868d0beb32752587b134c3c858b10ecd7056","impliedFormat":99},{"version":"0d2d8b0477b1cf16b34088e786e9745c3e8145bc8eea5919b700ad054e70a095","impliedFormat":99},{"version":"2a5e963b2b8f33a50bb516215ba54a20801cb379a8e9b1ae0b311e900dc7254c","impliedFormat":99},{"version":"d8307f62b55feeb5858529314761089746dce957d2b8fd919673a4985fa4342a","impliedFormat":99},{"version":"bf449ec80fc692b2703ad03e64ae007b3513ecd507dc2ab77f39be6f578e6f5c","impliedFormat":99},{"version":"f780213dd78998daf2511385dd51abf72905f709c839a9457b6ba2a55df57be7","impliedFormat":99},{"version":"2b7843e8a9a50bdf511de24350b6d429a3ee28430f5e8af7d3599b1e9aa7057f","impliedFormat":99},{"version":"05d95be6e25b4118c2eb28667e784f0b25882f6a8486147788df675c85391ab7","impliedFormat":99},{"version":"62d2721e9f2c9197c3e2e5cffeb2f76c6412121ae155153179049890011eb785","impliedFormat":99},{"version":"ff5668fb7594c02aca5e7ba7be6c238676226e450681ca96b457f4a84898b2d9","impliedFormat":99},{"version":"59fd37ea08657fef36c55ddea879eae550ffe21d7e3a1f8699314a85a30d8ae9","impliedFormat":99},{"version":"84e23663776e080e18b25052eb3459b1a0486b5b19f674d59b96347c0cb7312a","impliedFormat":99},{"version":"43e5934c7355731eec20c5a2aa7a859086f19f60a4e5fcd80e6684228f6fb767","impliedFormat":99},{"version":"a49c210c136c518a7c08325f6058fc648f59f911c41c93de2026db692bba0e47","impliedFormat":99},{"version":"1a92f93597ebc451e9ef4b158653c8d31902de5e6c8a574470ecb6da64932df4","impliedFormat":99},{"version":"256513ad066ac9898a70ca01e6fbdb3898a4e0fe408fbf70608fdc28ac1af224","impliedFormat":99},{"version":"d9835850b6cc05c21e8d85692a8071ebcf167a4382e5e39bf700c4a1e816437e","impliedFormat":99},{"version":"e5ab7190f818442e958d0322191c24c2447ddceae393c4e811e79cda6bd49836","impliedFormat":99},{"version":"91b4b77ef81466ce894f1aade7d35d3589ddd5c9981109d1dea11f55a4b807a0","impliedFormat":99},{"version":"03abb209bed94c8c893d9872639e3789f0282061c7aa6917888965e4047a8b5f","impliedFormat":99},{"version":"e97a07901de562219f5cba545b0945a1540d9663bd9abce66495721af3903eec","impliedFormat":99},{"version":"bf39ed1fdf29bc8178055ec4ff32be6725c1de9f29c252e31bdc71baf5c227e6","impliedFormat":99},{"version":"985eabf06dac7288fc355435b18641282f86107e48334a83605739a1fe82ac15","impliedFormat":99},{"version":"6112d33bcf51e3e6f6a81e419f29580e2f8e773529d53958c7c1c99728d4fb2e","impliedFormat":99},{"version":"89e9f7e87a573504acc2e7e5ad727a110b960330657d1b9a6d3526e77c83d8be","impliedFormat":99},{"version":"44bbb88abe9958c7c417e8687abf65820385191685009cc4b739c2d270cb02e9","impliedFormat":99},{"version":"ab4b506b53d2c4aec4cc00452740c540a0e6abe7778063e95c81a5cd557c19eb","impliedFormat":99},{"version":"858757bde6d615d0d1ee474c972131c6d79c37b0b61897da7fbd7110beb8af12","impliedFormat":99},{"version":"60b9dea33807b086a1b4b4b89f72d5da27ad0dd36d6436a6e306600c47438ac4","impliedFormat":99},{"version":"409c963b1166d0c1d49fdad1dfeb4de27fd2d6662d699009857de9baf43ca7c3","impliedFormat":99},{"version":"b7674ecfeb5753e965404f7b3d31eec8450857d1a23770cb867c82f264f546ab","impliedFormat":99},{"version":"c9800b9a9ad7fcdf74ed8972a5928b66f0e4ff674d55fd038a3b1c076911dcbe","impliedFormat":99},{"version":"99864433e35b24c61f8790d2224428e3b920624c01a6d26ea8b27ee1f62836bb","impliedFormat":99},{"version":"c391317b9ff8f87d28c6bfe4e50ed92e8f8bfab1bb8a03cd1fe104ff13186f83","impliedFormat":99},{"version":"42bdc3c98446fdd528e2591213f71ce6f7008fb9bb12413bd57df60d892a3fb5","impliedFormat":99},{"version":"542d2d689b58c25d39a76312ccaea2fcd10a45fb27b890e18015399c8032e2d9","impliedFormat":99},{"version":"97d1656f0a563dbb361d22b3d7c2487427b0998f347123abd1c69a4991326c96","impliedFormat":99},{"version":"d4f53ed7960c9fba8378af3fa28e3cc483d6c0b48e4a152a83ff0973d507307d","impliedFormat":99},{"version":"0665de5280d65ec32776dc55fb37128e259e60f389cde5b9803cf9e81ad23ce0","impliedFormat":99},{"version":"b6dc8fd1c6092da86725c338ca6c263d1c6dd3073046d3ec4eb2d68515062da2","impliedFormat":99},{"version":"d9198a0f01f00870653347560e10494efeca0bfa2de0988bd5d883a9d2c47edb","impliedFormat":99},{"version":"d4279865b926d7e2cfe8863b2eae270c4c035b6e923af8f9d7e6462d68679e07","impliedFormat":99},{"version":"73b6945448bb3425b764cfe7b1c4b0b56c010cc66e5f438ef320c53e469797eb","impliedFormat":99},{"version":"cf72fd8ffa5395f4f1a26be60246ec79c5a9ad201579c9ba63fd2607b5daf184","impliedFormat":99},{"version":"301a458744666096f84580a78cc3f6e8411f8bab92608cdaa33707546ca2906f","impliedFormat":99},{"version":"711e70c0916ff5f821ea208043ecd3e67ed09434b8a31d5616286802b58ebebe","impliedFormat":99},{"version":"e1f2fd9f88dd0e40c358fbf8c8f992211ab00a699e7d6823579b615b874a8453","impliedFormat":99},{"version":"17db3a9dcb2e1689ff7ace9c94fa110c88da64d69f01dc2f3cec698e4fc7e29e","impliedFormat":99},{"version":"73fb07305106bb18c2230890fcacf910fd1a7a77d93ac12ec40bc04c49ee5b8e","impliedFormat":99},{"version":"2c5f341625a45530b040d59a4bc2bc83824d258985ede10c67005be72d3e21d0","impliedFormat":99},{"version":"c4a262730d4277ecaaf6f6553dabecc84dcca8decaebbf2e16f1df8bbd996397","impliedFormat":99},{"version":"c23c533d85518f3358c55a7f19ab1a05aad290251e8bba0947bd19ea3c259467","impliedFormat":99},{"version":"5d0322a0b8cdc67b8c71e4ccaa30286b0c8453211d4c955a217ac2d3590e911f","impliedFormat":99},{"version":"f5e4032b6e4e116e7fec5b2620a2a35d0b6b8b4a1cc9b94a8e5ee76190153110","impliedFormat":99},{"version":"9ab26cb62a0e86ab7f669c311eb0c4d665457eb70a103508aa39da6ccee663da","impliedFormat":99},{"version":"5f64d1a11d8d4ce2c7ee3b72471df76b82d178a48964a14cdfdc7c5ef7276d70","impliedFormat":99},{"version":"24e2fbc48f65814e691d9377399807b9ec22cd54b51d631ba9e48ee18c5939dd","impliedFormat":99},{"version":"bfa2648b2ee90268c6b6f19e84da3176b4d46329c9ec0555d470e647d0568dfb","impliedFormat":99},{"version":"75ef3cb4e7b3583ba268a094c1bd16ce31023f2c3d1ac36e75ca65aca9721534","impliedFormat":99},{"version":"3be6b3304a81d0301838860fd3b4536c2b93390e785808a1f1a30e4135501514","impliedFormat":99},{"version":"da66c1b3e50ef9908e31ce7a281b137b2db41423c2b143c62524f97a536a53d9","impliedFormat":99},{"version":"3ada1b216e45bb9e32e30d8179a0a95870576fe949c33d9767823ccf4f4f4c97","impliedFormat":99},{"version":"1ace2885dffab849f7c98bffe3d1233260fbf07ee62cb58130167fd67a376a65","impliedFormat":99},{"version":"2126e5989c0ca5194d883cf9e9c10fe3e5224fbd3e4a4a6267677544e8be0aae","impliedFormat":99},{"version":"41a6738cf3c756af74753c5033e95c5b33dfc1f6e1287fa769a1ac4027335bf5","impliedFormat":99},{"version":"6e8630be5b0166cbc9f359b9f9e42801626d64ff1702dcb691af811149766154","impliedFormat":99},{"version":"e36b77c04e00b4a0bb4e1364f2646618a54910c27f6dc3fc558ca2ced8ca5bc5","impliedFormat":99},{"version":"2c4ea7e9f95a558f46c89726d1fedcb525ef649eb755a3d7d5055e22b80c2904","impliedFormat":99},{"version":"4875d65190e789fad05e73abd178297b386806b88b624328222d82e455c0f2e7","impliedFormat":99},{"version":"bf5302ecfaacee37c2316e33703723d62e66590093738c8921773ee30f2ecc38","impliedFormat":99},{"version":"62684064fe034d54b87f62ad416f41b98a405dee4146d0ec03b198c3634ea93c","impliedFormat":99},{"version":"be02cbdb1688c8387f8a76a9c6ed9d75d8bb794ec5b9b1d2ba3339a952a00614","impliedFormat":99},{"version":"cefaff060473a5dbf4939ee1b52eb900f215f8d6249dc7c058d6b869d599983c","impliedFormat":99},{"version":"b2797235a4c1a7442a6f326f28ffb966226c3419399dbb33634b8159af2c712f","impliedFormat":99},{"version":"164d633bbd4329794d329219fc173c3de85d5ad866d44e5b5f0fb60c140e98f2","impliedFormat":99},{"version":"b74300dd0a52eaf564b3757c07d07e1d92def4e3b8708f12eedb40033e4cafe9","impliedFormat":99},{"version":"a792f80b1e265b06dce1783992dbee2b45815a7bdc030782464b8cf982337cf2","impliedFormat":99},{"version":"8816b4b3a87d9b77f0355e616b38ed5054f993cc4c141101297f1914976a94b1","impliedFormat":99},{"version":"0f35e4da974793534c4ca1cdd9491eab6993f8cf47103dadfc048b899ed9b511","impliedFormat":99},{"version":"0ccdfcaebf297ec7b9dde20bbbc8539d5951a3d8aaa40665ca469da27f5a86e1","impliedFormat":99},{"version":"7fcb05c8ce81f05499c7b0488ae02a0a1ac6aebc78c01e9f8c42d98f7ba68140","impliedFormat":99},{"version":"81c376c9e4d227a4629c7fca9dde3bbdfa44bd5bd281aee0ed03801182368dc5","impliedFormat":99},{"version":"0f2448f95110c3714797e4c043bbc539368e9c4c33586d03ecda166aa9908843","impliedFormat":99},{"version":"b2f1a443f7f3982d7325775906b51665fe875c82a62be3528a36184852faa0bb","impliedFormat":99},{"version":"7568ff1f23363d7ee349105eb936e156d61aea8864187a4c5d85c60594b44a25","impliedFormat":99},{"version":"8c4d1d9a4eba4eac69e6da0f599a424b2689aee55a455f0b5a7f27a807e064db","impliedFormat":99},{"version":"e1beb9077c100bdd0fc8e727615f5dae2c6e1207de224569421907072f4ec885","impliedFormat":99},{"version":"3dda13836320ec71b95a68cd3d91a27118b34c05a2bfda3e7e51f1d8ca9b960b","impliedFormat":99},{"version":"fedc79cb91f2b3a14e832d7a8e3d58eb02b5d5411c843fcbdc79e35041316b36","impliedFormat":99},{"version":"99f395322ffae908dcdfbaa2624cc7a2a2cb7b0fbf1a1274aca506f7b57ebcb5","impliedFormat":99},{"version":"5e1f7c43e8d45f2222a5c61cbc88b074f4aaf1ca4b118ac6d6123c858efdcd71","impliedFormat":99},{"version":"7388273ab71cb8f22b3f25ffd8d44a37d5740077c4d87023da25575204d57872","impliedFormat":99},{"version":"0a48ceb01a0fdfc506aa20dfd8a3563edbdeaa53a8333ddf261d2ee87669ea7b","impliedFormat":99},{"version":"3182d06b874f31e8e55f91ea706c85d5f207f16273480f46438781d0bd2a46a1","impliedFormat":99},{"version":"ccd47cab635e8f71693fa4e2bbb7969f559972dae97bd5dbd1bbfee77a63b410","impliedFormat":99},{"version":"89770fa14c037f3dc3882e6c56be1c01bb495c81dec96fa29f868185d9555a5d","impliedFormat":99},{"version":"7048c397f08c54099c52e6b9d90623dc9dc6811ea142f8af3200e40d66a972e1","impliedFormat":99},{"version":"512120cd6f026ce1d3cf686c6ab5da80caa40ef92aa47466ec60ba61a48b5551","impliedFormat":99},{"version":"6cd0cb7f999f221e984157a7640e7871960131f6b221d67e4fdc2a53937c6770","impliedFormat":99},{"version":"f48b84a0884776f1bc5bf0fcf3f69832e97b97dc55d79d7557f344de900d259b","impliedFormat":99},{"version":"dca490d986411644b0f9edf6ea701016836558e8677c150dca8ad315178ec735","impliedFormat":99},{"version":"a028a04948cf98c1233166b48887dad324e8fe424a4be368a287c706d9ccd491","impliedFormat":99},{"version":"3046ed22c701f24272534b293c10cfd17b0f6a89c2ec6014c9a44a90963dfa06","impliedFormat":99},{"version":"394da10397d272f19a324c95bea7492faadf2263da157831e02ae1107bd410f5","impliedFormat":99},{"version":"0580595a99248b2d30d03f2307c50f14eb21716a55beb84dd09d240b1b087a42","impliedFormat":99},{"version":"a7da9510150f36a9bea61513b107b59a423fdff54429ad38547c7475cd390e95","impliedFormat":99},{"version":"659615f96e64361af7127645bb91f287f7b46c5d03bea7371e6e02099226d818","impliedFormat":99},{"version":"1f2a42974920476ce46bb666cd9b3c1b82b2072b66ccd0d775aa960532d78176","impliedFormat":99},{"version":"500b3ae6095cbab92d81de0b40c9129f5524d10ad955643f81fc07d726c5a667","impliedFormat":99},{"version":"a957ad4bd562be0662fb99599dbcf0e16d1631f857e5e1a83a3f3afb6c226059","impliedFormat":99},{"version":"e57a4915266a6a751c6c172e8f30f6df44a495608613e1f1c410196207da9641","impliedFormat":99},{"version":"7a12e57143b7bc5a52a41a8c4e6283a8f8d59a5e302478185fb623a7157fff5e","impliedFormat":99},{"version":"17b3426162e1d9cb0a843e8d04212aabe461d53548e671236de957ed3ae9471b","impliedFormat":99},{"version":"f38e86eb00398d63180210c5090ef6ed065004474361146573f98b3c8a96477d","impliedFormat":99},{"version":"231d9e32382d3971f58325e5a85ba283a2021243651cb650f82f87a1bf62d649","impliedFormat":99},{"version":"6532e3e87b87c95f0771611afce929b5bad9d2c94855b19b29b3246937c9840b","impliedFormat":99},{"version":"65704bbb8f0b55c73871335edd3c9cead7c9f0d4b21f64f5d22d0987c45687f0","impliedFormat":99},{"version":"787232f574af2253ac860f22a445c755d57c73a69a402823ae81ba0dfdd1ce23","impliedFormat":99},{"version":"5e63903cd5ebce02486b91647d951d61a16ad80d65f9c56581cd624f39a66007","impliedFormat":99},{"version":"bcc89a120d8f3c02411f4df6b1d989143c01369314e9b0e04794441e6b078d22","impliedFormat":99},{"version":"d17531ef42b7c76d953f63bd5c5cd927c4723e62a7e0b2badf812d5f35f784eb","impliedFormat":99},{"version":"6d4ee1a8e3a97168ea4c4cc1c68bb61a3fd77134f15c71bb9f3f63df3d26b54c","impliedFormat":99},{"version":"1eb04fea6b47b16922ed79625d90431a8b2fc7ba9d5768b255e62df0c96f1e3a","impliedFormat":99},{"version":"de0c2eece83bd81b8682f4496f558beb728263e17e74cbc4910e5c9ce7bef689","impliedFormat":99},{"version":"98866542d45306dab48ecc3ddd98ee54fa983353bc3139dfbc619df882f54d90","impliedFormat":99},{"version":"9e04c7708917af428c165f1e38536ddb2e8ecd576f55ed11a97442dc34b6b010","impliedFormat":99},{"version":"31fe6f6d02b53c1a7c34b8d8f8c87ee9b6dd4b67f158cbfff3034b4f3f69c409","impliedFormat":99},{"version":"2e1d853f84188e8e002361f4bfdd892ac31c68acaeac426a63cd4ff7abf150d0","impliedFormat":99},{"version":"666b5289ec8a01c4cc0977c62e3fd32e89a8e3fd9e97c8d8fd646f632e63c055","impliedFormat":99},{"version":"a1107bbb2b10982dba1f7958a6a5cf841e1a19d6976d0ecdc4c43269c7b0eaf2","impliedFormat":99},{"version":"07fa6122f7495331f39167ec9e4ebd990146a20f99c16c17bc0a98aa81f63b27","impliedFormat":99},{"version":"39c1483481b35c2123eaab5094a8b548a0c3f1e483ab7338102c3291f1ab18bf","impliedFormat":99},{"version":"b73e6242c13796e7d5fba225bf1c07c8ee66d31b7bb65f45be14226a9ae492d2","impliedFormat":99},{"version":"f2931608d541145d189390d6cfb74e1b1e88f73c0b9a80c4356a4daa7fa5e005","impliedFormat":99},{"version":"8684656fe3bf1425a91bd62b8b455a1c7ec18b074fd695793cfae44ae02e381a","impliedFormat":99},{"version":"ccf0b9057dd65c7fb5e237de34f706966ebc30c6d3669715ed05e76225f54fbd","impliedFormat":99},{"version":"d930f077da575e8ea761e3d644d4c6279e2d847bae2b3ea893bbd572315acc21","impliedFormat":99},{"version":"19b0616946cb615abde72c6d69049f136cc4821b784634771c1d73bec8005f73","impliedFormat":99},{"version":"553312560ad0ef97b344b653931935d6e80840c2de6ab90b8be43cbacf0d04cf","impliedFormat":99},{"version":"1225cf1910667bfd52b4daa9974197c3485f21fe631c3ce9db3b733334199faa","impliedFormat":99},{"version":"f7cb9e46bd6ab9d620d68257b525dbbbbc9b0b148adf500b819d756ebc339de0","impliedFormat":99},{"version":"e46d6c3120aca07ae8ec3189edf518c667d027478810ca67a62431a0fa545434","impliedFormat":99},{"version":"9d234b7d2f662a135d430d3190fc21074325f296273125244b2bf8328b5839a0","impliedFormat":99},{"version":"0554ef14d10acea403348c53436b1dd8d61e7c73ef5872e2fe69cc1c433b02f8","impliedFormat":99},{"version":"2f6ae5538090db60514336bd1441ca208a8fab13108cfa4b311e61eaca5ff716","impliedFormat":99},{"version":"17bf4ce505a4cff88fb56177a8f7eb48aa55c22ccc4cce3e49cc5c8ddc54b07d","impliedFormat":99},{"version":"3d735f493d7da48156b79b4d8a406bf2bbf7e3fe379210d8f7c085028143ee40","impliedFormat":99},{"version":"41de1b3ddd71bd0d9ed7ac217ca1b15b177dd731d5251cde094945c20a715d03","impliedFormat":99},{"version":"17d9c562a46c6a25bc2f317c9b06dd4e8e0368cbe9bdf89be6117aeafd577b36","impliedFormat":99},{"version":"ded799031fe18a0bb5e78be38a6ae168458ff41b6c6542392b009d2abe6a6f32","impliedFormat":99},{"version":"ed48d467a7b25ee1a2769adebc198b647a820e242c96a5f96c1e6c27a40ab131","impliedFormat":99},{"version":"b914114df05f286897a1ae85d2df39cfd98ed8da68754d73cf830159e85ddd15","impliedFormat":99},{"version":"73881e647da3c226f21e0b80e216feaf14a5541a861494c744e9fbe1c3b3a6af","impliedFormat":99},{"version":"d79e1d31b939fa99694f2d6fbdd19870147401dbb3f42214e84c011e7ec359ab","impliedFormat":99},{"version":"4f71097eae7aa37941bab39beb2e53e624321fd341c12cc1d400eb7a805691ff","impliedFormat":99},{"version":"58ebb4f21f3a90dda31a01764462aa617849fdb1b592f3a8d875c85019956aff","impliedFormat":99},{"version":"a8e8d0e6efff70f3c28d3e384f9d64530c7a7596a201e4879a7fd75c7d55cbb5","impliedFormat":99},{"version":"df5cbb80d8353bf0511a4047cc7b8434b0be12e280b6cf3de919d5a3380912c0","impliedFormat":99},{"version":"256eb0520e822b56f720962edd7807ed36abdf7ea23bcadf4a25929a3317c8cf","impliedFormat":99},{"version":"9cf2cbc9ceb5f718c1705f37ce5454f14d3b89f690d9864394963567673c1b5c","impliedFormat":99},{"version":"07d3dd790cf1e66bb6fc9806d014dd40bb2055f8d6ca3811cf0e12f92ba4cb9a","impliedFormat":99},{"version":"1f99fd62e9cff9b50c36f368caf3b9fb79fc6f6c75ca5d3c2ec4afaea08d9109","impliedFormat":99},{"version":"6558faaacba5622ef7f1fdfb843cd967af2c105469b9ff5c18a81ce85178fca7","impliedFormat":99},{"version":"34e7f17ae9395b0269cd3f2f0af10709e6dc975c5b44a36b6b70442dc5e25a38","impliedFormat":99},{"version":"a4295111b54f84c02c27e46b0855b02fad3421ae1d2d7e67ecf16cb49538280a","impliedFormat":99},{"version":"ce9746b2ceae2388b7be9fe1f009dcecbc65f0bdbc16f40c0027fab0fb848c3b","impliedFormat":99},{"version":"35ce823a59f397f0e85295387778f51467cea137d787df385be57a2099752bfb","impliedFormat":99},{"version":"2e5acd3ec67bc309e4f679a70c894f809863c33b9572a8da0b78db403edfa106","impliedFormat":99},{"version":"1872f3fcea0643d5e03b19a19d777704320f857d1be0eb4ee372681357e20c88","impliedFormat":99},{"version":"9689628941205e40dcbb2706d1833bd00ce7510d333b2ef08be24ecbf3eb1a37","impliedFormat":99},{"version":"0317a72a0b63094781476cf1d2d27585d00eb2b0ca62b5287124735912f3d048","impliedFormat":99},{"version":"6ce4c0ab3450a4fff25d60a058a25039cffd03141549589689f5a17055ad0545","impliedFormat":99},{"version":"9153ec7b0577ae77349d2c5e8c5dd57163f41853b80c4fb5ce342c7a431cbe1e","impliedFormat":99},{"version":"f490dfa4619e48edd594a36079950c9fca1230efb3a82aaf325047262ba07379","impliedFormat":99},{"version":"674f00085caff46d2cbc76fc74740fd31f49d53396804558573421e138be0c12","impliedFormat":99},{"version":"41d029194c4811f09b350a1e858143c191073007a9ee836061090ed0143ad94f","impliedFormat":99},{"version":"44a6259ffd6febd8510b9a9b13a700e1d022530d8b33663f0735dbb3bee67b3d","impliedFormat":99},{"version":"6f4322500aff8676d9b8eef7711c7166708d4a0686b792aa4b158e276ed946a7","impliedFormat":99},{"version":"e829ff9ecffa3510d3a4d2c3e4e9b54d4a4ccfef004bacbb1d6919ce3ccca01f","impliedFormat":99},{"version":"62e6fec9dbd012460b47af7e727ec4cd34345b6e4311e781f040e6b640d7f93e","impliedFormat":99},{"version":"4d180dd4d0785f2cd140bc069d56285d0121d95b53e4348feb4f62db2d7035d3","impliedFormat":99},{"version":"f1142cbba31d7f492d2e7c91d82211a8334e6642efe52b71d9a82cb95ba4e8ae","impliedFormat":99},{"version":"279cac827be5d48c0f69fe319dc38c876fdd076b66995d9779c43558552d8a50","impliedFormat":99},{"version":"a70ff3c65dc0e7213bfe0d81c072951db9f5b1e640eb66c1eaed0737879c797b","impliedFormat":99},{"version":"f75d3303c1750f4fdacd23354657eca09aae16122c344e65b8c14c570ff67df5","impliedFormat":99},{"version":"3ebae6a418229d4b303f8e0fdb14de83f39fba9f57b39d5f213398bca72137c7","impliedFormat":99},{"version":"21ba07e33265f59d52dece5ac44f933b2b464059514587e64ad5182ddf34a9b0","impliedFormat":99},{"version":"2d3d96efba00493059c460fd55e6206b0667fc2e73215c4f1a9eb559b550021f","impliedFormat":99},{"version":"d23d4a57fff5cec5607521ba3b72f372e3d735d0f6b11a4681655b0bdd0505f4","impliedFormat":99},{"version":"395c1f3da7e9c87097c8095acbb361541480bf5fd7fa92523985019fef7761dd","impliedFormat":99},{"version":"d61f3d719293c2f92a04ba73d08536940805938ecab89ac35ceabc8a48ccb648","impliedFormat":99},{"version":"ca693235a1242bcd97254f43a17592aa84af66ccb7497333ccfea54842fde648","impliedFormat":99},{"version":"cd41cf040b2e368382f2382ec9145824777233730e3965e9a7ba4523a6a4698e","impliedFormat":99},{"version":"2e7a9dba6512b0310c037a28d27330520904cf5063ca19f034b74ad280dbfe71","impliedFormat":99},{"version":"9f2a38baf702e6cb98e0392fa39d25a64c41457a827b935b366c5e0980a6a667","impliedFormat":99},{"version":"c1dc37f0e7252928f73d03b0d6b46feb26dea3d8737a531ca4c0ec4105e33120","impliedFormat":99},{"version":"25126b80243fb499517e94fc5afe5c9c5df3a0105618e33581fb5b2f2622f342","impliedFormat":99},{"version":"d332c2ddcb64012290eb14753c1b49fe3eee9ca067204efba1cf31c1ce1ee020","impliedFormat":99},{"version":"1be8da453470021f6fe936ba19ee0bfebc7cfa2406953fa56e78940467c90769","impliedFormat":99},{"version":"7c9f2d62d83f1292a183a44fb7fb1f16eb9037deb05691d307d4017ac8af850a","impliedFormat":99},{"version":"d0163ab7b0de6e23b8562af8b5b4adea4182884ca7543488f7ac2a3478f3ae6e","impliedFormat":99},{"version":"05224e15c6e51c4c6cd08c65f0766723f6b39165534b67546076c226661db691","impliedFormat":99},{"version":"a5f7158823c7700dd9fc1843a94b9edc309180c969fbfa6d591aeb0b33d3b514","impliedFormat":99},{"version":"7d30937f8cf9bb0d4b2c2a8fb56a415d7ef393f6252b24e4863f3d7b84285724","impliedFormat":99},{"version":"e04d074584483dc9c59341f9f36c7220f16eed09f7af1fa3ef9c64c26095faec","impliedFormat":99},{"version":"619697e06cbc2c77edda949a83a62047e777efacde1433e895b904fe4877c650","impliedFormat":99},{"version":"88d9a8593d2e6aee67f7b15a25bda62652c77be72b79afbee52bea61d5ffb39e","impliedFormat":99},{"version":"044d7acfc9bd1af21951e32252cf8f3a11c8b35a704169115ddcbde9fd717de2","impliedFormat":99},{"version":"a4ca8f13a91bd80e6d7a4f013b8a9e156fbf579bbec981fe724dad38719cfe01","impliedFormat":99},{"version":"5a216426a68418e37e55c7a4366bc50efc99bda9dc361eae94d7e336da96c027","impliedFormat":99},{"version":"13b65b640306755096d304e76d4a237d21103de88b474634f7ae13a2fac722d5","impliedFormat":99},{"version":"7478bd43e449d3ce4e94f3ed1105c65007b21f078b3a791ea5d2c47b30ea6962","impliedFormat":99},{"version":"601d3e8e71b7d6a24fc003aca9989a6c25fa2b3755df196fd0aaee709d190303","impliedFormat":99},{"version":"168e0850fcc94011e4477e31eca81a8a8a71e1aed66d056b7b50196b877e86c8","impliedFormat":99},{"version":"37ba82d63f5f8c6b4fc9b756f24902e47f62ea66aae07e89ace445a54190a86e","impliedFormat":99},{"version":"f5b66b855f0496bc05f1cd9ba51a6a9de3d989b24aa36f6017257f01c8b65a9f","impliedFormat":99},{"version":"823b16d378e8456fcc5503d6253c8b13659be44435151c6b9f140c4a38ec98c1","impliedFormat":99},{"version":"b58b254bf1b586222844c04b3cdec396e16c811463bf187615bb0a1584beb100","impliedFormat":99},{"version":"a367c2ccfb2460e222c5d10d304e980bd172dd668bcc02f6c2ff626e71e90d75","impliedFormat":99},{"version":"0718623262ac94b016cb0cfd8d54e4d5b7b1d3941c01d85cf95c25ec1ba5ed8d","impliedFormat":99},{"version":"d4f3c9a0bd129e9c7cbfac02b6647e34718a2b81a414d914e8bd6b76341172e0","impliedFormat":99},{"version":"824306df6196f1e0222ff775c8023d399091ada2f10f2995ce53f5e3d4aff7a4","impliedFormat":99},{"version":"84ca07a8d57f1a6ba8c0cf264180d681f7afae995631c6ca9f2b85ec6ee06c0f","impliedFormat":99},{"version":"35755e61e9f4ec82d059efdbe3d1abcccc97a8a839f1dbf2e73ac1965f266847","impliedFormat":99},{"version":"64a918a5aa97a37400ec085ffeea12a14211aa799cd34e5dc828beb1806e95bb","impliedFormat":99},{"version":"0c8f5489ba6af02a4b1d5ba280e7badd58f30dc8eb716113b679e9d7c31185e5","impliedFormat":99},{"version":"7b574ca9ae0417203cdfa621ab1585de5b90c4bc6eea77a465b2eb8b92aa5380","impliedFormat":99},{"version":"3334c03c15102700973e3e334954ac1dffb7be7704c67cc272822d5895215c93","impliedFormat":99},{"version":"aabcb169451df7f78eb43567fab877a74d134a0a6d9850aa58b38321374ab7c0","impliedFormat":99},{"version":"1b5effdd8b4e8d9897fc34ab4cd708a446bf79db4cb9a3467e4a30d55b502e14","impliedFormat":99},{"version":"d772776a7aea246fd72c5818de72c3654f556b2cf0d73b90930c9c187cc055fc","impliedFormat":99},{"version":"dbd4bd62f433f14a419e4c6130075199eb15f2812d2d8e7c9e1f297f4daac788","impliedFormat":99},{"version":"427df949f5f10c73bcc77b2999893bc66c17579ad073ee5f5270a2b30651c873","impliedFormat":99},{"version":"c4c1a5565b9b85abfa1d663ca386d959d55361e801e8d49155a14dd6ca41abe1","impliedFormat":99},{"version":"7a45a45c277686aaff716db75a8157d0458a0d854bacf072c47fee3d499d7a99","impliedFormat":99},{"version":"57005b72bce2dc26293e8924f9c6be7ee3a2c1b71028a680f329762fa4439354","impliedFormat":99},{"version":"8f53b1f97c53c3573c16d0225ee3187d22f14f01421e3c6da1a26a1aace32356","impliedFormat":99},{"version":"810fdc0e554ed7315c723b91f6fa6ef3a6859b943b4cd82879641563b0e6c390","impliedFormat":99},{"version":"87a36b177b04d23214aa4502a0011cd65079e208cd60654aefc47d0d65da68ea","impliedFormat":99},{"version":"28a1c17fcbb9e66d7193caca68bbd12115518f186d90fc729a71869f96e2c07b","impliedFormat":99},{"version":"cc2d2abbb1cc7d6453c6fee760b04a516aa425187d65e296a8aacff66a49598a","impliedFormat":99},{"version":"d2413645bc4ab9c3f3688c5281232e6538684e84b49a57d8a1a8b2e5cf9f2041","impliedFormat":99},{"version":"4e6e21a0f9718282d342e66c83b2cd9aa7cd777dfcf2abd93552da694103b3dc","impliedFormat":99},{"version":"9006cc15c3a35e49508598a51664aa34ae59fc7ab32d6cc6ea2ec68d1c39448e","impliedFormat":99},{"version":"74467b184eadee6186a17cac579938d62eceb6d89c923ae67d058e2bcded254e","impliedFormat":99},{"version":"4169b96bb6309a2619f16d17307da341758da2917ff40c615568217b14357f5e","impliedFormat":99},{"version":"4a94d6146b38050de0830019a1c6a7820c2e2b90eba1a5ee4e4ab3bc30a72036","impliedFormat":99},{"version":"48a35ece156203abf19864daa984475055bbed4dc9049d07f4462100363f1e85","impliedFormat":99},{"version":"eb5b19b86227ace1d29ea4cf81387279d04bb34051e944bc53df69f58914b788","affectsGlobalScope":true,"impliedFormat":1},{"version":"8a8eb4ebffd85e589a1cc7c178e291626c359543403d58c9cd22b81fab5b1fb9","impliedFormat":1},{"version":"87d9d29dbc745f182683f63187bf3d53fd8673e5fca38ad5eaab69798ed29fbc","impliedFormat":1},{"version":"472f5aab7edc498a0a761096e8e254c5bc3323d07a1e7f5f8b8ec0d6395b60a0","affectsGlobalScope":true,"impliedFormat":1},{"version":"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f","impliedFormat":1}],"root":[220],"options":{"allowSyntheticDefaultImports":true,"composite":true,"module":99,"skipLibCheck":true},"referencedMap":[[152,1],[154,2],[153,3],[150,1],[151,4],[221,1],[158,1],[222,1],[236,5],[237,5],[238,5],[239,5],[240,5],[241,5],[242,5],[243,5],[244,5],[245,5],[246,5],[247,5],[248,5],[249,5],[250,5],[251,5],[252,5],[253,5],[254,5],[255,5],[256,5],[257,5],[258,5],[259,5],[260,5],[261,5],[262,5],[263,5],[264,5],[265,5],[266,5],[267,5],[268,5],[269,5],[270,5],[271,5],[272,5],[273,5],[274,5],[275,5],[276,5],[277,5],[278,5],[279,5],[280,5],[281,5],[282,5],[283,5],[284,5],[285,5],[286,5],[287,5],[288,5],[289,5],[290,5],[291,5],[292,5],[293,5],[294,5],[295,5],[296,5],[297,5],[298,5],[299,5],[300,5],[301,5],[302,5],[303,5],[304,5],[305,5],[306,5],[307,5],[308,5],[309,5],[310,5],[311,5],[312,5],[313,5],[314,5],[315,5],[316,5],[317,5],[318,5],[319,5],[320,5],[321,5],[322,5],[323,5],[324,5],[325,5],[326,5],[327,5],[328,5],[329,5],[330,5],[331,5],[332,5],[540,6],[333,5],[334,5],[335,5],[336,5],[337,5],[338,5],[339,5],[340,5],[341,5],[342,5],[343,5],[344,5],[345,5],[346,5],[347,5],[348,5],[349,5],[350,5],[351,5],[352,5],[353,5],[354,5],[355,5],[356,5],[357,5],[358,5],[359,5],[360,5],[361,5],[362,5],[363,5],[364,5],[365,5],[366,5],[367,5],[368,5],[369,5],[370,5],[371,5],[372,5],[373,5],[374,5],[375,5],[376,5],[377,5],[378,5],[379,5],[380,5],[381,5],[382,5],[383,5],[384,5],[385,5],[386,5],[387,5],[388,5],[389,5],[390,5],[391,5],[392,5],[393,5],[394,5],[395,5],[396,5],[397,5],[398,5],[399,5],[400,5],[401,5],[402,5],[403,5],[404,5],[405,5],[406,5],[407,5],[408,5],[409,5],[410,5],[411,5],[412,5],[413,5],[414,5],[415,5],[416,5],[417,5],[418,5],[419,5],[420,5],[421,5],[422,5],[423,5],[424,5],[425,5],[426,5],[427,5],[428,5],[429,5],[430,5],[431,5],[432,5],[433,5],[434,5],[435,5],[436,5],[437,5],[438,5],[439,5],[440,5],[441,5],[442,5],[443,5],[444,5],[445,5],[446,5],[447,5],[448,5],[449,5],[450,5],[451,5],[452,5],[453,5],[454,5],[455,5],[456,5],[457,5],[458,5],[459,5],[460,5],[461,5],[462,5],[463,5],[464,5],[465,5],[466,5],[467,5],[468,5],[469,5],[470,5],[471,5],[472,5],[473,5],[474,5],[475,5],[476,5],[477,5],[478,5],[479,5],[480,5],[481,5],[482,5],[483,5],[484,5],[485,5],[486,5],[487,5],[488,5],[489,5],[490,5],[491,5],[492,5],[493,5],[494,5],[495,5],[496,5],[497,5],[498,5],[499,5],[500,5],[501,5],[502,5],[503,5],[504,5],[505,5],[506,5],[507,5],[508,5],[509,5],[510,5],[511,5],[512,5],[513,5],[514,5],[515,5],[516,5],[517,5],[518,5],[519,5],[520,5],[521,5],[522,5],[523,5],[524,5],[525,5],[526,5],[527,5],[528,5],[529,5],[530,5],[531,5],[532,5],[533,5],[534,5],[535,5],[536,5],[537,5],[538,5],[539,5],[224,7],[225,8],[223,9],[226,10],[227,11],[228,12],[229,13],[230,14],[231,15],[232,16],[233,17],[234,18],[235,19],[96,20],[97,20],[98,21],[54,22],[99,23],[100,24],[101,25],[52,1],[102,26],[103,27],[104,28],[105,29],[106,30],[107,31],[108,31],[110,1],[109,32],[111,33],[112,34],[113,35],[95,36],[53,1],[114,37],[115,38],[116,39],[149,40],[117,41],[118,42],[119,43],[120,44],[121,45],[122,46],[123,47],[124,48],[125,49],[126,50],[127,50],[128,51],[129,1],[130,1],[131,52],[133,53],[132,54],[134,55],[135,56],[136,57],[137,58],[138,59],[139,60],[140,61],[141,62],[142,63],[143,64],[144,65],[145,66],[146,67],[147,68],[148,69],[543,1],[545,70],[541,1],[544,71],[217,72],[219,73],[218,1],[542,1],[163,1],[191,1],[181,74],[179,75],[180,76],[168,77],[169,75],[176,78],[167,79],[172,80],[182,1],[173,81],[178,82],[184,83],[183,84],[166,85],[174,86],[175,87],[170,88],[177,74],[171,89],[160,90],[159,91],[204,92],[205,93],[187,94],[212,95],[206,1],[208,96],[209,96],[210,97],[207,1],[211,98],[190,99],[188,1],[189,100],[203,101],[186,1],[201,102],[192,103],[193,104],[194,103],[195,103],[202,105],[196,104],[197,102],[198,103],[199,104],[200,103],[165,1],[1,1],[50,1],[51,1],[9,1],[13,1],[12,1],[3,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[21,1],[4,1],[22,1],[23,1],[5,1],[24,1],[28,1],[25,1],[26,1],[27,1],[29,1],[30,1],[31,1],[6,1],[32,1],[33,1],[34,1],[35,1],[7,1],[39,1],[36,1],[37,1],[38,1],[40,1],[8,1],[41,1],[46,1],[47,1],[42,1],[43,1],[44,1],[45,1],[2,1],[48,1],[49,1],[11,1],[10,1],[72,106],[83,107],[70,108],[84,109],[93,110],[61,111],[62,112],[60,113],[92,114],[87,115],[91,116],[64,117],[80,118],[63,119],[90,120],[58,121],[59,115],[65,122],[66,1],[71,123],[69,122],[56,124],[94,125],[85,126],[75,127],[74,122],[76,128],[78,129],[73,130],[77,131],[88,114],[67,132],[68,133],[79,134],[57,109],[82,135],[81,122],[86,1],[55,1],[89,136],[216,137],[162,138],[156,139],[157,139],[155,1],[161,140],[214,1],[213,141],[185,1],[164,1],[215,142],[220,143]],"latestChangedDtsFile":"./vite.config.d.ts","version":"5.9.2"} \ No newline at end of file diff --git a/vite.config.d.ts b/vite.config.d.ts new file mode 100644 index 00000000..340562af --- /dev/null +++ b/vite.config.d.ts @@ -0,0 +1,2 @@ +declare const _default: import("vite").UserConfig; +export default _default;