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..d770f334 --- /dev/null +++ b/biome.json @@ -0,0 +1,104 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.2/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 9aa65e71..31754b0a 100644 --- a/flake.lock +++ b/flake.lock @@ -20,10 +20,12 @@ }, "nixpkgs": { "locked": { - "lastModified": 0, - "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", - "path": "/nix/store/5ds20jm3x2s4z7wn3581r6lc9ybmh45b-source", - "type": "path" + "lastModified": 1756159630, + "narHash": "sha256-ohMvsjtSVdT/bruXf5ClBh8ZYXRmD4krmjKrXhEvwMg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "84c256e42600cb0fdf25763b48d28df2f25a0c8b", + "type": "github" }, "original": { "id": "nixpkgs", @@ -58,11 +60,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1744770893, - "narHash": "sha256-RMyTyFHN3w8zwfpgvcfRHQ4vX4zTqhuZbif/MXROtx8=", + "lastModified": 1756262090, + "narHash": "sha256-PQHSup4d0cVXxJ7mlHrrxBx1WVrmudKiNQgnNl5xRas=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "1633514603fc0ed15ea0aef7327e26736ec003c0", + "rev": "df7ea78aded79f195a92fc5423de96af2b8a85d1", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3e68577f..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_18 - 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..8c01ad24 --- /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-h2nnwmjGnjxefq6KflaKgIH0HWPcyRvn6rxslwbYuwo="; + }; + + 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 0ac7fe96..f6527fd7 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,27 @@ { "name": "defguard-client", "private": false, - "version": "1.4.0", + "version": "1.5.0", "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,88 +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.85.6", + "@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.2", + "@tauri-apps/plugin-dialog": "^2.3.3", + "@tauri-apps/plugin-fs": "^2.4.2", + "@tauri-apps/plugin-http": "^2.5.2", + "@tauri-apps/plugin-log": "^2.6.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", - "byte-size": "^8.2.1", + "@use-gesture/react": "^10.3.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", + "html-react-parser": "^5.2.6", "itertools": "^2.4.1", + "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.1.2", "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.2", "@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.85.6", + "@tanstack/react-query-devtools": "^5.85.6", + "@tauri-apps/cli": "^2.8.3", "@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.3.0", + "@types/react": "^19.1.12", + "@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", + "postcss": "^8.5.6", + "prettier": "^3.6.2", "sass": "~1.70.0", - "typedoc": "^0.25.13", + "typedoc": "^0.28.12", "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.4" }, "volta": { "node": "20.5.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6ad8890..b41069ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,35 +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.85.6 + version: 5.85.6 '@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.2 + version: 2.4.2 + '@tauri-apps/plugin-dialog': + specifier: ^2.3.3 + version: 2.3.3 + '@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.6.0 + version: 2.6.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@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 @@ -48,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 @@ -62,24 +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.12)(react@19.1.1) itertools: specifier: ^2.4.1 version: 2.4.1 + 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.12) + millify: + specifier: ^6.1.0 + version: 6.1.0 + motion: + specifier: ^12.23.12 + version: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) p-timeout: specifier: ^6.1.4 version: 6.1.4 @@ -87,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.12)(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.1.2 + version: 3.1.2(@types/react@19.1.12)(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.12)(immer@10.1.1)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) devDependencies: + '@biomejs/biome': + specifier: ^2.2.2 + version: 2.2.2 '@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.12)(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.85.6 + version: 5.85.6(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.85.6 + version: 5.85.6(@tanstack/react-query@5.85.6(react@19.1.1))(react@19.1.1) '@tauri-apps/cli': - specifier: ^1.6.3 - version: 1.6.3 + specifier: ^2.8.3 + version: 2.8.3 '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 @@ -166,80 +205,47 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^20.17.48 - version: 20.17.48 + specifier: ^24.3.0 + version: 24.3.0 '@types/react': - specifier: ^18.3.21 - version: 18.3.21 + specifier: ^19.1.12 + version: 19.1.12 '@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.12) '@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.4(@types/node@24.3.0)(sass@1.70.0)(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.4(@types/node@24.3.0)(sass@1.70.0)(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 typedoc: - specifier: ^0.25.13 - version: 0.25.13(typescript@5.8.3) + specifier: ^0.28.12 + version: 0.28.12(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.4 + version: 7.1.4(@types/node@24.3.0)(sass@1.70.0)(yaml@2.8.1) packages: @@ -251,28 +257,32 @@ packages: 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.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.1': - resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} + '@babel/core@7.28.3': + resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} 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 @@ -293,12 +303,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.3': + resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + '@babel/parser@7.28.3': + resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} engines: {node: '>=6.0.0'} hasBin: true @@ -314,22 +324,75 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} + '@babel/runtime@7.28.3': + resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} 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.3': + resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@biomejs/biome@2.2.2': + resolution: {integrity: sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.2': + resolution: {integrity: sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.2': + resolution: {integrity: sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.2': + resolution: {integrity: sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.2': + resolution: {integrity: sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.2': + resolution: {integrity: sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.2': + resolution: {integrity: sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.2': + resolution: {integrity: sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.2': + resolution: {integrity: sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -339,15 +402,9 @@ 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/memoize@0.9.0': resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} @@ -366,8 +423,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': '*' @@ -390,176 +447,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.0': + resolution: {integrity: sha512-CF1vkfe2ViPtmoFEvtUWilEc4dOCiFzV8+J7/vEISSsslKQ97FjeTPNMCqUhZEiKySmKRgK3UO/CxtkyOp7DvA==} '@hookform/devtools@4.4.0': resolution: {integrity: sha512-Mtlic+uigoYBPXlfvPBfiYYUZuyMrD3pTjDpVIhL6eCZTvQkHsKBSKeZCvXWUZr8fqrkzDg27N+ZuazLKq6Vmg==} @@ -572,52 +638,18 @@ 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'} - - '@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/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} '@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.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@pkgr/core@0.2.4': - resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} '@react-hook/latest@1.0.3': resolution: {integrity: sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==} @@ -634,36 +666,176 @@ packages: peerDependencies: react: '>=18' + '@reduxjs/toolkit@2.8.2': + resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==} + 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.0': + resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.50.0': + resolution: {integrity: sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.50.0': + resolution: {integrity: sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.50.0': + resolution: {integrity: sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.50.0': + resolution: {integrity: sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.50.0': + resolution: {integrity: sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.50.0': + resolution: {integrity: sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.50.0': + resolution: {integrity: sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.50.0': + resolution: {integrity: sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.50.0': + resolution: {integrity: sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.50.0': + resolution: {integrity: sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.50.0': + resolution: {integrity: sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.50.0': + resolution: {integrity: sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.50.0': + resolution: {integrity: sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.50.0': + resolution: {integrity: sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.50.0': + resolution: {integrity: sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.50.0': + resolution: {integrity: sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.50.0': + resolution: {integrity: sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.0': + resolution: {integrity: sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.50.0': + resolution: {integrity: sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.50.0': + resolution: {integrity: sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==} + cpu: [x64] + os: [win32] + + '@shikijs/engine-oniguruma@3.12.0': + resolution: {integrity: sha512-IfDl3oXPbJ/Jr2K8mLeQVpnF+FxjAc7ZPDkgr38uEw/Bg3u638neSrpwqOTnTHXt1aU0Fk1/J+/RBdst1kVqLg==} + + '@shikijs/langs@3.12.0': + resolution: {integrity: sha512-HIca0daEySJ8zuy9bdrtcBPhcYBo8wR1dyHk1vKrOuwDsITtZuQeGhEkcEfWc6IDyTcom7LRFCH6P7ljGSCEiQ==} + + '@shikijs/themes@3.12.0': + resolution: {integrity: sha512-/lxvQxSI5s4qZLV/AuFaA4Wt61t/0Oka/P9Lmpr1UV+HydNCczO3DMHOC/CsXCCpbv4Zq8sMD0cDa7mvaVoj0Q==} + + '@shikijs/types@3.12.0': + resolution: {integrity: sha512-jsFzm8hCeTINC3OCmTZdhR9DOl/foJWplH2Px0bTi4m8z59fnsueLsweX82oGcjRQ7mfQAluQYKGoH2VzsWY4A==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@stablelib/base64@2.0.1': + resolution: {integrity: sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ==} - '@stablelib/binary@1.0.1': - resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} + '@stablelib/binary@2.0.1': + resolution: {integrity: sha512-U9iAO8lXgEDONsA0zPPSgcf3HUBNAqHiJmSHgZz62OvC3Hi2Bhc5kTnQ3S1/L+sthDTHtCMhcEiklmIly6uQ3w==} - '@stablelib/bytes@1.0.1': - resolution: {integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==} + '@stablelib/bytes@2.0.1': + resolution: {integrity: sha512-QIzI6V7nkJA5CjOZ7GoceBd4CIKrJoC471VaI6jh1xPQ2cMhkhQK4HddyzCXOR2y+fBF3/5B2HO3FXXI9C+Xzg==} - '@stablelib/int@1.0.1': - resolution: {integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==} + '@stablelib/int@2.0.1': + resolution: {integrity: sha512-Ht63fQp3wz/F8U4AlXEPb7hfJOIILs8Lq55jgtD7KueWtyjhVuzcsGLSTAWtZs3XJDZYdF1WcSKn+kBtbzupww==} - '@stablelib/keyagreement@1.0.1': - resolution: {integrity: sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==} + '@stablelib/keyagreement@2.0.1': + resolution: {integrity: sha512-2+tWBLCMtWlHQ7GqjD5L+lQRyWtun4Lou0IOdTML8zuTuAS0EgihnHFx+4uMZwYU1In40J/WlpyKSLidHfStRQ==} - '@stablelib/random@1.0.2': - resolution: {integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==} + '@stablelib/random@2.0.1': + resolution: {integrity: sha512-W6GAtXEEs7r+dSbuBsvoFmlyL3gLxle41tQkjKu17dDWtDdjhVUbtRfRCQcCUeczwkgjQxMPopgwYEvxXtHXGw==} - '@stablelib/wipe@1.0.1': - resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==} + '@stablelib/wipe@2.0.1': + resolution: {integrity: sha512-1eU2K9EgOcV4qc9jcP6G72xxZxEm5PfeI5H55l08W95b4oRJaqhmlWRc4xZAm6IVSKhVNxMi66V67hCzzuMTAg==} - '@stablelib/x25519@1.0.3': - resolution: {integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==} + '@stablelib/x25519@2.0.1': + resolution: {integrity: sha512-qi04HS2puHaBf50kM/kes5QcZFGsx8yF0YmCjLCOa/LPmnBaKEKX9ZR82OnnCwMn72YH13R/bBZgr/UP0aPFfA==} + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} @@ -750,68 +922,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' @@ -822,103 +994,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.24': + resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==} - '@tanstack/query-core@5.76.0': - resolution: {integrity: sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==} + '@tanstack/query-core@5.85.6': + resolution: {integrity: sha512-hCj0TktzdCv2bCepIdfwqVwUVWb+GSHm1Jnn8w+40lfhQ3m7lCO7ADRUJy+2unxQ/nzjh2ipC6ye69NDW3l73g==} - '@tanstack/query-devtools@5.76.0': - resolution: {integrity: sha512-1p92nqOBPYVqVDU0Ua5nzHenC6EGZNrLnB2OZphYw8CNA1exuvI97FVgIKON7Uug3uQqvH/QY8suUKpQo8qHNQ==} + '@tanstack/query-devtools@5.84.0': + resolution: {integrity: sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==} - '@tanstack/react-query-devtools@5.76.1': - resolution: {integrity: sha512-LFVWgk/VtXPkerNLfYIeuGHh0Aim/k9PFGA+JxLdRaUiroQ4j4eoEqBrUpQ1Pd/KXoG4AB9vVE/M6PUQ9vwxBQ==} + '@tanstack/react-query-devtools@5.85.6': + resolution: {integrity: sha512-A6rE39FypFV7eonefk4fxC/vuV/7YJMAcQT94CFAvCpiw65QZX8MOuUpdLBeG1cXajy4Pj8T8sEWHigccntJqg==} peerDependencies: - '@tanstack/react-query': ^5.76.1 + '@tanstack/react-query': ^5.85.6 react: ^18 || ^19 - '@tanstack/react-query@5.76.1': - resolution: {integrity: sha512-YxdLZVGN4QkT5YT1HKZQWiIlcgauIXEIsMOTSjvyD5wLYK8YVvKZUPAysMqossFJJfDpJW3pFn7WNZuPOqq+fw==} + '@tanstack/react-query@5.85.6': + resolution: {integrity: sha512-VUAag4ERjh+qlmg0wNivQIVCZUrYndqYu3/wPCVZd4r0E+1IqotbeyGTc+ICroL/PqbpSaGZg02zSWYfcvxbdA==} 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.3': + resolution: {integrity: sha512-+X/DjTlH9ZLT9kWrU+Mk9TSu8vVpv30GgfAOKUxlCQobaRSOzN0cxgZfRcgWaDLu80/gWsJ7Ktk9jLfJ9h9CVA==} 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.3': + resolution: {integrity: sha512-Bs+DK+gGinSj373DEeAuZMUrvTE1m7X5Ef2jC2lU2X8ZhQf4VBV+gNMRoOlSuwIlSTU2eKDQsExtKeFFSpbc8A==} 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.3': + resolution: {integrity: sha512-9pri7KWES6x0M0DWCr5RIsGtXD4yy83Zsf8xuSmn8z6xboFquSnfJZmFsfPz25G8awLFIhxUkxP0YtZGiIUy7g==} 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.3': + resolution: {integrity: sha512-2+qRdUgnFJ7pDW69dFZxYduWEZPya3U2YA6GaDhrYTHBq8/ypPSpuUT+BZ6n9r68+ij7tFMTj+vwNDgNp3M/0w==} 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.3': + resolution: {integrity: sha512-DJHW1vcqmLMqZCBiu9qv7/oYAygNC6xvrxwrUWvWMvaz/qKNy9NVXZm/EUx+sLTCcOxWHyJe+CII1kW3ouI18Q==} 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.3': + resolution: {integrity: sha512-+CbLaQXAqd5lPJnfXGyitbgp/q5mnsvCoToGspeVMBYNGd04ES/6XDEcXH7EwNCTgXBTJVRYf3qhI8a8/x31Aw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.8.3': + resolution: {integrity: sha512-FGjLnA+3PTJwoN5KEMAi6Q8I6SkuW5w8qSFKldGx2Mma8GqtttXqIDw1BzxcIw/LMcr6JrxjbIRULzmV05q/QA==} 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.3': + resolution: {integrity: sha512-tWRX3rQJCeUq9mR0Rc0tUV+pdgGL94UqVIzPn0/VmhDehdiDouRdXOUPggJrYUz2Aj/4RvVa83J6B8Hg37s8RQ==} 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.3': + resolution: {integrity: sha512-UQHDmbSMIeWs/Yr3KmtfZFs5m6b+NWUe2+NE7fHu3o4EzPrvNa/Uf4U2XsYKOr0V/yetxZH0/fc+xovcnsqyqA==} 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.3': + resolution: {integrity: sha512-aIP38i2KeADboPD1wsBFYdodEQ9PIJe0HW2urd3ocHpGxF8gX/KMiGOwGVSobu9gFlCpFNoVwCX6J1S5pJCRIQ==} 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.3': + resolution: {integrity: sha512-Z+H+PwK+3yMffG1rN7iqs+uPo6FkPyHJ4MTtFhnEvvGzc3aH711bwFb6+PXwMXfOb/jPR/LB+o6kEXghBu9ynQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@1.6.3': - resolution: {integrity: sha512-q46umd6QLRKDd4Gg6WyZBGa2fWvk0pbeUA5vFomm4uOs1/17LIciHv2iQ4UD+2Yv5H7AO8YiE1t50V0POiEGEw==} + '@tauri-apps/cli@2.8.3': + resolution: {integrity: sha512-5IlcOtVBI6HYcTRFH4tuLZV+FX09Psi4Xi+TyFf/S8T8w+ZzPNnrehHz6KUGRbuXHfJhtmRDoUULXNEhpdVkfA==} 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.2': + resolution: {integrity: sha512-og3F/wNrTKh1vdgnGr5fG/vJYHvok114PIZy2zjx753GKqHAaZpngaNc6aP3pKnSqvUp3sjgBbPn/DeJ+CXwrA==} + + '@tauri-apps/plugin-dialog@2.3.3': + resolution: {integrity: sha512-cWXB9QJDbLIA0v7I5QY183awazBEQNPhp19iPvrMZoJRX8SbFkhWFx1/q7zy7xGpXXzxz29qtq6z21Ho7W5Iew==} + + '@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.6.0': + resolution: {integrity: sha512-gVp3l31akA1Jk2bZsTA0hMFD5/gLe49Nw1btu5lViau0QqgC2XyT79LSwvy7a44ewtQbSexchqIg7oTJKMIbXQ==} + + '@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'} @@ -932,8 +1140,8 @@ 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==} @@ -971,8 +1179,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==} @@ -980,17 +1188,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==} @@ -998,25 +1200,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.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} '@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.12': + resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1024,98 +1220,36 @@ 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==} - '@vitejs/plugin-react-swc@3.9.0': - resolution: {integrity: sha512-jYFUSXhwMCYsh/aQTgSGLIN3Foz5wMbH9ahb0Zva//UzwZYbMiZd7oT3AU9jHT9DLswYDswsRwPU9jVF3yA48Q==} - peerDependencies: - vite: ^4 || ^5 || ^6 + '@use-gesture/core@10.3.1': + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} - '@vitejs/plugin-react@4.4.1': - resolution: {integrity: sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==} - engines: {node: ^14.18.0 || >=16.0.0} + '@use-gesture/react@10.3.1': + resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + react: '>= 16.8.0' - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + '@vitejs/plugin-react-swc@4.0.1': + resolution: {integrity: sha512-NQhPjysi5duItyrMd5JWZFf2vNOuSMyw+EoZyTBDzk+DkfYD8WNrsUs09sELV2cr1P15nufsN25hsUBt4CKF9Q==} + engines: {node: ^20.19.0 || >=22.12.0} 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 + vite: ^4 || ^5 || ^6 || ^7 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + '@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 || ^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'} @@ -1135,34 +1269,6 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - 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==} - 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'} @@ -1199,24 +1305,29 @@ packages: 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.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} 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==} @@ -1238,8 +1349,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001718: - resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} + caniuse-lite@1.0.30001739: + resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1271,6 +1382,10 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1328,12 +1443,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==} @@ -1343,8 +1454,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: @@ -1414,16 +1525,8 @@ 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==} @@ -1437,11 +1540,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==} @@ -1469,21 +1569,6 @@ packages: 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==} @@ -1504,22 +1589,25 @@ 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.211: + resolution: {integrity: sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} entities@4.5.0: 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: @@ -1530,10 +1618,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'} @@ -1542,17 +1626,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: @@ -1567,121 +1650,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==} @@ -1689,29 +1662,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==} @@ -1723,17 +1681,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'} @@ -1741,12 +1688,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: @@ -1774,6 +1724,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1793,35 +1747,15 @@ packages: 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'} @@ -1829,9 +1763,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'} @@ -1881,8 +1812,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 @@ -1896,9 +1827,8 @@ 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.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} immutable@4.3.7: resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} @@ -1907,10 +1837,6 @@ packages: 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. @@ -1985,6 +1911,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.0: resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} @@ -2000,6 +1930,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'} @@ -2008,10 +1942,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'} @@ -2058,13 +1988,12 @@ 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.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==} @@ -2077,47 +2006,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: @@ -2127,16 +2032,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==} @@ -2156,9 +2054,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: @@ -2195,22 +2092,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==} @@ -2274,9 +2170,9 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + millify@6.1.0: + resolution: {integrity: sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA==} + hasBin: true minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2285,16 +2181,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==} @@ -2304,9 +2213,6 @@ 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==} @@ -2351,41 +2257,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'} @@ -2405,22 +2283,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==} @@ -2439,6 +2305,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'} @@ -2455,25 +2325,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 @@ -2483,18 +2345,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: @@ -2509,13 +2368,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 @@ -2531,8 +2390,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' @@ -2540,24 +2399,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' @@ -2567,17 +2438,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==} @@ -2585,8 +2447,8 @@ 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: @@ -2597,15 +2459,21 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - recharts-scale@0.4.5: - resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} - - recharts@2.15.3: - resolution: {integrity: sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==} - engines: {node: '>=14'} + recharts@3.1.2: + resolution: {integrity: sha512-vhNbYwaxNbk/IATK0Ki29k3qvTkGqwvCgyQAQ9MavvvBwjvKnMTswdbklJpcOAoMPN/qxF3Lyqob0zO+ZXkZ4g==} + 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==} @@ -2624,6 +2492,13 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + require-directory@2.1.1: + 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'} @@ -2633,27 +2508,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.0: + resolution: {integrity: sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==} + 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==} @@ -2674,8 +2533,8 @@ packages: 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==} @@ -2685,11 +2544,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'} @@ -2706,25 +2560,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'} @@ -2741,10 +2584,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==} @@ -2768,20 +2607,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==} - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + 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.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'} @@ -2805,15 +2645,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==} @@ -2838,27 +2674,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.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2869,26 +2694,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'} @@ -2905,12 +2713,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.12: + resolution: {integrity: sha512-H5ODu4f7N+myG4MfuSp2Vh6wV+WLoZaEYxKPt2y8hmmqNEMVrH69DAjjdmYivF4tP/C2jrIZCZhPalZlTU/ipA==} + 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==} @@ -2918,24 +2726,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.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -2961,9 +2765,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: @@ -2976,10 +2777,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==} @@ -2988,48 +2789,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.4: + resolution: {integrity: sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==} + 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==} @@ -3051,18 +2858,17 @@ 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'} wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -3070,15 +2876,24 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + 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'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} - zod@3.24.4: - resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - 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' @@ -3102,8 +2917,8 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 '@babel/code-frame@7.27.1': dependencies: @@ -3111,20 +2926,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.27.2': {} + '@babel/compat-data@7.28.0': {} - '@babel/core@7.27.1': + '@babel/core@7.28.3': 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.3) + '@babel/helpers': 7.28.3 + '@babel/parser': 7.28.3 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -3133,35 +2948,37 @@ snapshots: 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.3 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.2 + '@babel/compat-data': 7.28.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.24.5 + browserslist: 4.25.4 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.3 + '@babel/types': 7.28.2 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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.28.3 transitivePeerDependencies: - supports-color @@ -3173,54 +2990,89 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.27.1': + '@babel/helpers@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.28.2 - '@babel/parser@7.27.2': + '@babel/parser@7.28.3': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.2 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.27.1': {} + '@babel/runtime@7.28.3': {} '@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.3 + '@babel/types': 7.28.2 - '@babel/traverse@7.27.1': + '@babel/traverse@7.28.3': 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.3 '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.28.2 debug: 4.4.1 - globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.27.1': + '@babel/types@7.28.2': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@biomejs/biome@2.2.2': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.2 + '@biomejs/cli-darwin-x64': 2.2.2 + '@biomejs/cli-linux-arm64': 2.2.2 + '@biomejs/cli-linux-arm64-musl': 2.2.2 + '@biomejs/cli-linux-x64': 2.2.2 + '@biomejs/cli-linux-x64-musl': 2.2.2 + '@biomejs/cli-win32-arm64': 2.2.2 + '@biomejs/cli-win32-x64': 2.2.2 + + '@biomejs/cli-darwin-arm64@2.2.2': + optional: true + + '@biomejs/cli-darwin-x64@2.2.2': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.2': + optional: true + + '@biomejs/cli-linux-arm64@2.2.2': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.2': + optional: true + + '@biomejs/cli-linux-x64@2.2.2': + optional: true + + '@biomejs/cli-win32-arm64@2.2.2': + optional: true + + '@biomejs/cli-win32-x64@2.2.2': + 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.3 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -3243,33 +3095,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': 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.12)(react@19.1.1)': dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.3 '@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.12 transitivePeerDependencies: - supports-color @@ -3283,303 +3127,372 @@ 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.12)(react@19.1.1))(@types/react@19.1.12)(react@19.1.1)': dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.3 '@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/react': 11.14.0(@types/react@19.1.12)(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.12 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.0': + dependencies: + '@shikijs/engine-oniguruma': 3.12.0 + '@shikijs/langs': 3.12.0 + '@shikijs/themes': 3.12.0 + '@shikijs/types': 3.12.0 + '@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.12)(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.12)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.12)(react@19.1.1))(@types/react@19.1.12)(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.30 - '@humanwhocodes/module-importer@1.0.1': {} + '@jridgewell/resolve-uri@3.1.2': {} - '@humanwhocodes/object-schema@2.0.3': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/trace-mapping@0.3.30': 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/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/set-array@1.2.1': {} + '@react-hook/latest@1.0.3(react@19.1.1)': + dependencies: + react: 19.1.1 - '@jridgewell/sourcemap-codec@1.5.0': {} + '@react-hook/passive-layout-effect@1.2.1(react@19.1.1)': + dependencies: + react: 19.1.1 - '@jridgewell/trace-mapping@0.3.25': + '@react-hook/resize-observer@2.0.2(react@19.1.1)': dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@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.scandir@2.1.5': + '@reduxjs/toolkit@2.8.2(react-redux@9.2.0(@types/react@19.1.12)(react@19.1.1)(redux@5.0.1))(react@19.1.1)': dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 10.1.1 + 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.12)(react@19.1.1)(redux@5.0.1) + + '@remix-run/router@1.23.0': {} - '@nodelib/fs.stat@2.0.5': {} + '@rolldown/pluginutils@1.0.0-beta.32': {} - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + '@rolldown/pluginutils@1.0.0-beta.34': {} + + '@rollup/rollup-android-arm-eabi@4.50.0': + optional: true + + '@rollup/rollup-android-arm64@4.50.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.50.0': + optional: true + + '@rollup/rollup-darwin-x64@4.50.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.50.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.50.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.50.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.50.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.50.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.50.0': + optional: true - '@pkgr/core@0.2.4': {} + '@rollup/rollup-linux-riscv64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.50.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.50.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.50.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.50.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.0': + optional: true - '@react-hook/latest@1.0.3(react@18.3.1)': + '@shikijs/engine-oniguruma@3.12.0': dependencies: - react: 18.3.1 + '@shikijs/types': 3.12.0 + '@shikijs/vscode-textmate': 10.0.2 - '@react-hook/passive-layout-effect@1.2.1(react@18.3.1)': + '@shikijs/langs@3.12.0': dependencies: - react: 18.3.1 + '@shikijs/types': 3.12.0 - '@react-hook/resize-observer@2.0.2(react@18.3.1)': + '@shikijs/themes@3.12.0': 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.0 - '@remix-run/router@1.23.0': {} + '@shikijs/types@3.12.0': + 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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@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.3)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.28.3 - '@svgr/babel-preset@8.1.0(@babel/core@7.27.1)': + '@svgr/babel-preset@8.1.0(@babel/core@7.28.3)': 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.3 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.3) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.3) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.3) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.3) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.3) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.3) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.3) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.3) - '@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 @@ -3590,12 +3503,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.3 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.3) 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 @@ -3603,177 +3516,220 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.2 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.3 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.3) + '@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.24 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.24': dependencies: '@swc/counter': 0.1.3 - '@tanstack/query-core@5.76.0': {} + '@tanstack/query-core@5.85.6': {} - '@tanstack/query-devtools@5.76.0': {} + '@tanstack/query-devtools@5.84.0': {} - '@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.85.6(@tanstack/react-query@5.85.6(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.84.0 + '@tanstack/react-query': 5.85.6(react@19.1.1) + react: 19.1.1 - '@tanstack/react-query@5.76.1(react@18.3.1)': + '@tanstack/react-query@5.85.6(react@19.1.1)': dependencies: - '@tanstack/query-core': 5.76.0 - react: 18.3.1 + '@tanstack/query-core': 5.85.6 + 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.3': optional: true - '@tauri-apps/cli-darwin-x64@1.6.3': + '@tauri-apps/cli-darwin-x64@2.8.3': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@1.6.3': + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.3': optional: true - '@tauri-apps/cli-linux-arm64-gnu@1.6.3': + '@tauri-apps/cli-linux-arm64-gnu@2.8.3': optional: true - '@tauri-apps/cli-linux-arm64-musl@1.6.3': + '@tauri-apps/cli-linux-arm64-musl@2.8.3': optional: true - '@tauri-apps/cli-linux-x64-gnu@1.6.3': + '@tauri-apps/cli-linux-riscv64-gnu@2.8.3': optional: true - '@tauri-apps/cli-linux-x64-musl@1.6.3': + '@tauri-apps/cli-linux-x64-gnu@2.8.3': optional: true - '@tauri-apps/cli-win32-arm64-msvc@1.6.3': + '@tauri-apps/cli-linux-x64-musl@2.8.3': optional: true - '@tauri-apps/cli-win32-ia32-msvc@1.6.3': + '@tauri-apps/cli-win32-arm64-msvc@2.8.3': optional: true - '@tauri-apps/cli-win32-x64-msvc@1.6.3': + '@tauri-apps/cli-win32-ia32-msvc@2.8.3': optional: true - '@tauri-apps/cli@1.6.3': - dependencies: - semver: 7.7.2 + '@tauri-apps/cli-win32-x64-msvc@2.8.3': + optional: true + + '@tauri-apps/cli@2.8.3': 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.3 + '@tauri-apps/cli-darwin-x64': 2.8.3 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.8.3 + '@tauri-apps/cli-linux-arm64-gnu': 2.8.3 + '@tauri-apps/cli-linux-arm64-musl': 2.8.3 + '@tauri-apps/cli-linux-riscv64-gnu': 2.8.3 + '@tauri-apps/cli-linux-x64-gnu': 2.8.3 + '@tauri-apps/cli-linux-x64-musl': 2.8.3 + '@tauri-apps/cli-win32-arm64-msvc': 2.8.3 + '@tauri-apps/cli-win32-ia32-msvc': 2.8.3 + '@tauri-apps/cli-win32-x64-msvc': 2.8.3 + + '@tauri-apps/plugin-clipboard-manager@2.3.0': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-deep-link@2.4.2': + dependencies: + '@tauri-apps/api': 2.8.0 + + '@tauri-apps/plugin-dialog@2.3.3': + 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.6.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.3 + '@babel/types': 7.28.2 '@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.2 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 - '@types/babel__traverse@7.20.7': + '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.2 '@types/byte-size@8.1.2': {} @@ -3807,9 +3763,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': {} @@ -3817,15 +3773,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: @@ -3833,152 +3785,57 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@20.17.48': + '@types/node@24.3.0': dependencies: - undici-types: 6.19.8 + undici-types: 7.10.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.12)': dependencies: - '@types/react': 18.3.21 + '@types/react': 19.1.12 - '@types/react@18.3.21': + '@types/react@19.1.12': 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 + '@types/use-sync-external-store@0.0.6': {} - '@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 + '@ungap/structured-clone@1.3.0': {} - '@typescript-eslint/types@6.21.0': {} + '@use-gesture/core@10.3.1': {} - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.8.3)': + '@use-gesture/react@10.3.1(react@19.1.1)': 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 + '@use-gesture/core': 10.3.1 + react: 19.1.1 - '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.8.3)': + '@vitejs/plugin-react-swc@4.0.1(vite@7.1.4(@types/node@24.3.0)(sass@1.70.0)(yaml@2.8.1))': 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 - - '@ungap/structured-clone@1.3.0': {} - - '@vitejs/plugin-react-swc@3.9.0(vite@4.5.14(@types/node@20.17.48)(sass@1.70.0))': - 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.4(@types/node@24.3.0)(sass@1.70.0)(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.4(@types/node@24.3.0)(sass@1.70.0)(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.3 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.3) + '@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.4(@types/node@24.3.0)(sass@1.70.0)(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 @@ -3999,78 +3856,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.25.4 + caniuse-lite: 1.0.30001739 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: @@ -4079,7 +3884,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.3 cosmiconfig: 7.1.0 resolve: 1.22.10 @@ -4091,12 +3896,12 @@ snapshots: 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 @@ -4104,14 +3909,14 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.5: + browserslist@4.25.4: dependencies: - caniuse-lite: 1.0.30001718 - electron-to-chromium: 1.5.155 + caniuse-lite: 1.0.30001739 + electron-to-chromium: 1.5.211 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.5) + update-browserslist-db: 1.1.3(browserslist@4.25.4) - byte-size@8.2.1: {} + byte-size@9.0.1: {} call-bind-apply-helpers@1.0.2: dependencies: @@ -4134,7 +3939,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001718: {} + caniuse-lite@1.0.30001739: {} ccount@2.0.1: {} @@ -4171,6 +3976,12 @@ snapshots: classnames@2.5.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} color-convert@1.9.3: @@ -4207,14 +4018,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: @@ -4224,16 +4035,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 @@ -4248,7 +4053,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: @@ -4314,11 +4119,7 @@ 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: dependencies: @@ -4326,12 +4127,10 @@ snapshots: 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: {} @@ -4356,23 +4155,6 @@ snapshots: 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 @@ -4402,17 +4184,19 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.155: {} + electron-to-chromium@1.5.211: {} + + 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 @@ -4441,7 +4225,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 @@ -4453,287 +4239,89 @@ snapshots: own-keys: 1.0.1 regexp.prototype.flags: 1.5.4 safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - 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 + 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 - 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 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + 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: {} @@ -4743,32 +4331,21 @@ snapshots: 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.3.1)(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.3.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) fs.realpath@1.0.0: {} @@ -4790,6 +4367,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4820,19 +4399,6 @@ snapshots: 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 @@ -4841,32 +4407,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: {} @@ -4899,7 +4448,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 @@ -4911,9 +4460,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 @@ -4932,15 +4481,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.12)(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.12 html-url-attributes@3.0.1: {} @@ -4949,9 +4498,9 @@ 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.1: {} immutable@4.3.7: {} @@ -4960,8 +4509,6 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -5040,6 +4587,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.0: dependencies: call-bound: 1.0.4 @@ -5055,6 +4604,8 @@ snapshots: is-map@2.0.3: {} + is-negative-zero@2.0.3: {} + is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -5062,8 +4613,6 @@ snapshots: is-number@7.0.0: {} - is-path-inside@3.0.3: {} - is-plain-obj@4.1.0: {} is-regex@1.2.1: @@ -5109,17 +4658,10 @@ 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.4.1: {} + js-base64@3.7.8: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -5128,45 +4670,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: @@ -5175,14 +4693,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: {} @@ -5201,7 +4713,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: {} @@ -5209,7 +4728,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 @@ -5246,7 +4765,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 @@ -5298,17 +4817,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.12): optionalDependencies: - '@types/react': 18.3.21 - - merge2@1.4.1: {} + '@types/react': 19.1.12 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 @@ -5383,7 +4902,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 @@ -5421,7 +4940,7 @@ snapshots: dependencies: '@types/debug': 4.1.12 debug: 4.4.1 - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-factory-space: 2.0.1 @@ -5439,35 +4958,41 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.8: + millify@6.1.0: dependencies: - braces: 3.0.3 - picomatch: 2.3.1 + yargs: 17.7.2 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 - minimist@1.2.8: {} + motion-utils@12.23.6: {} + + motion@12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + framer-motion: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.3.1 + 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: @@ -5497,7 +5022,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: @@ -5519,60 +5044,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: @@ -5584,7 +5065,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 @@ -5601,14 +5082,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: @@ -5621,6 +5096,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.3: {} + pidtree@0.3.1: {} pify@3.0.0: {} @@ -5629,21 +5106,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: @@ -5653,51 +5124,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@12.1.1: {} - radash@11.0.0: {} - - 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.12)(react@19.1.1): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 18.3.21 + '@types/react': 19.1.12 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 @@ -5708,55 +5176,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.12)(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.12 + 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: @@ -5768,28 +5228,37 @@ snapshots: dependencies: picomatch: 2.3.1 - recharts-scale@0.4.5: - dependencies: - decimal.js-light: 2.5.1 - - recharts@2.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + recharts@3.1.2(@types/react@19.1.12)(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.8.2(react-redux@9.2.0(@types/react@19.1.12)(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.1 + 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.12)(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 @@ -5827,6 +5296,10 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + require-directory@2.1.1: {} + + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve@1.22.10: @@ -5835,26 +5308,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.0: dependencies: - glob: 7.2.3 - - rollup@3.29.5: + '@types/estree': 1.0.8 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.50.0 + '@rollup/rollup-android-arm64': 4.50.0 + '@rollup/rollup-darwin-arm64': 4.50.0 + '@rollup/rollup-darwin-x64': 4.50.0 + '@rollup/rollup-freebsd-arm64': 4.50.0 + '@rollup/rollup-freebsd-x64': 4.50.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.0 + '@rollup/rollup-linux-arm-musleabihf': 4.50.0 + '@rollup/rollup-linux-arm64-gnu': 4.50.0 + '@rollup/rollup-linux-arm64-musl': 4.50.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.0 + '@rollup/rollup-linux-ppc64-gnu': 4.50.0 + '@rollup/rollup-linux-riscv64-gnu': 4.50.0 + '@rollup/rollup-linux-riscv64-musl': 4.50.0 + '@rollup/rollup-linux-s390x-gnu': 4.50.0 + '@rollup/rollup-linux-x64-gnu': 4.50.0 + '@rollup/rollup-linux-x64-musl': 4.50.0 + '@rollup/rollup-openharmony-arm64': 4.50.0 + '@rollup/rollup-win32-arm64-msvc': 4.50.0 + '@rollup/rollup-win32-ia32-msvc': 4.50.0 + '@rollup/rollup-win32-x64-msvc': 4.50.0 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -5884,16 +5364,12 @@ snapshots: immutable: 4.3.7 source-map-js: 1.2.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 @@ -5920,22 +5396,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: @@ -5965,8 +5428,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 @@ -5981,52 +5442,42 @@ 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: {} - string.prototype.matchall@4.0.12: + stop-iteration-iterator@1.1.0: 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-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 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 @@ -6054,13 +5505,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 @@ -6082,29 +5531,20 @@ 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.14: 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: @@ -6114,25 +5554,8 @@ snapshots: 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 @@ -6166,25 +5589,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.12(typescript@5.9.2): dependencies: + '@gerrit0/mini-shiki': 3.12.0 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: @@ -6193,7 +5613,7 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@6.19.8: {} + undici-types@7.10.0: {} unified@11.0.5: dependencies: @@ -6228,31 +5648,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.25.4): dependencies: - browserslist: 4.24.5 + browserslist: 4.25.4 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.3 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: {} @@ -6261,7 +5676,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 @@ -6269,9 +5684,9 @@ 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-ease': 3.0.2 @@ -6288,19 +5703,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.4(@types/node@24.3.0)(sass@1.70.0)(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.0 + tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 20.17.48 + '@types/node': 24.3.0 fsevents: 2.3.3 sass: 1.70.0 - - vscode-oniguruma@1.7.0: {} - - vscode-textmate@8.0.0: {} + yaml: 2.8.1 which-boxed-primitive@1.1.1: dependencies: @@ -6347,26 +5762,41 @@ snapshots: dependencies: isexe: 2.0.0 - which@2.0.2: + wrap-ansi@7.0.0: dependencies: - isexe: 2.0.0 - - word-wrap@1.2.5: {} + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 wrappy@1.0.2: {} + y18n@5.0.8: {} + yallist@3.1.1: {} yaml@1.10.2: {} - yocto-queue@0.1.0: {} + yaml@2.8.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 - 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.12)(immer@10.1.1)(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.12 + immer: 10.1.1 + 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-76998bbd9e0096deb957c8e60df4b7f10eb86c8341e3cc0bc081e5c4dcbcee44.json b/src-tauri/.sqlx/query-65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1.json similarity index 82% rename from src-tauri/.sqlx/query-76998bbd9e0096deb957c8e60df4b7f10eb86c8341e3cc0bc081e5c4dcbcee44.json rename to src-tauri/.sqlx/query-65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1.json index 5ed80c68..57f2b8f7 100644 --- a/src-tauri/.sqlx/query-76998bbd9e0096deb957c8e60df4b7f10eb86c8341e3cc0bc081e5c4dcbcee44.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 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": [ { @@ -47,6 +47,11 @@ "name": "enterprise_enabled", "ordinal": 8, "type_info": "Bool" + }, + { + "name": "openid_display_name", + "ordinal": 9, + "type_info": "Text" } ], "parameters": { @@ -61,8 +66,9 @@ false, true, false, - false + false, + true ] }, - "hash": "76998bbd9e0096deb957c8e60df4b7f10eb86c8341e3cc0bc081e5c4dcbcee44" + "hash": "65b503d8af2eb227b555274ec98411966a0a410314d5c109df6477ce799b40d1" } diff --git a/src-tauri/.sqlx/query-db3aa093e5e74398f5b921ddb6e833962b82c95d4b98993264cb55f9e96c81c0.json b/src-tauri/.sqlx/query-758d3c67336eecafab5fd20e4b03574995383092cdb4d7c3ebda6f933ea0f472.json similarity index 58% rename from src-tauri/.sqlx/query-db3aa093e5e74398f5b921ddb6e833962b82c95d4b98993264cb55f9e96c81c0.json rename to src-tauri/.sqlx/query-758d3c67336eecafab5fd20e4b03574995383092cdb4d7c3ebda6f933ea0f472.json index eb12a208..40a4087a 100644 --- a/src-tauri/.sqlx/query-db3aa093e5e74398f5b921ddb6e833962b82c95d4b98993264cb55f9e96c81c0.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 WHERE id = $9;", + "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": 9 + "Right": 10 }, "nullable": [] }, - "hash": "db3aa093e5e74398f5b921ddb6e833962b82c95d4b98993264cb55f9e96c81c0" + "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-c688e91a89197793b2a63dcefc41c652fc89286481300450763a7a9b66d146f9.json b/src-tauri/.sqlx/query-aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a.json similarity index 82% rename from src-tauri/.sqlx/query-c688e91a89197793b2a63dcefc41c652fc89286481300450763a7a9b66d146f9.json rename to src-tauri/.sqlx/query-aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a.json index b09e9828..bfefdf36 100644 --- a/src-tauri/.sqlx/query-c688e91a89197793b2a63dcefc41c652fc89286481300450763a7a9b66d146f9.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 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": [ { @@ -47,6 +47,11 @@ "name": "enterprise_enabled", "ordinal": 8, "type_info": "Bool" + }, + { + "name": "openid_display_name", + "ordinal": 9, + "type_info": "Text" } ], "parameters": { @@ -61,8 +66,9 @@ false, true, false, - false + false, + true ] }, - "hash": "c688e91a89197793b2a63dcefc41c652fc89286481300450763a7a9b66d146f9" + "hash": "aa7d2c4c2100151b6f555ade668e72ceef8e7d1e39fcf0bcef0b7433457d3d2a" } diff --git a/src-tauri/.sqlx/query-afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21.json b/src-tauri/.sqlx/query-ac02b04f6490a768571290d7dc77444eb0ca55a3a7e159c3b2e529ebf75f224f.json similarity index 84% rename from src-tauri/.sqlx/query-afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21.json rename to src-tauri/.sqlx/query-ac02b04f6490a768571290d7dc77444eb0ca55a3a7e159c3b2e529ebf75f224f.json index 8ab4e1e5..6df78777 100644 --- a/src-tauri/.sqlx/query-afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21.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 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 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": "afab9e8172fcd2187e99f7222eb1394fbc68b54cf5ebf36764becbc42c89ad21" + "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-9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a.json b/src-tauri/.sqlx/query-e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11.json similarity index 85% rename from src-tauri/.sqlx/query-9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a.json rename to src-tauri/.sqlx/query-e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11.json index fac4f5fc..eb35ee44 100644 --- a/src-tauri/.sqlx/query-9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a.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 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 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": "9d946237727089451a9bfc7ce2015d8b14eb0816c5c18cf81bb4a3b1c8e9aa6a" + "hash": "e91278b90769f39e2cdf1677ffa1193580af693f9871a7162c47393daac8af11" } diff --git a/src-tauri/.sqlx/query-d84dc04e42e2ef85f990b3f01c4db1ac59ec5e5940a7fa7ff1f6d2181a1f4763.json b/src-tauri/.sqlx/query-f4b187b6f90edb7fd65e82a60e786942680cd7c66c42a315de0e62e47c4f2df4.json similarity index 76% rename from src-tauri/.sqlx/query-d84dc04e42e2ef85f990b3f01c4db1ac59ec5e5940a7fa7ff1f6d2181a1f4763.json rename to src-tauri/.sqlx/query-f4b187b6f90edb7fd65e82a60e786942680cd7c66c42a315de0e62e47c4f2df4.json index 3898614a..881650b4 100644 --- a/src-tauri/.sqlx/query-d84dc04e42e2ef85f990b3f01c4db1ac59ec5e5940a7fa7ff1f6d2181a1f4763.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 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": [ { @@ -47,6 +47,11 @@ "name": "enterprise_enabled", "ordinal": 8, "type_info": "Bool" + }, + { + "name": "openid_display_name", + "ordinal": 9, + "type_info": "Text" } ], "parameters": { @@ -61,8 +66,9 @@ false, true, false, - false + false, + true ] }, - "hash": "d84dc04e42e2ef85f990b3f01c4db1ac59ec5e5940a7fa7ff1f6d2181a1f4763" + "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 1a74ee7a..3c867eec 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -64,6 +64,23 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +[[package]] +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 = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -75,9 +92,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 +122,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.5.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" +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 +179,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 +188,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 +218,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 +237,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 +249,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.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" dependencies = [ - "async-lock 3.4.0", + "async-lock", "blocking", - "futures-lite 2.6.0", + "futures-lite", ] [[package]] @@ -262,70 +278,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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -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" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ - "async-lock 3.4.0", + "async-lock", "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.0.8", "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.60.2", ] [[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 +322,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.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" 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.0.8", ] [[package]] @@ -385,42 +353,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.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" 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.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[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 +400,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 +408,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 +463,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 +481,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 +521,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 +539,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 +547,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" dependencies = [ "serde", ] @@ -638,12 +566,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 +590,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 +626,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,29 +642,19 @@ 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.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-unit" @@ -779,9 +691,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,43 +718,76 @@ 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.3", "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.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0b03af37dad7a14518b7691d81acb0f8222604ad3d1b02f6b4bed5188c0cd5" +dependencies = [ + "serde", +] + +[[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.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "jobserver", "libc", @@ -866,15 +811,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 +823,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" @@ -914,9 +850,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", "clap_derive", @@ -924,9 +860,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstream", "anstyle", @@ -936,14 +872,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -954,49 +890,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" @@ -1015,9 +915,9 @@ dependencies = [ [[package]] name = "common" -version = "1.0.0" +version = "1.5.0" dependencies = [ - "nix 0.29.0", + "nix", ] [[package]] @@ -1118,25 +1018,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.3", + "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.3", + "core-foundation 0.10.1", "libc", ] @@ -1166,9 +1066,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 +1082,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" @@ -1218,9 +1099,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -1234,15 +1115,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 +1137,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 +1147,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 +1174,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1302,7 +1183,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 +1212,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1342,12 +1223,18 @@ 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.4.0" +version = "1.5.0" dependencies = [ "anyhow", "base64 0.22.1", @@ -1357,13 +1244,14 @@ dependencies = [ "dark-light", "defguard_wireguard_rs", "dirs-next", + "hyper-util", "log", - "nix 0.29.0", + "nix", "prost", - "prost-build", "regex", - "reqwest 0.12.20", + "reqwest", "rust-ini", + "semver", "serde", "serde_json", "serde_with", @@ -1372,15 +1260,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", + "tonic-prost", + "tonic-prost-build", + "tower", "tracing", "tracing-appender", "tracing-subscriber", @@ -1393,7 +1292,7 @@ dependencies = [ [[package]] name = "defguard-dg" -version = "1.4.0" +version = "1.5.0" dependencies = [ "clap", "common", @@ -1401,10 +1300,10 @@ dependencies = [ "dirs-next", "prost", "prost-build", - "reqwest 0.12.20", + "reqwest", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tracing", "tracing-subscriber", @@ -1412,8 +1311,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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2d2f56ffaf56903a51b128c6f6730b8b344fab0d0be0f5db0b65dcccbb7334" dependencies = [ "base64 0.22.1", "libc", @@ -1424,9 +1324,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", ] @@ -1443,30 +1343,19 @@ 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" +name = "derive_builder" +version = "0.20.2" 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] @@ -1480,7 +1369,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1490,7 +1379,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]] @@ -1503,7 +1392,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1518,6 +1407,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" @@ -1528,6 +1426,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.60.2", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1535,7 +1445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -1551,8 +1461,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.3", + "block2 0.6.1", + "libc", + "objc2 0.6.2", ] [[package]] @@ -1563,7 +1475,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]] @@ -1596,6 +1540,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" @@ -1619,9 +1572,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" @@ -1634,16 +1587,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]] @@ -1685,7 +1638,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]] @@ -1694,6 +1657,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.13" @@ -1729,20 +1702,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", @@ -1755,24 +1717,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" @@ -1785,9 +1758,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", ] @@ -1804,22 +1777,10 @@ 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - [[package]] name = "fixedbitset" version = "0.4.2" @@ -1842,15 +1803,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" @@ -1880,7 +1832,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]] @@ -1889,11 +1862,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", ] @@ -1960,26 +1939,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", @@ -1994,7 +1958,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2037,11 +2001,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", @@ -2053,35 +2016,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", @@ -2091,47 +2054,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]] @@ -2146,12 +2110,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.0.8", + "windows-targets 0.52.6", ] [[package]] @@ -2172,8 +2136,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]] @@ -2183,9 +2149,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.3+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -2196,31 +2164,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", ] @@ -2230,7 +2200,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.3", "libc", "libgit2-sys", "log", @@ -2239,19 +2209,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.3", "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", @@ -2259,47 +2232,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" @@ -2315,23 +2274,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", @@ -2342,16 +2300,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", @@ -2362,36 +2319,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.9.0", + "http", + "indexmap 2.11.0", "slab", "tokio", "tokio-util", @@ -2399,22 +2355,13 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.4.10" +name = "half" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.3.1", - "indexmap 2.9.0", - "slab", - "tokio", - "tokio-util", - "tracing", + "cfg-if", + "crunchy", ] [[package]] @@ -2434,9 +2381,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", @@ -2449,16 +2396,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]] @@ -2473,12 +2411,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" @@ -2520,59 +2452,35 @@ 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", + "match_token", ] [[package]] name = "http" -version = "0.2.12" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -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", -] - -[[package]] -name = "http-body" -version = "1.0.1" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http", ] [[package]] @@ -2583,17 +2491,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" @@ -2608,44 +2510,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.10", - "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", @@ -2657,14 +2537,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]] @@ -2673,26 +2554,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" @@ -2701,7 +2569,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-util", "native-tls", "tokio", @@ -2711,24 +2579,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", @@ -2766,7 +2634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" dependencies = [ "byteorder", - "png", + "png 0.17.16", ] [[package]] @@ -2863,9 +2731,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", @@ -2882,44 +2750,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.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "1c6a3ce16143778e24df6f95365f12ed105425b22abefd289dd88a64bab59605" dependencies = [ "bytemuck", "byteorder-lite", + "moxcms", "num-traits", - "png", + "png 0.18.0", "tiff", ] @@ -2936,42 +2777,33 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" 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" +name = "io-uring" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ + "bitflags 2.9.3", "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]] @@ -2990,6 +2822,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" @@ -3005,12 +2856,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" @@ -3019,9 +2864,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", @@ -3030,28 +2875,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]] @@ -3078,20 +2909,14 @@ 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" @@ -3104,9 +2929,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", @@ -3116,25 +2941,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.3", + "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.0", "selectors", ] @@ -3158,9 +2992,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", @@ -3171,20 +3005,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" @@ -3208,6 +3042,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" @@ -3216,11 +3060,11 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", "redox_syscall", ] @@ -3248,12 +3092,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" @@ -3274,9 +3112,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" @@ -3299,19 +3137,10 @@ dependencies = [ ] [[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" @@ -3321,46 +3150,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 = [ - "regex-automata 0.1.10", + "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", ] [[package]] @@ -3371,9 +3202,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" @@ -3391,15 +3222,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" @@ -3442,6 +3264,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" @@ -3460,21 +3313,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.3", "jni-sys", + "log", "ndk-sys", "num_enum", + "raw-window-handle", "thiserror 1.0.69", ] @@ -3486,9 +3341,9 @@ 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", ] @@ -3523,7 +3378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" dependencies = [ "anyhow", - "bitflags 2.9.1", + "bitflags 2.9.3", "byteorder", "libc", "log", @@ -3574,42 +3429,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.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset 0.9.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.3", "cfg-if", "cfg_aliases", "libc", - "memoffset 0.9.1", + "memoffset", ] [[package]] @@ -3634,22 +3464,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 +3536,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]] @@ -3736,31 +3566,10 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc-sys" +version = "0.3.5" 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" [[package]] name = "objc2" @@ -3774,11 +3583,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 +3597,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.3", + "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.3", + "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.3", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -3799,9 +3638,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.3", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -3810,26 +3649,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.3", "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.3", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -3841,10 +3699,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.3", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -3854,27 +3712,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.3", + "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.3", + "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.3", + "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.3", + "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.3", + "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.3", + "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.3", + "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 +3823,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.3", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -3921,7 +3858,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3932,9 +3869,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.0+3.5.0" +version = "300.5.2+3.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" dependencies = [ "cc", ] @@ -3952,6 +3889,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 +3915,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 +3937,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 +3952,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]] @@ -4048,6 +3997,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4059,9 +4014,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 +4025,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.9.0", + "indexmap 2.11.0", ] [[package]] @@ -4080,7 +4035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.9.0", + "indexmap 2.11.0", ] [[package]] @@ -4089,9 +4044,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 +4053,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 +4080,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 +4120,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 +4142,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4234,7 +4189,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4256,7 +4211,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 +4244,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.9.0", - "quick-xml", + "indexmap 2.11.0", + "quick-xml 0.38.3", "serde", "time", ] @@ -4314,41 +4269,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.3", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", ] [[package]] name = "polling" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" 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.0.8", + "windows-sys 0.60.2", ] [[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 +4327,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 +4345,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 +4395,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 +4414,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 +4427,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 +4492,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.3", + "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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e790881194f6f6e86945f0a42a6981977323669aeb6c40e9c7ec253133b96f8" +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 +4536,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 +4646,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 +4676,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 +4704,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 +4733,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.3", ] [[package]] @@ -4667,6 +4757,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +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" @@ -4684,52 +4785,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", + "regex-automata", + "regex-syntax", ] [[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", -] - -[[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 +4828,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.23" 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.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", @@ -4794,13 +4838,14 @@ dependencies = [ "cookie_store", "encoding_rs", "futures-core", - "h2 0.4.10", - "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 +4853,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]] @@ -4940,9 +4992,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 +5011,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.3", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4982,24 +5026,26 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[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 +5053,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.3.0", ] [[package]] @@ -5021,14 +5070,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.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -5037,9 +5087,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" @@ -5065,6 +5115,21 @@ dependencies = [ "windows-sys 0.59.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]] name = "schemars" version = "0.9.0" @@ -5077,6 +5142,30 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "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" @@ -5101,13 +5190,26 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +dependencies = [ + "bitflags 2.9.3", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" version = "2.14.0" @@ -5120,22 +5222,20 @@ 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]] @@ -5156,6 +5256,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -5164,17 +5275,27 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ - "indexmap 2.9.0", - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -5188,7 +5309,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5200,6 +5321,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" @@ -5207,23 +5337,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] [[package]] name = "serde_with" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", - "schemars", + "indexmap 2.11.0", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -5233,14 +5364,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5262,14 +5393,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", @@ -5314,9 +5445,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", ] @@ -5357,9 +5488,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" @@ -5372,50 +5503,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]] @@ -5462,14 +5603,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.9.0", + "indexmap 2.11.0", "log", "memchr", "once_cell", @@ -5478,7 +5619,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tokio-stream", "tracing", @@ -5496,7 +5637,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5519,7 +5660,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.104", + "syn 2.0.106", "tokio", "url", ] @@ -5532,7 +5673,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.1", + "bitflags 2.9.3", "byteorder", "bytes", "chrono", @@ -5548,7 +5689,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "itoa 1.0.15", + "itoa", "log", "md-5", "memchr", @@ -5562,7 +5703,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "uuid", "whoami", @@ -5576,7 +5717,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.1", + "bitflags 2.9.3", "byteorder", "chrono", "crc", @@ -5589,7 +5730,7 @@ dependencies = [ "hkdf", "hmac", "home", - "itoa 1.0.15", + "itoa", "log", "md-5", "memchr", @@ -5601,7 +5742,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "uuid", "whoami", @@ -5627,7 +5768,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.12", + "thiserror 2.0.16", "tracing", "url", "uuid", @@ -5639,15 +5780,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" @@ -5698,44 +5830,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]] @@ -5744,6 +5875,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" @@ -5757,21 +5899,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" @@ -5789,18 +5925,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]] @@ -5809,19 +5943,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.3", "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]] @@ -5834,78 +5958,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.3", + "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", + "windows-version", "x11-dl", ] @@ -5917,7 +6019,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5926,17 +6028,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" @@ -5945,216 +6036,415 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "1.8.3" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae1f57c291a6ab8e1d2e6b8ad0a35ff769c9925deb8a89de85425ff08762d0c" +checksum = "5d545ccf7b60dcd44e07c6fb5aeb09140966f0aabd5d2aa14a6821df7bc99348" 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.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db08694eec06f53625cfc6fff3a363e084e5e9a238166d2989996413c346453" +checksum = "67945dbaf8920dbe3a1e56721a419a0c3d085254ab24cff5b9ad55e2b0016e0b" 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 = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", "semver", "serde", "serde_json", - "tauri-utils", - "tauri-winres", - "walkdir", + "sha2", + "syn 2.0.106", + "tauri-utils", + "thiserror 2.0.16", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d430110d4ee102a9b673d3c03ff48098c80fe8ca71ba1ff52d8a5919538a1a6" +dependencies = [ + "dunce", + "plist", + "rust-ini", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.16", + "tracing", + "url", + "windows-registry", + "windows-result", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee5a3c416dc59d7d9aa0de5490a82d6e201c67ffe97388979d77b69b08cda40" +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 = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a59139183e0907cec1499dddee4e085f5a801dc659efa0848ee224f461371426" +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-codegen" -version = "1.4.6" +name = "tauri-plugin-notification" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53438d78c4a037ffe5eafa19e447eea599bedfb10844cb08ec53c2471ac3ac3f" +checksum = "d2fbc86b929b5376ab84b25c060f966d146b2fbd59b6af8264027b343c82c219" dependencies = [ - "base64 0.21.7", - "brotli", - "ico", - "json-patch", - "plist", - "png", - "proc-macro2", - "quote", - "semver", + "log", + "notify-rust", + "rand 0.9.2", "serde", "serde_json", - "sha2", - "tauri-utils", - "thiserror 1.0.69", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.16", "time", - "uuid", - "walkdir", + "url", ] [[package]] -name = "tauri-macros" -version = "1.4.7" +name = "tauri-plugin-opener" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233988ac08c1ed3fe794cd65528d48d8f7ed4ab3895ca64cdaa6ad4d00c45c0b" +checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 1.0.109", - "tauri-codegen", - "tauri-utils", + "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-log" -version = "0.0.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#565679ef36c75e871ed41eb21fc5a97f53fa077e" +name = "tauri-plugin-os" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba" dependencies = [ - "byte-unit", - "fern", + "gethostname", "log", + "os_info", "serde", "serde_json", - "serde_repr", + "serialize-to-javascript", + "sys-locale", "tauri", - "time", + "tauri-plugin", + "thiserror 2.0.16", ] [[package]] name = "tauri-plugin-single-instance" -version = "0.0.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#565679ef36c75e871ed41eb21fc5a97f53fa077e" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236043404a4d1502ed7cce11a8ec88ea1e85597eec9887b4701bb10b66b13b6e" dependencies = [ - "log", "serde", "serde_json", "tauri", - "thiserror 1.0.69", - "windows-sys 0.59.0", - "zbus 3.15.2", + "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#565679ef36c75e871ed41eb21fc5a97f53fa077e" +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.3", "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", @@ -6163,24 +6453,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]] @@ -6189,23 +6485,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.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ - "fastrand 2.3.0", + "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -6219,12 +6515,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" @@ -6236,11 +6526,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]] @@ -6251,18 +6541,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]] @@ -6276,23 +6566,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.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" dependencies = [ "deranged", - "itoa 1.0.15", "libc", "num-conv", "num_threads", @@ -6304,15 +6596,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" dependencies = [ "num-conv", "time-core", @@ -6339,9 +6631,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", ] @@ -6354,20 +6646,23 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.10", + "slab", + "socket2", "tokio-macros", - "windows-sys 0.52.0", + "tracing", + "windows-sys 0.59.0", ] [[package]] @@ -6378,7 +6673,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6414,9 +6709,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", @@ -6427,42 +6722,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.0", "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", ] @@ -6473,10 +6771,19 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.9.0", - "serde", - "serde_spanned", - "toml_datetime", + "indexmap 2.11.0", + "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.0", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -6486,45 +6793,55 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.11.0", "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 = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +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 = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tonic" -version = "0.12.3" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" dependencies = [ - "async-stream", "async-trait", "axum", "base64 0.22.1", "bytes", - "h2 0.4.10", - "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", @@ -6532,36 +6849,41 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.3" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" 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.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" 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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.106", + "tempfile", + "tonic-build", ] [[package]] @@ -6572,11 +6894,15 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "indexmap 2.11.0", "pin-project-lite", - "sync_wrapper 1.0.2", + "slab", + "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -6585,14 +6911,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.3", "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", ] @@ -6641,7 +6967,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6677,14 +7003,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", @@ -6696,13 +7022,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", @@ -6721,6 +7068,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" @@ -6733,11 +7086,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" @@ -6779,9 +7179,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", @@ -6789,6 +7189,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" @@ -6815,12 +7227,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", + "serde", "wasm-bindgen", ] @@ -6881,12 +7294,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" @@ -6919,12 +7326,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" @@ -6958,11 +7359,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -6993,7 +7394,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -7028,7 +7429,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7057,36 +7458,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.0.8", + "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.3", + "rustix 1.0.8", "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.3", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7094,11 +7496,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.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7107,21 +7509,23 @@ 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", ] @@ -7135,6 +7539,16 @@ dependencies = [ "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", +] + [[package]] name = "webbrowser" version = "1.0.5" @@ -7142,10 +7556,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", @@ -7153,9 +7567,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", @@ -7171,20 +7585,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", @@ -7192,48 +7604,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", + "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", ] [[package]] @@ -7244,11 +7662,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", ] @@ -7276,11 +7694,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7290,39 +7708,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]] @@ -7338,16 +7735,6 @@ dependencies = [ "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" @@ -7363,7 +7750,7 @@ 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", @@ -7381,16 +7768,6 @@ dependencies = [ "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" -dependencies = [ - "syn 1.0.109", - "windows-tokens", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -7399,7 +7776,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7410,7 +7787,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7419,12 +7796,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" -[[package]] -name = "windows-metadata" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" - [[package]] name = "windows-numerics" version = "0.2.0" @@ -7437,9 +7808,9 @@ dependencies = [ [[package]] name = "windows-registry" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ "windows-link", "windows-result", @@ -7461,7 +7832,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.3", "widestring", "windows-sys 0.52.0", ] @@ -7517,7 +7888,7 @@ 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]] @@ -7568,10 +7939,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", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7591,12 +7963,6 @@ dependencies = [ "windows-link", ] -[[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" @@ -7630,18 +7996,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" @@ -7666,18 +8020,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" @@ -7714,18 +8056,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" @@ -7750,18 +8080,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" @@ -7810,18 +8128,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" @@ -7857,18 +8163,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", @@ -7876,22 +8182,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.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "wl-clipboard-rs" @@ -7904,7 +8207,7 @@ dependencies = [ "os_pipe", "rustix 0.38.44", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.16", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -7920,40 +8223,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", + "windows-version", + "x11-dl", ] [[package]] @@ -7988,20 +8298,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.0.8", "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" @@ -8015,26 +8325,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "xattr" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" -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" @@ -8055,122 +8345,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.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" 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.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" 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]] @@ -8181,8 +8406,8 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.11", - "zvariant 5.5.3", + "winnow 0.7.13", + "zvariant", ] [[package]] @@ -8202,7 +8427,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8222,7 +8447,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -8243,7 +8468,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8259,9 +8484,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", @@ -8276,85 +8501,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.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" 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 e6aa2b48..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"] @@ -11,7 +11,7 @@ edition = "2021" homepage = "https://github.com/DefGuard/client" license-file = "../LICENSE.md" rust-version = "1.80" -version = "1.4.0" +version = "1.5.0" [package] name = "defguard-client" @@ -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,58 +40,74 @@ 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"] } windows-service = "0.7" -[target.'cfg(unix)'.dependencies] -nix = { version = "0.29", features = ["net"] } - [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes. 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..845f9a65 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,76 @@ +{ + "$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", + "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/common/Cargo.toml b/src-tauri/common/Cargo.toml index a47b3ca0..88b5b5a9 100644 --- a/src-tauri/common/Cargo.toml +++ b/src-tauri/common/Cargo.toml @@ -1,11 +1,13 @@ [package] name = "common" -version = "1.0.0" -edition = "2021" -rust-version = "1.80" -license-file = "../../LICENSE.md" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +rust-version.workspace = true +version.workspace = true [dependencies] [target.'cfg(unix)'.dependencies] -nix = { version = "0.29", features = ["net"] } +nix = { version = "0.30", features = ["net"] } 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/20250623083017_openid_mfa.sql b/src-tauri/migrations/20250623083017_openid_mfa.sql new file mode 100644 index 00000000..1bf588b7 --- /dev/null +++ b/src-tauri/migrations/20250623083017_openid_mfa.sql @@ -0,0 +1,2 @@ +ALTER TABLE instance ADD COLUMN use_openid_for_mfa BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE instance ADD COLUMN openid_display_name TEXT; 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 6e1308a8..fa9c14ef 160000 --- a/src-tauri/proto +++ b/src-tauri/proto @@ -1 +1 @@ -Subproject commit 6e1308a8043d81b007a10a7f3e62f11b64e5435d +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 90c11ed2..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(app_handle: &AppHandle) -> Self { + 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(AppConfig::new(app_handle)), - 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 82dc9434..3f0699db 100644 --- a/src-tauri/src/bin/defguard-client.rs +++ b/src-tauri/src/bin/defguard-client.rs @@ -8,40 +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")] { @@ -56,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() @@ -87,216 +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| { + // Register for linux and dev windows builds + #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] { - let state = AppState::new(&app.app_handle()); - app.manage(state); + use tauri_plugin_deep_link::DeepLinkExt; + app.deep_link().register_all()?; } - let app_state: State = app.state(); - // use config default if deriving from env value fails so that env can override config file - let config_log_level = app_state.app_config.lock().unwrap().log_level; + let app_handle = app.app_handle(); + + // Prepare `AppConfig`. + let config = AppConfig::new(app_handle); + // Setup logging. + + // 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(), + )?; - info!("App setup completed, log level: {log_level}"); + 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 - debug!( - "Starting periodic tasks (config, version polling and active connection verification)..." - ); - 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 738b4d96..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,6 +325,7 @@ 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 { @@ -340,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 { @@ -372,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); } @@ -407,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, } } @@ -421,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 { @@ -492,6 +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.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() { @@ -539,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 { @@ -610,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) @@ -639,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:#?}"); @@ -662,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) @@ -677,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) } @@ -697,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 @@ -718,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 {}", @@ -738,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(), @@ -759,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!( @@ -776,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."); @@ -799,18 +804,19 @@ 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 pool = &app_state.db; - let Some(instance) = Instance::find_by_id(pool, instance_id).await? else { + 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."); return Err(Error::NotFound); }; debug!("The instance that is being deleted has been identified as {instance}"); - let instance_locations = Location::find_by_instance_id(pool, instance_id).await?; + 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 { @@ -824,18 +830,30 @@ 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(pool).await?; + instance.delete(&mut *transaction).await?; + + transaction.commit().await?; + reload_tray_menu(&handle).await; - handle.emit_all(INSTANCE_UPDATE, ())?; + handle.emit(EventKey::InstanceUpdate.into(), ())?; info!("Successfully deleted instance {instance}."); Ok(()) } @@ -853,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(()) } @@ -893,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 { @@ -924,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 { @@ -943,9 +944,10 @@ 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 pool = &app_state.db; - let Some(tunnel) = Tunnel::find_by_id(pool, tunnel_id).await? else { + 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."); return Err(Error::NotFound); }; @@ -1011,7 +1013,9 @@ pub async fn delete_tunnel(tunnel_id: Id, handle: AppHandle) -> Result<(), Error ); } } - tunnel.delete(pool).await?; + tunnel.delete(&mut *transaction).await?; + + transaction.commit().await?; info!("Successfully deleted tunnel {tunnel}"); Ok(()) @@ -1033,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 { @@ -1045,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 d2f2fdc4..e2a03b6a 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -3,36 +3,46 @@ pub mod models; use std::{ env, fs::{create_dir_all, File}, + str::FromStr, + sync::LazyLock, }; +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; - -/// Initializes the database -pub fn init_db() -> Result { - let db_url = prepare_db_url()?; - debug!("Connecting to database: {db_url}"); - let pool = DbPool::connect_lazy(&db_url)?; +pub(crate) type DbPool = SqlitePool; - Ok(pool) -} +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:?}"); + SqlitePool::connect_lazy_with(opts) +}); -/// 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.", @@ -62,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 45935459..a9aac5b6 100644 --- a/src-tauri/src/database/models/instance.rs +++ b/src-tauri/src/database/models/instance.rs @@ -17,6 +17,7 @@ pub struct Instance { pub token: Option, pub disable_all_traffic: bool, pub enterprise_enabled: bool, + pub openid_display_name: Option, } impl fmt::Display for Instance { @@ -37,6 +38,7 @@ impl From for Instance { token: None, disable_all_traffic: instance_info.disable_all_traffic, enterprise_enabled: instance_info.enterprise_enabled, + openid_display_name: instance_info.openid_display_name, } } } @@ -48,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 WHERE id = $9;", + disable_all_traffic = $6, enterprise_enabled = $7, token = $8, openid_display_name = $9 WHERE id = $10;", self.name, self.uuid, self.url, @@ -57,6 +59,7 @@ impl Instance { self.disable_all_traffic, self.enterprise_enabled, self.token, + self.openid_display_name, self.id ) .execute(executor) @@ -71,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 FROM instance;" + disable_all_traffic, enterprise_enabled, openid_display_name FROM instance ORDER BY name ASC;" ) .fetch_all(executor) .await?; @@ -85,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 FROM instance WHERE id = $1;", + disable_all_traffic, enterprise_enabled, openid_display_name FROM instance WHERE id = $1;", id ) .fetch_optional(executor) @@ -119,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 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?; @@ -138,6 +141,7 @@ impl PartialEq for Instance { && self.username == other.username && self.disable_all_traffic == other.disable_all_traffic && self.enterprise_enabled == other.enterprise_enabled + && self.openid_display_name == other.openid_display_name } } @@ -173,6 +177,7 @@ impl Instance { token: self.token, disable_all_traffic: self.disable_all_traffic, enterprise_enabled: self.enterprise_enabled, + openid_display_name: self.openid_display_name, }) } } @@ -188,6 +193,7 @@ pub struct InstanceInfo { pub pubkey: String, pub disable_all_traffic: bool, pub enterprise_enabled: bool, + pub openid_display_name: Option, } impl fmt::Display for InstanceInfo { diff --git a/src-tauri/src/database/models/location.rs b/src-tauri/src/database/models/location.rs index 3bd1b5f3..2767597c 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,8 @@ 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 +80,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 +90,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 +110,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 id = $1", location_id ) @@ -107,8 +128,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 +146,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 +180,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 +197,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 +209,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 +226,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 +245,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..5cb32286 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,132 @@ 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 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 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 + }; + + if !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 88050426..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); @@ -233,9 +230,7 @@ impl GlobalLogWatcher { break; } } else { - trace!("Read service log line: {service_line:?}"); if let Some(parsed_line) = self.parse_service_log_line(&service_line) { - trace!("Parsed service log line: {parsed_line:?}"); parsed_lines.push(parsed_line); } service_line.clear(); @@ -260,15 +255,13 @@ impl GlobalLogWatcher { if client_line_read > 0 { match self.parse_client_log_line(&client_line) { Ok(Some(parsed_line)) => { - trace!("Parsed client log line: {parsed_line:?}"); parsed_lines.push(parsed_line); } Ok(None) => { - trace!("The following log line was filtered out: {client_line:?}"); + // Line was filtered out, do nothing } - Err(err) => { - // trace here is intentional, adding error logs would loop the reader infinitely - trace!("Couldn't parse client log line: {client_line:?}: {err}"); + Err(_) => { + // Don't log it, as it will cause an endless loop } } client_line.clear(); @@ -284,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(); } @@ -300,45 +293,31 @@ impl GlobalLogWatcher { /// Deserializes the log line into a known struct. /// Also performs filtering by log level and optional timestamp. fn parse_service_log_line(&self, line: &str) -> Option { - trace!("Parsing service log line: {line}"); let Ok(mut log_line) = serde_json::from_str::(line) else { warn!("Failed to parse service log line: {line}"); return None; }; - trace!("Parsed service log line into: {log_line:?}"); // filter by log level if log_line.level > self.log_level { - trace!( - "Log level {} is above configured verbosity threshold {}. Skipping line...", - log_line.level, - self.log_level - ); return None; } // filter by optional timestamp if let Some(from) = self.from { if log_line.timestamp < from { - trace!( - "Timestamp {} is below configured threshold {from}. Skipping line...", - log_line.timestamp - ); return None; } } log_line.source = Some(LogSource::Service); - trace!("Successfully parsed service log line."); - Some(log_line) } /// Parse a client log line into a known struct using regex. /// If the line doesn't match the regex, it's filtered out. fn parse_client_log_line(&self, line: &str) -> Result, LogWatcherError> { - trace!("Parsing client log line: {line}"); // Example log: // [2024-10-09][09:08:41][DEBUG][defguard_client::commands] Retrieving all locations. let regex = Regex::new(r"\[(.*?)\]\[(.*?)\]\[(.*?)\]\[(.*?)\] (.*)")?; @@ -398,25 +377,15 @@ impl GlobalLogWatcher { }; if log_line.level > self.log_level { - trace!( - "Log level {} is above configured verbosity threshold {}. Skipping line...", - log_line.level, - self.log_level - ); return Ok(None); } if let Some(from) = self.from { if log_line.timestamp < from { - trace!("Timestamp is before configured threshold {from}. Skipping line..."); return Ok(None); } } - trace!( - "Successfully parsed client log line from file {:?}", - self.log_dirs.client_log_dir - ); Ok(Some(log_line)) } } @@ -443,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 f3df75a1..0b4fab97 100644 --- a/src-tauri/src/periodic/mod.rs +++ b/src-tauri/src/periodic/mod.rs @@ -1,15 +1,20 @@ -use connection::verify_active_connections; +use self::{ + connection::verify_active_connections, purge_stats::purge_stats, version::poll_version, +}; use tauri::AppHandle; use tokio::select; -use version::poll_version; use crate::enterprise::periodic::config::poll_config; pub mod connection; +pub mod purge_stats; pub mod version; /// Runs all the client periodic tasks, finishing when any of them returns. pub async fn run_periodic_tasks(app_handle: &AppHandle) { + debug!( + "Starting periodic tasks (config, version polling, stats purging and active connection verification)..." + ); select! { () = poll_version(app_handle.clone()) => { error!("Version polling task has stopped unexpectedly"); @@ -20,5 +25,8 @@ 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() => { + 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 new file mode 100644 index 00000000..81fafd06 --- /dev/null +++ b/src-tauri/src/periodic/purge_stats.rs @@ -0,0 +1,54 @@ +use std::time::Duration; + +use tokio::time::interval; + +use crate::database::{ + models::{location_stats::LocationStats, tunnel::TunnelStats}, + DB_POOL, +}; + +// 12 hours +const PURGE_INTERVAL: Duration = Duration::from_secs(12 * 60 * 60); + +/// Periodically purges location and tunnel stats. +/// +/// 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() { + debug!("Starting the stats purging loop."); + let mut interval = interval(PURGE_INTERVAL); + + loop { + // wait for next iteration + interval.tick().await; + + // begin transaction + 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 + ); + continue; + }; + + debug!("Purging old stats from the database..."); + if let Err(err) = LocationStats::purge(&mut *transaction).await { + error!("Failed to purge location stats: {err}"); + } else { + debug!("Old location stats have been purged successfully."); + } + if let Err(err) = TunnelStats::purge(&mut *transaction).await { + error!("Failed to purge tunnel stats: {err}"); + } else { + debug!("Old tunnel stats have been purged successfully."); + } + + // commit transaction + if let Err(err) = transaction.commit().await { + 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 cf363e94..c571b5af 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -6,13 +6,17 @@ pub mod utils; #[cfg(windows)] pub mod windows; +#[cfg(windows)] +use std::net::{Ipv4Addr, SocketAddr}; use std::{ collections::HashMap, - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::IpAddr, pin::Pin, 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; @@ -25,12 +29,18 @@ 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, }; use thiserror::Error; +#[cfg(unix)] +use tokio::net::UnixListener; use tokio::{sync::mpsc, time::interval}; +#[cfg(unix)] +use tokio_stream::wrappers::UnixListenerStream; use tonic::{ codegen::tokio_stream::{wrappers::ReceiverStream, Stream}, transport::Server, @@ -40,10 +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)] @@ -96,6 +118,8 @@ pub fn setup_wgapi(ifname: &str) -> Result, Status> { #[tonic::async_trait] impl DesktopDaemonService for DaemonService { + type ReadInterfaceDataStream = InterfaceDataStream; + async fn create_interface( &self, request: tonic::Request, @@ -225,8 +249,6 @@ impl DesktopDaemonService for DaemonService { Ok(Response::new(())) } - type ReadInterfaceDataStream = InterfaceDataStream; - async fn read_interface_data( &self, request: tonic::Request, @@ -323,16 +345,55 @@ impl DesktopDaemonService for DaemonService { } } +#[cfg(unix)] +pub async fn run_server(config: Config) -> anyhow::Result<()> { + debug!("Starting Defguard interface management daemon"); + + let daemon_service = DaemonService::new(&config); + + // Remove existing socket if it exists + if Path::new(DAEMON_SOCKET_PATH).exists() { + fs::remove_file(DAEMON_SOCKET_PATH)?; + } + + 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 '{}' not found", DAEMON_SOCKET_GROUP)) + })?; + + // change ownership - keep current user, change group + chown(DAEMON_SOCKET_PATH, None, Some(group.gid))?; + + // Set socket permissions to allow client access + // 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); + + info!("Defguard daemon version {VERSION} started, listening on socket {DAEMON_SOCKET_PATH}",); + debug!("Defguard daemon configuration: {config:?}"); + + Server::builder() + .trace_fn(|_| tracing::info_span!("defguard_service")) + .add_service(DesktopDaemonServiceServer::new(daemon_service)) + .serve_with_incoming(uds_stream) + .await?; + + Ok(()) +} + +#[cfg(windows)] pub async fn run_server(config: Config) -> anyhow::Result<()> { debug!("Starting Defguard interface management daemon"); let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DAEMON_HTTP_PORT); let daemon_service = DaemonService::new(&config); - info!( - "Defguard daemon version {} started, listening on {addr}", - VERSION - ); + info!("Defguard daemon version {VERSION} started, listening on {addr}",); debug!("Defguard daemon configuration: {config:?}"); Server::builder() @@ -365,7 +426,7 @@ impl From for InterfaceConfiguration { fn from(config: proto::InterfaceConfig) -> Self { let addresses = config .address - .split(",") + .split(',') .filter_map(|ip| IpAddrMask::from_str(ip.trim()).ok()) .collect(); Self { diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index ed3d6034..3d35d868 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,6 +1,14 @@ -use std::io::stdout; +use std::{io::stdout, sync::LazyLock}; +#[cfg(unix)] +use hyper_util::rt::TokioIo; +#[cfg(unix)] +use tokio::net::UnixStream; use tonic::transport::channel::{Channel, Endpoint}; +#[cfg(unix)] +use tonic::transport::Uri; +#[cfg(unix)] +use tower::service_fn; use tracing::{debug, Level}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{ @@ -8,17 +16,35 @@ use tracing_subscriber::{ Layer, }; -use crate::service::{ - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DaemonError, DAEMON_BASE_URL, -}; +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; -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) -} +#[cfg(unix)] +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(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 9e2c19f3..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,28 +16,34 @@ use windows_service::{ use crate::{ appstate::AppState, - commands::{LocationInterfaceDetails, Payload}, + commands::LocationInterfaceDetails, database::{ models::{ connection::{ActiveConnection, Connection}, location::Location, - location_stats::{peer_to_location_stats, LocationStats}, - tunnel::{peer_to_tunnel_stats, Tunnel, TunnelConnection, TunnelStats}, + location_stats::peer_to_location_stats, + tunnel::{peer_to_tunnel_stats, Tunnel, TunnelConnection}, 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"; @@ -137,7 +143,7 @@ pub(crate) async fn setup_interface( debug!("Found free port: {port} for interface {interface_name}."); let addresses = location .address - .split(",") + .split(',') .map(str::trim) .map(IpAddrMask::from_str) .collect::>() @@ -210,29 +216,37 @@ pub(crate) async fn stats_handler( match stream.message().await { Ok(Some(interface_data)) => { debug!("Received new network usage statistics for interface {interface_name}."); - if let Err(err) = LocationStats::purge(&pool).await { - error!("Failed to purge location stats: {err}"); - } - if let Err(err) = TunnelStats::purge(&pool).await { - error!("Failed to purge tunnel stats: {err}"); - } - trace!("Received interface data: {interface_data:?}"); + + // begin transaction + let mut transaction = match pool.begin().await { + Ok(transactions) => transactions, + Err(err) => { + error!( + "Failed to begin database transaction for saving location/tunnel stats: {err}", + ); + continue; + } + }; + let peers: Vec = interface_data.peers.into_iter().map(Into::into).collect(); for peer in peers { if connection_type.eq(&ConnectionType::Location) { - let location_stats = - match peer_to_location_stats(&peer, interface_data.listen_port, &pool) - .await - { - Ok(stats) => stats, - Err(err) => { - error!("Failed to convert peer data to location stats: {err}"); - continue; - } - }; + let location_stats = match peer_to_location_stats( + &peer, + interface_data.listen_port, + &mut *transaction, + ) + .await + { + Ok(stats) => stats, + Err(err) => { + error!("Failed to convert peer data to location stats: {err}"); + continue; + } + }; let location_name = location_stats - .get_name(&pool) + .get_name(&mut *transaction) .await .unwrap_or("UNKNOWN".to_string()); @@ -241,7 +255,7 @@ pub(crate) async fn stats_handler( (interface {interface_name})." ); trace!("Stats: {location_stats:?}"); - match location_stats.save(&pool).await { + match location_stats.save(&mut *transaction).await { Ok(_) => { debug!("Saved network usage stats for location {location_name}"); } @@ -253,25 +267,28 @@ pub(crate) async fn stats_handler( } } } else { - let tunnel_stats = - match peer_to_tunnel_stats(&peer, interface_data.listen_port, &pool) - .await - { - Ok(stats) => stats, - Err(err) => { - error!("Failed to convert peer data to tunnel stats: {err}"); - continue; - } - }; + let tunnel_stats = match peer_to_tunnel_stats( + &peer, + interface_data.listen_port, + &mut *transaction, + ) + .await + { + Ok(stats) => stats, + Err(err) => { + error!("Failed to convert peer data to tunnel stats: {err}"); + continue; + } + }; let tunnel_name = tunnel_stats - .get_name(&pool) + .get_name(&mut *transaction) .await .unwrap_or("UNKNOWN".to_string()); debug!( "Saving network usage stats related to tunnel {tunnel_name} \ (interface {interface_name}): {tunnel_stats:?}" ); - match tunnel_stats.save(&pool).await { + match tunnel_stats.save(&mut *transaction).await { Ok(_) => { debug!("Saved stats for tunnel {tunnel_name}"); } @@ -281,6 +298,14 @@ 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}", + ); + } } Ok(None) => { debug!("gRPC stream to the defguard-service managing connections has been closed"); @@ -404,7 +429,7 @@ pub async fn setup_interface_tunnel( let addresses = tunnel .address - .split(",") + .split(',') .map(str::trim) .map(IpAddrMask::from_str) .collect::>() @@ -604,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 @@ -613,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 @@ -644,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 @@ -700,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" @@ -746,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 @@ -759,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" @@ -807,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 @@ -827,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)), @@ -863,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)) @@ -910,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(()); } @@ -924,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}..."); @@ -953,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(), ) }, @@ -980,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 168376be..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.4.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..01e654e2 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'; @@ -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 ecf3ae9b..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', }, @@ -727,6 +743,24 @@ If you want to disengage your VPN connection, simply press "deactivate". useEmailCode: 'Use your email code', saveAuthenticationMethodForFutureLogins: 'Use this method for future logins', buttonSubmit: 'Verify', + openidLogin: { + description: + 'In order to connect to the VPN please login with {provider}. To do so, please click "Authenticate with {provider}" button below.', + browserWarning: + '**This will open a new window in your Web Browser** and automatically redirect you to the {provider} login page. After authenticating with {provider} please get back here.', + buttonText: 'Authenticate with {provider}', + }, + openidPending: { + description: 'Waiting for authentication in your browser...', + tryAgain: 'Try again', + errorDescription: + 'There was an error during authentication. Use the try again button below to retry the authentication process.', + }, + openidUnavailable: { + description: + 'The OpenID authentication is currently unavailable. This may be due to a configuration issue or the Defguard instance is down. Please contact your administrator or try again later.', + tryAgain: 'Try again', + }, errors: { mfaNotConfigured: 'Selected method has not been configured.', mfaStartGeneric: @@ -736,6 +770,10 @@ If you want to disengage your VPN connection, simply press "deactivate". invalidCode: 'Error, this code is invalid, try again or contact your administrator.', tokenExpired: 'Token has expired. Please try to connect again.', + authenticationTimeout: + 'Authentication took too long and timed out. Please try connecting again.', + sessionInvalidated: + 'Error: Your login session might have been invalidated or expired. Please try again.', }, }, }, diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index ffa80991..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 @@ -1598,6 +1642,47 @@ type RootTranslation = { * V​e​r​i​f​y */ buttonSubmit: string + openidLogin: { + /** + * I​n​ ​o​r​d​e​r​ ​t​o​ ​c​o​n​n​e​c​t​ ​t​o​ ​t​h​e​ ​V​P​N​ ​p​l​e​a​s​e​ ​l​o​g​i​n​ ​w​i​t​h​ ​{​p​r​o​v​i​d​e​r​}​.​ ​T​o​ ​d​o​ ​s​o​,​ ​p​l​e​a​s​e​ ​c​l​i​c​k​ ​"​A​u​t​h​e​n​t​i​c​a​t​e​ ​w​i​t​h​ ​{​p​r​o​v​i​d​e​r​}​"​ ​b​u​t​t​o​n​ ​b​e​l​o​w​. + * @param {unknown} provider + */ + description: RequiredParams<'provider' | 'provider'> + /** + * *​*​T​h​i​s​ ​w​i​l​l​ ​o​p​e​n​ ​a​ ​n​e​w​ ​w​i​n​d​o​w​ ​i​n​ ​y​o​u​r​ ​W​e​b​ ​B​r​o​w​s​e​r​*​*​ ​a​n​d​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​r​e​d​i​r​e​c​t​ ​y​o​u​ ​t​o​ ​t​h​e​ ​{​p​r​o​v​i​d​e​r​}​ ​l​o​g​i​n​ ​p​a​g​e​.​ ​A​f​t​e​r​ ​a​u​t​h​e​n​t​i​c​a​t​i​n​g​ ​w​i​t​h​ ​{​p​r​o​v​i​d​e​r​}​ ​p​l​e​a​s​e​ ​g​e​t​ ​b​a​c​k​ ​h​e​r​e​. + * @param {unknown} provider + */ + browserWarning: RequiredParams<'provider' | 'provider'> + /** + * A​u​t​h​e​n​t​i​c​a​t​e​ ​w​i​t​h​ ​{​p​r​o​v​i​d​e​r​} + * @param {unknown} provider + */ + buttonText: RequiredParams<'provider'> + } + openidPending: { + /** + * W​a​i​t​i​n​g​ ​f​o​r​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​i​n​ ​y​o​u​r​ ​b​r​o​w​s​e​r​.​.​. + */ + description: string + /** + * T​r​y​ ​a​g​a​i​n + */ + tryAgain: string + /** + * T​h​e​r​e​ ​w​a​s​ ​a​n​ ​e​r​r​o​r​ ​d​u​r​i​n​g​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​.​ ​U​s​e​ ​t​h​e​ ​t​r​y​ ​a​g​a​i​n​ ​b​u​t​t​o​n​ ​b​e​l​o​w​ ​t​o​ ​r​e​t​r​y​ ​t​h​e​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​p​r​o​c​e​s​s​. + */ + errorDescription: string + } + openidUnavailable: { + /** + * T​h​e​ ​O​p​e​n​I​D​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​i​s​ ​c​u​r​r​e​n​t​l​y​ ​u​n​a​v​a​i​l​a​b​l​e​.​ ​T​h​i​s​ ​m​a​y​ ​b​e​ ​d​u​e​ ​t​o​ ​a​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​i​s​s​u​e​ ​o​r​ ​t​h​e​ ​D​e​f​g​u​a​r​d​ ​i​n​s​t​a​n​c​e​ ​i​s​ ​d​o​w​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​ ​o​r​ ​t​r​y​ ​a​g​a​i​n​ ​l​a​t​e​r​. + */ + description: string + /** + * T​r​y​ ​a​g​a​i​n + */ + tryAgain: string + } errors: { /** * S​e​l​e​c​t​e​d​ ​m​e​t​h​o​d​ ​h​a​s​ ​n​o​t​ ​b​e​e​n​ ​c​o​n​f​i​g​u​r​e​d​. @@ -1623,6 +1708,14 @@ type RootTranslation = { * T​o​k​e​n​ ​h​a​s​ ​e​x​p​i​r​e​d​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​t​o​ ​c​o​n​n​e​c​t​ ​a​g​a​i​n​. */ tokenExpired: string + /** + * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​t​o​o​k​ ​t​o​o​ ​l​o​n​g​ ​a​n​d​ ​t​i​m​e​d​ ​o​u​t​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​c​o​n​n​e​c​t​i​n​g​ ​a​g​a​i​n​. + */ + authenticationTimeout: string + /** + * E​r​r​o​r​:​ ​Y​o​u​r​ ​l​o​g​i​n​ ​s​e​s​s​i​o​n​ ​m​i​g​h​t​ ​h​a​v​e​ ​b​e​e​n​ ​i​n​v​a​l​i​d​a​t​e​d​ ​o​r​ ​e​x​p​i​r​e​d​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​. + */ + sessionInvalidated: string } } } @@ -1778,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: { @@ -2713,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 @@ -3193,6 +3324,44 @@ export type TranslationFunctions = { * Verify */ buttonSubmit: () => LocalizedString + openidLogin: { + /** + * In order to connect to the VPN please login with {provider}. To do so, please click "Authenticate with {provider}" button below. + */ + description: (arg: { provider: unknown }) => LocalizedString + /** + * **This will open a new window in your Web Browser** and automatically redirect you to the {provider} login page. After authenticating with {provider} please get back here. + */ + browserWarning: (arg: { provider: unknown }) => LocalizedString + /** + * Authenticate with {provider} + */ + buttonText: (arg: { provider: unknown }) => LocalizedString + } + openidPending: { + /** + * Waiting for authentication in your browser... + */ + description: () => LocalizedString + /** + * Try again + */ + tryAgain: () => LocalizedString + /** + * There was an error during authentication. Use the try again button below to retry the authentication process. + */ + errorDescription: () => LocalizedString + } + openidUnavailable: { + /** + * The OpenID authentication is currently unavailable. This may be due to a configuration issue or the Defguard instance is down. Please contact your administrator or try again later. + */ + description: () => LocalizedString + /** + * Try again + */ + tryAgain: () => LocalizedString + } errors: { /** * Selected method has not been configured. @@ -3218,6 +3387,14 @@ export type TranslationFunctions = { * Token has expired. Please try to connect again. */ tokenExpired: () => LocalizedString + /** + * Authentication took too long and timed out. Please try connecting again. + */ + authenticationTimeout: () => LocalizedString + /** + * Error: Your login session might have been invalidated or expired. Please try again. + */ + sessionInvalidated: () => LocalizedString } } } diff --git a/src/pages/client/ClientPage.tsx b/src/pages/client/ClientPage.tsx index ff8b49a1..2bbdc31c 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, + type DeadConReconnectedPayload, + TauriEventKey, +} 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) => { @@ -130,6 +177,13 @@ export const ClientPage = () => { }, ); + const mfaTrigger = listen( + TauriEventKey.MFA_TRIGGER, + (data) => { + openMFAModal(data.payload); + }, + ); + return () => { deadConnectionDropped.then((cleanup) => cleanup()); deadConnectionReconnected.then((cleanup) => cleanup()); @@ -137,7 +191,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 +209,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 +233,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..367f9824 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, WireguardInstanceType } from '../types'; export type GetLocationsRequest = { instanceId: number; diff --git a/src/pages/client/components/ClientSideBar/ClientSideBar.tsx b/src/pages/client/components/ClientSideBar/ClientSideBar.tsx index 9e7161c5..d856af46 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 { useAddInstanceStore } from '../../pages/ClientAddInstancePage/hooks/useAddInstanceStore'; import { WireguardInstanceType } 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(); @@ -133,13 +131,19 @@ const FooterApplicationInfo = () => { ); @@ -168,11 +172,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..0195f2ae 100644 --- a/src/pages/client/components/ClientSideBar/components/ClientBarItem/ClientBarItem.tsx +++ b/src/pages/client/components/ClientSideBar/components/ClientBarItem/ClientBarItem.tsx @@ -7,7 +7,7 @@ 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 { WireguardInstanceType } from '../../../../types'; type Props = { itemType: WireguardInstanceType; @@ -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..daeac28d 100644 --- a/src/pages/client/hooks/useClientStore.tsx +++ b/src/pages/client/hooks/useClientStore.tsx @@ -3,11 +3,11 @@ 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, + type CommonWireguardFields, + type DefguardInstance, + type SelectedInstance, WireguardInstanceType, } from '../types'; 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..ecf39356 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, WireguardInstanceType } 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, }; - 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..c2b72abe 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, WireguardInstanceType } 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'); @@ -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/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/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..a60943e4 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, WireguardInstanceType } from '../../types'; import { LocationsList } from './components/LocationsList/LocationsList'; import { StatsFilterSelect } from './components/StatsFilterSelect/StatsFilterSelect'; import { StatsLayoutSelect } from './components/StatsLayoutSelect/StatsLayoutSelect'; 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..676d3210 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, + type CommonWireguardFields, + type DefguardInstance, WireguardInstanceType, } 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; @@ -52,6 +51,8 @@ export const LocationsList = ({ } }, [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..571c7632 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,14 +7,14 @@ 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, + type CommonWireguardFields, + type DefguardInstance, WireguardInstanceType, } from '../../../../../../types'; import { LocationConnectionHistory } from './components/LocationConnectionHistory/LocationConnectionHistory'; 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..ea7400f2 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,7 +6,10 @@ 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, + WireguardInstanceType, +} from '../../../../../../../../types'; import { LocationCardNeverConnected } from '../../../LocationCardNeverConnected/LocationCardNeverConnected'; import { LocationHistoryTable } from './LocationHistoryTable/LocationHistoryTable'; 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..74a4ea31 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,7 +10,10 @@ 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, + WireguardInstanceType, +} from '../../../../../../../../types'; import { LocationLogs } from '../LocationLogs/LocationLogs'; type Props = { @@ -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..b0b35203 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,19 +1,21 @@ 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, + WireguardInstanceType, +} from '../../../../../../../../types'; import { LocationLogsSelect } from './LocationLogsSelect'; type 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(() => { @@ -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..8026f149 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,7 +12,7 @@ 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, diff --git a/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/Icons.tsx b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/Icons.tsx new file mode 100644 index 00000000..eccb4faa --- /dev/null +++ b/src/pages/client/pages/ClientInstancePage/components/LocationsList/modals/MFAModal/Icons.tsx @@ -0,0 +1,212 @@ +export const GoToBrowserIcon = () => ( + + + + + + + + + + + + + + + + +); + +export const BrowserPendingIcon = () => ( + + + + + + + + + + + + + + + +); + +export const BrowserErrorIcon = () => ( + + + + + + + + + + + + + + + + +); 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 5193af62..2307f534 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,11 +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 { useCallback, useMemo, useState } from 'react'; +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 { error } from 'tauri-plugin-log-api'; +import { type SubmitHandler, useForm } from 'react-hook-form'; +import ReactMarkdown from 'react-markdown'; import { z } from 'zod'; import { shallow } from 'zustand/shallow'; @@ -16,11 +18,17 @@ import { ButtonSize, ButtonStyleVariant, } from '../../../../../../../../shared/defguard-ui/components/Layout/Button/types'; +import { LoaderSpinner } from '../../../../../../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; import { MessageBox } from '../../../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; 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 { type DefguardInstance, LocationMfaType } from '../../../../../../types'; +import { MfaMobileApprove } from './components/MfaMobileApprove/MfaMobileApprove'; +import { BrowserErrorIcon, BrowserPendingIcon, GoToBrowserIcon } from './Icons'; import { useMFAModal } from './useMFAModal'; const { connect } = clientApi; @@ -42,93 +50,141 @@ const defaultValues: FormFields = { type MFAStartResponse = { token: string; + challenge?: string; }; +type Screen = + | 'start' + | 'authenticator_app' + | 'email' + | 'openid_login' + | 'openid_pending' + | 'openid_unavailable' + | 'mobile_approve'; + export const MFAModal = () => { const { LL } = useI18nContext(); const toaster = useToaster(); - const [authMethod, setAuthMethod] = useState<0 | 1>(0); - const [screen, setScreen] = useState<'start' | 'authenticator_app' | 'email'>('start'); - const [mfaToken, setMFAToken] = useState(''); + const [authMethod, setAuthMethod] = useState(0); + const [screen, setScreen] = useState('start'); 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 instances = useClientStore((state) => state.instances); + const selectedInstance = useMemo((): DefguardInstance | undefined => { + const instanceId = location?.instance_id; + if (!isUndefined(instanceId)) { + return instances.find((i) => i.id === instanceId); + } + }, [location, instances]); const resetState = () => { reset(); setScreen('start'); - setMFAToken(''); + setStartResponse(undefined); }; const resetAuthState = () => { setScreen('start'); - setMFAToken(''); + setStartResponse(undefined); }; - const startMFA = async (selectedMethod: number) => { - if (!location) return toaster.error(localLL.errors.locationNotSpecified()); - - const clientInstances = await clientApi.getInstances(); - const instance = clientInstances.find((i) => i.id === location.instance_id); - - if (!instance) { - return toaster.error(localLL.errors.instanceNotFound()); - } - - setProxyUrl(instance.proxy_url + CLIENT_MFA_ENDPOINT); - const mfaStartUrl = instance.proxy_url + CLIENT_MFA_ENDPOINT + '/start'; - - // selectedMethod: 0 = authenticator app, 1 = email - const data = { - method: selectedMethod, - pubkey: instance.pubkey, - location_id: location.network_id, - }; + // selectedMethod: 0 = authenticator app, 1 = email, 2 = OpenID, 3 = MobileApprove + const startMFA = useCallback( + async (method: number) => { + if (!location) return toaster.error(localLL.errors.locationNotSpecified()); - const response = await fetch(mfaStartUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: Body.json(data), - }); - - if (response.ok) { - const { token } = response.data; + if (!selectedInstance) { + return toaster.error(localLL.errors.instanceNotFound()); + } - setScreen(selectedMethod === 0 ? 'authenticator_app' : 'email'); - setMFAToken(token); + setProxyUrl(selectedInstance.proxy_url); + const mfaStartUrl = `${selectedInstance.proxy_url + CLIENT_MFA_ENDPOINT}/start`; + + const data = { + method, + pubkey: selectedInstance.pubkey, + location_id: location.network_id, + }; + + const response = await fetch(mfaStartUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); - return response.data; - } else { - const error = (response.data as unknown as MFAError).error; - if (error === 'selected MFA method not available') { - toaster.error(localLL.errors.mfaNotConfigured()); + 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 { - toaster.error(localLL.errors.mfaStartGeneric()); + 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; + } + + if (errorData === 'selected MFA method not available') { + toaster.error(localLL.errors.mfaNotConfigured()); + } else { + toaster.error(localLL.errors.mfaStartGeneric()); + } + + return; } + }, + [ + localLL.errors.instanceNotFound, + localLL.errors.locationNotSpecified, + localLL.errors.mfaNotConfigured, + localLL.errors.mfaStartGeneric, + location, + selectedInstance, + toaster.error, + ], + ); - return; - } - }; + const useOpenIDMFA = useMemo(() => { + 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 handleMfaStart = (method: number) => { + setAuthMethod(method); + mutate(method); + }; return ( { onClose={close} afterClose={resetState} > - {screen === 'start' ? ( + {useOpenIDMFA && screen === 'start' && ( + { + handleMfaStart(2); + }} + /> + )} + {useOpenIDMFA && screen === 'openid_unavailable' && ( + + )} + {screen === 'start' && !useOpenIDMFA && ( - ) : ( - + )} + {screen === 'openid_pending' && isPresent(startResponse) && ( + )} + {(screen === 'authenticator_app' || screen === 'email') && + isPresent(startResponse) && ( + + )} + {screen === 'mobile_approve' && + isPresent(startResponse) && + isPresent(selectedInstance) && ( + + )} ); }; @@ -164,16 +261,53 @@ export const MFAModal = () => { type MFAStartProps = { isPending: boolean; authMethod: number; - showAuthenticatorAppCodeForm: () => void; - showEmailCodeForm: () => void; + startMfa: (method: number) => void; }; -const MFAStart = ({ +const OpenIDMFAUnavailable = ({ resetState }: { resetState: () => void }) => { + const { LL } = useI18nContext(); + const localLL = LL.modals.mfa.authentication; + + return ( +
+
+ +
+
+

{localLL.openidUnavailable.description()}

+
+
+
+
+ ); +}; + +const OpenIDMFAStart = ({ isPending, - authMethod, - showAuthenticatorAppCodeForm, - showEmailCodeForm, -}: MFAStartProps) => { + showOpenIDScreen, +}: { + isPending: boolean; + showOpenIDScreen: () => void; +}) => { + useEffect(() => { + if (!isPending) { + showOpenIDScreen(); + } + }, [isPending, showOpenIDScreen]); + + return ( +
+ +
+ ); +}; + +const MFAStart = ({ isPending, authMethod, startMfa }: MFAStartProps) => { const { LL } = useI18nContext(); const localLL = LL.modals.mfa.authentication; @@ -193,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); + }} />