diff --git a/.env.example b/.env.example index cbb81425b..575dea759 100644 --- a/.env.example +++ b/.env.example @@ -6,13 +6,18 @@ DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch JWT_SECRET="secret1233" TOKEN_SALT_COMPLEXITY=10 MAGIC_LINK_TOKEN_VALIDITY= 3 -REFRESH_TOKEN_VALIDITY="604800000" # Default validity is 7 days (604800000 ms) in ms -ACCESS_TOKEN_VALIDITY="86400000" # Default validity is 1 day (86400000 ms) in ms +# Default validity is 7 days (604800000 ms) in ms +REFRESH_TOKEN_VALIDITY="604800000" +# Default validity is 1 day (86400000 ms) in ms +ACCESS_TOKEN_VALIDITY="86400000" SESSION_SECRET='add some secret here' # Reccomended to be true, set to false if you are using http # Note: Some auth providers may not support http requests ALLOW_SECURE_COOKIES=true +# Sensitive Data Encryption Key while storing in Database (32 character) +DATA_ENCRYPTION_KEY="data encryption key with 32 char" + # Hoppscotch App Domain Config REDIRECT_URL="http://localhost:3000" WHITELISTED_ORIGINS="http://localhost:3170,http://localhost:3000,http://localhost:3100" diff --git a/.github/workflows/build-hoppscotch-agent.yml b/.github/workflows/build-hoppscotch-agent.yml new file mode 100644 index 000000000..f041f1218 --- /dev/null +++ b/.github/workflows/build-hoppscotch-agent.yml @@ -0,0 +1,249 @@ +on: + workflow_dispatch: + inputs: + version: + description: Tag of the version to build + required: true + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + strategy: + fail-fast: false + matrix: + platform: [macos-latest, ubuntu-22.04, windows-latest] + + runs-on: ${{ matrix.platform }} + defaults: + run: + shell: bash + + steps: + - name: Checkout hoppscotch/hoppscotch + uses: actions/checkout@v3 + with: + repository: hoppscotch/hoppscotch + ref: main + token: ${{ secrets.CHECKOUT_GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 9 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Install Rust targets (Mac) + if: matrix.platform == 'macos-latest' + run: | + rustup target add aarch64-apple-darwin + rustup target add x86_64-apple-darwin + + - name: Install additional tools (Linux) + if: matrix.platform == 'ubuntu-22.04' + run: | + # Install Tauri CLI (binary) + curl -LO "https://github.com/tauri-apps/tauri/releases/download/tauri-cli-v2.0.1/cargo-tauri-x86_64-unknown-linux-gnu.tgz" + tar -xzf cargo-tauri-x86_64-unknown-linux-gnu.tgz + chmod +x cargo-tauri + sudo mv cargo-tauri /usr/local/bin/tauri + + # Install Trunk (binary) + curl -LO "https://github.com/thedodd/trunk/releases/download/v0.17.5/trunk-x86_64-unknown-linux-gnu.tar.gz" + tar -xzf trunk-x86_64-unknown-linux-gnu.tar.gz + chmod +x trunk + sudo mv trunk /usr/local/bin/ + + - name: Install additional tools (Mac) + if: matrix.platform == 'macos-latest' + run: | + # Install Tauri CLI (binary) + mkdir __dist/ + cd __dist/ + curl -LO "https://github.com/tauri-apps/tauri/releases/download/tauri-cli-v2.0.1/cargo-tauri-aarch64-apple-darwin.zip" + unzip cargo-tauri-aarch64-apple-darwin.zip + chmod +x cargo-tauri + sudo mv cargo-tauri /usr/local/bin/tauri + + - name: Install system dependencies (Ubuntu only) + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev \ + build-essential \ + curl \ + wget \ + file \ + libxdo-dev \ + libssl-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev + + - name: Setting up Windows Environment and injecting before bundle command (Windows only) + if: matrix.platform == 'windows-latest' + shell: bash + env: + WINDOWS_SIGN_COMMAND: trusted-signing-cli -e ${{ secrets.AZURE_ENDPOINT }} -a ${{ secrets.AZURE_CODE_SIGNING_NAME }} -c ${{ secrets.AZURE_CERT_PROFILE_NAME }} %1 + run: | + cd packages/hoppscotch-agent + # Inject signing command into main conf. + cat './src-tauri/tauri.conf.json' | jq '.bundle .windows += { "signCommand": env.WINDOWS_SIGN_COMMAND}' > './src-tauri/temp.json' && mv './src-tauri/temp.json' './src-tauri/tauri.conf.json' + # Inject signing command into portable conf. + cat './src-tauri/tauri.portable.conf.json' | jq '.bundle .windows += { "signCommand": env.WINDOWS_SIGN_COMMAND}' > './src-tauri/temp_portable.json' && mv './src-tauri/temp_portable.json' './src-tauri/tauri.portable.conf.json' + cargo install trusted-signing-cli@0.3.0 + + - name: Set platform-specific variables + run: | + if [ "${{ matrix.platform }}" = "ubuntu-22.04" ]; then + echo "target_arch=$(rustc -Vv | grep host | awk '{print $2}')" >> $GITHUB_ENV + echo "target_ext=" >> $GITHUB_ENV + echo "target_os_name=linux" >> $GITHUB_ENV + elif [ "${{ matrix.platform }}" = "windows-latest" ]; then + echo "target_arch=x86_64-pc-windows-msvc" >> $GITHUB_ENV + echo "target_ext=.exe" >> $GITHUB_ENV + echo "target_os_name=win" >> $GITHUB_ENV + elif [ "${{ matrix.platform }}" = "macos-latest" ]; then + echo "target_os_name=mac" >> $GITHUB_ENV + fi + + - name: Setup macOS code signing + if: matrix.platform == 'macos-latest' + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12 + security create-keychain -p $KEYCHAIN_PASSWORD build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain + security import certificate.p12 -k build.keychain -P $APPLE_CERTIFICATE_PASSWORD -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain + + - name: Cache Rust dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Install dependencies + shell: bash + run: | + cd packages/hoppscotch-agent + pnpm install --filter hoppscotch-agent + + - name: Build Tauri app (Linux) + if: matrix.platform == 'ubuntu-22.04' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.AGENT_TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.AGENT_TAURI_SIGNING_PASSWORD }} + run: | + cd packages/hoppscotch-agent + pnpm tauri build --verbose -b deb -b appimage -b updater + + - name: Build Tauri app (Mac) + if: matrix.platform == 'macos-latest' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.AGENT_TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.AGENT_TAURI_SIGNING_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + run: | + cd packages/hoppscotch-agent + pnpm tauri build --verbose --target x86_64-apple-darwin + pnpm tauri build --verbose --target aarch64-apple-darwin + + - name: Build Tauri app (Windows) + if: matrix.platform == 'windows-latest' + shell: powershell + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.AGENT_TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.AGENT_TAURI_SIGNING_PASSWORD }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + run: | + cd packages/hoppscotch-agent + # Build the portable version first and move it. + # This way the next build will regenerate `hoppscotch-agent.exe`. + pnpm tauri build --verbose --config src-tauri/tauri.portable.conf.json -- --no-default-features --features portable + Rename-Item -Path "src-tauri/target/release/hoppscotch-agent.exe" -NewName "hoppscotch-agent-portable.exe" + + # Build the installer version. + pnpm tauri build --verbose -b msi -b updater + + - name: Zip portable executable (Windows) + if: matrix.platform == 'windows-latest' + shell: powershell + run: | + Compress-Archive -Path "packages/hoppscotch-agent/src-tauri/target/release/hoppscotch-agent-portable.exe" -DestinationPath "packages/hoppscotch-agent/src-tauri/target/release/Hoppscotch_Agent_win_x64_portable.zip" + + - name: Prepare artifacts + shell: bash + run: | + mkdir artifacts + mkdir artifacts/sigs + if [ "${{ matrix.platform }}" = "ubuntu-22.04" ]; then + mv packages/hoppscotch-agent/src-tauri/target/release/bundle/appimage/*.AppImage artifacts/Hoppscotch_Agent_linux_x64.AppImage + mv packages/hoppscotch-agent/src-tauri/target/release/bundle/appimage/*.AppImage.sig artifacts/sigs/Hoppscotch_Agent_linux_x64.AppImage.sig + mv packages/hoppscotch-agent/src-tauri/target/release/bundle/deb/*.deb artifacts/Hoppscotch_Agent_linux_x64.deb + elif [ "${{ matrix.platform }}" = "macos-latest" ]; then + mv packages/hoppscotch-agent/src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/*_x64.dmg artifacts/Hoppscotch_Agent_mac_x64.dmg + mv packages/hoppscotch-agent/src-tauri/target/x86_64-apple-darwin/release/bundle/macos/*.app.tar.gz artifacts/Hoppscotch_Agent_mac_update_x64.tar.gz + mv packages/hoppscotch-agent/src-tauri/target/x86_64-apple-darwin/release/bundle/macos/*.app.tar.gz.sig artifacts/sigs/Hoppscotch_Agent_mac_update_x64.tar.gz.sig + mv packages/hoppscotch-agent/src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/*_aarch64.dmg artifacts/Hoppscotch_Agent_mac_aarch64.dmg + mv packages/hoppscotch-agent/src-tauri/target/aarch64-apple-darwin/release/bundle/macos/*.app.tar.gz artifacts/Hoppscotch_Agent_mac_update_aarch64.tar.gz + mv packages/hoppscotch-agent/src-tauri/target/aarch64-apple-darwin/release/bundle/macos/*.app.tar.gz.sig artifacts/sigs/Hoppscotch_Agent_mac_update_aarch64.tar.gz.sig + elif [ "${{ matrix.platform }}" = "windows-latest" ]; then + mv packages/hoppscotch-agent/src-tauri/target/release/bundle/msi/*_x64_en-US.msi artifacts/Hoppscotch_Agent_win_x64.msi + mv packages/hoppscotch-agent/src-tauri/target/release/bundle/msi/*_x64_en-US.msi.sig artifacts/sigs/Hoppscotch_Agent_win_x64.msi.sig + mv packages/hoppscotch-agent/src-tauri/target/release/Hoppscotch_Agent_win_x64_portable.zip artifacts/Hoppscotch_Agent_win_x64_portable.zip + fi + + - name: Generate checksums (Linux) + if: matrix.platform == 'ubuntu-22.04' + run: | + cd artifacts + mkdir shas + for file in *; do + if [ -f "$file" ]; then + sha256sum "$file" > "shas/${file}.sha256" + fi + done + + - name: Generate checksums (Mac) + if: matrix.platform == 'macos-latest' + run: | + cd artifacts + mkdir shas + for file in *; do + if [ -f "$file" ]; then + shasum -a 256 "$file" > "shas/${file}.sha256" + fi + done + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: Hoppscotch_Agent-${{ matrix.platform }} + path: artifacts/* diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ec5e254ef..bb87b6e42 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/release-push-docker.yml b/.github/workflows/release-push-docker.yml index c8f10f968..6f0bfed91 100644 --- a/.github/workflows/release-push-docker.yml +++ b/.github/workflows/release-push-docker.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup environment run: cp .env.example .env diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 00cb46d6f..5d281bca1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,9 +2,9 @@ name: Node.js CI on: push: - branches: [main, staging, "release/**"] + branches: [main, next, patch] pull_request: - branches: [main, staging, "release/**"] + branches: [main, next, patch] jobs: test: diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml index d6ccf93e9..b66ee93fa 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup environment run: mv .env.example .env @@ -26,7 +26,7 @@ jobs: run_install: true - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} cache: pnpm diff --git a/.gitignore b/.gitignore index 71152c462..bdfa619cd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ pids *.pid *.seed *.pid.lock +*.env # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/.husky/commit-msg b/.husky/commit-msg index fe4c17a22..dab272daf 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - npx --no-install commitlint --edit "" diff --git a/.husky/pre-commit b/.husky/pre-commit index d0612ad37..13823865a 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npm run pre-commit +npm run pre-commit \ No newline at end of file diff --git a/aio-multiport-setup.Caddyfile b/aio-multiport-setup.Caddyfile index 70ce1c1f2..6140f464e 100644 --- a/aio-multiport-setup.Caddyfile +++ b/aio-multiport-setup.Caddyfile @@ -1,3 +1,8 @@ +{ + admin off + persist_config off +} + :3000 { try_files {path} / root * /site/selfhost-web @@ -13,7 +18,3 @@ :3170 { reverse_proxy localhost:8080 } - -:80 { - respond 404 -} diff --git a/aio-subpath-access.Caddyfile b/aio-subpath-access.Caddyfile index 46a8436e4..7318eecd2 100644 --- a/aio-subpath-access.Caddyfile +++ b/aio-subpath-access.Caddyfile @@ -1,16 +1,9 @@ -:3000 { - respond 404 +{ + admin off + persist_config off } -:3100 { - respond 404 -} - -:3170 { - reverse_proxy localhost:8080 -} - -:80 { +:{$HOPP_AIO_ALTERNATE_PORT:80} { # Serve the `selfhost-web` SPA by default root * /site/selfhost-web file_server diff --git a/aio_run.mjs b/aio_run.mjs index 59c2684be..2690078f7 100644 --- a/aio_run.mjs +++ b/aio_run.mjs @@ -51,7 +51,7 @@ fs.rmSync("build.env") const caddyFileName = process.env.ENABLE_SUBPATH_BASED_ACCESS === 'true' ? 'aio-subpath-access.Caddyfile' : 'aio-multiport-setup.Caddyfile' const caddyProcess = runChildProcessWithPrefix("caddy", ["run", "--config", `/etc/caddy/${caddyFileName}`, "--adapter", "caddyfile"], "App/Admin Dashboard Caddy") -const backendProcess = runChildProcessWithPrefix("pnpm", ["run", "start:prod"], "Backend Server") +const backendProcess = runChildProcessWithPrefix("node", ["/dist/backend/dist/main.js"], "Backend Server") caddyProcess.on("exit", (code) => { console.log(`Exiting process because Caddy Server exited with code ${code}`) diff --git a/docker-compose.deploy.yml b/docker-compose.deploy.yml index 3e2028f7d..9659a8cf6 100644 --- a/docker-compose.deploy.yml +++ b/docker-compose.deploy.yml @@ -1,8 +1,6 @@ # THIS IS NOT TO BE USED FOR PERSONAL DEPLOYMENTS! # Internal Docker Compose Image used for internal testing deployments -version: "3.7" - services: hoppscotch-db: image: postgres:15 diff --git a/docker-compose.yml b/docker-compose.yml index 7bae7928b..ee5bd337a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,6 @@ # To make it easier to self-host, we have a preset docker compose config that also # has a container with a Postgres instance running. # You can tweak around this file to match your instances -version: "3.7" services: # This service runs the backend app in the port 3170 diff --git a/healthcheck.sh b/healthcheck.sh index c772d6bf5..c95076899 100644 --- a/healthcheck.sh +++ b/healthcheck.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh curlCheck() { if ! curl -s --head "$1" | head -n 1 | grep -q "HTTP/1.[01] [23].."; then diff --git a/package.json b/package.json index dbe13220e..a4656c8b4 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "MIT", "scripts": { "preinstall": "npx only-allow pnpm", - "prepare": "husky install", + "prepare": "husky", "dev": "pnpm -r do-dev", "gen-gql": "cross-env GQL_SCHEMA_EMIT_LOCATION='../../../gql-gen/backend-schema.gql' pnpm -r generate-gql-sdl", "generate": "pnpm -r do-build-prod", @@ -23,18 +23,28 @@ "./packages/*" ], "devDependencies": { - "@commitlint/cli": "16.3.0", - "@commitlint/config-conventional": "16.2.4", - "@hoppscotch/ui": "0.2.0", - "@types/node": "17.0.27", + "@commitlint/cli": "19.5.0", + "@commitlint/config-conventional": "19.5.0", + "@hoppscotch/ui": "0.2.2", + "@types/node": "22.7.6", "cross-env": "7.0.3", "http-server": "14.1.1", - "husky": "7.0.4", - "lint-staged": "12.4.0" + "husky": "9.1.6", + "lint-staged": "15.2.10" }, "pnpm": { "overrides": { - "vue": "3.3.9" + "cookie": "0.7.2", + "vue": "3.5.12", + "@nestjs-modules/mailer>mjml": "5.0.0-alpha.4", + "subscriptions-transport-ws>ws": "7.5.10", + "braces": "3.0.3", + "send": "0.19.0", + "pug": "3.0.3", + "body-parser": "1.20.3", + "path-to-regexp@3.2.0": "3.3.0", + "micromatch@<4.0.8": "4.0.8", + "dset@3.1.3": "3.1.4" }, "packageExtensions": { "@hoppscotch/httpsnippet": { diff --git a/packages/codemirror-lang-graphql/package.json b/packages/codemirror-lang-graphql/package.json index c5fb23f70..d322bc2c2 100644 --- a/packages/codemirror-lang-graphql/package.json +++ b/packages/codemirror-lang-graphql/package.json @@ -5,7 +5,7 @@ "author": "Hoppscotch (support@hoppscotch.io)", "license": "MIT", "scripts": { - "prepare": "rollup -c" + "prepare": "rollup -c && tsc --emitDeclarationOnly --declaration" }, "type": "module", "main": "dist/index.cjs", @@ -25,8 +25,7 @@ "@lezer/generator": "1.5.1", "mocha": "9.2.2", "rollup": "3.29.4", - "rollup-plugin-dts": "6.0.2", - "rollup-plugin-ts": "3.4.5", + "@rollup/plugin-typescript": "12.1.1", "typescript": "5.2.2" } -} \ No newline at end of file +} diff --git a/packages/codemirror-lang-graphql/rollup.config.js b/packages/codemirror-lang-graphql/rollup.config.js index 44dffbd51..d19001c39 100644 --- a/packages/codemirror-lang-graphql/rollup.config.js +++ b/packages/codemirror-lang-graphql/rollup.config.js @@ -1,4 +1,4 @@ -import typescript from "rollup-plugin-ts" +import typescript from "@rollup/plugin-typescript" import { lezer } from "@lezer/generator/rollup" export default { @@ -8,5 +8,10 @@ export default { { file: "dist/index.cjs", format: "cjs" }, { dir: "./dist", format: "es" }, ], - plugins: [lezer(), typescript()], + plugins: [ + lezer(), + typescript({ + tsconfig: "./tsconfig.json" + }) + ], } diff --git a/packages/codemirror-lang-graphql/tsconfig.json b/packages/codemirror-lang-graphql/tsconfig.json index 7eaca8b2e..b258d4ce0 100644 --- a/packages/codemirror-lang-graphql/tsconfig.json +++ b/packages/codemirror-lang-graphql/tsconfig.json @@ -5,6 +5,7 @@ "module": "es2020", "newLine": "lf", "declaration": true, + "declarationDir": "./dist", "moduleResolution": "node", "allowJs": true }, diff --git a/packages/hoppscotch-agent/.envrc b/packages/hoppscotch-agent/.envrc new file mode 100644 index 000000000..5bf8fc159 --- /dev/null +++ b/packages/hoppscotch-agent/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0=" + +use devenv \ No newline at end of file diff --git a/packages/hoppscotch-agent/.gitignore b/packages/hoppscotch-agent/.gitignore new file mode 100644 index 000000000..e9f02db4e --- /dev/null +++ b/packages/hoppscotch-agent/.gitignore @@ -0,0 +1,33 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/packages/hoppscotch-agent/README.md b/packages/hoppscotch-agent/README.md new file mode 100644 index 000000000..5047afc64 --- /dev/null +++ b/packages/hoppscotch-agent/README.md @@ -0,0 +1,16 @@ +# Tauri + Vue + TypeScript + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/packages/hoppscotch-agent/package.json b/packages/hoppscotch-agent/package.json new file mode 100644 index 000000000..19b6f64e1 --- /dev/null +++ b/packages/hoppscotch-agent/package.json @@ -0,0 +1,34 @@ +{ + "name": "hoppscotch-agent", + "private": true, + "version": "0.1.3", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "@hoppscotch/ui": "^0.2.1", + "@tauri-apps/api": "^2.0.2", + "@tauri-apps/plugin-shell": "^2.0.0", + "@vueuse/core": "^11.1.0", + "axios": "^1.7.7", + "fp-ts": "^2.16.9", + "vue": "3.3.9" + }, + "devDependencies": { + "@iconify-json/lucide": "^1.2.8", + "@tauri-apps/cli": "^2.0.3", + "@types/node": "^22.7.5", + "@vitejs/plugin-vue": "^5.1.4", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.13", + "typescript": "^5.6.3", + "unplugin-icons": "^0.19.3", + "vite": "^5.4.8", + "vue-tsc": "^2.1.6" + } +} diff --git a/packages/hoppscotch-agent/pnpm-lock.yaml b/packages/hoppscotch-agent/pnpm-lock.yaml new file mode 100644 index 000000000..f64dd66fd --- /dev/null +++ b/packages/hoppscotch-agent/pnpm-lock.yaml @@ -0,0 +1,2952 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@hoppscotch/ui': + specifier: ^0.2.1 + version: 0.2.1(eslint@9.11.1(jiti@1.21.6))(terser@5.34.0)(typescript@5.6.2)(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0))(vue@3.5.8(typescript@5.6.2)) + '@tauri-apps/api': + specifier: '>=2.0.0-rc.0' + version: 2.0.0-rc.5 + '@tauri-apps/plugin-shell': + specifier: '>=2.0.0-rc.0' + version: 2.0.0-rc.1 + '@vueuse/core': + specifier: ^11.1.0 + version: 11.1.0(vue@3.5.8(typescript@5.6.2)) + axios: + specifier: ^1.7.7 + version: 1.7.7 + fp-ts: + specifier: ^2.16.9 + version: 2.16.9 + vue: + specifier: ^3.3.4 + version: 3.5.8(typescript@5.6.2) + devDependencies: + '@iconify-json/lucide': + specifier: ^1.2.6 + version: 1.2.6 + '@tauri-apps/cli': + specifier: '>=2.0.0-rc.0' + version: 2.0.0-rc.16 + '@types/node': + specifier: ^22.7.0 + version: 22.7.0 + '@vitejs/plugin-vue': + specifier: ^5.1.4 + version: 5.1.4(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0))(vue@3.5.8(typescript@5.6.2)) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.47) + postcss: + specifier: ^8.4.47 + version: 8.4.47 + tailwindcss: + specifier: ^3.4.13 + version: 3.4.13 + typescript: + specifier: ^5.2.2 + version: 5.6.2 + unplugin-icons: + specifier: ^0.19.3 + version: 0.19.3(@vue/compiler-sfc@3.5.8) + vite: + specifier: ^5.4.7 + version: 5.4.7(@types/node@22.7.0)(terser@5.34.0) + vue-tsc: + specifier: ^2.0.22 + version: 2.1.6(typescript@5.6.2) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@antfu/install-pkg@0.4.1': + resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} + + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.25.6': + resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/standalone@7.25.6': + resolution: {integrity: sha512-Kf2ZcZVqsKbtYhlA7sP0z5A3q5hmCVYMKMWRWNK/5OVwHIve3JY1djVRmIVAx8FMueLIfZGKQDIILK2w8zO4mg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.6': + resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} + engines: {node: '>=6.9.0'} + + '@boringer-avatars/vue3@0.2.1': + resolution: {integrity: sha512-KzAfh31SDXToTvFL0tBNG5Ur+VzfD1PP4jmY5/GS+eIuObGTIAiUu9eiht0LjuAGI+0xCgnaEgsTrOx8H3vLOQ==} + peerDependencies: + vue: ^3.0.0 + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + 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.11.1': + resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.18.0': + resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.6.0': + resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.11.1': + resolution: {integrity: sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.4': + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.0': + resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@fontsource-variable/inter@5.1.0': + resolution: {integrity: sha512-Wj2dUGP0vUpxRGQTXQTCNJO+aLcFcQm+gUPXfj/aS877bQkEPBPv9JvZJpwdm2vzelt8NTZ+ausKlBCJjh2XIg==} + + '@fontsource-variable/material-symbols-rounded@5.1.0': + resolution: {integrity: sha512-gQZli8YyPTymXuAzMQ13d6Fzw9zQkMXIxqZyCrSeNLKfOpLVKXoh0LZ0SV9thOpZhR2KQURD8S1Y0YLUV6KZNw==} + + '@fontsource-variable/roboto-mono@5.1.0': + resolution: {integrity: sha512-87USlDpEi7dS/ayPXwf/08vdgJKEYxdQmuZk5kCzWBtumimdwWlA9Vh36TCtYqFC+dGgDxPX/4PJK+0lxkEd5A==} + + '@hoppscotch/ui@0.2.1': + resolution: {integrity: sha512-orwItZFlOZUFfMf0B7RNAQa2ByucnXcl9ufP0aJggyFafRk/X9eyMPC6JrF/OATml/ZztksjiB2636cMYsEnSw==} + engines: {node: '>=16'} + peerDependencies: + vue: ^3.2.25 + + '@hoppscotch/vue-sonner@1.2.3': + resolution: {integrity: sha512-P1gyvHHLsPeB8lsLP5SrqwQatuwOKtbsP83sKhyIV3WL2rJj3+DiFfqo2ErNBa+Sl0gM68o1V+wuOS7zbR//6g==} + + '@hoppscotch/vue-toasted@0.1.0': + resolution: {integrity: sha512-DIgmeTHxWwX5UeaHLEqDYNLJFGRosx/5N1fCHkaO8zt+sZv8GrHlkrIpjfKF2drmA3kKw5cY42Cw7WuCoabR3g==} + peerDependencies: + vue: ^3.2.37 + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} + + '@iconify-json/lucide@1.2.6': + resolution: {integrity: sha512-HVLLxmG/rJ91GqpEE97OHdEwb/LfjwMVdynWIrnjvNRQNoKvrYG2N2+jABSBibEhnYQOfv+k8/7X6fK44PEtKA==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@2.1.33': + resolution: {integrity: sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@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/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@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'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + + '@rollup/rollup-android-arm-eabi@4.22.4': + resolution: {integrity: sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.22.4': + resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.22.4': + resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.22.4': + resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': + resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.22.4': + resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.22.4': + resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.22.4': + resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': + resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.22.4': + resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.22.4': + resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.22.4': + resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.22.4': + resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.22.4': + resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.22.4': + resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.22.4': + resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} + cpu: [x64] + os: [win32] + + '@tauri-apps/api@2.0.0-rc.5': + resolution: {integrity: sha512-JWs69pE9NsQdcqTpEVBepAZ08+jgQWuthIiTiaWRq/YlCLgnqq0KfC9sDem55uJ0YpgsytZuyj+m6b6q8oiD2g==} + + '@tauri-apps/cli-darwin-arm64@2.0.0-rc.16': + resolution: {integrity: sha512-lISZU4gG0c9PbY7h/j/gW7nJLxZEygNBrYEET6zN8R99Znf5rSO+CfjenaMcJUUj6yTAd8gzdakRpLqNSAWegA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.0.0-rc.16': + resolution: {integrity: sha512-D9uxPCxpyYcTSQulJGFX3POAKPOJd8WcWHFH8x6YVM1cIx6EWRXIE1sZnPUOjFr7qCg+bSdYdr8/BFHcZGcApQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.16': + resolution: {integrity: sha512-WsVdKm4D1I1XV8I9yRnmOINZRwwWfh6xcLV3m19+B9g6TohK8RkRxWfxvs3TLQlWOLQ6lo8BzS9rzXB+KtjDpg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.16': + resolution: {integrity: sha512-2jpZDagNs6rrqposHJihHBayttgOl5aB2+bYiVEC6ye+haiFtmYmpdaPOaVIw+YVQs6lepf5PVrisCoU9DmYsg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.16': + resolution: {integrity: sha512-SNEDcB+sWOls/B0a+UpUHVa/oegvlXXKYWsTxuXtgWIr5VbWG7rXLZ3fZpLLP3SpRVGTGTnABcyqshFbWSqqKQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.16': + resolution: {integrity: sha512-Zsq29MM1ooeH1+chQBa7ffDFnzAZebBBFdsvs4e05tS1H8gn4oKE+PSMn9p/okzVXykEk9ri2/n7BG1XFeifMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-linux-x64-musl@2.0.0-rc.16': + resolution: {integrity: sha512-g+pwSuis2YMxhJJ/pJYwp/Nps5CWvlv/5MV5UfDvClkCkeAyzIqVX+HbBLPcs5S0CePUQNeP0j4d4jBWUqZZQg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.16': + resolution: {integrity: sha512-PpPqdMTwJSDAK4KnNjvh77ShSkY+7pih1f6e50EtXar8bjC17e3XcEqFhDNne5mxEVTLYhibs6p1JLPad0ZjRA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.16': + resolution: {integrity: sha512-io2yIcEcG7YLP+9n13NbilB93SjcB7jIl8GbURC4XZT4/4t9D1PWHpJr5hySVsGRLCz5e8NzwC5RlnenNzmpPQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.16': + resolution: {integrity: sha512-Rfkmxe3k+cBVA/kVYt8O25QrQqWKJlH9AiH7Q3C6xBzzG9PCSRMBszCp+JhBF8jhVlwNmOBv6UG+lm85kspwGg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tauri-apps/cli@2.0.0-rc.16': + resolution: {integrity: sha512-wdjZg/M3dcxiqgWG6VRnABpX0dYxRww93t0d1MYoZxFDrnyoUz5kYwFQ0v4J9u0qenEgskjoypvon7V/Nj9qrg==} + engines: {node: '>= 10'} + hasBin: true + + '@tauri-apps/plugin-shell@2.0.0-rc.1': + resolution: {integrity: sha512-JtNROc0rqEwN/g93ig5pK4cl1vUo2yn+osCpY9de64cy/d9hRzof7AuYOgvt/Xcd5VPQmlgo2AGvUh5sQRSR1A==} + + '@types/eslint@8.56.12': + resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.7.0': + resolution: {integrity: sha512-MOdOibwBs6KW1vfqz2uKMlxq5xAfAZ98SZjO8e3XnAbFnTJtAspqhWk7hrdSAs9/Y14ZWMiy7/MxMUzAOadYEw==} + + '@types/web-bluetooth@0.0.14': + resolution: {integrity: sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A==} + + '@types/web-bluetooth@0.0.20': + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + + '@vitejs/plugin-legacy@2.3.1': + resolution: {integrity: sha512-J5KaGBlSt2tEYPVjM/C8dA6DkRzkFkbPe+Xb4IX5G+XOV5OGbVAfkMjKywdrkO3gGynO8S98i71Lmsff4cWkCQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + terser: ^5.4.0 + vite: ^3.0.0 + + '@vitejs/plugin-vue@5.1.4': + resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.5': + resolution: {integrity: sha512-F4tA0DCO5Q1F5mScHmca0umsi2ufKULAnMOVBfMsZdT4myhVl4WdKRwCaKcfOkIEuyrAVvtq1ESBdZ+rSyLVww==} + + '@volar/source-map@2.4.5': + resolution: {integrity: sha512-varwD7RaKE2J/Z+Zu6j3mNNJbNT394qIxXwdvz/4ao/vxOfyClZpSDtLKkwWmecinkOVos5+PWkWraelfMLfpw==} + + '@volar/typescript@2.4.5': + resolution: {integrity: sha512-mcT1mHvLljAEtHviVcBuOyAwwMKz1ibXTi5uYtP/pf4XxoAzpdkQ+Br2IC0NPCvLCbjPZmbf3I0udndkfB1CDg==} + + '@vue/compiler-core@3.5.8': + resolution: {integrity: sha512-Uzlxp91EPjfbpeO5KtC0KnXPkuTfGsNDeaKQJxQN718uz+RqDYarEf7UhQJGK+ZYloD2taUbHTI2J4WrUaZQNA==} + + '@vue/compiler-dom@3.5.8': + resolution: {integrity: sha512-GUNHWvoDSbSa5ZSHT9SnV5WkStWfzJwwTd6NMGzilOE/HM5j+9EB9zGXdtu/fCNEmctBqMs6C9SvVPpVPuk1Eg==} + + '@vue/compiler-sfc@3.5.8': + resolution: {integrity: sha512-taYpngQtSysrvO9GULaOSwcG5q821zCoIQBtQQSx7Uf7DxpR6CIHR90toPr9QfDD2mqHQPCSgoWBvJu0yV9zjg==} + + '@vue/compiler-ssr@3.5.8': + resolution: {integrity: sha512-W96PtryNsNG9u0ZnN5Q5j27Z/feGrFV6zy9q5tzJVyJaLiwYxvC0ek4IXClZygyhjm+XKM7WD9pdKi/wIRVC/Q==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/language-core@2.1.6': + resolution: {integrity: sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.8': + resolution: {integrity: sha512-mlgUyFHLCUZcAYkqvzYnlBRCh0t5ZQfLYit7nukn1GR96gc48Bp4B7OIcSfVSvlG1k3BPfD+p22gi1t2n9tsXg==} + + '@vue/runtime-core@3.5.8': + resolution: {integrity: sha512-fJuPelh64agZ8vKkZgp5iCkPaEqFJsYzxLk9vSC0X3G8ppknclNDr61gDc45yBGTaN5Xqc1qZWU3/NoaBMHcjQ==} + + '@vue/runtime-dom@3.5.8': + resolution: {integrity: sha512-DpAUz+PKjTZPUOB6zJgkxVI3GuYc2iWZiNeeHQUw53kdrparSTG6HeXUrYDjaam8dVsCdvQxDz6ZWxnyjccUjQ==} + + '@vue/server-renderer@3.5.8': + resolution: {integrity: sha512-7AmC9/mEeV9mmXNVyUIm1a1AjUhyeeGNbkLh39J00E7iPeGks8OGRB5blJiMmvqSh8SkaS7jkLWSpXtxUCeagA==} + peerDependencies: + vue: 3.5.8 + + '@vue/shared@3.5.8': + resolution: {integrity: sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==} + + '@vueuse/core@11.1.0': + resolution: {integrity: sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==} + + '@vueuse/core@8.9.4': + resolution: {integrity: sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q==} + peerDependencies: + '@vue/composition-api': ^1.1.0 + vue: ^2.6.0 || ^3.2.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue: + optional: true + + '@vueuse/metadata@11.1.0': + resolution: {integrity: sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==} + + '@vueuse/metadata@8.9.4': + resolution: {integrity: sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==} + + '@vueuse/shared@11.1.0': + resolution: {integrity: sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==} + + '@vueuse/shared@8.9.4': + resolution: {integrity: sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==} + peerDependencies: + '@vue/composition-api': ^1.1.0 + vue: ^2.6.0 || ^3.2.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue: + optional: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.0: + resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001663: + resolution: {integrity: sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + + concat-map@0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + core-js@3.38.1: + resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.28: + resolution: {integrity: sha512-VufdJl+rzaKZoYVUijN13QcXVF5dWPZANeFTLNy+OSpHdDL5ynXTF35+60RSBbaQYB1ae723lQXHCrf4pyLsMw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@8.0.2: + resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.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-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.11.1: + resolution: {integrity: sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.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-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + 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.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + fp-ts@2.16.9: + resolution: {integrity: sha512-+I2+FnVB+tVaxcYyQkHUq7ZdKScaBlX53A41mxQtpIccsfyv8PzdzP7fzp2AY832T4aoK6UZ5WRX/ebGd8uZuQ==} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + 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'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + 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==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + 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==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + magic-string@0.26.7: + resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} + engines: {node: '>=12'} + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + 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'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-manager-detector@0.2.0: + resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + 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==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path@0.12.7: + resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-types@1.2.0: + resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + + rollup@4.22.4: + resolution: {integrity: sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sortablejs@1.14.0: + resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + systemjs@6.15.1: + resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} + + tailwindcss@3.4.13: + resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} + engines: {node: '>=14.0.0'} + hasBin: true + + terser@5.34.0: + resolution: {integrity: sha512-y5NUX+U9HhVsK/zihZwoq4r9dICLyV2jXGOriDAVOeKhq3LKVjgJbGO90FisozXLlJfvjHqgckGmJFBb9KYoWQ==} + engines: {node: '>=10'} + hasBin: true + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + unplugin-icons@0.19.3: + resolution: {integrity: sha512-EUegRmsAI6+rrYr0vXjFlIP+lg4fSC4zb62zAZKx8FGXlWAGgEGBCa3JDe27aRAXhistObLPbBPhwa/0jYLFkQ==} + peerDependencies: + '@svgr/core': '>=7.0.0' + '@svgx/core': ^1.0.1 + '@vue/compiler-sfc': ^3.0.2 || ^2.7.0 + vue-template-compiler: ^2.6.12 + vue-template-es2015-compiler: ^1.9.0 + peerDependenciesMeta: + '@svgr/core': + optional: true + '@svgx/core': + optional: true + '@vue/compiler-sfc': + optional: true + vue-template-compiler: + optional: true + vue-template-es2015-compiler: + optional: true + + unplugin@1.14.1: + resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==} + engines: {node: '>=14.0.0'} + peerDependencies: + webpack-sources: ^3 + peerDependenciesMeta: + webpack-sources: + optional: true + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + + vite-plugin-eslint@1.8.1: + resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==} + peerDependencies: + eslint: '>=7' + vite: '>=2' + + vite@5.4.7: + resolution: {integrity: sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + 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-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-promise-modals@0.1.0: + resolution: {integrity: sha512-LmPejeqvZSkxj4KkJe6ZUEJmCUQXVeEAj9ihTX+BRFfZftVCZSZd3B4uuZSKF0iCeQUemkodXUZFxcsNT/2dmg==} + + vue-tsc@2.1.6: + resolution: {integrity: sha512-f98dyZp5FOukcYmbFpuSCJ4Z0vHSOSmxGttZJCsFeX0M4w/Rsq0s4uKXjcSRsZqsRgQa6z7SfuO+y0HVICE57Q==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.8: + resolution: {integrity: sha512-hvuvuCy51nP/1fSRvrrIqTLSvrSyz2Pq+KQ8S8SXCxTWVE0nMaOnSDnSOxV1eYmGfvK7mqiwvd1C59CEEz7dAQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vuedraggable-es@4.1.1: + resolution: {integrity: sha512-F35pjSwC8HS/lnaOd+B59nYR4FZmwuhWAzccK9xftRuWds8SU1TZh5myKVM86j5dFOI7S26O64Kwe7LUHnXjlA==} + peerDependencies: + vue: ^3.2.31 + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + 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'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@antfu/install-pkg@0.4.1': + dependencies: + package-manager-detector: 0.2.0 + tinyexec: 0.3.0 + + '@antfu/utils@0.7.10': {} + + '@babel/helper-string-parser@7.24.8': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/parser@7.25.6': + dependencies: + '@babel/types': 7.25.6 + + '@babel/standalone@7.25.6': {} + + '@babel/types@7.25.6': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@boringer-avatars/vue3@0.2.1(vue@3.5.8(typescript@5.6.2))': + dependencies: + vue: 3.5.8(typescript@5.6.2) + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@9.11.1(jiti@1.21.6))': + dependencies: + eslint: 9.11.1(jiti@1.21.6) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.1': {} + + '@eslint/config-array@0.18.0': + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.6.0': {} + + '@eslint/eslintrc@3.1.0': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 10.1.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.11.1': {} + + '@eslint/object-schema@2.1.4': {} + + '@eslint/plugin-kit@0.2.0': + dependencies: + levn: 0.4.1 + + '@fontsource-variable/inter@5.1.0': {} + + '@fontsource-variable/material-symbols-rounded@5.1.0': {} + + '@fontsource-variable/roboto-mono@5.1.0': {} + + '@hoppscotch/ui@0.2.1(eslint@9.11.1(jiti@1.21.6))(terser@5.34.0)(typescript@5.6.2)(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0))(vue@3.5.8(typescript@5.6.2))': + dependencies: + '@boringer-avatars/vue3': 0.2.1(vue@3.5.8(typescript@5.6.2)) + '@fontsource-variable/inter': 5.1.0 + '@fontsource-variable/material-symbols-rounded': 5.1.0 + '@fontsource-variable/roboto-mono': 5.1.0 + '@hoppscotch/vue-sonner': 1.2.3 + '@hoppscotch/vue-toasted': 0.1.0(vue@3.5.8(typescript@5.6.2)) + '@vitejs/plugin-legacy': 2.3.1(terser@5.34.0)(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0)) + '@vueuse/core': 8.9.4(vue@3.5.8(typescript@5.6.2)) + fp-ts: 2.16.9 + lodash-es: 4.17.21 + path: 0.12.7 + vite-plugin-eslint: 1.8.1(eslint@9.11.1(jiti@1.21.6))(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0)) + vue: 3.5.8(typescript@5.6.2) + vue-promise-modals: 0.1.0(typescript@5.6.2) + vuedraggable-es: 4.1.1(vue@3.5.8(typescript@5.6.2)) + transitivePeerDependencies: + - '@vue/composition-api' + - eslint + - terser + - typescript + - vite + + '@hoppscotch/vue-sonner@1.2.3': {} + + '@hoppscotch/vue-toasted@0.1.0(vue@3.5.8(typescript@5.6.2))': + dependencies: + vue: 3.5.8(typescript@5.6.2) + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.0': {} + + '@iconify-json/lucide@1.2.6': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@2.1.33': + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/types': 2.0.0 + debug: 4.3.7 + kolorist: 1.8.0 + local-pkg: 0.5.0 + mlly: 1.7.1 + transitivePeerDependencies: + - supports-color + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + 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/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + + '@rollup/rollup-android-arm-eabi@4.22.4': + optional: true + + '@rollup/rollup-android-arm64@4.22.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.22.4': + optional: true + + '@rollup/rollup-darwin-x64@4.22.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.22.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.22.4': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.22.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.22.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.22.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.22.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.22.4': + optional: true + + '@tauri-apps/api@2.0.0-rc.5': {} + + '@tauri-apps/cli-darwin-arm64@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-darwin-x64@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-linux-x64-musl@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.16': + optional: true + + '@tauri-apps/cli@2.0.0-rc.16': + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.0.0-rc.16 + '@tauri-apps/cli-darwin-x64': 2.0.0-rc.16 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.0-rc.16 + '@tauri-apps/cli-linux-arm64-gnu': 2.0.0-rc.16 + '@tauri-apps/cli-linux-arm64-musl': 2.0.0-rc.16 + '@tauri-apps/cli-linux-x64-gnu': 2.0.0-rc.16 + '@tauri-apps/cli-linux-x64-musl': 2.0.0-rc.16 + '@tauri-apps/cli-win32-arm64-msvc': 2.0.0-rc.16 + '@tauri-apps/cli-win32-ia32-msvc': 2.0.0-rc.16 + '@tauri-apps/cli-win32-x64-msvc': 2.0.0-rc.16 + + '@tauri-apps/plugin-shell@2.0.0-rc.1': + dependencies: + '@tauri-apps/api': 2.0.0-rc.5 + + '@types/eslint@8.56.12': + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.5': {} + + '@types/estree@1.0.6': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.7.0': + dependencies: + undici-types: 6.19.8 + + '@types/web-bluetooth@0.0.14': {} + + '@types/web-bluetooth@0.0.20': {} + + '@vitejs/plugin-legacy@2.3.1(terser@5.34.0)(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0))': + dependencies: + '@babel/standalone': 7.25.6 + core-js: 3.38.1 + magic-string: 0.26.7 + regenerator-runtime: 0.13.11 + systemjs: 6.15.1 + terser: 5.34.0 + vite: 5.4.7(@types/node@22.7.0)(terser@5.34.0) + + '@vitejs/plugin-vue@5.1.4(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0))(vue@3.5.8(typescript@5.6.2))': + dependencies: + vite: 5.4.7(@types/node@22.7.0)(terser@5.34.0) + vue: 3.5.8(typescript@5.6.2) + + '@volar/language-core@2.4.5': + dependencies: + '@volar/source-map': 2.4.5 + + '@volar/source-map@2.4.5': {} + + '@volar/typescript@2.4.5': + dependencies: + '@volar/language-core': 2.4.5 + path-browserify: 1.0.1 + vscode-uri: 3.0.8 + + '@vue/compiler-core@3.5.8': + dependencies: + '@babel/parser': 7.25.6 + '@vue/shared': 3.5.8 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.8': + dependencies: + '@vue/compiler-core': 3.5.8 + '@vue/shared': 3.5.8 + + '@vue/compiler-sfc@3.5.8': + dependencies: + '@babel/parser': 7.25.6 + '@vue/compiler-core': 3.5.8 + '@vue/compiler-dom': 3.5.8 + '@vue/compiler-ssr': 3.5.8 + '@vue/shared': 3.5.8 + estree-walker: 2.0.2 + magic-string: 0.30.11 + postcss: 8.4.47 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.8': + dependencies: + '@vue/compiler-dom': 3.5.8 + '@vue/shared': 3.5.8 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/language-core@2.1.6(typescript@5.6.2)': + dependencies: + '@volar/language-core': 2.4.5 + '@vue/compiler-dom': 3.5.8 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.8 + computeds: 0.0.1 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.6.2 + + '@vue/reactivity@3.5.8': + dependencies: + '@vue/shared': 3.5.8 + + '@vue/runtime-core@3.5.8': + dependencies: + '@vue/reactivity': 3.5.8 + '@vue/shared': 3.5.8 + + '@vue/runtime-dom@3.5.8': + dependencies: + '@vue/reactivity': 3.5.8 + '@vue/runtime-core': 3.5.8 + '@vue/shared': 3.5.8 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.8(vue@3.5.8(typescript@5.6.2))': + dependencies: + '@vue/compiler-ssr': 3.5.8 + '@vue/shared': 3.5.8 + vue: 3.5.8(typescript@5.6.2) + + '@vue/shared@3.5.8': {} + + '@vueuse/core@11.1.0(vue@3.5.8(typescript@5.6.2))': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 11.1.0 + '@vueuse/shared': 11.1.0(vue@3.5.8(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.8(typescript@5.6.2)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/core@8.9.4(vue@3.5.8(typescript@5.6.2))': + dependencies: + '@types/web-bluetooth': 0.0.14 + '@vueuse/metadata': 8.9.4 + '@vueuse/shared': 8.9.4(vue@3.5.8(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.8(typescript@5.6.2)) + optionalDependencies: + vue: 3.5.8(typescript@5.6.2) + + '@vueuse/metadata@11.1.0': {} + + '@vueuse/metadata@8.9.4': {} + + '@vueuse/shared@11.1.0(vue@3.5.8(typescript@5.6.2))': + dependencies: + vue-demi: 0.14.10(vue@3.5.8(typescript@5.6.2)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/shared@8.9.4(vue@3.5.8(typescript@5.6.2))': + dependencies: + vue-demi: 0.14.10(vue@3.5.8(typescript@5.6.2)) + optionalDependencies: + vue: 3.5.8(typescript@5.6.2) + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.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-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + asynckit@0.4.0: {} + + autoprefixer@10.4.20(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + caniuse-lite: 1.0.30001663 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + axios@1.7.7: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.0: + dependencies: + caniuse-lite: 1.0.30001663 + electron-to-chromium: 1.5.28 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.24.0) + + buffer-from@1.1.2: {} + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001663: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@2.20.3: {} + + commander@4.1.1: {} + + computeds@0.0.1: {} + + concat-map@0.0.1: {} + + confbox@0.1.7: {} + + core-js@3.38.1: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + csstype@3.1.3: {} + + de-indent@1.0.2: {} + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + delayed-stream@1.0.0: {} + + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.28: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@8.0.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.0.0: {} + + eslint@9.11.1(jiti@1.21.6): + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@1.21.6)) + '@eslint-community/regexpp': 4.11.1 + '@eslint/config-array': 0.18.0 + '@eslint/core': 0.6.0 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.11.1 + '@eslint/plugin-kit': 0.2.0 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 + '@nodelib/fs.walk': 1.2.8 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.7 + escape-string-regexp: 4.0.0 + eslint-scope: 8.0.2 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + json-stable-stringify-without-jsonify: 1.0.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 + optionalDependencies: + jiti: 1.21.6 + transitivePeerDependencies: + - supports-color + + espree@10.1.0: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 4.0.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.2: + 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.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + + flatted@3.3.1: {} + + follow-redirects@1.15.9: {} + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fp-ts@2.16.9: {} + + fraction.js@4.3.7: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + globals@14.0.0: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.3: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kolorist@1.8.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + lines-and-columns@1.2.4: {} + + local-pkg@0.5.0: + dependencies: + mlly: 1.7.1 + pkg-types: 1.2.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash-es@4.17.21: {} + + lodash.merge@4.6.2: {} + + lru-cache@10.4.3: {} + + magic-string@0.26.7: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minipass@7.1.2: {} + + mlly@1.7.1: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.2.0 + ufo: 1.5.4 + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.18: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + 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 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.0: {} + + package-manager-detector@0.2.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path@0.12.7: + dependencies: + process: 0.11.10 + util: 0.10.4 + + pathe@1.1.2: {} + + picocolors@1.1.0: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pirates@4.0.6: {} + + pkg-types@1.2.0: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + postcss-import@15.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.47): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.47 + + postcss-load-config@4.0.2(postcss@8.4.47): + dependencies: + lilconfig: 3.1.2 + yaml: 2.5.1 + optionalDependencies: + postcss: 8.4.47 + + postcss-nested@6.2.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + process@0.11.10: {} + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regenerator-runtime@0.13.11: {} + + resolve-from@4.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + rollup@2.79.1: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.22.4: + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.22.4 + '@rollup/rollup-android-arm64': 4.22.4 + '@rollup/rollup-darwin-arm64': 4.22.4 + '@rollup/rollup-darwin-x64': 4.22.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.22.4 + '@rollup/rollup-linux-arm-musleabihf': 4.22.4 + '@rollup/rollup-linux-arm64-gnu': 4.22.4 + '@rollup/rollup-linux-arm64-musl': 4.22.4 + '@rollup/rollup-linux-powerpc64le-gnu': 4.22.4 + '@rollup/rollup-linux-riscv64-gnu': 4.22.4 + '@rollup/rollup-linux-s390x-gnu': 4.22.4 + '@rollup/rollup-linux-x64-gnu': 4.22.4 + '@rollup/rollup-linux-x64-musl': 4.22.4 + '@rollup/rollup-win32-arm64-msvc': 4.22.4 + '@rollup/rollup-win32-ia32-msvc': 4.22.4 + '@rollup/rollup-win32-x64-msvc': 4.22.4 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + semver@7.6.3: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + sortablejs@1.14.0: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + sourcemap-codec@1.4.8: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-json-comments@3.1.1: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + systemjs@6.15.1: {} + + tailwindcss@3.4.13: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47) + postcss-nested: 6.2.0(postcss@8.4.47) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + terser@5.34.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinyexec@0.3.0: {} + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@5.6.2: {} + + ufo@1.5.4: {} + + undici-types@6.19.8: {} + + unplugin-icons@0.19.3(@vue/compiler-sfc@3.5.8): + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/utils': 2.1.33 + debug: 4.3.7 + kolorist: 1.8.0 + local-pkg: 0.5.0 + unplugin: 1.14.1 + optionalDependencies: + '@vue/compiler-sfc': 3.5.8 + transitivePeerDependencies: + - supports-color + - webpack-sources + + unplugin@1.14.1: + dependencies: + acorn: 8.12.1 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.0(browserslist@4.24.0): + dependencies: + browserslist: 4.24.0 + escalade: 3.2.0 + picocolors: 1.1.0 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + util@0.10.4: + dependencies: + inherits: 2.0.3 + + vite-plugin-eslint@1.8.1(eslint@9.11.1(jiti@1.21.6))(vite@5.4.7(@types/node@22.7.0)(terser@5.34.0)): + dependencies: + '@rollup/pluginutils': 4.2.1 + '@types/eslint': 8.56.12 + eslint: 9.11.1(jiti@1.21.6) + rollup: 2.79.1 + vite: 5.4.7(@types/node@22.7.0)(terser@5.34.0) + + vite@5.4.7(@types/node@22.7.0)(terser@5.34.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.22.4 + optionalDependencies: + '@types/node': 22.7.0 + fsevents: 2.3.3 + terser: 5.34.0 + + vscode-uri@3.0.8: {} + + vue-demi@0.14.10(vue@3.5.8(typescript@5.6.2)): + dependencies: + vue: 3.5.8(typescript@5.6.2) + + vue-promise-modals@0.1.0(typescript@5.6.2): + dependencies: + vue: 3.5.8(typescript@5.6.2) + transitivePeerDependencies: + - typescript + + vue-tsc@2.1.6(typescript@5.6.2): + dependencies: + '@volar/typescript': 2.4.5 + '@vue/language-core': 2.1.6(typescript@5.6.2) + semver: 7.6.3 + typescript: 5.6.2 + + vue@3.5.8(typescript@5.6.2): + dependencies: + '@vue/compiler-dom': 3.5.8 + '@vue/compiler-sfc': 3.5.8 + '@vue/runtime-dom': 3.5.8 + '@vue/server-renderer': 3.5.8(vue@3.5.8(typescript@5.6.2)) + '@vue/shared': 3.5.8 + optionalDependencies: + typescript: 5.6.2 + + vuedraggable-es@4.1.1(vue@3.5.8(typescript@5.6.2)): + dependencies: + sortablejs: 1.14.0 + vue: 3.5.8(typescript@5.6.2) + + webpack-virtual-modules@0.6.2: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + yaml@2.5.1: {} + + yocto-queue@0.1.0: {} diff --git a/packages/hoppscotch-agent/postcss.config.js b/packages/hoppscotch-agent/postcss.config.js new file mode 100644 index 000000000..2e7af2b7f --- /dev/null +++ b/packages/hoppscotch-agent/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/hoppscotch-agent/public/tauri.svg b/packages/hoppscotch-agent/public/tauri.svg new file mode 100644 index 000000000..31b62c928 --- /dev/null +++ b/packages/hoppscotch-agent/public/tauri.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/hoppscotch-agent/public/vite.svg b/packages/hoppscotch-agent/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/packages/hoppscotch-agent/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/hoppscotch-agent/src-tauri/.cargo/config.toml b/packages/hoppscotch-agent/src-tauri/.cargo/config.toml new file mode 100644 index 000000000..9017258b4 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/.cargo/config.toml @@ -0,0 +1,25 @@ +# Enable static linking for C runtime library on Windows. +# +# Rust uses the msvc toolchain on Windows, +# which by default dynamically links the C runtime (CRT) to the binary. +# +# This creates a runtime dependency on the Visual C++ Redistributable (`vcredist`), +# meaning the target machine must have `vcredist` installed for the application to run. +# +# Since `portable` version doesn't have an installer, +# we can't rely on it to install dependencies, so this config. +# +# Basically: +# - The `+crt-static` flag instructs the Rust compiler to statically link the C runtime for Windows builds.\ +# - To avoids runtime errors related to missing `vcredist` installations. +# - Results in a larger binary size because the runtime is bundled directly into the executable. +# +# For MSVC targets specifically, it will compile code with `/MT` or static linkage. +# See: - RFC 1721: https://rust-lang.github.io/rfcs/1721-crt-static.html +# - Rust Reference - Runtime: https://doc.rust-lang.org/reference/runtime.html +# - MSVC Linking Options: https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library +# - Rust Issue #37406: https://github.com/rust-lang/rust/issues/37406 +# - Tauri Issue #3048: https://github.com/tauri-apps/tauri/issues/3048 +# - Rust Linkage: https://doc.rust-lang.org/reference/linkage.html +[target.'cfg(windows)'] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/packages/hoppscotch-agent/src-tauri/.gitignore b/packages/hoppscotch-agent/src-tauri/.gitignore new file mode 100644 index 000000000..b21bd681d --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Generated by Tauri +# will have schema files for capabilities auto-completion +/gen/schemas diff --git a/packages/hoppscotch-agent/src-tauri/Cargo.lock b/packages/hoppscotch-agent/src-tauri/Cargo.lock new file mode 100644 index 000000000..4893f4e86 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/Cargo.lock @@ -0,0 +1,6482 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "ashpd" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "raw-window-handle 0.6.2", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auto-launch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" +dependencies = [ + "dirs 4.0.0", + "thiserror", + "winreg 0.10.1", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "axum" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa 1.0.11", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.6.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cargo_toml" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" +dependencies = [ + "serde", + "toml 0.8.2", +] + +[[package]] +name = "cc" +version = "1.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation 0.1.2", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" +dependencies = [ + "bitflags 2.6.0", + "block", + "cocoa-foundation 0.2.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "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 0.1.3", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "libc", + "objc", +] + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa" +dependencies = [ + "cookie", + "idna 0.5.0", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.85", +] + +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn 2.0.85", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curl" +version = "0.4.47" +source = "git+https://github.com/CuriousCorrelation/curl-rust.git#1ec8079cf527b9cf47cc7a48c68b458affdae273" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "socket2", +] + +[[package]] +name = "curl-sys" +version = "0.4.77+curl-8.10.1" +source = "git+https://github.com/CuriousCorrelation/curl-rust.git#1ec8079cf527b9cf47cc7a48c68b458affdae273" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "windows-sys 0.52.0", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.85", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "serde", +] + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.85", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.5", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "embed-resource" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e24052d7be71f0efb50c201557f6fe7d237cfd5a64fd5bcd7fd8fe32dbbffa" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.2", + "vswhom", + "winreg 0.52.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fdeflate" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "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 = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "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 = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "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.85", +] + +[[package]] +name = "foreign-types-shared" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.6.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hoppscotch-agent" +version = "0.1.3" +dependencies = [ + "aes-gcm", + "axum", + "axum-extra", + "base16", + "chrono", + "dashmap", + "env_logger", + "hoppscotch-relay", + "lazy_static", + "log", + "mockito", + "native-dialog", + "rand 0.8.5", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-autostart", + "tauri-plugin-dialog", + "tauri-plugin-http", + "tauri-plugin-shell", + "tauri-plugin-single-instance", + "tauri-plugin-store", + "tauri-plugin-updater", + "tempfile", + "thiserror", + "tokio", + "tokio-util", + "tower-http", + "uuid", + "winreg 0.52.0", + "x25519-dalek", +] + +[[package]] +name = "hoppscotch-relay" +version = "0.1.1" +dependencies = [ + "curl", + "env_logger", + "http", + "log", + "openssl", + "openssl-sys", + "serde", + "serde_json", + "thiserror", + "tokio-util", + "url-escape", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.0", + "serde", +] + +[[package]] +name = "infer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +dependencies = [ + "cfb", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +dependencies = [ + "jsonptr 0.4.7", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr 0.6.3", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonptr" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +dependencies = [ + "fluent-uri", + "serde", + "serde_json", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "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.6.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "minisign-verify" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a05b5d0594e0cb1ad8cee3373018d2b84e25905dc75b2468114cc9a8e86cfc20" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "mockito" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand 0.8.5", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + +[[package]] +name = "muda" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8123dfd4996055ac9b15a60ad263b44b01e539007523ad7a4a533a3d93b0591" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.59.0", +] + +[[package]] +name = "native-dialog" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e7038885d2aeab236bd60da9e159a5967b47cde3292da3b15ff1bec27c039f" +dependencies = [ + "ascii", + "block", + "cocoa 0.25.0", + "core-foundation 0.9.4", + "dirs-next", + "objc", + "objc-foundation", + "objc_id", + "once_cell", + "raw-window-handle 0.5.2", + "thiserror", + "versions", + "wfd", + "which", + "winapi", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.6.2", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[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" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.4.0+3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pathdiff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +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]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64 0.22.1", + "indexmap 2.6.0", + "quick-xml 0.32.0", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +dependencies = [ + "idna 0.3.0", + "psl-types", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "async-compression", + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "rfd" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +dependencies = [ + "ashpd", + "block2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "raw-window-handle 0.6.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.85", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.213" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + +[[package]] +name = "serde_derive" +version = "1.0.213" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[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.85", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa 1.0.11", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa 1.0.11", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.6.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared_child" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +dependencies = [ + "bytemuck", + "cfg_aliases", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc2", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle 0.6.2", + "redox_syscall", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0dbbebe82d02044dfa481adca1550d6dd7bd16e086bc34fa0fbecceb5a63751" +dependencies = [ + "bitflags 2.6.0", + "cocoa 0.26.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "crossbeam-channel", + "dispatch", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle 0.6.2", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.58.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "tar" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3889b392db6d32a105d3757230ea0220090b8f94c90d3e60b6c5eb91178ab1b" +dependencies = [ + "anyhow", + "bytes", + "dirs 5.0.1", + "dunce", + "embed_plist", + "futures-util", + "getrandom 0.2.15", + "glob", + "gtk", + "heck 0.5.0", + "http", + "image", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "percent-encoding", + "plist", + "raw-window-handle 0.6.2", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror", + "tokio", + "tray-icon", + "url", + "urlpattern", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f96827ccfb1aa40d55d0ded79562d18ba18566657a553f992a982d755148376" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs 5.0.1", + "glob", + "heck 0.5.0", + "json-patch 3.0.1", + "schemars", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "toml 0.8.2", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947f16f47becd9e9cd39b74ee337fd1981574d78819be18e4384d85e5a0b82f" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch 2.0.0", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.85", + "tauri-utils", + "thiserror", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd1c8d4a66799d3438747c3a79705cd665a95d6f24cb5f315413ff7a981fe2a" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.85", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa4e6c94cb1d635f65a770c69e23de1bc054b0e4c554fa037a7cc7676333d39" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils", + "toml 0.8.2", + "walkdir", +] + +[[package]] +name = "tauri-plugin-autostart" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bba6bb936e0fd0a58ed958b49e2e423dd40949c9d9425cc991be996959e3838e" +dependencies = [ + "auto-launch", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4307310e1d2c09ab110235834722e7c2b85099b683e1eb7342ab351b0be5ada3" +dependencies = [ + "log", + "raw-window-handle 0.6.2", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ba7d46e86db8c830d143ef90ab5a453328365b0cc834c24edea4267b16aba0" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror", + "url", + "uuid", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c752aee1b00ec3c4d4f440095995d9bd2c640b478f2067d1fba388900b82eb96" +dependencies = [ + "data-url", + "http", + "regex", + "reqwest", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror", + "tokio", + "url", + "urlpattern", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad7880c5586b6b2104be451e3d7fc0f3800c84bda69e9ba81c828f87cb34267" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror", + "tokio", +] + +[[package]] +name = "tauri-plugin-single-instance" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25ac834491d089699a2bc9266a662faf373c9f779f05a2235bc6e4d9e61769a" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "thiserror", + "windows-sys 0.59.0", + "zbus", +] + +[[package]] +name = "tauri-plugin-store" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a580be53f04bb62422d239aa798e88522877f58a0d4a0e745f030055a51bb4" +dependencies = [ + "dunce", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", + "tokio", +] + +[[package]] +name = "tauri-plugin-updater" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd3d2fe0f02bf52eebb5a9d23b987fffac6684646ab6fd683d706dafb18da87" +dependencies = [ + "base64 0.22.1", + "dirs 5.0.1", + "flate2", + "futures-util", + "http", + "infer", + "minisign-verify", + "percent-encoding", + "reqwest", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror", + "time", + "tokio", + "url", + "windows-sys 0.59.0", + "zip", +] + +[[package]] +name = "tauri-runtime" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ef7363e7229ac8d04e8a5d405670dbd43dde8fc4bc3bc56105c35452d03784" +dependencies = [ + "dpi", + "gtk", + "http", + "jni", + "raw-window-handle 0.6.2", + "serde", + "serde_json", + "tauri-utils", + "thiserror", + "url", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62fa2068e8498ad007b54d5773d03d57c3ff6dd96f8c8ce58beff44d0d5e0d30" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "percent-encoding", + "raw-window-handle 0.6.2", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc65d6f5c54e56b66258948a6d9e47a82ea41f4b5a7612bfbdd1634c2913ed0" +dependencies = [ + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "html5ever", + "infer", + "json-patch 2.0.0", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "proc-macro2", + "quote", + "regex", + "schemars", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror", + "toml 0.8.2", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.8", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "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.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.11", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.6.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.6.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "http", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92af36a182b46206723bdf8a7942e20838cde1cf062e5b97854d57eb01763b" +dependencies = [ + "core-graphics 0.24.0", + "crossbeam-channel", + "dirs 5.0.1", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.59.0", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "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 = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", + "serde", +] + +[[package]] +name = "url-escape" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44e0ce4d1246d075ca5abec4b41d33e87a6054d08e2366b63205665e950db218" +dependencies = [ + "percent-encoding", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom 0.2.15", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "versions" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd" +dependencies = [ + "itertools", + "nom", +] + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.85", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +dependencies = [ + "bitflags 2.6.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml 0.36.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.58.0", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "webview2-com-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" +dependencies = [ + "thiserror", + "windows", + "windows-core 0.58.0", +] + +[[package]] +name = "wfd" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea403deff7b51fff19e261330f71608ff2cdef5721d72b64180bb95be7c4150" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-foundation", + "raw-window-handle 0.6.2", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.46.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd5cdf57c66813d97601181349c63b96994b3074fc3d7a31a8cce96e968e3bbd" +dependencies = [ + "base64 0.22.1", + "block2", + "crossbeam-channel", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.2", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.58.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[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 = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "zip" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap 2.6.0", + "memchr", + "thiserror", +] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/packages/hoppscotch-agent/src-tauri/Cargo.toml b/packages/hoppscotch-agent/src-tauri/Cargo.toml new file mode 100644 index 000000000..79b8140ac --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "hoppscotch-agent" +version = "0.1.3" +description = "A cross-platform HTTP request agent for Hoppscotch for advanced request handling including custom headers, certificates, proxies, and local system integration." +authors = ["AndrewBastin", "CuriousCorrelation"] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "hoppscotch_agent_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2.0.1", features = [] } + +[dependencies] +tauri = { version = "2.0.4", features = ["tray-icon", "image-png"] } +tauri-plugin-shell = "2.0.1" +tauri-plugin-autostart = { version = "2.0.1", optional = true } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1.40.0", features = ["full"] } +dashmap = { version = "6.1.0", features = ["serde"] } +axum = { version = "0.7.7" } +axum-extra = { version = "0.9.4", features = ["typed-header"] } +tower-http = { version = "0.6.1", features = ["cors"] } +tokio-util = "0.7.12" +uuid = { version = "1.11.0", features = [ "v4", "fast-rng" ] } +chrono = { version = "0.4", features = ["serde"] } +rand = "0.8.5" +log = "0.4.22" +env_logger = "0.11.5" +hoppscotch-relay = { path = "../../hoppscotch-relay" } +thiserror = "1.0.64" +tauri-plugin-store = "2.1.0" +x25519-dalek = { version = "2.0.1", features = ["getrandom"] } +base16 = "0.2.1" +aes-gcm = { version = "0.10.3", features = ["aes"] } +tauri-plugin-updater = "2.0.2" +tauri-plugin-dialog = "2.0.1" +lazy_static = "1.5.0" +tauri-plugin-single-instance = "2.0.1" +tauri-plugin-http = { version = "2.0.1", features = ["gzip"] } +native-dialog = "0.7.0" + +[target.'cfg(windows)'.dependencies] +tempfile = { version = "3.13.0" } +winreg = { version = "0.52.0" } + +[dev-dependencies] +mockito = "1.5.0" + +[features] +default = ["tauri-plugin-autostart"] +portable = [] diff --git a/packages/hoppscotch-agent/src-tauri/build.rs b/packages/hoppscotch-agent/src-tauri/build.rs new file mode 100644 index 000000000..65c5c67e6 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/build.rs @@ -0,0 +1,5 @@ +fn main() { + tauri_build::build(); + println!("cargo::rerun-if-env-changed=UPDATER_PUB_KEY"); + println!("cargo::rerun-if-env-changed=UPDATER_URL"); +} diff --git a/packages/hoppscotch-agent/src-tauri/capabilities/default.json b/packages/hoppscotch-agent/src-tauri/capabilities/default.json new file mode 100644 index 000000000..fe0f3775c --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/capabilities/default.json @@ -0,0 +1,24 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for the main window", + "windows": ["main", "test"], + "permissions": [ + { + "identifier": "http:default", + "allow": [ + { + "url": "https://*.tauri.app" + }, + { + "url": "https://*.microsoft.*" + } + ] + }, + "core:default", + "shell:allow-open", + "core:window:allow-close", + "core:window:allow-set-focus", + "core:window:allow-set-always-on-top" + ] +} diff --git a/packages/hoppscotch-agent/src-tauri/icons/128x128.png b/packages/hoppscotch-agent/src-tauri/icons/128x128.png new file mode 100644 index 000000000..3778416df Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/128x128.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/128x128@2x.png b/packages/hoppscotch-agent/src-tauri/icons/128x128@2x.png new file mode 100644 index 000000000..e5b884652 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/128x128@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/32x32.png b/packages/hoppscotch-agent/src-tauri/icons/32x32.png new file mode 100644 index 000000000..e553d90f7 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/32x32.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square107x107Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 000000000..e8e31c233 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square107x107Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square142x142Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 000000000..938bf8dc6 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square142x142Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square150x150Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 000000000..647408c58 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square150x150Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square284x284Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 000000000..a898743cc Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square284x284Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square30x30Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 000000000..e1a1f772b Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square30x30Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square310x310Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 000000000..69bf56b18 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square310x310Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square44x44Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 000000000..bb5b89519 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square44x44Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square71x71Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 000000000..68541683f Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square71x71Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/Square89x89Logo.png b/packages/hoppscotch-agent/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 000000000..94a65e2a4 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/Square89x89Logo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/StoreLogo.png b/packages/hoppscotch-agent/src-tauri/icons/StoreLogo.png new file mode 100644 index 000000000..db4b61ba5 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/StoreLogo.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..b334e000a Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..ea31185cd Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..b334e000a Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..ee4bfd7da Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..794a5c62d Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..ee4bfd7da Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..fbef3290a Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..9aee0bf90 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..fbef3290a Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d63d25bcb Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..1443a0c15 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..d63d25bcb Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..3e0f1c398 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..5f449d412 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..3e0f1c398 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/icon.icns b/packages/hoppscotch-agent/src-tauri/icons/icon.icns new file mode 100644 index 000000000..293556d77 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/icon.icns differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/icon.ico b/packages/hoppscotch-agent/src-tauri/icons/icon.ico new file mode 100644 index 000000000..3e3249c9d Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/icon.ico differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/icon.png b/packages/hoppscotch-agent/src-tauri/icons/icon.png new file mode 100644 index 000000000..a91230bbc Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/icon.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@1x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 000000000..e63a928c1 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 000000000..8caffb7b0 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 000000000..8caffb7b0 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@3x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 000000000..c51270a05 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@1x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 000000000..c89515b2f Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 000000000..4c23c6878 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 000000000..4c23c6878 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@3x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 000000000..cd117af20 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@1x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 000000000..8caffb7b0 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 000000000..9f5c51d78 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 000000000..9f5c51d78 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@3x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 000000000..97738c12d Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-512@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 000000000..ed29af682 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-60x60@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 000000000..97738c12d Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-60x60@3x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 000000000..6eb833364 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-76x76@1x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 000000000..38d7d795d Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-76x76@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 000000000..4dbf0d792 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 000000000..aed361332 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/packages/hoppscotch-agent/src-tauri/icons/tray_icon.png b/packages/hoppscotch-agent/src-tauri/icons/tray_icon.png new file mode 100644 index 000000000..b29857f47 Binary files /dev/null and b/packages/hoppscotch-agent/src-tauri/icons/tray_icon.png differ diff --git a/packages/hoppscotch-agent/src-tauri/src/controller.rs b/packages/hoppscotch-agent/src-tauri/src/controller.rs new file mode 100644 index 000000000..00e0006f9 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/controller.rs @@ -0,0 +1,220 @@ +use axum::{ + body::Bytes, + extract::{Path, State}, + http::HeaderMap, + Json, +}; +use axum_extra::{ + headers::{authorization::Bearer, Authorization}, + TypedHeader, +}; +use hoppscotch_relay::{RequestWithMetadata, ResponseWithMetadata}; +use std::sync::Arc; +use tauri::{AppHandle, Emitter}; +use x25519_dalek::{EphemeralSecret, PublicKey}; + +use crate::{ + error::{AgentError, AgentResult}, + model::{AuthKeyResponse, ConfirmedRegistrationRequest, HandshakeResponse}, + state::{AppState, Registration}, + util::EncryptedJson, +}; +use chrono::Utc; +use rand::Rng; +use serde_json::json; +use uuid::Uuid; + +fn generate_otp() -> String { + let otp: u32 = rand::thread_rng().gen_range(0..1_000_000); + + format!("{:06}", otp) +} + +pub async fn handshake( + State((_, app_handle)): State<(Arc, AppHandle)>, +) -> AgentResult> { + Ok(Json(HandshakeResponse { + status: "success".to_string(), + __hoppscotch__agent__: true, + agent_version: app_handle.package_info().version.to_string(), + })) +} + +pub async fn receive_registration( + State((state, app_handle)): State<(Arc, AppHandle)>, +) -> AgentResult> { + let otp = generate_otp(); + + let mut active_registration_code = state.active_registration_code.write().await; + + if !active_registration_code.is_none() { + return Ok(Json( + json!({ "message": "There is already an existing registration happening" }), + )); + } + + *active_registration_code = Some(otp.clone()); + + app_handle + .emit("registration_received", otp) + .map_err(|_| AgentError::InternalServerError)?; + + Ok(Json( + json!({ "message": "Registration received and stored" }), + )) +} + +pub async fn verify_registration( + State((state, app_handle)): State<(Arc, AppHandle)>, + Json(confirmed_registration): Json, +) -> AgentResult> { + state + .validate_registration(&confirmed_registration.registration) + .await + .then_some(()) + .ok_or(AgentError::InvalidRegistration)?; + + let auth_key = Uuid::new_v4().to_string(); + let created_at = Utc::now(); + + let auth_key_copy = auth_key.clone(); + + let agent_secret_key = EphemeralSecret::random(); + let agent_public_key = PublicKey::from(&agent_secret_key); + + let their_public_key = { + let public_key_slice: &[u8; 32] = + &base16::decode(&confirmed_registration.client_public_key_b16) + .map_err(|_| AgentError::InvalidClientPublicKey)?[0..32] + .try_into() + .map_err(|_| AgentError::InvalidClientPublicKey)?; + + PublicKey::from(public_key_slice.to_owned()) + }; + + let shared_secret = agent_secret_key.diffie_hellman(&their_public_key); + + let _ = state.update_registrations(app_handle.clone(), |regs| { + regs.insert( + auth_key_copy, + Registration { + registered_at: created_at, + shared_secret_b16: base16::encode_lower(shared_secret.as_bytes()), + }, + ); + })?; + + let auth_payload = json!({ + "auth_key": auth_key, + "created_at": created_at + }); + + app_handle + .emit("authenticated", &auth_payload) + .map_err(|_| AgentError::InternalServerError)?; + + Ok(Json(AuthKeyResponse { + auth_key, + created_at, + agent_public_key_b16: base16::encode_lower(agent_public_key.as_bytes()), + })) +} + +pub async fn run_request( + State((state, _app_handle)): State<(Arc, T)>, + TypedHeader(auth_header): TypedHeader>, + headers: HeaderMap, + body: Bytes, +) -> AgentResult> { + let nonce = headers + .get("X-Hopp-Nonce") + .ok_or(AgentError::Unauthorized)? + .to_str() + .map_err(|_| AgentError::Unauthorized)?; + + let req: RequestWithMetadata = state + .validate_access_and_get_data(auth_header.token(), nonce, &body) + .ok_or(AgentError::Unauthorized)?; + + let req_id = req.req_id; + + let reg_info = state + .get_registration_info(auth_header.token()) + .ok_or(AgentError::Unauthorized)?; + + let cancel_token = tokio_util::sync::CancellationToken::new(); + state.add_cancellation_token(req.req_id, cancel_token.clone()); + + let cancel_token_clone = cancel_token.clone(); + // Execute the HTTP request in a blocking thread pool and handles cancellation. + // + // It: + // 1. Uses `spawn_blocking` to run the sync `run_request_task` + // without blocking the main Tokio runtime. + // 2. Uses `select!` to concurrently wait for either + // a. the task to complete, + // b. or a cancellation signal. + // + // Why spawn_blocking? + // - `run_request_task` uses synchronous curl operations which would block + // the async runtime if not run in a separate thread. + // - `spawn_blocking` moves this operation to a thread pool designed for + // blocking tasks, so other async operations to continue unblocked. + let result = tokio::select! { + res = tokio::task::spawn_blocking(move || hoppscotch_relay::run_request_task(&req, cancel_token_clone)) => { + match res { + Ok(task_result) => Ok(task_result?), + Err(_) => Err(AgentError::InternalServerError), + } + }, + _ = cancel_token.cancelled() => { + Err(AgentError::RequestCancelled) + } + }; + + state.remove_cancellation_token(req_id); + + result.map(|val| EncryptedJson { + key_b16: reg_info.shared_secret_b16, + data: val, + }) +} + +/// Provides a way for registered clients to check if their +/// registration still holds, this route is supposed to return +/// an encrypted `true` value if the given auth_key is good. +/// Since its encrypted with the shared secret established during +/// registration, the client also needs the shared secret to verify +/// if the read fails, or the auth_key didn't validate and this route returns +/// undefined, we can count on the registration not being valid anymore. +pub async fn registered_handshake( + State((state, _)): State<(Arc, AppHandle)>, + TypedHeader(auth_header): TypedHeader>, +) -> AgentResult> { + let reg_info = state.get_registration_info(auth_header.token()); + + match reg_info { + Some(reg) => Ok(EncryptedJson { + key_b16: reg.shared_secret_b16, + data: json!(true), + }), + None => Err(AgentError::Unauthorized), + } +} + +pub async fn cancel_request( + State((state, _app_handle)): State<(Arc, T)>, + TypedHeader(auth_header): TypedHeader>, + Path(req_id): Path, +) -> AgentResult> { + if !state.validate_access(auth_header.token()) { + return Err(AgentError::Unauthorized); + } + + if let Some((_, token)) = state.remove_cancellation_token(req_id) { + token.cancel(); + Ok(Json(json!({"message": "Request cancelled successfully"}))) + } else { + Err(AgentError::RequestNotFound) + } +} diff --git a/packages/hoppscotch-agent/src-tauri/src/dialog.rs b/packages/hoppscotch-agent/src-tauri/src/dialog.rs new file mode 100644 index 000000000..5b8a19ecc --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/dialog.rs @@ -0,0 +1,58 @@ +use native_dialog::{MessageDialog, MessageType}; + +pub fn panic(msg: &str) { + const FATAL_ERROR: &str = "Fatal error"; + + MessageDialog::new() + .set_type(MessageType::Error) + .set_title(FATAL_ERROR) + .set_text(msg) + .show_alert() + .unwrap_or_default(); + + log::error!("{}: {}", FATAL_ERROR, msg); + + panic!("{}: {}", FATAL_ERROR, msg); +} + +pub fn info(msg: &str) { + log::info!("{}", msg); + + MessageDialog::new() + .set_type(MessageType::Info) + .set_title("Info") + .set_text(msg) + .show_alert() + .unwrap_or_default(); +} + +pub fn warn(msg: &str) { + log::warn!("{}", msg); + + MessageDialog::new() + .set_type(MessageType::Warning) + .set_title("Warning") + .set_text(msg) + .show_alert() + .unwrap_or_default(); +} + +pub fn error(msg: &str) { + log::error!("{}", msg); + + MessageDialog::new() + .set_type(MessageType::Error) + .set_title("Error") + .set_text(msg) + .show_alert() + .unwrap_or_default(); +} + +pub fn confirm(title: &str, msg: &str, icon: MessageType) -> bool { + MessageDialog::new() + .set_type(icon) + .set_title(title) + .set_text(msg) + .show_confirm() + .unwrap_or_default() +} diff --git a/packages/hoppscotch-agent/src-tauri/src/error.rs b/packages/hoppscotch-agent/src-tauri/src/error.rs new file mode 100644 index 000000000..697b3dd3b --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/error.rs @@ -0,0 +1,81 @@ +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, + Json, +}; +use serde_json::json; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum AgentError { + #[error("Invalid Registration")] + InvalidRegistration, + #[error("Invalid Client Public Key")] + InvalidClientPublicKey, + #[error("Unauthorized")] + Unauthorized, + #[error("Request not found or already completed")] + RequestNotFound, + #[error("Internal server error")] + InternalServerError, + #[error("Invalid request: {0}")] + BadRequest(String), + #[error("Client certificate error")] + ClientCertError, + #[error("Root certificate error")] + RootCertError, + #[error("Invalid method")] + InvalidMethod, + #[error("Invalid URL")] + InvalidUrl, + #[error("Invalid headers")] + InvalidHeaders, + #[error("Request run error: {0}")] + RequestRunError(String), + #[error("Request cancelled")] + RequestCancelled, + #[error("Failed to clear registrations")] + RegistrationClearError, + #[error("Failed to insert registrations")] + RegistrationInsertError, + #[error("Failed to save registrations to store")] + RegistrationSaveError, + #[error("Serde error: {0}")] + Serde(#[from] serde_json::Error), + #[error("Store error: {0}")] + TauriPluginStore(#[from] tauri_plugin_store::Error), + #[error("Relay error: {0}")] + Relay(#[from] hoppscotch_relay::RelayError), +} + +impl IntoResponse for AgentError { + fn into_response(self) -> Response { + let (status, error_message) = match self { + AgentError::InvalidRegistration => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::InvalidClientPublicKey => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()), + AgentError::RequestNotFound => (StatusCode::NOT_FOUND, self.to_string()), + AgentError::InternalServerError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), + AgentError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg), + AgentError::ClientCertError => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::RootCertError => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::InvalidMethod => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::InvalidUrl => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::InvalidHeaders => (StatusCode::BAD_REQUEST, self.to_string()), + AgentError::RequestRunError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg), + AgentError::RequestCancelled => (StatusCode::BAD_REQUEST, self.to_string()), + _ => ( + StatusCode::INTERNAL_SERVER_ERROR, + "Internal Server Error".to_string(), + ), + }; + + let body = Json(json!({ + "error": error_message, + })); + + (status, body).into_response() + } +} + +pub type AgentResult = std::result::Result; diff --git a/packages/hoppscotch-agent/src-tauri/src/global.rs b/packages/hoppscotch-agent/src-tauri/src/global.rs new file mode 100644 index 000000000..feaf7fe85 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/global.rs @@ -0,0 +1,2 @@ +pub const AGENT_STORE: &str = "app_data.bin"; +pub const REGISTRATIONS: &str = "registrations"; diff --git a/packages/hoppscotch-agent/src-tauri/src/lib.rs b/packages/hoppscotch-agent/src-tauri/src/lib.rs new file mode 100644 index 000000000..b8312eb0d --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/lib.rs @@ -0,0 +1,178 @@ +pub mod controller; +pub mod dialog; +pub mod error; +pub mod global; +pub mod model; +pub mod route; +pub mod server; +pub mod state; +pub mod tray; +pub mod updater; +pub mod util; +pub mod webview; + +use log::{error, info}; +use std::sync::Arc; +use tauri::{Emitter, Listener, Manager, WebviewWindowBuilder}; +use tauri_plugin_updater::UpdaterExt; +use tokio_util::sync::CancellationToken; + +use model::Payload; +use state::AppState; + +#[tauri::command] +async fn get_otp(state: tauri::State<'_, Arc>) -> Result, ()> { + Ok(state.active_registration_code.read().await.clone()) +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + env_logger::init(); + + // The installer takes care of installing `WebView`, + // this check is only required for portable variant. + #[cfg(all(feature = "portable", windows))] + webview::init_webview(); + + let cancellation_token = CancellationToken::new(); + let server_cancellation_token = cancellation_token.clone(); + + tauri::Builder::default() + // NOTE: Currently, plugins run in the order they were added in to the builder, + // so `tauri_plugin_single_instance` needs to be registered first. + // See: https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/single-instance + .plugin(tauri_plugin_single_instance::init(|app, args, cwd| { + info!("{}, {args:?}, {cwd}", app.package_info().name); + + app.emit("single-instance", Payload::new(args, cwd)) + .unwrap(); + + // Application is already running, bring it to foreground. + if let Some(window) = app.get_webview_window("main") { + let _ = window.show(); + let _ = window.set_focus(); + } else { + error!("Failed to get `main` window"); + } + })) + .plugin(tauri_plugin_store::Builder::new().build()) + .setup(move |app| { + let app_handle = app.app_handle(); + + #[cfg(all(desktop, not(feature = "portable")))] + { + use tauri_plugin_autostart::MacosLauncher; + use tauri_plugin_autostart::ManagerExt; + + let _ = app.handle().plugin(tauri_plugin_autostart::init( + MacosLauncher::LaunchAgent, + None, + )); + + let autostart_manager = app.autolaunch(); + + println!( + "autostart enabled: {}", + autostart_manager.is_enabled().unwrap() + ); + + if !autostart_manager.is_enabled().unwrap() { + let _ = autostart_manager.enable(); + println!( + "autostart updated: {}", + autostart_manager.is_enabled().unwrap() + ); + } + }; + + #[cfg(desktop)] + { + let _ = app + .handle() + .plugin(tauri_plugin_updater::Builder::new().build()); + + let _ = app.handle().plugin(tauri_plugin_dialog::init()); + + let updater = app.updater_builder().build().unwrap(); + + let app_handle_ref = app_handle.clone(); + + tauri::async_runtime::spawn_blocking(|| { + tauri::async_runtime::block_on(async { + updater::check_and_install_updates(app_handle_ref, updater).await; + }) + }); + }; + + let app_state = Arc::new(AppState::new(app_handle.clone())?); + + app.manage(app_state.clone()); + + let server_cancellation_token = server_cancellation_token.clone(); + + let server_app_handle = app_handle.clone(); + + tauri::async_runtime::spawn(async move { + server::run_server(app_state, server_cancellation_token, server_app_handle).await; + }); + + #[cfg(all(desktop))] + { + let handle = app.handle(); + tray::create_tray(handle)?; + } + + // Blocks the app from populating the macOS dock + #[cfg(target_os = "macos")] + { + app_handle + .set_activation_policy(tauri::ActivationPolicy::Accessory) + .unwrap(); + }; + + let app_handle_ref = app_handle.clone(); + + app_handle.listen("registration_received", move |_| { + WebviewWindowBuilder::from_config( + &app_handle_ref, + &app_handle_ref.config().app.windows[0], + ) + .unwrap() + .build() + .unwrap() + .show() + .unwrap(); + }); + + Ok(()) + }) + .manage(cancellation_token) + .on_window_event(|window, event| { + match &event { + tauri::WindowEvent::CloseRequested { .. } => { + let app_state = window.state::>(); + + let mut current_code = app_state.active_registration_code.blocking_write(); + + if current_code.is_some() { + *current_code = None; + } + } + _ => {} + }; + }) + .invoke_handler(tauri::generate_handler![get_otp]) + .build(tauri::generate_context!()) + .expect("error while building tauri application") + .run(|app_handle, event| match event { + tauri::RunEvent::ExitRequested { api, code, .. } => { + if code.is_none() || matches!(code, Some(0)) { + api.prevent_exit() + } else if code.is_some() { + let state = app_handle.state::(); + state.cancel(); + } + } + _ => {} + }); +} diff --git a/packages/hoppscotch-agent/src-tauri/src/main.rs b/packages/hoppscotch-agent/src-tauri/src/main.rs new file mode 100644 index 000000000..bf757fbd5 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + hoppscotch_agent_lib::run() +} diff --git a/packages/hoppscotch-agent/src-tauri/src/model.rs b/packages/hoppscotch-agent/src-tauri/src/model.rs new file mode 100644 index 000000000..68589fe93 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/model.rs @@ -0,0 +1,47 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +/// Single instance payload. +#[derive(Clone, Serialize)] +pub struct Payload { + args: Vec, + cwd: String, +} + +impl Payload { + pub fn new(args: Vec, cwd: String) -> Self { + Self { args, cwd } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HandshakeResponse { + #[allow(non_snake_case)] + pub __hoppscotch__agent__: bool, + + pub status: String, + pub agent_version: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ConfirmedRegistrationRequest { + pub registration: String, + + /// base16 (lowercase) encoded public key shared by the client + /// to the agent so that the agent can establish a shared secret + /// which will be used to encrypt traffic between agent + /// and client after registration + pub client_public_key_b16: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AuthKeyResponse { + pub auth_key: String, + pub created_at: DateTime, + + /// base16 (lowercase) encoded public key shared by the + /// agent so that the client can establish a shared secret + /// which will be used to encrypt traffic between agent + /// and client after registration + pub agent_public_key_b16: String, +} diff --git a/packages/hoppscotch-agent/src-tauri/src/route.rs b/packages/hoppscotch-agent/src-tauri/src/route.rs new file mode 100644 index 000000000..290668129 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/route.rs @@ -0,0 +1,28 @@ +use axum::{ + routing::{get, post}, + Router, +}; +use std::sync::Arc; +use tauri::AppHandle; + +use crate::{controller, state::AppState}; + +pub fn route(state: Arc, app_handle: AppHandle) -> Router { + Router::new() + .route("/handshake", get(controller::handshake)) + .route( + "/receive-registration", + post(controller::receive_registration), + ) + .route( + "/verify-registration", + post(controller::verify_registration), + ) + .route( + "/registered-handshake", + get(controller::registered_handshake), + ) + .route("/request", post(controller::run_request)) + .route("/cancel-request/:req_id", post(controller::cancel_request)) + .with_state((state, app_handle)) +} diff --git a/packages/hoppscotch-agent/src-tauri/src/server.rs b/packages/hoppscotch-agent/src-tauri/src/server.rs new file mode 100644 index 000000000..1e9210fae --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/server.rs @@ -0,0 +1,34 @@ +use axum::Router; +use std::sync::Arc; +use tokio_util::sync::CancellationToken; +use tower_http::cors::CorsLayer; + +use crate::route; +use crate::state::AppState; + +pub async fn run_server( + state: Arc, + cancellation_token: CancellationToken, + app_handle: tauri::AppHandle, +) { + let cors = CorsLayer::permissive(); + + let app = Router::new() + .merge(route::route(state, app_handle)) + .layer(cors); + + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 9119)); + + println!("Server running on http://{}", addr); + + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + + axum::serve(listener, app.into_make_service()) + .with_graceful_shutdown(async move { + cancellation_token.cancelled().await; + }) + .await + .unwrap(); + + println!("Server shut down"); +} diff --git a/packages/hoppscotch-agent/src-tauri/src/state.rs b/packages/hoppscotch-agent/src-tauri/src/state.rs new file mode 100644 index 000000000..c8e8c928b --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/state.rs @@ -0,0 +1,161 @@ +use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; +use axum::body::Bytes; +use chrono::{DateTime, Utc}; +use dashmap::DashMap; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use tauri_plugin_store::StoreExt; +use tokio::sync::RwLock; +use tokio_util::sync::CancellationToken; + +use crate::{ + error::{AgentError, AgentResult}, + global::{AGENT_STORE, REGISTRATIONS}, +}; + +/// Describes one registered app instance +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Registration { + pub registered_at: DateTime, + + /// base16 (lowercase) encoded shared secret that the client + /// and agent established during registration that is used + /// to encrypt traffic between them + pub shared_secret_b16: String, +} + +#[derive(Default)] +pub struct AppState { + /// The active registration code that is being registered. + pub active_registration_code: RwLock>, + + /// Cancellation Tokens for the running requests + pub cancellation_tokens: DashMap, + + /// Registrations against the agent, the key is the auth + /// token associated to the registration + registrations: DashMap, +} + +impl AppState { + pub fn new(app_handle: tauri::AppHandle) -> AgentResult { + let store = app_handle.store(AGENT_STORE)?; + + // Try loading and parsing registrations from the store, if that failed, + // load the default list + let registrations = store + .get(REGISTRATIONS) + .and_then(|val| serde_json::from_value(val.clone()).ok()) + .unwrap_or_else(|| DashMap::new()); + + // Try to save the latest registrations list + let _ = store.set(REGISTRATIONS, serde_json::to_value(®istrations)?); + let _ = store.save(); + + Ok(Self { + active_registration_code: RwLock::new(None), + cancellation_tokens: DashMap::new(), + registrations, + }) + } + + /// Gets you a readonly reference to the registrations list + /// NOTE: Although DashMap API allows you to update the list from an immutable + /// reference, you shouldn't do it for registrations as `update_registrations` + /// performs save operation that needs to be done and should be used instead + pub fn get_registrations(&self) -> &DashMap { + &self.registrations + } + + /// Provides you an opportunity to update the registrations list + /// and also persists the data to the disk. + /// This function bypasses `store.reload()` to avoid issues from stale or inconsistent + /// data on disk. By relying solely on the in-memory `self.registrations`, + /// we make sure that updates are applied based on the most recent changes in memory. + pub fn update_registrations( + &self, + app_handle: tauri::AppHandle, + update_func: impl FnOnce(&DashMap), + ) -> Result<(), AgentError> { + update_func(&self.registrations); + + let store = app_handle.store(AGENT_STORE)?; + + if store.has(REGISTRATIONS) { + // We've confirmed `REGISTRATIONS` exists in the store + store + .delete(REGISTRATIONS) + .then_some(()) + .ok_or(AgentError::RegistrationClearError)?; + } else { + log::debug!("`REGISTRATIONS` key not found in store; continuing with update."); + } + + // Since we've established `self.registrations` as the source of truth, + // we avoid reloading the store from disk and instead choose to override it. + + store.set( + REGISTRATIONS, + serde_json::to_value(self.registrations.clone())?, + ); + + // Explicitly save the changes + store.save()?; + + Ok(()) + } + + /// Clear all the registrations + pub fn clear_registrations(&self, app_handle: tauri::AppHandle) -> Result<(), AgentError> { + Ok(self.update_registrations(app_handle, |registrations| registrations.clear())?) + } + + pub async fn validate_registration(&self, registration: &str) -> bool { + self.active_registration_code.read().await.as_deref() == Some(registration) + } + + pub fn remove_cancellation_token(&self, req_id: usize) -> Option<(usize, CancellationToken)> { + self.cancellation_tokens.remove(&req_id) + } + + pub fn add_cancellation_token(&self, req_id: usize, cancellation_tokens: CancellationToken) { + self.cancellation_tokens.insert(req_id, cancellation_tokens); + } + + pub fn validate_access(&self, auth_key: &str) -> bool { + self.registrations.get(auth_key).is_some() + } + + pub fn validate_access_and_get_data( + &self, + auth_key: &str, + nonce: &str, + data: &Bytes, + ) -> Option + where + T: DeserializeOwned, + { + if let Some(registration) = self.registrations.get(auth_key) { + let key: [u8; 32] = base16::decode(®istration.shared_secret_b16).ok()?[0..32] + .try_into() + .ok()?; + + let nonce: [u8; 12] = base16::decode(nonce).ok()?[0..12].try_into().ok()?; + + let cipher = Aes256Gcm::new(&key.into()); + + let data = data.iter().cloned().collect::>(); + + let plain_data = cipher.decrypt(&nonce.into(), data.as_slice()).ok()?; + + serde_json::from_reader(plain_data.as_slice()).ok() + } else { + None + } + } + + pub fn get_registration_info(&self, auth_key: &str) -> Option { + self.registrations + .get(auth_key) + .map(|reference| reference.value().clone()) + } +} diff --git a/packages/hoppscotch-agent/src-tauri/src/tray.rs b/packages/hoppscotch-agent/src-tauri/src/tray.rs new file mode 100644 index 000000000..1b1f62e81 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/tray.rs @@ -0,0 +1,91 @@ +use crate::state::AppState; +use lazy_static::lazy_static; +use std::sync::Arc; +use tauri::{ + image::Image, + menu::{MenuBuilder, MenuItem}, + tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}, + AppHandle, Manager, +}; + +const TRAY_ICON_DATA: &'static [u8] = include_bytes!("../icons/tray_icon.png"); + +lazy_static! { + static ref TRAY_ICON: Image<'static> = Image::from_bytes(TRAY_ICON_DATA).unwrap(); +} + +pub fn create_tray(app: &AppHandle) -> tauri::Result<()> { + let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?; + let clear_registrations = MenuItem::with_id( + app, + "clear_registrations", + "Clear Registrations", + true, + None::<&str>, + )?; + + let pkg_info = app.package_info(); + let app_name = pkg_info.name.clone(); + let app_version = pkg_info.version.clone(); + + let app_name_item = MenuItem::with_id(app, "app_name", app_name, false, None::<&str>)?; + let app_version_item = MenuItem::with_id( + app, + "app_version", + format!("Version: {}", app_version), + false, + None::<&str>, + )?; + + let menu = MenuBuilder::new(app) + .item(&app_name_item) + .item(&app_version_item) + .separator() + .item(&clear_registrations) + .item(&quit_i) + .build()?; + + let _ = TrayIconBuilder::with_id("hopp-tray") + .tooltip("Hoppscotch Agent") + .icon(if cfg!(target_os = "macos") { + TRAY_ICON.clone() + } else { + app.default_window_icon().unwrap().clone() + }) + .icon_as_template(cfg!(target_os = "macos")) + .menu(&menu) + .menu_on_left_click(true) + .on_menu_event(move |app, event| match event.id.as_ref() { + "quit" => { + log::info!("Exiting the agent..."); + app.exit(-1); + } + "clear_registrations" => { + let app_state = app.state::>(); + + app_state + .clear_registrations(app.clone()) + .expect("Invariant violation: Failed to clear registrations"); + } + _ => { + log::warn!("Unhandled menu event: {:?}", event.id); + } + }) + .on_tray_icon_event(|tray, event| { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = event + { + let app = tray.app_handle(); + if let Some(window) = app.get_webview_window("main") { + let _ = window.show(); + let _ = window.set_focus(); + } + } + }) + .build(app); + + Ok(()) +} diff --git a/packages/hoppscotch-agent/src-tauri/src/updater.rs b/packages/hoppscotch-agent/src-tauri/src/updater.rs new file mode 100644 index 000000000..490e1f7aa --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/updater.rs @@ -0,0 +1,67 @@ +use tauri::Manager; +use tauri_plugin_dialog::DialogExt; +use tauri_plugin_dialog::MessageDialogButtons; +use tauri_plugin_dialog::MessageDialogKind; + +#[cfg(feature = "portable")] +use {crate::dialog, crate::util, native_dialog::MessageType}; + +pub async fn check_and_install_updates( + app: tauri::AppHandle, + updater: tauri_plugin_updater::Updater, +) { + let update = updater.check().await; + + if let Ok(Some(update)) = update { + #[cfg(not(feature = "portable"))] + { + let do_update = app + .dialog() + .message(format!( + "Update to {} is available!{}", + update.version, + update + .body + .clone() + .map(|body| format!("\n\nRelease Notes: {}", body)) + .unwrap_or("".into()) + )) + .title("Update Available") + .kind(MessageDialogKind::Info) + .buttons(MessageDialogButtons::OkCancelCustom( + "Update".to_string(), + "Cancel".to_string(), + )) + .blocking_show(); + + if do_update { + let _ = update.download_and_install(|_, _| {}, || {}).await; + + tauri::process::restart(&app.env()); + } + } + #[cfg(feature = "portable")] + { + let download_url = "https://hoppscotch.com/download"; + let message = format!( + "An update (version {}) is available for the Hoppscotch Agent.\n\nPlease download the latest portable version from our website.", + update.version + ); + + dialog::info(&message); + + if dialog::confirm( + "Open Download Page", + "Would you like to open the download page in your browser?", + MessageType::Info, + ) { + if let None = util::open_link(download_url) { + dialog::error(&format!( + "Failed to open download page. Please visit {}", + download_url + )); + } + } + } + } +} diff --git a/packages/hoppscotch-agent/src-tauri/src/util.rs b/packages/hoppscotch-agent/src-tauri/src/util.rs new file mode 100644 index 000000000..e383f0b93 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/util.rs @@ -0,0 +1,86 @@ +use std::process::{Command, Stdio}; + +use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, KeyInit}; +use axum::{ + body::Body, + response::{IntoResponse, Response}, +}; +use rand::rngs::OsRng; +use serde::Serialize; + +pub fn open_link(link: &str) -> Option<()> { + let null = Stdio::null(); + + #[cfg(target_os = "windows")] + { + Command::new("rundll32") + .args(["url.dll,FileProtocolHandler", link]) + .stdout(null) + .spawn() + .ok() + .map(|_| ()) + } + + #[cfg(target_os = "macos")] + { + Command::new("open") + .arg(link) + .stdout(null) + .spawn() + .ok() + .map(|_| ()) + } + + #[cfg(target_os = "linux")] + { + Command::new("xdg-open") + .arg(link) + .stdout(null) + .spawn() + .ok() + .map(|_| ()) + } + + #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))] + { + None + } +} + +#[derive(Debug)] +pub struct EncryptedJson { + pub key_b16: String, + pub data: T, +} + +impl IntoResponse for EncryptedJson +where + T: Serialize, +{ + fn into_response(self) -> Response { + let serialized_response = serde_json::to_vec(&self.data) + .expect("Failed serializing response to vec for encryption"); + + let key: [u8; 32] = base16::decode(&self.key_b16).unwrap()[0..32] + .try_into() + .unwrap(); + + let cipher = Aes256Gcm::new(&key.into()); + + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); + + let nonce_b16 = base16::encode_lower(&nonce); + + let encrypted_response = cipher + .encrypt(&nonce, serialized_response.as_slice()) + .expect("Failed encrypting response"); + + let mut response = Response::new(Body::from(encrypted_response)); + let response_headers = response.headers_mut(); + + response_headers.insert("Content-Type", "application/octet-stream".parse().unwrap()); + response_headers.insert("X-Hopp-Nonce", nonce_b16.parse().unwrap()); + + response + } +} diff --git a/packages/hoppscotch-agent/src-tauri/src/webview/error.rs b/packages/hoppscotch-agent/src-tauri/src/webview/error.rs new file mode 100644 index 000000000..7bfeff524 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/webview/error.rs @@ -0,0 +1,15 @@ +use std::io; + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum WebViewError { + #[error("Failed to open URL: {0}")] + UrlOpen(#[from] io::Error), + #[error("Failed to download WebView2 installer: {0}")] + Download(String), + #[error("WebView2 installation failed: {0}")] + Installation(String), + #[error("Failed during request: {0}")] + Request(#[from] tauri_plugin_http::reqwest::Error), +} diff --git a/packages/hoppscotch-agent/src-tauri/src/webview/mod.rs b/packages/hoppscotch-agent/src-tauri/src/webview/mod.rs new file mode 100644 index 000000000..f66279b44 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/src/webview/mod.rs @@ -0,0 +1,212 @@ +/// The WebView2 Runtime is a critical dependency for Tauri applications on Windows. +/// We need to check for its presence, see [Source: GitHub Issue #59 - Portable windows build](https://github.com/tauri-apps/tauri-action/issues/59#issuecomment-827142638) +/// +/// > "Tauri requires an installer if you define app resources, external binaries or running on environments that do not have Webview2 runtime installed. So I don't think it's a good idea to have a "portable" option since a Tauri binary itself isn't 100% portable." +/// +/// The approach for checking WebView2 installation is based on Microsoft's official documentation, which states: +/// +/// > ###### Detect if a WebView2 Runtime is already installed +/// > +/// > To verify that a WebView2 Runtime is installed, use one of the following approaches: +/// > +/// > * Approach 1: Inspect the `pv (REG_SZ)` regkey for the WebView2 Runtime at both of the following registry locations. The `HKEY_LOCAL_MACHINE` regkey is used for _per-machine_ install. The `HKEY_CURRENT_USER` regkey is used for _per-user_ install. +/// > +/// > For WebView2 applications, at least one of these regkeys must be present and defined with a version greater than 0.0.0.0. If neither regkey exists, or if only one of these regkeys exists but its value is `null`, an empty string, or 0.0.0.0, this means that the WebView2 Runtime isn't installed on the client. Inspect these regkeys to detect whether the WebView2 Runtime is installed, and to get the version of the WebView2 Runtime. Find `pv (REG_SZ)` at the following two locations. +/// > +/// > The two registry locations to inspect on 64-bit Windows: +/// > +/// > ``` +/// > HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5} +/// > +/// > HKEY_CURRENT_USER\Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5} +/// > ``` +/// > +/// > The two registry locations to inspect on 32-bit Windows: +/// > +/// > ``` +/// > HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5} +/// > +/// > HKEY_CURRENT_USER\Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5} +/// > ``` +/// > +/// > * Approach 2: Run [GetAvailableCoreWebView2BrowserVersionString](/microsoft-edge/webview2/reference/win32/webview2-idl#getavailablecorewebview2browserversionstring) and evaluate whether the `versionInfo` is `nullptr`. `nullptr` indicates that the WebView2 Runtime isn't installed. This API returns version information for the WebView2 Runtime or for any installed preview channels of Microsoft Edge (Beta, Dev, or Canary). +/// +/// See: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution?tabs=dotnetcsharp#detect-if-a-webview2-runtime-is-already-installed +/// +/// Our implementation uses Approach 1, checking both the 32-bit (WOW6432Node) and 64-bit registry locations +/// to make sure we have critical dependencis compatibility with different system architectures. +pub mod error; + +use std::{io, ops::Not}; + +use native_dialog::MessageType; + +use crate::{dialog, util}; +use error::WebViewError; + +#[cfg(windows)] +use { + std::io::Cursor, + std::process::Command, + tauri_plugin_http::reqwest, + tempfile::TempDir, + winreg::{ + enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}, + RegKey, + }, +}; + +const TAURI_WEBVIEW_REF: &str = "https://v2.tauri.app/references/webview-versions/"; +const WINDOWS_WEBVIEW_REF: &str = + "https://developer.microsoft.com/microsoft-edge/webview2/#download-section"; + +fn is_available() -> bool { + #[cfg(windows)] + { + const KEY_WOW64: &str = r"SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"; + const KEY: &str = + r"SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"; + + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + let hkcu = RegKey::predef(HKEY_CURRENT_USER); + + [ + hklm.open_subkey(KEY_WOW64), + hkcu.open_subkey(KEY_WOW64), + hklm.open_subkey(KEY), + hkcu.open_subkey(KEY), + ] + .into_iter() + .any(|result| result.is_ok()) + } + + #[cfg(not(windows))] + { + true + } +} + +fn open_install_website() -> Result<(), WebViewError> { + let url = if cfg!(windows) { + WINDOWS_WEBVIEW_REF + } else { + TAURI_WEBVIEW_REF + }; + + util::open_link(url).map(|_| ()).ok_or_else(|| { + WebViewError::UrlOpen(io::Error::new( + io::ErrorKind::Other, + "Failed to open browser to WebView download section", + )) + }) +} + +#[cfg(windows)] +async fn install() -> Result<(), WebViewError> { + const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703"; + const DEFAULT_FILENAME: &str = "MicrosoftEdgeWebview2Setup.exe"; + + let client = reqwest::Client::builder() + .user_agent("Hoppscotch Agent") + .gzip(true) + .build()?; + + let response = client.get(WEBVIEW2_BOOTSTRAPPER_URL).send().await?; + + if !response.status().is_success() { + return Err(WebViewError::Download(format!( + "Failed to download WebView2 bootstrapper. Status: {}", + response.status() + ))); + } + + let filename = + get_filename_from_response(&response).unwrap_or_else(|| DEFAULT_FILENAME.to_owned()); + + let tmp_dir = TempDir::with_prefix("WebView-setup-")?; + let installer_path = tmp_dir.path().join(filename); + + let content = response.bytes().await?; + { + let mut file = std::fs::File::create(&installer_path)?; + io::copy(&mut Cursor::new(content), &mut file)?; + } + + let status = Command::new(&installer_path).args(["/install"]).status()?; + + if !status.success() { + return Err(WebViewError::Installation(format!( + "Installer exited with code `{}`.", + status.code().unwrap_or(-1) + ))); + } + + Ok(()) +} + +#[cfg(windows)] +fn get_filename_from_response(response: &reqwest::Response) -> Option { + response + .headers() + .get("content-disposition") + .and_then(|value| value.to_str().ok()) + .and_then(|value| value.split("filename=").last()) + .map(|name| name.trim().replace('\"', "")) + .or_else(|| { + response + .url() + .path_segments() + .and_then(|segments| segments.last()) + .map(|name| name.to_string()) + }) + .filter(|name| !name.is_empty()) +} + +#[cfg(not(windows))] +async fn install() -> Result<(), WebViewError> { + Err(WebViewError::Installation( + "Unable to auto-install WebView. Please refer to https://v2.tauri.app/references/webview-versions/".to_string(), + )) +} + +pub fn init_webview() { + if is_available() { + return; + } + + if dialog::confirm( + "WebView Error", + "WebView is required for this application to work.\n\n\ + Do you want to install it?", + MessageType::Error, + ) + .not() + { + log::warn!("Declined to setup WebView."); + + std::process::exit(1); + } + + if let Err(e) = tauri::async_runtime::block_on(install()) { + dialog::error(&format!( + "Failed to install WebView: {}\n\n\ + Please install it manually from webpage that should open when you click 'Ok'.\n\n\ + If that doesn't work, please visit Microsoft Edge Webview2 download section.", + e + )); + + if let Err(e) = open_install_website() { + log::warn!("Failed to launch WebView website:\n{}", e); + } + + std::process::exit(1); + } + + if is_available().not() { + dialog::panic( + "Unable to setup WebView:\n\n\ + Please install it manually and relaunch the application.\n\ + https://developer.microsoft.com/microsoft-edge/webview2/#download-section", + ); + } +} diff --git a/packages/hoppscotch-agent/src-tauri/tauri.conf.json b/packages/hoppscotch-agent/src-tauri/tauri.conf.json new file mode 100644 index 000000000..13dfb3d02 --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/tauri.conf.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://schema.tauri.app/config/2.0.0-rc", + "productName": "Hoppscotch Agent", + "version": "0.1.3", + "identifier": "io.hoppscotch.agent", + "build": { + "beforeDevCommand": "pnpm dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "pnpm build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "title": "Hoppscotch Agent", + "width": 600, + "height": 400, + "center": true, + "resizable": false, + "maximizable": false, + "minimizable": false, + "focus": true, + "alwaysOnTop": true, + "create": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "createUpdaterArtifacts": true + }, + "plugins": { + "updater": { + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDRBQzgxQjc3MzJCMjZENEMKUldSTWJiSXlkeHZJU3EvQW1abFVlREVhRDNlM0ZhOVJYaHN4M0FpbXZhcUFzSVdVbG84RWhPa1AK", + "endpoints": [ + "https://releases.hoppscotch.com/hoppscotch-agent.json" + ] + } + } +} diff --git a/packages/hoppscotch-agent/src-tauri/tauri.portable.conf.json b/packages/hoppscotch-agent/src-tauri/tauri.portable.conf.json new file mode 100644 index 000000000..1797f52de --- /dev/null +++ b/packages/hoppscotch-agent/src-tauri/tauri.portable.conf.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://schema.tauri.app/config/2.0.0-rc", + "productName": "Hoppscotch Agent Portable", + "version": "0.1.3", + "identifier": "io.hoppscotch.agent", + "build": { + "beforeDevCommand": "pnpm dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "pnpm build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "title": "Hoppscotch Agent Portable", + "width": 600, + "height": 400, + "center": true, + "resizable": false, + "maximizable": false, + "minimizable": false, + "focus": true, + "alwaysOnTop": true, + "create": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": false, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "createUpdaterArtifacts": false + }, + "plugins": { + "updater": { + "active": false + } + } +} diff --git a/packages/hoppscotch-agent/src/App.vue b/packages/hoppscotch-agent/src/App.vue new file mode 100644 index 000000000..f58644c25 --- /dev/null +++ b/packages/hoppscotch-agent/src/App.vue @@ -0,0 +1,71 @@ + + + diff --git a/packages/hoppscotch-agent/src/index.css b/packages/hoppscotch-agent/src/index.css new file mode 100644 index 000000000..b5c61c956 --- /dev/null +++ b/packages/hoppscotch-agent/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/packages/hoppscotch-agent/src/main.ts b/packages/hoppscotch-agent/src/main.ts new file mode 100644 index 000000000..9380216e6 --- /dev/null +++ b/packages/hoppscotch-agent/src/main.ts @@ -0,0 +1,13 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './index.css' + +import { plugin as HoppUI } from "@hoppscotch/ui" + +import "@hoppscotch/ui/themes.css" + +import "@hoppscotch/ui/style.css" + +createApp(App) + .use(HoppUI) + .mount('#app') diff --git a/packages/hoppscotch-agent/src/vite-env.d.ts b/packages/hoppscotch-agent/src/vite-env.d.ts new file mode 100644 index 000000000..fc812394b --- /dev/null +++ b/packages/hoppscotch-agent/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/packages/hoppscotch-agent/tailwind.config.js b/packages/hoppscotch-agent/tailwind.config.js new file mode 100644 index 000000000..8d2a79d66 --- /dev/null +++ b/packages/hoppscotch-agent/tailwind.config.js @@ -0,0 +1,6 @@ +import preset from '@hoppscotch/ui/ui-preset' + +export default { + content: ['src/**/*.{vue,html}'], + presets: [preset] +} diff --git a/packages/hoppscotch-agent/tsconfig.json b/packages/hoppscotch-agent/tsconfig.json new file mode 100644 index 000000000..f82888f3d --- /dev/null +++ b/packages/hoppscotch-agent/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/hoppscotch-agent/tsconfig.node.json b/packages/hoppscotch-agent/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/packages/hoppscotch-agent/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/hoppscotch-agent/vite.config.ts b/packages/hoppscotch-agent/vite.config.ts new file mode 100644 index 000000000..9a9a1549b --- /dev/null +++ b/packages/hoppscotch-agent/vite.config.ts @@ -0,0 +1,50 @@ +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; +import tailwindcss from 'tailwindcss'; +import autoprefixer from 'autoprefixer'; +import path from 'path'; +import Icons from "unplugin-icons/vite"; + +const host = process.env.TAURI_DEV_HOST; + +// https://vitejs.dev/config/ +export default defineConfig(async () => ({ + plugins: [ + vue(), + Icons({}) + ], + css: { + postcss: { + plugins: [ + tailwindcss, + autoprefixer, + ], + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './src') + } + }, + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1420, + strictPort: true, + host: host || false, + hmr: host + ? { + protocol: "ws", + host, + port: 1421, + } + : undefined, + watch: { + // 3. tell vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], + }, + }, +})); diff --git a/packages/hoppscotch-backend/backend.Caddyfile b/packages/hoppscotch-backend/backend.Caddyfile index d5bd13244..523516b8b 100644 --- a/packages/hoppscotch-backend/backend.Caddyfile +++ b/packages/hoppscotch-backend/backend.Caddyfile @@ -1,3 +1,8 @@ +{ + admin off + persist_config off +} + :80 :3170 { reverse_proxy localhost:8080 } diff --git a/packages/hoppscotch-backend/cross-env b/packages/hoppscotch-backend/cross-env deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/hoppscotch-backend/eslint b/packages/hoppscotch-backend/eslint deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index 0cbc17d4a..61e34587c 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -1,10 +1,14 @@ { "name": "hoppscotch-backend", - "version": "2024.7.0", + "version": "2024.11.0", "description": "", "author": "", "private": true, "license": "UNLICENSED", + "files": [ + "prisma", + "dist" + ], "scripts": { "prebuild": "rimraf dist", "build": "nest build", @@ -24,87 +28,86 @@ "do-test": "pnpm run test" }, "dependencies": { - "@apollo/server": "4.9.5", - "@nestjs-modules/mailer": "1.9.1", - "@nestjs/apollo": "12.0.9", - "@nestjs/common": "10.2.7", - "@nestjs/config": "3.1.1", - "@nestjs/core": "10.2.7", - "@nestjs/graphql": "12.0.9", - "@nestjs/jwt": "10.1.1", - "@nestjs/passport": "10.0.2", - "@nestjs/platform-express": "10.2.7", - "@nestjs/schedule": "4.0.1", - "@nestjs/swagger": "7.4.0", + "@apollo/server": "4.11.0", + "@nestjs-modules/mailer": "2.0.2", + "@nestjs/apollo": "12.2.0", + "@nestjs/common": "10.4.4", + "@nestjs/config": "3.2.3", + "@nestjs/core": "10.4.4", + "@nestjs/graphql": "12.2.0", + "@nestjs/jwt": "10.2.0", + "@nestjs/passport": "10.0.3", + "@nestjs/platform-express": "10.4.4", + "@nestjs/schedule": "4.1.1", + "@nestjs/swagger": "7.4.2", "@nestjs/terminus": "10.2.3", - "@nestjs/throttler": "5.0.1", - "@prisma/client": "5.8.1", - "argon2": "0.30.3", - "bcrypt": "5.1.0", + "@nestjs/throttler": "6.2.1", + "@prisma/client": "5.20.0", + "argon2": "0.41.1", + "bcrypt": "5.1.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", - "cookie": "0.5.0", - "cookie-parser": "1.4.6", - "cron": "3.1.6", - "express": "4.18.2", - "express-session": "1.17.3", - "fp-ts": "2.13.1", - "graphql": "16.8.1", - "graphql-query-complexity": "0.12.0", - "graphql-redis-subscriptions": "2.6.0", + "cookie": "1.0.0", + "cookie-parser": "1.4.7", + "cron": "3.1.7", + "express": "4.21.1", + "express-session": "1.18.1", + "fp-ts": "2.16.9", + "graphql": "16.9.0", + "graphql-query-complexity": "1.0.0", + "graphql-redis-subscriptions": "2.6.1", "graphql-subscriptions": "2.0.0", - "handlebars": "4.7.7", - "io-ts": "2.2.16", - "luxon": "3.2.1", - "nodemailer": "6.9.1", - "passport": "0.6.0", + "handlebars": "4.7.8", + "io-ts": "2.2.21", + "luxon": "3.5.0", + "nodemailer": "6.9.15", + "passport": "0.7.0", "passport-github2": "0.1.12", "passport-google-oauth20": "2.0.0", "passport-jwt": "4.0.1", "passport-local": "1.0.0", - "passport-microsoft": "1.0.0", - "posthog-node": "3.6.3", - "prisma": "5.8.1", - "reflect-metadata": "0.1.13", - "rimraf": "3.0.2", - "rxjs": "7.6.0" + "passport-microsoft": "2.1.0", + "posthog-node": "4.2.0", + "prisma": "5.20.0", + "reflect-metadata": "0.2.2", + "rimraf": "6.0.1", + "rxjs": "7.8.1" }, "devDependencies": { - "@nestjs/cli": "10.2.1", - "@nestjs/schematics": "10.0.3", - "@nestjs/testing": "10.2.7", - "@relmify/jest-fp-ts": "2.0.2", - "@types/argon2": "0.15.0", - "@types/bcrypt": "5.0.0", - "@types/cookie": "0.5.1", - "@types/cookie-parser": "1.4.3", - "@types/express": "4.17.14", - "@types/jest": "29.4.0", - "@types/luxon": "3.2.0", - "@types/node": "18.11.10", - "@types/nodemailer": "6.4.7", - "@types/passport-github2": "1.2.5", - "@types/passport-google-oauth20": "2.0.11", - "@types/passport-jwt": "3.0.8", - "@types/passport-microsoft": "0.0.0", - "@types/supertest": "2.0.12", - "@typescript-eslint/eslint-plugin": "5.45.0", - "@typescript-eslint/parser": "5.45.0", + "@nestjs/cli": "10.4.5", + "@nestjs/schematics": "10.1.4", + "@nestjs/testing": "10.4.4", + "@relmify/jest-fp-ts": "2.1.1", + "@types/bcrypt": "5.0.2", + "@types/cookie": "0.6.0", + "@types/cookie-parser": "1.4.7", + "@types/express": "5.0.0", + "@types/jest": "29.5.13", + "@types/luxon": "3.4.2", + "@types/node": "22.7.5", + "@types/nodemailer": "6.4.16", + "@types/passport-github2": "1.2.9", + "@types/passport-google-oauth20": "2.0.16", + "@types/passport-jwt": "4.0.1", + "@types/passport-microsoft": "1.0.3", + "@types/supertest": "6.0.2", + "@typescript-eslint/eslint-plugin": "8.8.1", + "@typescript-eslint/parser": "8.8.1", "cross-env": "7.0.3", - "eslint": "8.29.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-prettier": "4.2.1", - "jest": "29.4.1", - "jest-mock-extended": "3.0.1", + "eslint": "8.57.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-prettier": "5.2.1", + "jest": "29.7.0", + "jest-mock-extended": "4.0.0-beta1", "jwt": "link:@types/nestjs/jwt", - "prettier": "2.8.4", + "prettier": "3.3.3", "source-map-support": "0.5.21", - "supertest": "6.3.2", - "ts-jest": "29.0.5", - "ts-loader": "9.4.2", - "ts-node": "10.9.1", - "tsconfig-paths": "4.1.1", - "typescript": "4.9.3" + "supertest": "7.0.0", + "ts-jest": "29.2.5", + "ts-loader": "9.5.1", + "ts-node": "10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "5.5.4" }, "jest": { "moduleFileExtensions": [ diff --git a/packages/hoppscotch-backend/prisma/migrations/20240725043411_infra_config_encryption/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20240725043411_infra_config_encryption/migration.sql new file mode 100644 index 000000000..71cf28a01 --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20240725043411_infra_config_encryption/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "InfraConfig" ADD COLUMN "isEncrypted" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/hoppscotch-backend/prisma/migrations/20241118054346_infra_config_sync_with_env_file/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20241118054346_infra_config_sync_with_env_file/migration.sql new file mode 100644 index 000000000..23ccdb576 --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20241118054346_infra_config_sync_with_env_file/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "InfraConfig" DROP COLUMN "active", +ADD COLUMN "lastSyncedEnvFileValue" TEXT; diff --git a/packages/hoppscotch-backend/prisma/schema.prisma b/packages/hoppscotch-backend/prisma/schema.prisma index b0731c957..45d177372 100644 --- a/packages/hoppscotch-backend/prisma/schema.prisma +++ b/packages/hoppscotch-backend/prisma/schema.prisma @@ -214,12 +214,13 @@ enum TeamMemberRole { } model InfraConfig { - id String @id @default(cuid()) - name String @unique - value String? - active Boolean @default(true) // Use case: Let's say, Admin wants to disable Google SSO, but doesn't want to delete the config - createdOn DateTime @default(now()) @db.Timestamp(3) - updatedOn DateTime @updatedAt @db.Timestamp(3) + id String @id @default(cuid()) + name String @unique + value String? + lastSyncedEnvFileValue String? + isEncrypted Boolean @default(false) // Use case: Let's say, Admin wants to store a Secret Key, but doesn't want to store it in plain text in `value` column + createdOn DateTime @default(now()) @db.Timestamp(3) + updatedOn DateTime @updatedAt @db.Timestamp(3) } model PersonalAccessToken { diff --git a/packages/hoppscotch-backend/prod_run.mjs b/packages/hoppscotch-backend/prod_run.mjs index 2d4a8c1d9..6b375991b 100644 --- a/packages/hoppscotch-backend/prod_run.mjs +++ b/packages/hoppscotch-backend/prod_run.mjs @@ -39,8 +39,8 @@ const caddyProcess = runChildProcessWithPrefix( 'App/Admin Dashboard Caddy', ); const backendProcess = runChildProcessWithPrefix( - 'pnpm', - ['run', 'start:prod'], + 'node', + ['/dist/backend/dist/main.js'], 'Backend Server', ); diff --git a/packages/hoppscotch-backend/src/admin/admin.service.ts b/packages/hoppscotch-backend/src/admin/admin.service.ts index a55524a87..33e7d8868 100644 --- a/packages/hoppscotch-backend/src/admin/admin.service.ts +++ b/packages/hoppscotch-backend/src/admin/admin.service.ts @@ -231,9 +231,8 @@ export class AdminService { * @returns a count of team members */ async membersCountInTeam(teamID: string) { - const teamMembersCount = await this.teamService.getCountOfMembersInTeam( - teamID, - ); + const teamMembersCount = + await this.teamService.getCountOfMembersInTeam(teamID); return teamMembersCount; } @@ -276,9 +275,8 @@ export class AdminService { * @returns an array team invitations */ async pendingInvitationCountInTeam(teamID: string) { - const invitations = await this.teamInvitationService.getTeamInvitations( - teamID, - ); + const invitations = + await this.teamInvitationService.getTeamInvitations(teamID); return invitations; } @@ -427,7 +425,7 @@ export class AdminService { * Remove a user account by UID * @param userUid User UID * @returns an Either of boolean or error - * @deprecated use removeUserAccounts instead + * */ async removeUserAccount(userUid: string) { const user = await this.userService.findUserById(userUid); @@ -614,9 +612,8 @@ export class AdminService { * @returns an Either of boolean or error */ async revokeTeamInviteByID(inviteID: string) { - const teamInvite = await this.teamInvitationService.revokeInvitation( - inviteID, - ); + const teamInvite = + await this.teamInvitationService.revokeInvitation(inviteID); if (E.isLeft(teamInvite)) return E.left(teamInvite.left); diff --git a/packages/hoppscotch-backend/src/auth/auth.module.ts b/packages/hoppscotch-backend/src/auth/auth.module.ts index 0dc4c737c..8bce41d82 100644 --- a/packages/hoppscotch-backend/src/auth/auth.module.ts +++ b/packages/hoppscotch-backend/src/auth/auth.module.ts @@ -13,8 +13,8 @@ import { MicrosoftStrategy } from './strategies/microsoft.strategy'; import { AuthProvider, authProviderCheck } from './helper'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { + getConfiguredSSOProvidersFromInfraConfig, isInfraConfigTablePopulated, - loadInfraConfiguration, } from 'src/infra-config/helper'; import { InfraConfigModule } from 'src/infra-config/infra-config.module'; @@ -42,8 +42,8 @@ export class AuthModule { return { module: AuthModule }; } - const env = await loadInfraConfiguration(); - const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS; + const allowedAuthProviders = + await getConfiguredSSOProvidersFromInfraConfig(); const providers = [ ...(authProviderCheck(AuthProvider.GOOGLE, allowedAuthProviders) diff --git a/packages/hoppscotch-backend/src/auth/auth.service.spec.ts b/packages/hoppscotch-backend/src/auth/auth.service.spec.ts index 3df559b86..91e636fc8 100644 --- a/packages/hoppscotch-backend/src/auth/auth.service.spec.ts +++ b/packages/hoppscotch-backend/src/auth/auth.service.spec.ts @@ -9,7 +9,6 @@ import { MAGIC_LINK_EXPIRED, VERIFICATION_TOKEN_DATA_NOT_FOUND, USER_NOT_FOUND, - USERS_NOT_FOUND, } from 'src/errors'; import { MailerService } from 'src/mailer/mailer.service'; import { PrismaService } from 'src/prisma/prisma.service'; @@ -18,8 +17,6 @@ import { UserService } from 'src/user/user.service'; import { AuthService } from './auth.service'; import * as O from 'fp-ts/Option'; import { VerifyMagicDto } from './dto/verify-magic.dto'; -import { DateTime } from 'luxon'; -import * as argon2 from 'argon2'; import * as E from 'fp-ts/Either'; import { ConfigService } from '@nestjs/config'; import { InfraConfigService } from 'src/infra-config/infra-config.service'; diff --git a/packages/hoppscotch-backend/src/errors.ts b/packages/hoppscotch-backend/src/errors.ts index 1d424c066..75d0a7b43 100644 --- a/packages/hoppscotch-backend/src/errors.ts +++ b/packages/hoppscotch-backend/src/errors.ts @@ -49,6 +49,18 @@ export const AUTH_PROVIDER_NOT_CONFIGURED = export const ENV_NOT_FOUND_KEY_AUTH_PROVIDERS = '"VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file'; +/** + * Environment variable "DATA_ENCRYPTION_KEY" is not present in .env file + */ +export const ENV_NOT_FOUND_KEY_DATA_ENCRYPTION_KEY = + '"DATA_ENCRYPTION_KEY" is not present in .env file'; + +/** + * Environment variable "DATA_ENCRYPTION_KEY" is changed in .env file + */ +export const ENV_INVALID_DATA_ENCRYPTION_KEY = + '"DATA_ENCRYPTION_KEY" value changed in .env file. Please undo the changes and restart the server'; + /** * Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is empty in .env file */ diff --git a/packages/hoppscotch-backend/src/infra-config/helper.ts b/packages/hoppscotch-backend/src/infra-config/helper.ts index f5909873d..b8e059e44 100644 --- a/packages/hoppscotch-backend/src/infra-config/helper.ts +++ b/packages/hoppscotch-backend/src/infra-config/helper.ts @@ -2,17 +2,26 @@ import { AuthProvider } from 'src/auth/helper'; import { AUTH_PROVIDER_NOT_CONFIGURED, DATABASE_TABLE_NOT_EXIST, + ENV_INVALID_DATA_ENCRYPTION_KEY, } from 'src/errors'; import { PrismaService } from 'src/prisma/prisma.service'; import { InfraConfigEnum } from 'src/types/InfraConfig'; -import { throwErr } from 'src/utils'; +import { decrypt, encrypt, throwErr } from 'src/utils'; import { randomBytes } from 'crypto'; +import { InfraConfig } from '@prisma/client'; export enum ServiceStatus { ENABLE = 'ENABLE', DISABLE = 'DISABLE', } +type DefaultInfraConfig = { + name: InfraConfigEnum; + value: string; + lastSyncedEnvFileValue: string; + isEncrypted: boolean; +}; + const AuthProviderConfigurations = { [AuthProvider.GOOGLE]: [ InfraConfigEnum.GOOGLE_CLIENT_ID, @@ -33,17 +42,18 @@ const AuthProviderConfigurations = { InfraConfigEnum.MICROSOFT_SCOPE, InfraConfigEnum.MICROSOFT_TENANT, ], - [AuthProvider.EMAIL]: !!process.env.MAILER_USE_CUSTOM_CONFIGS - ? [ - InfraConfigEnum.MAILER_SMTP_HOST, - InfraConfigEnum.MAILER_SMTP_PORT, - InfraConfigEnum.MAILER_SMTP_SECURE, - InfraConfigEnum.MAILER_SMTP_USER, - InfraConfigEnum.MAILER_SMTP_PASSWORD, - InfraConfigEnum.MAILER_TLS_REJECT_UNAUTHORIZED, - InfraConfigEnum.MAILER_ADDRESS_FROM, - ] - : [InfraConfigEnum.MAILER_SMTP_URL, InfraConfigEnum.MAILER_ADDRESS_FROM], + [AuthProvider.EMAIL]: + process.env.MAILER_USE_CUSTOM_CONFIGS === 'true' + ? [ + InfraConfigEnum.MAILER_SMTP_HOST, + InfraConfigEnum.MAILER_SMTP_PORT, + InfraConfigEnum.MAILER_SMTP_SECURE, + InfraConfigEnum.MAILER_SMTP_USER, + InfraConfigEnum.MAILER_SMTP_PASSWORD, + InfraConfigEnum.MAILER_TLS_REJECT_UNAUTHORIZED, + InfraConfigEnum.MAILER_ADDRESS_FROM, + ] + : [InfraConfigEnum.MAILER_SMTP_URL, InfraConfigEnum.MAILER_ADDRESS_FROM], }; /** @@ -60,11 +70,18 @@ export async function loadInfraConfiguration() { let environmentObject: Record = {}; infraConfigs.forEach((infraConfig) => { - environmentObject[infraConfig.name] = infraConfig.value; + if (infraConfig.isEncrypted) { + environmentObject[infraConfig.name] = decrypt(infraConfig.value); + } else { + environmentObject[infraConfig.name] = infraConfig.value; + } }); return { INFRA: environmentObject }; } catch (error) { + if (error.code === 'ERR_OSSL_BAD_DECRYPT') + throw new Error(ENV_INVALID_DATA_ENCRYPTION_KEY); + // Prisma throw error if 'Can't reach at database server' OR 'Table does not exist' // Reason for not throwing error is, we want successful build during 'postinstall' and generate dist files return { INFRA: {} }; @@ -75,120 +92,175 @@ export async function loadInfraConfiguration() { * Read the default values from .env file and return them as an array * @returns Array of default infra configs */ -export async function getDefaultInfraConfigs(): Promise< - { name: InfraConfigEnum; value: string }[] -> { +export async function getDefaultInfraConfigs(): Promise { const prisma = new PrismaService(); // Prepare rows for 'infra_config' table with default values (from .env) for each 'name' - const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [ + const configuredSSOProviders = getConfiguredSSOProvidersFromEnvFile(); + const generatedAnalyticsUserId = generateAnalyticsUserId(); + + const infraConfigDefaultObjs: DefaultInfraConfig[] = [ { name: InfraConfigEnum.MAILER_SMTP_ENABLE, value: process.env.MAILER_SMTP_ENABLE ?? 'true', + lastSyncedEnvFileValue: process.env.MAILER_SMTP_ENABLE ?? 'true', + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_USE_CUSTOM_CONFIGS, value: process.env.MAILER_USE_CUSTOM_CONFIGS ?? 'false', + lastSyncedEnvFileValue: process.env.MAILER_USE_CUSTOM_CONFIGS ?? 'false', + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_SMTP_URL, - value: process.env.MAILER_SMTP_URL, + value: encrypt(process.env.MAILER_SMTP_URL), + lastSyncedEnvFileValue: encrypt(process.env.MAILER_SMTP_URL), + isEncrypted: true, }, { name: InfraConfigEnum.MAILER_ADDRESS_FROM, value: process.env.MAILER_ADDRESS_FROM, + lastSyncedEnvFileValue: process.env.MAILER_ADDRESS_FROM, + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_SMTP_HOST, value: process.env.MAILER_SMTP_HOST, + lastSyncedEnvFileValue: process.env.MAILER_SMTP_HOST, + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_SMTP_PORT, value: process.env.MAILER_SMTP_PORT, + lastSyncedEnvFileValue: process.env.MAILER_SMTP_PORT, + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_SMTP_SECURE, value: process.env.MAILER_SMTP_SECURE, + lastSyncedEnvFileValue: process.env.MAILER_SMTP_SECURE, + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_SMTP_USER, value: process.env.MAILER_SMTP_USER, + lastSyncedEnvFileValue: process.env.MAILER_SMTP_USER, + isEncrypted: false, }, { name: InfraConfigEnum.MAILER_SMTP_PASSWORD, - value: process.env.MAILER_SMTP_PASSWORD, + value: encrypt(process.env.MAILER_SMTP_PASSWORD), + lastSyncedEnvFileValue: encrypt(process.env.MAILER_SMTP_PASSWORD), + isEncrypted: true, }, { name: InfraConfigEnum.MAILER_TLS_REJECT_UNAUTHORIZED, value: process.env.MAILER_TLS_REJECT_UNAUTHORIZED, + lastSyncedEnvFileValue: process.env.MAILER_TLS_REJECT_UNAUTHORIZED, + isEncrypted: false, }, { name: InfraConfigEnum.GOOGLE_CLIENT_ID, - value: process.env.GOOGLE_CLIENT_ID, + value: encrypt(process.env.GOOGLE_CLIENT_ID), + lastSyncedEnvFileValue: encrypt(process.env.GOOGLE_CLIENT_ID), + isEncrypted: true, }, { name: InfraConfigEnum.GOOGLE_CLIENT_SECRET, - value: process.env.GOOGLE_CLIENT_SECRET, + value: encrypt(process.env.GOOGLE_CLIENT_SECRET), + lastSyncedEnvFileValue: encrypt(process.env.GOOGLE_CLIENT_SECRET), + isEncrypted: true, }, { name: InfraConfigEnum.GOOGLE_CALLBACK_URL, value: process.env.GOOGLE_CALLBACK_URL, + lastSyncedEnvFileValue: process.env.GOOGLE_CALLBACK_URL, + isEncrypted: false, }, { name: InfraConfigEnum.GOOGLE_SCOPE, value: process.env.GOOGLE_SCOPE, + lastSyncedEnvFileValue: process.env.GOOGLE_SCOPE, + isEncrypted: false, }, { name: InfraConfigEnum.GITHUB_CLIENT_ID, - value: process.env.GITHUB_CLIENT_ID, + value: encrypt(process.env.GITHUB_CLIENT_ID), + lastSyncedEnvFileValue: encrypt(process.env.GITHUB_CLIENT_ID), + isEncrypted: true, }, { name: InfraConfigEnum.GITHUB_CLIENT_SECRET, - value: process.env.GITHUB_CLIENT_SECRET, + value: encrypt(process.env.GITHUB_CLIENT_SECRET), + lastSyncedEnvFileValue: encrypt(process.env.GITHUB_CLIENT_SECRET), + isEncrypted: true, }, { name: InfraConfigEnum.GITHUB_CALLBACK_URL, value: process.env.GITHUB_CALLBACK_URL, + lastSyncedEnvFileValue: process.env.GITHUB_CALLBACK_URL, + isEncrypted: false, }, { name: InfraConfigEnum.GITHUB_SCOPE, value: process.env.GITHUB_SCOPE, + lastSyncedEnvFileValue: process.env.GITHUB_SCOPE, + isEncrypted: false, }, { name: InfraConfigEnum.MICROSOFT_CLIENT_ID, - value: process.env.MICROSOFT_CLIENT_ID, + value: encrypt(process.env.MICROSOFT_CLIENT_ID), + lastSyncedEnvFileValue: encrypt(process.env.MICROSOFT_CLIENT_ID), + isEncrypted: true, }, { name: InfraConfigEnum.MICROSOFT_CLIENT_SECRET, - value: process.env.MICROSOFT_CLIENT_SECRET, + value: encrypt(process.env.MICROSOFT_CLIENT_SECRET), + lastSyncedEnvFileValue: encrypt(process.env.MICROSOFT_CLIENT_SECRET), + isEncrypted: true, }, { name: InfraConfigEnum.MICROSOFT_CALLBACK_URL, value: process.env.MICROSOFT_CALLBACK_URL, + lastSyncedEnvFileValue: process.env.MICROSOFT_CALLBACK_URL, + isEncrypted: false, }, { name: InfraConfigEnum.MICROSOFT_SCOPE, value: process.env.MICROSOFT_SCOPE, + lastSyncedEnvFileValue: process.env.MICROSOFT_SCOPE, + isEncrypted: false, }, { name: InfraConfigEnum.MICROSOFT_TENANT, value: process.env.MICROSOFT_TENANT, + lastSyncedEnvFileValue: process.env.MICROSOFT_TENANT, + isEncrypted: false, }, { name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, - value: getConfiguredSSOProviders(), + value: configuredSSOProviders, + lastSyncedEnvFileValue: configuredSSOProviders, + isEncrypted: false, }, { name: InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION, value: false.toString(), + lastSyncedEnvFileValue: null, + isEncrypted: false, }, { name: InfraConfigEnum.ANALYTICS_USER_ID, - value: generateAnalyticsUserId(), + value: generatedAnalyticsUserId, + lastSyncedEnvFileValue: null, + isEncrypted: false, }, { name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP, value: (await prisma.infraConfig.count()) === 0 ? 'true' : 'false', + lastSyncedEnvFileValue: null, + isEncrypted: false, }, ]; @@ -199,12 +271,11 @@ export async function getDefaultInfraConfigs(): Promise< * Get the missing entries in the 'infra_config' table * @returns Array of InfraConfig */ -export async function getMissingInfraConfigEntries() { +export async function getMissingInfraConfigEntries( + infraConfigDefaultObjs: DefaultInfraConfig[], +) { const prisma = new PrismaService(); - const [dbInfraConfigs, infraConfigDefaultObjs] = await Promise.all([ - prisma.infraConfig.findMany(), - getDefaultInfraConfigs(), - ]); + const dbInfraConfigs = await prisma.infraConfig.findMany(); const missingEntries = infraConfigDefaultObjs.filter( (config) => @@ -214,14 +285,78 @@ export async function getMissingInfraConfigEntries() { return missingEntries; } +/** + * Get the encryption required entries in the 'infra_config' table + * @returns Array of InfraConfig + */ +export async function getEncryptionRequiredInfraConfigEntries( + infraConfigDefaultObjs: DefaultInfraConfig[], +) { + const prisma = new PrismaService(); + const dbInfraConfigs = await prisma.infraConfig.findMany(); + + const requiredEncryption = dbInfraConfigs.filter((dbConfig) => { + const defaultConfig = infraConfigDefaultObjs.find( + (config) => config.name === dbConfig.name, + ); + if (!defaultConfig) return false; + return defaultConfig.isEncrypted !== dbConfig.isEncrypted; + }); + + return requiredEncryption; +} + +/** + * Sync the 'infra_config' table with .env file + * @returns Array of InfraConfig + */ +export async function syncInfraConfigWithEnvFile() { + const prisma = new PrismaService(); + const dbInfraConfigs = await prisma.infraConfig.findMany(); + + const updateRequiredObjs: (Partial & { id: string })[] = []; + + for (const dbConfig of dbInfraConfigs) { + let envValue = process.env[dbConfig.name]; + + // lastSyncedEnvFileValue null check for backward compatibility from 2024.10.2 and below + if (!dbConfig.lastSyncedEnvFileValue && envValue) { + const configValue = dbConfig.isEncrypted ? encrypt(envValue) : envValue; + updateRequiredObjs.push({ + id: dbConfig.id, + value: dbConfig.value === null ? configValue : undefined, + lastSyncedEnvFileValue: configValue, + }); + continue; + } + + // If the value in the database is different from the value in the .env file, means the value in the .env file has been updated + const rawLastSyncedEnvFileValue = dbConfig.isEncrypted + ? decrypt(dbConfig.lastSyncedEnvFileValue) + : dbConfig.lastSyncedEnvFileValue; + + if (rawLastSyncedEnvFileValue != envValue) { + const configValue = dbConfig.isEncrypted ? encrypt(envValue) : envValue; + updateRequiredObjs.push({ + id: dbConfig.id, + value: configValue ?? null, + lastSyncedEnvFileValue: configValue ?? null, + }); + } + } + + return updateRequiredObjs; +} + /** * Verify if 'infra_config' table is loaded with all entries * @returns boolean */ export async function isInfraConfigTablePopulated(): Promise { - const prisma = new PrismaService(); try { - const propsRemainingToInsert = await getMissingInfraConfigEntries(); + const defaultInfraConfigs = await getDefaultInfraConfigs(); + const propsRemainingToInsert = + await getMissingInfraConfigEntries(defaultInfraConfigs); if (propsRemainingToInsert.length > 0) { console.log( @@ -250,10 +385,11 @@ export function stopApp() { } /** - * Get the configured SSO providers + * Get the configured SSO providers from .env file + * @description This function verify if the required parameters for each SSO provider are configured in .env file. Usage on first time setup and reset. * @returns Array of configured SSO providers */ -export function getConfiguredSSOProviders() { +export function getConfiguredSSOProvidersFromEnvFile() { const allowedAuthProviders: string[] = process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(','); let configuredAuthProviders: string[] = []; @@ -264,7 +400,6 @@ export function getConfiguredSSOProviders() { const isConfigured = configParameters.every((configParameter) => { return process.env[configParameter]; }); - if (isConfigured) configuredAuthProviders.push(provider); }; @@ -281,7 +416,47 @@ export function getConfiguredSSOProviders() { console.log( `${unConfiguredAuthProviders.join( ',', - )} SSO auth provider(s) are not configured properly. Do configure them from Admin Dashboard.`, + )} SSO auth provider(s) are not configured properly in .env file. Do configure them from Admin Dashboard.`, + ); + } + + return configuredAuthProviders.join(','); +} + +/** + * Get the configured SSO providers from 'infra_config' table. + * @description Usage every time the app starts by AuthModule to initiate Strategies. + * @returns Array of configured SSO providers + */ +export async function getConfiguredSSOProvidersFromInfraConfig() { + const env = await loadInfraConfiguration(); + + const allowedAuthProviders: string[] = + env['INFRA'].VITE_ALLOWED_AUTH_PROVIDERS.split(','); + let configuredAuthProviders: string[] = []; + + const addProviderIfConfigured = (provider) => { + const configParameters: string[] = AuthProviderConfigurations[provider]; + + const isConfigured = configParameters.every((configParameter) => { + return env['INFRA'][configParameter]; + }); + if (isConfigured) configuredAuthProviders.push(provider); + }; + + allowedAuthProviders.forEach((provider) => addProviderIfConfigured(provider)); + + if (configuredAuthProviders.length === 0) { + return ''; + } else if (allowedAuthProviders.length !== configuredAuthProviders.length) { + const prisma = new PrismaService(); + await prisma.infraConfig.update({ + where: { name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS }, + data: { value: configuredAuthProviders.join(',') }, + }); + stopApp(); + console.log( + `${configuredAuthProviders.join(',')} SSO auth provider(s) are configured properly. To enable other SSO providers, configure them from Admin Dashboard.`, ); } diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.service.spec.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.service.spec.ts index 707294231..a8576b41e 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.service.spec.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.service.spec.ts @@ -28,7 +28,8 @@ const dbInfraConfigs: dbInfraConfig[] = [ id: '3', name: InfraConfigEnum.GOOGLE_CLIENT_ID, value: 'abcdefghijkl', - active: true, + lastSyncedEnvFileValue: 'abcdefghijkl', + isEncrypted: false, createdOn: INITIALIZED_DATE_CONST, updatedOn: INITIALIZED_DATE_CONST, }, @@ -36,7 +37,8 @@ const dbInfraConfigs: dbInfraConfig[] = [ id: '4', name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, value: 'google', - active: true, + lastSyncedEnvFileValue: 'google', + isEncrypted: false, createdOn: INITIALIZED_DATE_CONST, updatedOn: INITIALIZED_DATE_CONST, }, @@ -62,11 +64,16 @@ describe('InfraConfigService', () => { const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const value = 'true'; + // @ts-ignore + mockPrisma.infraConfig.findUnique.mockResolvedValueOnce({ + isEncrypted: false, + }); mockPrisma.infraConfig.update.mockResolvedValueOnce({ id: '', name, value, - active: true, + lastSyncedEnvFileValue: value, + isEncrypted: false, createdOn: new Date(), updatedOn: new Date(), }); @@ -82,11 +89,16 @@ describe('InfraConfigService', () => { const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const value = 'true'; + // @ts-ignore + mockPrisma.infraConfig.findUnique.mockResolvedValueOnce({ + isEncrypted: false, + }); mockPrisma.infraConfig.update.mockResolvedValueOnce({ id: '', name, value, - active: true, + lastSyncedEnvFileValue: value, + isEncrypted: false, createdOn: new Date(), updatedOn: new Date(), }); @@ -102,11 +114,16 @@ describe('InfraConfigService', () => { const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const value = 'true'; + // @ts-ignore + mockPrisma.infraConfig.findUnique.mockResolvedValueOnce({ + isEncrypted: false, + }); mockPrisma.infraConfig.update.mockResolvedValueOnce({ id: '', name, value, - active: true, + lastSyncedEnvFileValue: value, + isEncrypted: false, createdOn: new Date(), updatedOn: new Date(), }); @@ -120,6 +137,11 @@ describe('InfraConfigService', () => { const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const value = 'true'; + // @ts-ignore + mockPrisma.infraConfig.findUnique.mockResolvedValueOnce({ + isEncrypted: false, + }); + jest.spyOn(helper, 'stopApp').mockReturnValueOnce(); await infraConfigService.update(name, value); @@ -151,7 +173,8 @@ describe('InfraConfigService', () => { id: '', name, value, - active: true, + lastSyncedEnvFileValue: value, + isEncrypted: false, createdOn: new Date(), updatedOn: new Date(), }); diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts index 168f37546..cf4d0ac0b 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts @@ -15,6 +15,8 @@ import { INFRA_CONFIG_OPERATION_NOT_ALLOWED, } from 'src/errors'; import { + decrypt, + encrypt, throwErr, validateSMTPEmail, validateSMTPUrl, @@ -24,8 +26,10 @@ import { ConfigService } from '@nestjs/config'; import { ServiceStatus, getDefaultInfraConfigs, + getEncryptionRequiredInfraConfigEntries, getMissingInfraConfigEntries, stopApp, + syncInfraConfigWithEnvFile, } from './helper'; import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args'; import { AuthProvider } from 'src/auth/helper'; @@ -62,10 +66,50 @@ export class InfraConfigService implements OnModuleInit { */ async initializeInfraConfigTable() { try { - const propsToInsert = await getMissingInfraConfigEntries(); + const defaultInfraConfigs = await getDefaultInfraConfigs(); + + // Adding missing InfraConfigs to the database (with encrypted values) + const propsToInsert = + await getMissingInfraConfigEntries(defaultInfraConfigs); if (propsToInsert.length > 0) { await this.prisma.infraConfig.createMany({ data: propsToInsert }); + } + + // Encrypting previous InfraConfigs that are required to be encrypted + const encryptionRequiredEntries = + await getEncryptionRequiredInfraConfigEntries(defaultInfraConfigs); + + if (encryptionRequiredEntries.length > 0) { + const dbOperations = encryptionRequiredEntries.map((dbConfig) => { + return this.prisma.infraConfig.update({ + where: { name: dbConfig.name }, + data: { value: encrypt(dbConfig.value), isEncrypted: true }, + }); + }); + + await Promise.allSettled(dbOperations); + } + + // Sync the InfraConfigs with the .env file, if .env file updates later on + const envFileChangesRequired = await syncInfraConfigWithEnvFile(); + if (envFileChangesRequired.length > 0) { + const dbOperations = envFileChangesRequired.map((dbConfig) => { + const { id, ...dataObj } = dbConfig; + return this.prisma.infraConfig.update({ + where: { id: dbConfig.id }, + data: dataObj, + }); + }); + await Promise.allSettled(dbOperations); + } + + // Restart the app if needed + if ( + propsToInsert.length > 0 || + encryptionRequiredEntries.length > 0 || + envFileChangesRequired.length > 0 + ) { stopApp(); } } catch (error) { @@ -76,6 +120,7 @@ export class InfraConfigService implements OnModuleInit { // Prisma error code for 'Table does not exist' throwErr(DATABASE_TABLE_NOT_EXIST); } else { + console.log(error); throwErr(error); } } @@ -87,9 +132,13 @@ export class InfraConfigService implements OnModuleInit { * @returns InfraConfig model */ cast(dbInfraConfig: DBInfraConfig) { + const plainValue = dbInfraConfig.isEncrypted + ? decrypt(dbInfraConfig.value) + : dbInfraConfig.value; + return { name: dbInfraConfig.name, - value: dbInfraConfig.value ?? '', + value: plainValue ?? '', }; } @@ -99,10 +148,16 @@ export class InfraConfigService implements OnModuleInit { */ async getInfraConfigsMap() { const infraConfigs = await this.prisma.infraConfig.findMany(); + const infraConfigMap: Record = {}; infraConfigs.forEach((config) => { - infraConfigMap[config.name] = config.value; + if (config.isEncrypted) { + infraConfigMap[config.name] = decrypt(config.value); + } else { + infraConfigMap[config.name] = config.value; + } }); + return infraConfigMap; } @@ -118,9 +173,14 @@ export class InfraConfigService implements OnModuleInit { if (E.isLeft(isValidate)) return E.left(isValidate.left); try { + const { isEncrypted } = await this.prisma.infraConfig.findUnique({ + where: { name }, + select: { isEncrypted: true }, + }); + const infraConfig = await this.prisma.infraConfig.update({ where: { name }, - data: { value }, + data: { value: isEncrypted ? encrypt(value) : value }, }); if (restartEnabled) stopApp(); @@ -146,11 +206,23 @@ export class InfraConfigService implements OnModuleInit { if (E.isLeft(isValidate)) return E.left(isValidate.left); try { + const dbInfraConfig = await this.prisma.infraConfig.findMany({ + select: { name: true, isEncrypted: true }, + }); + await this.prisma.$transaction(async (tx) => { for (let i = 0; i < infraConfigs.length; i++) { + const isEncrypted = dbInfraConfig.find( + (p) => p.name === infraConfigs[i].name, + )?.isEncrypted; + await tx.infraConfig.update({ where: { name: infraConfigs[i].name }, - data: { value: infraConfigs[i].value }, + data: { + value: isEncrypted + ? encrypt(infraConfigs[i].value) + : infraConfigs[i].value, + }, }); } }); diff --git a/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts b/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts index 67b12e5c9..21e55ed13 100644 --- a/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts +++ b/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts @@ -27,6 +27,8 @@ import { UpdateUserAdminStatusResponse, CreateUserInvitationRequest, CreateUserInvitationResponse, + DeleteUserResponse, + GetUserWorkspacesResponse, } from './request-response.dto'; import * as E from 'fp-ts/Either'; import * as O from 'fp-ts/Option'; @@ -103,9 +105,8 @@ export class InfraTokensController { async getPendingUserInvitation( @Query() paginationQuery: OffsetPaginationArgs, ) { - const pendingInvitedUsers = await this.adminService.fetchInvitedUsers( - paginationQuery, - ); + const pendingInvitedUsers = + await this.adminService.fetchInvitedUsers(paginationQuery); return plainToInstance(GetUserInvitationResponse, pendingInvitedUsers, { excludeExtraneousValues: true, @@ -208,6 +209,35 @@ export class InfraTokensController { }); } + @Delete('users/:uid') + @ApiOkResponse({ + description: 'Delete a user from the instance', + type: DeleteUserResponse, + }) + @ApiBadRequestResponse({ type: ExceptionResponse }) + @ApiNotFoundResponse({ type: ExceptionResponse }) + async deleteUser(@Param('uid') uid: string) { + const deletedUser = await this.adminService.removeUserAccount(uid); + + if (E.isLeft(deletedUser)) { + const statusCode = + (deletedUser.left as string) === USER_NOT_FOUND + ? HttpStatus.NOT_FOUND + : HttpStatus.BAD_REQUEST; + + throwHTTPErr({ message: deletedUser.left, statusCode }); + } + + return plainToInstance( + DeleteUserResponse, + { message: deletedUser.right }, + { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }, + ); + } + @Patch('users/:uid/admin-status') @ApiOkResponse({ description: 'Update user admin status', @@ -245,4 +275,28 @@ export class InfraTokensController { }, ); } + + @Get('users/:uid/workspaces') + @ApiOkResponse({ + description: 'Get user workspaces', + type: [GetUserWorkspacesResponse], + }) + @ApiNotFoundResponse({ type: ExceptionResponse }) + async getUserWorkspaces(@Param('uid') uid: string) { + const userWorkspaces = await this.userService.fetchUserWorkspaces(uid); + + if (E.isLeft(userWorkspaces)) { + const statusCode = + userWorkspaces.left === USER_NOT_FOUND + ? HttpStatus.NOT_FOUND + : HttpStatus.BAD_REQUEST; + + throwHTTPErr({ message: userWorkspaces.left, statusCode }); + } + + return plainToInstance(GetUserWorkspacesResponse, userWorkspaces.right, { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }); + } } diff --git a/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts b/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts index 9bacfbf12..f3c64f8fc 100644 --- a/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts +++ b/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts @@ -10,6 +10,7 @@ import { IsString, MinLength, } from 'class-validator'; +import { TeamMemberRole } from 'src/team/team.model'; import { OffsetPaginationArgs } from 'src/types/input-types.args'; // POST v1/infra/user-invitations @@ -79,6 +80,14 @@ export class GetUserResponse { @ApiProperty() @Expose() isAdmin: boolean; + + @ApiProperty() + @Expose() + lastLoggedOn: Date; + + @ApiProperty() + @Expose() + lastActiveOn: Date; } // PATCH v1/infra/users/:uid @@ -113,3 +122,41 @@ export class ExceptionResponse { @Expose() statusCode: number; } + +// Delete v1/infra/users/:uid +export class DeleteUserResponse { + @ApiProperty() + @Expose() + message: string; +} + +// GET v1/infra/users/:uid/workspaces +export class GetUserWorkspacesResponse { + @ApiProperty() + @Expose() + id: string; + + @ApiProperty() + @Expose() + name: string; + + @ApiProperty({ enum: TeamMemberRole }) + @Expose() + role: string; + + @ApiProperty() + @Expose() + owner_count: number; + + @ApiProperty() + @Expose() + editor_count: number; + + @ApiProperty() + @Expose() + viewer_count: number; + + @ApiProperty() + @Expose() + member_count: number; +} diff --git a/packages/hoppscotch-backend/src/mailer/helper.ts b/packages/hoppscotch-backend/src/mailer/helper.ts index 1095777d8..e5629418e 100644 --- a/packages/hoppscotch-backend/src/mailer/helper.ts +++ b/packages/hoppscotch-backend/src/mailer/helper.ts @@ -35,7 +35,7 @@ export function getTransportOption(env, config): TransportType { console.log('Using advanced mailer configuration'); return { host: env.INFRA.MAILER_SMTP_HOST ?? config.get('MAILER_SMTP_HOST'), - port: +env.INFRA.MAILER_SMTP_PORT ?? +config.get('MAILER_SMTP_PORT'), + port: +(env.INFRA.MAILER_SMTP_PORT ?? config.get('MAILER_SMTP_PORT')), secure: (env.INFRA.MAILER_SMTP_SECURE ?? config.get('MAILER_SMTP_SECURE')) === 'true', diff --git a/packages/hoppscotch-backend/src/plugins/GQLComplexityPlugin.ts b/packages/hoppscotch-backend/src/plugins/GQLComplexityPlugin.ts index 52b894557..31655facf 100644 --- a/packages/hoppscotch-backend/src/plugins/GQLComplexityPlugin.ts +++ b/packages/hoppscotch-backend/src/plugins/GQLComplexityPlugin.ts @@ -7,6 +7,7 @@ import { import { Plugin } from '@nestjs/apollo'; import { GraphQLError } from 'graphql'; import { + ComplexityEstimatorArgs, fieldExtensionsEstimator, getComplexity, simpleEstimator, @@ -29,6 +30,14 @@ export class GQLComplexityPlugin implements ApolloServerPlugin { query: document, variables: request.variables, estimators: [ + // Custom estimator for introspection fields + (args: ComplexityEstimatorArgs) => { + const fieldName = args.field.name; + if (fieldName.startsWith('__')) { + return 0; // Return 0 complexity for introspection fields + } + return; + }, fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 }), ], diff --git a/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts b/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts index 49506c67e..b36171221 100644 --- a/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts +++ b/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts @@ -23,6 +23,7 @@ import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; import { InvitedUser } from '../admin/invited-user.model'; import { UserCollection, + UserCollectionDuplicatedData, UserCollectionRemovedData, UserCollectionReorderData, } from 'src/user-collection/user-collections.model'; @@ -48,6 +49,7 @@ export type TopicDef = { [ topic: `user_coll/${string}/${'created' | 'updated' | 'moved'}` ]: UserCollection; + [topic: `user_coll/${string}/${'duplicated'}`]: UserCollectionDuplicatedData; [topic: `user_coll/${string}/${'deleted'}`]: UserCollectionRemovedData; [topic: `user_coll/${string}/${'order_updated'}`]: UserCollectionReorderData; [topic: `team/${string}/member_removed`]: string; diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts index a1f0c7efb..94b2c8c7b 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts @@ -546,10 +546,12 @@ describe('ShortcodeService', () => { ); expect(result).toEqual([ { - id: shortcodes[1].id, - request: JSON.stringify(shortcodes[1].request), - properties: JSON.stringify(shortcodes[1].embedProperties), - createdOn: shortcodes[1].createdOn, + id: shortcodesWithUserEmail[1].id, + request: JSON.stringify(shortcodesWithUserEmail[1].request), + properties: JSON.stringify( + shortcodesWithUserEmail[1].embedProperties, + ), + createdOn: shortcodesWithUserEmail[1].createdOn, creator: { uid: user.uid, email: user.email, diff --git a/packages/hoppscotch-backend/src/team-collection/input-type.args.ts b/packages/hoppscotch-backend/src/team-collection/input-type.args.ts index 2b4dd20a8..6e999f18e 100644 --- a/packages/hoppscotch-backend/src/team-collection/input-type.args.ts +++ b/packages/hoppscotch-backend/src/team-collection/input-type.args.ts @@ -47,14 +47,12 @@ export class RenameTeamCollectionArgs { @Field(() => ID, { name: 'collectionID', description: 'ID of the collection', - deprecationReason: 'Switch to updateTeamCollection mutation instead', }) collectionID: string; @Field({ name: 'newTitle', description: 'The updated title of the collection', - deprecationReason: 'Switch to updateTeamCollection mutation instead', }) newTitle: string; } diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts index 4c6b00ea6..1fc4c1086 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts @@ -102,6 +102,36 @@ export class TeamCollectionResolver { return jsonString.right; } + @Query(() => String, { + description: + 'Returns a JSON string of all the contents of a Team Collection', + }) + @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) + @RequiresTeamRole( + TeamMemberRole.VIEWER, + TeamMemberRole.EDITOR, + TeamMemberRole.OWNER, + ) + async exportCollectionToJSON( + @Args({ name: 'teamID', description: 'ID of the team', type: () => ID }) + teamID: string, + @Args({ + name: 'collectionID', + description: 'ID of the collection', + type: () => ID, + }) + collectionID: string, + ) { + const collectionData = + await this.teamCollectionService.exportCollectionToJSONObject( + teamID, + collectionID, + ); + + if (E.isLeft(collectionData)) throwErr(collectionData.left as string); + return JSON.stringify(collectionData.right); + } + @Query(() => [TeamCollection], { description: 'Returns the collections of a team', }) diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts index 093c25393..6779e64aa 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts @@ -103,10 +103,10 @@ export class TeamCollectionService { * @param collectionID The Collection ID * @returns A JSON string containing all the contents of a collection */ - private async exportCollectionToJSONObject( + async exportCollectionToJSONObject( teamID: string, collectionID: string, - ) { + ): Promise | E.Left> { const collection = await this.getCollection(collectionID); if (E.isLeft(collection)) return E.left(TEAM_INVALID_COLL_ID); diff --git a/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts b/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts index f2b28b70b..91779e4b5 100644 --- a/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts +++ b/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts @@ -194,7 +194,7 @@ export class TeamEnvironmentsService { const result = await this.prisma.teamEnvironment.create({ data: { - name: environment.name, + name: `${environment.name} - Duplicate`, teamID: environment.teamID, variables: environment.variables as Prisma.JsonArray, }, diff --git a/packages/hoppscotch-backend/src/team/team.service.ts b/packages/hoppscotch-backend/src/team/team.service.ts index f1afcf908..80a2240b1 100644 --- a/packages/hoppscotch-backend/src/team/team.service.ts +++ b/packages/hoppscotch-backend/src/team/team.service.ts @@ -38,7 +38,7 @@ export class TeamService implements UserDataHandler, OnModuleInit { canAllowUserDeletion(user: AuthUser): TO.TaskOption { return pipe( - this.isUserOwnerRoleInTeams(user.uid), + this.isUserSoleOwnerInAnyTeam(user.uid), TO.fromTask, TO.chain((isOwner) => (isOwner ? TO.some(USER_IS_OWNER) : TO.none)), ); @@ -396,18 +396,34 @@ export class TeamService implements UserDataHandler, OnModuleInit { return teamMember ? teamMember.role : null; } - isUserOwnerRoleInTeams(uid: string): T.Task { - return pipe( - () => - this.prisma.teamMember.count({ + isUserSoleOwnerInAnyTeam(uid: string): T.Task { + return async () => { + // Find all teams where the user is an OWNER + const userOwnedTeams = await this.prisma.teamMember.findMany({ + where: { + userUid: uid, + role: TeamMemberRole.OWNER, + }, + select: { + teamID: true, + }, + }); + + for (const userOwnedTeam of userOwnedTeams) { + const ownerCount = await this.prisma.teamMember.count({ where: { - userUid: uid, + teamID: userOwnedTeam.teamID, role: TeamMemberRole.OWNER, }, - take: 1, - }), - T.map((count) => count > 0), - ); + }); + + // early return true if the user is the sole owner + if (ownerCount === 1) return true; + } + + // return false if the user is not the sole owner in any team + return false; + }; } deleteUserFromAllTeams(uid: string) { diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts index 8bc2f22e5..df530e3d7 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts @@ -16,6 +16,7 @@ import { AuthUser } from 'src/types/AuthUser'; import { UserCollectionService } from './user-collection.service'; import { UserCollection, + UserCollectionDuplicatedData, UserCollectionExportJSONData, UserCollectionRemovedData, UserCollectionReorderData, @@ -185,6 +186,30 @@ export class UserCollectionResolver { return jsonString.right; } + @Query(() => String, { + description: + 'Returns a JSON string of all the contents of a User Collection', + }) + @UseGuards(GqlAuthGuard) + async exportUserCollectionToJSON( + @GqlUser() user: AuthUser, + @Args({ + type: () => ID, + name: 'collectionID', + description: 'ID of the user collection', + }) + collectionID: string, + ) { + const jsonString = + await this.userCollectionService.exportUserCollectionToJSONObject( + user.uid, + collectionID, + ); + + if (E.isLeft(jsonString)) throwErr(jsonString.left as string); + return JSON.stringify(jsonString.right); + } + // Mutations @Mutation(() => UserCollection, { description: 'Creates root REST user collection(no parent user collection)', @@ -470,4 +495,14 @@ export class UserCollectionResolver { userCollectionOrderUpdated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/order_updated`); } + + @Subscription(() => UserCollectionDuplicatedData, { + description: 'Listen to when a User Collection has been duplicated', + resolve: (value) => value, + }) + @SkipThrottle() + @UseGuards(GqlAuthGuard) + userCollectionDuplicated(@GqlUser() user: AuthUser) { + return this.pubSub.asyncIterator(`user_coll/${user.uid}/duplicated`); + } } diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts index 69358854c..4366caf43 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts @@ -23,6 +23,7 @@ import { Prisma, UserCollection, ReqType as DBReqType } from '@prisma/client'; import { UserCollection as UserCollectionModel, UserCollectionExportJSONData, + UserCollectionDuplicatedData, } from './user-collections.model'; import { ReqType } from 'src/types/RequestTypes'; import { @@ -835,7 +836,7 @@ export class UserCollectionService { * @param collectionID The Collection ID * @returns A JSON string containing all the contents of a collection */ - private async exportUserCollectionToJSONObject( + async exportUserCollectionToJSONObject( userUID: string, collectionID: string, ): Promise | E.Right> { @@ -1035,6 +1036,7 @@ export class UserCollectionService { userID: string, destCollectionID: string | null, reqType: DBReqType, + isCollectionDuplication = false, ) { // Check to see if jsonString is valid const collectionsList = stringToJson(jsonString); @@ -1087,9 +1089,24 @@ export class UserCollectionService { ), ); - userCollections.forEach((collection) => - this.pubsub.publish(`user_coll/${userID}/created`, this.cast(collection)), - ); + if (isCollectionDuplication) { + const collectionData = await this.fetchCollectionData( + userCollections[0].id, + ); + if (E.isRight(collectionData)) { + this.pubsub.publish( + `user_coll/${userID}/duplicated`, + collectionData.right, + ); + } + } else { + userCollections.forEach((collection) => + this.pubsub.publish( + `user_coll/${userID}/created`, + this.cast(collection), + ), + ); + } return E.right(true); } @@ -1182,9 +1199,66 @@ export class UserCollectionService { userID, collection.right.parentID, reqType, + true, ); if (E.isLeft(result)) return E.left(result.left as string); return E.right(true); } + + /** + * Generates a JSON containing all the contents of a collection + * + * @param collection Collection whose details we want to fetch + * @returns A JSON string containing all the contents of a collection + */ + private async fetchCollectionData( + collectionID: string, + ): Promise | E.Right> { + const collection = await this.getUserCollection(collectionID); + if (E.isLeft(collection)) return E.left(collection.left); + + const { id, title, data, type, parentID, userUid } = collection.right; + const orderIndex = 'asc'; + + const [childCollections, requests] = await Promise.all([ + this.prisma.userCollection.findMany({ + where: { parentID: id }, + orderBy: { orderIndex }, + }), + this.prisma.userRequest.findMany({ + where: { collectionID: id }, + orderBy: { orderIndex }, + }), + ]); + + const childCollectionDataList = await Promise.all( + childCollections.map(({ id }) => this.fetchCollectionData(id)), + ); + + const failedChildData = childCollectionDataList.find(E.isLeft); + if (failedChildData) return E.left(failedChildData.left); + + const childCollectionsJSONStr = JSON.stringify( + (childCollectionDataList as E.Right[]).map( + (childCollection) => childCollection.right, + ), + ); + + const transformedRequests = requests.map((requestObj) => ({ + ...requestObj, + request: JSON.stringify(requestObj.request), + })); + + return E.right({ + id, + title, + data, + type, + parentID, + userID: userUid, + childCollections: childCollectionsJSONStr, + requests: transformedRequests, + }); + } } diff --git a/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts b/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts index 89c26861c..d0f7717fc 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts @@ -1,5 +1,7 @@ import { ObjectType, Field, ID, registerEnumType } from '@nestjs/graphql'; +import { User } from '@prisma/client'; import { ReqType } from 'src/types/RequestTypes'; +import { UserRequest } from 'src/user-request/user-request.model'; @ObjectType() export class UserCollection { @@ -73,3 +75,52 @@ export class UserCollectionExportJSONData { }) collectionType: ReqType; } + +@ObjectType() +export class UserCollectionDuplicatedData { + @Field(() => ID, { + description: 'ID of the user collection', + }) + id: string; + + @Field({ + description: 'Displayed title of the user collection', + }) + title: string; + + @Field({ + description: 'JSON string representing the collection data', + nullable: true, + }) + data: string; + + @Field(() => ReqType, { + description: 'Type of the user collection', + }) + type: ReqType; + + @Field({ + description: 'Parent ID of the duplicated User Collection', + nullable: true, + }) + parentID: string | null; + + @Field({ + description: 'User ID of the duplicated User Collection', + }) + userID: string; + + @Field({ + description: 'Child collections of the duplicated User Collection', + }) + childCollections: string; + + @Field(() => [UserRequest], { + description: 'Requests of the duplicated User Collection', + }) + requests: UserRequest[]; +} + +registerEnumType(ReqType, { + name: 'CollType', +}); diff --git a/packages/hoppscotch-backend/src/user/user.service.spec.ts b/packages/hoppscotch-backend/src/user/user.service.spec.ts index 87c05c236..8f9b7e63a 100644 --- a/packages/hoppscotch-backend/src/user/user.service.spec.ts +++ b/packages/hoppscotch-backend/src/user/user.service.spec.ts @@ -153,6 +153,14 @@ const exampleSSOProfileData = { photos: 'https://en.wikipedia.org/wiki/Dwight_Schrute', }; +beforeAll(() => { + process.env.DATA_ENCRYPTION_KEY = '12345678901234567890123456789012'; +}); + +afterAll(() => { + delete process.env.DATA_ENCRYPTION_KEY; // Clean up after tests +}); + beforeEach(() => { mockReset(mockPrisma); mockPubSub.publish.mockClear(); diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts index f28b4167e..c3162e85a 100644 --- a/packages/hoppscotch-backend/src/user/user.service.ts +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -16,10 +16,12 @@ import { import { SessionType, User } from './user.model'; import { USER_UPDATE_FAILED } from 'src/errors'; import { PubSubService } from 'src/pubsub/pubsub.service'; -import { stringToJson, taskEitherValidateArraySeq } from 'src/utils'; +import { encrypt, stringToJson, taskEitherValidateArraySeq } from 'src/utils'; import { UserDataHandler } from './user.data.handler'; import { User as DbUser } from '@prisma/client'; import { OffsetPaginationArgs } from 'src/types/input-types.args'; +import { GetUserWorkspacesResponse } from 'src/infra-token/request-response.dto'; +import { TeamMemberRole } from 'src/team/team.model'; @Injectable() export class UserService { @@ -208,8 +210,8 @@ export class UserService { data: { provider: profile.provider, providerAccountId: profile.id, - providerRefreshToken: refreshToken ? refreshToken : null, - providerAccessToken: accessToken ? accessToken : null, + providerRefreshToken: refreshToken ? encrypt(refreshToken) : null, + providerAccessToken: accessToken ? encrypt(accessToken) : null, user: { connect: { uid: user.uid, @@ -598,4 +600,52 @@ export class UserService { return E.right(true); } + + async fetchUserWorkspaces(userUid: string) { + const user = await this.prisma.user.findUnique({ where: { uid: userUid } }); + if (!user) return E.left(USER_NOT_FOUND); + + const team = await this.prisma.team.findMany({ + where: { + members: { + some: { + userUid, + }, + }, + }, + include: { + members: { + select: { + userUid: true, + role: true, + }, + }, + }, + }); + + const workspaces: GetUserWorkspacesResponse[] = []; + team.forEach((t) => { + const ownerCount = t.members.filter( + (m) => m.role === TeamMemberRole.OWNER, + ).length; + const editorCount = t.members.filter( + (m) => m.role === TeamMemberRole.EDITOR, + ).length; + const viewerCount = t.members.filter( + (m) => m.role === TeamMemberRole.VIEWER, + ).length; + const memberCount = t.members.length; + + workspaces.push({ + id: t.id, + name: t.name, + role: t.members.find((m) => m.userUid === userUid)?.role, + owner_count: ownerCount, + editor_count: editorCount, + viewer_count: viewerCount, + member_count: memberCount, + }); + }); + return E.right(workspaces); + } } diff --git a/packages/hoppscotch-backend/src/utils.ts b/packages/hoppscotch-backend/src/utils.ts index 5be926174..23afc3fa3 100644 --- a/packages/hoppscotch-backend/src/utils.ts +++ b/packages/hoppscotch-backend/src/utils.ts @@ -12,11 +12,13 @@ import { AuthProvider } from './auth/helper'; import { ENV_EMPTY_AUTH_PROVIDERS, ENV_NOT_FOUND_KEY_AUTH_PROVIDERS, + ENV_NOT_FOUND_KEY_DATA_ENCRYPTION_KEY, ENV_NOT_SUPPORT_AUTH_PROVIDERS, JSON_INVALID, } from './errors'; import { TeamMemberRole } from './team/team.model'; import { RESTError } from './types/RESTError'; +import * as crypto from 'crypto'; /** * A workaround to throw an exception in an expression. @@ -114,6 +116,17 @@ export const getGqlArg = ( ); /** + * To the daring adventurer who has stumbled upon this relic of code... welcome. + * Many have gazed upon its depths, yet few have returned with answers. + * I could have deleted it, but that felt... too easy, too final. + * + * If you're still reading, perhaps you're the one destined to unravel its secrets. + * Or, maybe you're like me—content to let it linger, a puzzle for the ages. + * The choice is yours, but beware... once you start, there is no turning back. + * + * PLEASE, NO ONE KNOWS HOW THIS WORKS... + * -- Balu, whispering from the great beyond... probably still trying to understand this damn thing. + * * Sequences an array of TaskEither values while maintaining an array of all the error values * @param arr Array of TaskEithers * @returns A TaskEither saying all the errors possible on the left or all the success values on the right @@ -316,3 +329,57 @@ export function transformCollectionData( ? collectionData : JSON.stringify(collectionData); } + +// Encrypt and Decrypt functions. InfraConfig and Account table uses these functions to encrypt and decrypt the data. +const ENCRYPTION_ALGORITHM = 'aes-256-cbc'; + +/** + * Encrypts a text using a key + * @param text The text to encrypt + * @param key The key to use for encryption + * @returns The encrypted text + */ +export function encrypt(text: string, key = process.env.DATA_ENCRYPTION_KEY) { + if (!key) throw new Error(ENV_NOT_FOUND_KEY_DATA_ENCRYPTION_KEY); + + if (text === null || text === undefined) return text; + + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv( + ENCRYPTION_ALGORITHM, + Buffer.from(key), + iv, + ); + let encrypted = cipher.update(text); + encrypted = Buffer.concat([encrypted, cipher.final()]); + return iv.toString('hex') + ':' + encrypted.toString('hex'); +} + +/** + * Decrypts a text using a key + * @param text The text to decrypt + * @param key The key to use for decryption + * @returns The decrypted text + */ +export function decrypt( + encryptedData: string, + key = process.env.DATA_ENCRYPTION_KEY, +) { + if (!key) throw new Error(ENV_NOT_FOUND_KEY_DATA_ENCRYPTION_KEY); + + if (encryptedData === null || encryptedData === undefined) { + return encryptedData; + } + + const textParts = encryptedData.split(':'); + const iv = Buffer.from(textParts.shift(), 'hex'); + const encryptedText = Buffer.from(textParts.join(':'), 'hex'); + const decipher = crypto.createDecipheriv( + ENCRYPTION_ALGORITHM, + Buffer.from(key), + iv, + ); + let decrypted = decipher.update(encryptedText); + decrypted = Buffer.concat([decrypted, decipher.final()]); + return decrypted.toString(); +} diff --git a/packages/hoppscotch-backend/tsconfig.build.json b/packages/hoppscotch-backend/tsconfig.build.json index 64f86c6bd..ffc791946 100644 --- a/packages/hoppscotch-backend/tsconfig.build.json +++ b/packages/hoppscotch-backend/tsconfig.build.json @@ -1,4 +1,8 @@ { "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"], + "compilerOptions": { + "declaration": false, + "sourceMap": false + } } diff --git a/packages/hoppscotch-cli/README.md b/packages/hoppscotch-cli/README.md index 5eac16511..28a1efdb5 100644 --- a/packages/hoppscotch-cli/README.md +++ b/packages/hoppscotch-cli/README.md @@ -28,31 +28,50 @@ hopp [options or commands] arguments - Displays the help text 3. #### **`hopp test [options] `** + - Interactive CLI to accept Hoppscotch collection JSON path - Parses the collection JSON and executes each requests - Executes pre-request script. - Outputs the response of each request. - Executes and outputs test-script response. - #### Options: + #### Options: - ##### `-e ` / `--env ` + ##### `-e ` / `--env ` - - Accepts path to env.json with contents in below format: + - Accepts path to env.json with contents in below format: - ```json - { - "ENV1":"value1", - "ENV2":"value2" - } - ``` + ```json + { + "ENV1": "value1", + "ENV2": "value2" + } + ``` - - You can now access those variables using `pw.env.get('')` + - You can now access those variables using `pw.env.get('')` - Taking the above example, `pw.env.get("ENV1")` will return `"value1"` + Taking the above example, `pw.env.get("ENV1")` will return `"value1"` + + ##### `--iteration-count ` + + - Accepts the number of iterations to run the collection + + ##### `--iteration-data ` + + - Accepts the path to a CSV file with contents in the below format: + + ```text + key1,key2,key3 + value1,value2,value3 + value4,value5,value6 + ``` + + For every iteration the values will be replaced with the respective keys in the environment. For iteration 1 the value1,value2,value3 will be replaced and for iteration 2 value4,value5,value6 will be replaced and so on. ## Install + - Before you install Hoppscotch CLI you need to make sure you have the dependencies it requires to run. + - **Windows & macOS**: You will need `node-gyp` installed. Find instructions here: https://github.com/nodejs/node-gyp - **Debian/Ubuntu derivatives**: ```sh @@ -75,7 +94,6 @@ hopp [options or commands] arguments sudo dnf install python3 make gcc gcc-c++ zlib-devel brotli-devel openssl-devel libuv-devel ``` - - Once the dependencies are installed, install [@hoppscotch/cli](https://www.npmjs.com/package/@hoppscotch/cli) from npm by running: ``` npm i -g @hoppscotch/cli @@ -112,39 +130,39 @@ Please note we have a code of conduct, please follow it in all your interactions 1. After cloning the repository, execute the following commands: - ```bash - pnpm install - pnpm run build - ``` + ```bash + pnpm install + pnpm run build + ``` 2. In order to test locally, you can use two types of package linking: - 1. The 'pnpm exec' way (preferred since it does not hamper your original installation of the CLI): + 1. The 'pnpm exec' way (preferred since it does not hamper your original installation of the CLI): - ```bash - pnpm link @hoppscotch/cli + ```bash + pnpm link @hoppscotch/cli - // Then to use or test the CLI: - pnpm exec hopp + // Then to use or test the CLI: + pnpm exec hopp - // After testing, to remove the package linking: - pnpm rm @hoppscotch/cli - ``` + // After testing, to remove the package linking: + pnpm rm @hoppscotch/cli + ``` - 2. The 'global' way (warning: this might override the globally installed CLI, if exists): + 2. The 'global' way (warning: this might override the globally installed CLI, if exists): - ```bash - sudo pnpm link --global + ```bash + sudo pnpm link --global - // Then to use or test the CLI: - hopp + // Then to use or test the CLI: + hopp - // After testing, to remove the package linking: - sudo pnpm rm --global @hoppscotch/cli - ``` + // After testing, to remove the package linking: + sudo pnpm rm --global @hoppscotch/cli + ``` 3. To use the Typescript watch scripts: - ```bash - pnpm run dev - ``` + ```bash + pnpm run dev + ``` diff --git a/packages/hoppscotch-cli/bin/hopp.js b/packages/hoppscotch-cli/bin/hopp.js index 2a5e91a60..57212501d 100755 --- a/packages/hoppscotch-cli/bin/hopp.js +++ b/packages/hoppscotch-cli/bin/hopp.js @@ -2,27 +2,57 @@ // * The entry point of the CLI // @ts-check -import { cli } from "../dist/index.js"; - +import chalk from "chalk"; import { spawnSync } from "child_process"; +import fs from "fs"; import { cloneDeep } from "lodash-es"; +import semver from "semver"; +import { fileURLToPath } from "url"; -const nodeVersion = parseInt(process.versions.node.split(".")[0]); +const highlightVersion = (version) => chalk.black.bgYellow(`v${version}`); + +const packageJsonPath = fileURLToPath( + new URL("../package.json", import.meta.url) +); +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); + +const requiredNodeVersionRange = packageJson.engines?.node || ">=20"; + +// Extract the major version from the start of the range +const requiredNodeVersion = semver.major( + semver.minVersion(requiredNodeVersionRange) ?? "20" +); + +const currentNodeVersion = process.versions.node; + +// Last supported version of the CLI for Node.js v18 +const lastSupportedVersion = "0.11.1"; + +if (!semver.satisfies(currentNodeVersion, requiredNodeVersionRange)) { + console.error( + `${chalk.greenBright("Hoppscotch CLI")} requires Node.js ${highlightVersion(requiredNodeVersion)} or higher and you're on Node.js ${highlightVersion(currentNodeVersion)}.` + ); + + console.error( + `\nIf you prefer staying on Node.js ${highlightVersion("18")}, you can install the last supported version of the CLI:\n` + + `${chalk.green(`npm install -g @hoppscotch/cli@${lastSupportedVersion}`)}` + ); + process.exit(1); +} + +// Dynamically importing the module after the Node.js version check prevents errors due to unrecognized APIs in older Node.js versions +const { cli } = await import("../dist/index.js"); // As per isolated-vm documentation, we need to supply `--no-node-snapshot` for node >= 20 // src: https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements -if (nodeVersion >= 20 && !process.execArgv.includes("--no-node-snapshot")) { +if (!process.execArgv.includes("--no-node-snapshot")) { const argCopy = cloneDeep(process.argv); // Replace first argument with --no-node-snapshot // We can get argv[0] from process.argv0 argCopy[0] = "--no-node-snapshot"; - const result = spawnSync( - process.argv0, - argCopy, - { stdio: "inherit" } - ); + const result = spawnSync(process.argv0, argCopy, { stdio: "inherit" }); // Exit with the same status code as the spawned process process.exit(result.status ?? 0); diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 1f82f83b7..45b9aa53d 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -1,6 +1,6 @@ { "name": "@hoppscotch/cli", - "version": "0.10.1", + "version": "0.20.0", "description": "A CLI to run Hoppscotch test scripts in CI environments.", "homepage": "https://hoppscotch.io", "type": "module", @@ -12,7 +12,7 @@ "access": "public" }, "engines": { - "node": ">=18" + "node": ">=20" }, "scripts": { "build": "pnpm exec tsup", @@ -41,28 +41,32 @@ "license": "MIT", "private": false, "dependencies": { - "axios": "1.6.7", + "aws4fetch": "1.0.20", + "axios": "1.7.7", "chalk": "5.3.0", - "commander": "11.1.0", - "isolated-vm": "4.7.2", + "commander": "12.1.0", + "isolated-vm": "5.0.1", + "js-md5": "0.8.3", "lodash-es": "4.17.21", - "qs": "6.11.2", - "verzod": "0.2.2", + "papaparse": "5.4.1", + "qs": "6.13.0", + "verzod": "0.2.3", "xmlbuilder2": "3.1.1", - "zod": "3.22.4" + "zod": "3.23.8" }, "devDependencies": { "@hoppscotch/data": "workspace:^", "@hoppscotch/js-sandbox": "workspace:^", "@relmify/jest-fp-ts": "2.1.1", - "@swc/core": "1.4.2", "@types/lodash-es": "4.17.12", - "@types/qs": "6.9.12", - "fp-ts": "2.16.2", - "prettier": "3.2.5", + "@types/papaparse": "5.3.14", + "@types/qs": "6.9.16", + "fp-ts": "2.16.9", + "prettier": "3.3.3", "qs": "6.11.2", - "tsup": "8.0.2", - "typescript": "5.3.3", - "vitest": "0.34.6" + "semver": "7.6.3", + "tsup": "8.3.0", + "typescript": "5.6.3", + "vitest": "2.1.2" } } diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap b/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap index 055c9a861..070d36bd2 100644 --- a/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap +++ b/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap @@ -1,529 +1,529 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report at the default path 1`] = ` -" - - - - - +" + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + >\\" + "key": "<>" } (ENV_EXPAND_LOOP)]]> - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + " `; exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report at the specified path 1`] = ` -" - - - - - +" + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + >\\" + "key": "<>" } (ENV_EXPAND_LOOP)]]> - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + " `; exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report for a collection referring to environment variables 1`] = ` -" - - - - - - - - - - - - - - +" + + + + + + + + + + + + + + " `; exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report for a collection with authorization/headers set at the collection level 1`] = ` -" - - - - +" + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + " `; diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts index 90affa48c..f386046ec 100644 --- a/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/e2e/commands/test.spec.ts @@ -1,12 +1,12 @@ import { ExecException } from "child_process"; -import { afterAll, beforeAll, describe, expect, test } from "vitest"; import fs from "fs"; import path from "path"; +import { afterAll, beforeAll, describe, expect, test } from "vitest"; import { HoppErrorCode } from "../../../types/errors"; import { getErrorCode, getTestJsonFilePath, runCLI } from "../../utils"; -describe("hopp test [options] ", () => { +describe("hopp test [options] ", { timeout: 100000 }, () => { const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; describe("Test `hopp test ` command:", () => { @@ -126,19 +126,15 @@ describe("hopp test [options] ", () => { expect(error).toBeNull(); }); - test( - "Successfully inherits/overrides authorization and headers at each level with multiple child collections", - async () => { - const args = `test ${getTestJsonFilePath( - "multiple-child-collections-auth-headers-coll.json", - "collection" - )}`; - const { error } = await runCLI(args); + test("Successfully inherits/overrides authorization and headers at each level with multiple child collections", async () => { + const args = `test ${getTestJsonFilePath( + "multiple-child-collections-auth-headers-coll.json", + "collection" + )}`; + const { error } = await runCLI(args); - expect(error).toBeNull(); - }, - { timeout: 50000 } - ); + expect(error).toBeNull(); + }); test("Persists environment variables set in the pre-request script for consumption in the test script", async () => { const args = `test ${getTestJsonFilePath( @@ -149,6 +145,79 @@ describe("hopp test [options] ", () => { expect(error).toBeNull(); }); + + test("The `Content-Type` header takes priority over the value set at the request body", async () => { + const args = `test ${getTestJsonFilePath( + "content-type-header-scenarios.json", + "collection" + )}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + + describe("OAuth 2 Authorization type with Authorization Code Grant Type", () => { + test("Successfully translates the authorization information to headers/query params and sends it along with the request", async () => { + const args = `test ${getTestJsonFilePath( + "oauth2-auth-code-coll.json", + "collection" + )}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + + describe("multipart/form-data content type", () => { + test("Successfully derives the relevant headers based and sends the form data in the request body", async () => { + const args = `test ${getTestJsonFilePath( + "oauth2-auth-code-coll.json", + "collection" + )}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + }); + + test("Ensures tests run in sequence order based on request path", async () => { + // Expected order of collection runs + const expectedOrder = [ + "root-collection-request", + "folder-1/folder-1-request", + "folder-1/folder-11/folder-11-request", + "folder-1/folder-12/folder-12-request", + "folder-1/folder-13/folder-13-request", + "folder-2/folder-2-request", + "folder-2/folder-21/folder-21-request", + "folder-2/folder-22/folder-22-request", + "folder-2/folder-23/folder-23-request", + "folder-3/folder-3-request", + "folder-3/folder-31/folder-31-request", + "folder-3/folder-32/folder-32-request", + "folder-3/folder-33/folder-33-request", + ]; + + const normalizePath = (path: string) => path.replace(/\\/g, "/"); + + const extractRunningOrder = (stdout: string): string[] => + [...stdout.matchAll(/Running:.*?\/(.*?)\r?\n/g)].map( + ([, path]) => normalizePath(path.replace(/\x1b\[\d+m/g, "")) // Remove ANSI codes and normalize paths + ); + + const args = `test ${getTestJsonFilePath( + "multiple-child-collections-auth-headers-coll.json", + "collection" + )}`; + + const { stdout, error } = await runCLI(args); + + // Verify the actual order matches the expected order + expect(extractRunningOrder(stdout)).toStrictEqual(expectedOrder); + + // Ensure no errors occurred + expect(error).toBeNull(); }); describe("Test `hopp test --env ` command:", () => { @@ -205,12 +274,12 @@ describe("hopp test [options] ", () => { }); test("Successfully resolves values from the supplied environment export file", async () => { - const TESTS_PATH = getTestJsonFilePath( + const COLL_PATH = getTestJsonFilePath( "env-flag-tests-coll.json", "collection" ); const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); - const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; const { error } = await runCLI(args); expect(error).toBeNull(); @@ -221,118 +290,195 @@ describe("hopp test [options] ", () => { "req-body-env-vars-coll.json", "collection" ); - const ENVS_PATH = getTestJsonFilePath( + const ENV_PATH = getTestJsonFilePath( "req-body-env-vars-envs.json", "environment" ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; const { error } = await runCLI(args); expect(error).toBeNull(); }); test("Works with short `-e` flag", async () => { - const TESTS_PATH = getTestJsonFilePath( + const COLL_PATH = getTestJsonFilePath( "env-flag-tests-coll.json", "collection" ); const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); - const args = `test ${TESTS_PATH} -e ${ENV_PATH}`; + const args = `test ${COLL_PATH} -e ${ENV_PATH}`; const { error } = await runCLI(args); expect(error).toBeNull(); }); - describe( - "Secret environment variables", - () => { - // Reads secret environment values from system environment - test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => { - const env = { - ...process.env, - secretBearerToken: "test-token", - secretBasicAuthUsername: "test-user", - secretBasicAuthPassword: "test-pass", - secretQueryParamValue: "secret-query-param-value", - secretBodyValue: "secret-body-value", - secretHeaderValue: "secret-header-value", - }; + describe("Secret environment variables", () => { + // Reads secret environment values from system environment + test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => { + const env = { + ...process.env, + secretBearerToken: "test-token", + secretBasicAuthUsername: "test-user", + secretBasicAuthPassword: "test-pass", + secretQueryParamValue: "secret-query-param-value", + secretBodyValue: "secret-body-value", + secretHeaderValue: "secret-header-value", + }; - const COLL_PATH = getTestJsonFilePath( - "secret-envs-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + const COLL_PATH = getTestJsonFilePath( + "secret-envs-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath("secret-envs.json", "environment"); + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; - const { error, stdout } = await runCLI(args, { env }); + const { error, stdout } = await runCLI(args, { env }); - expect(stdout).toContain( - "https://httpbin.org/basic-auth/*********/*********" - ); - expect(error).toBeNull(); - }); + expect(stdout).toContain( + "https://httpbin.org/basic-auth/*********/*********" + ); + expect(error).toBeNull(); + }); - // Prefers values specified in the environment export file over values set in the system environment - test("Successfully picks the values for secret environment variables set directly in the environment export file and persists the environment variables set from the pre-request script", async () => { - const COLL_PATH = getTestJsonFilePath( - "secret-envs-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-supplied-values-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + // Prefers values specified in the environment export file over values set in the system environment + test("Successfully picks the values for secret environment variables set directly in the environment export file and persists the environment variables set from the pre-request script", async () => { + const COLL_PATH = getTestJsonFilePath( + "secret-envs-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "secret-supplied-values-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; - const { error, stdout } = await runCLI(args); + const { error, stdout } = await runCLI(args); - expect(stdout).toContain( - "https://httpbin.org/basic-auth/*********/*********" - ); - expect(error).toBeNull(); - }); + expect(stdout).toContain( + "https://httpbin.org/basic-auth/*********/*********" + ); + expect(error).toBeNull(); + }); - // Values set from the scripting context takes the highest precedence - test("Setting values for secret environment variables from the pre-request script overrides values set at the supplied environment export file", async () => { - const COLL_PATH = getTestJsonFilePath( - "secret-envs-persistence-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-supplied-values-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + // Values set from the scripting context takes the highest precedence + test("Setting values for secret environment variables from the pre-request script overrides values set at the supplied environment export file", async () => { + const COLL_PATH = getTestJsonFilePath( + "secret-envs-persistence-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "secret-supplied-values-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; - const { error, stdout } = await runCLI(args); + const { error, stdout } = await runCLI(args); - expect(stdout).toContain( - "https://httpbin.org/basic-auth/*********/*********" - ); - expect(error).toBeNull(); - }); + expect(stdout).toContain( + "https://httpbin.org/basic-auth/*********/*********" + ); + expect(error).toBeNull(); + }); - test("Persists secret environment variable values set from the pre-request script for consumption in the request and post-request script context", async () => { - const COLL_PATH = getTestJsonFilePath( - "secret-envs-persistence-scripting-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-envs-persistence-scripting-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + test("Persists secret environment variable values set from the pre-request script for consumption in the request and post-request script context", async () => { + const COLL_PATH = getTestJsonFilePath( + "secret-envs-persistence-scripting-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "secret-envs-persistence-scripting-envs.json", + "environment" + ); - const { error } = await runCLI(args); - expect(error).toBeNull(); - }); - }, - { timeout: 20000 } - ); + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + }); + + describe("Request variables", () => { + test("Picks active request variables and ignores inactive entries alongside the usage of environment variables", async () => { + const env = { + ...process.env, + secretBasicAuthPasswordEnvVar: "password", + }; + + const COLL_PATH = getTestJsonFilePath( + "request-vars-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "request-vars-envs.json", + "environment" + ); + + const args = `test ${COLL_PATH} --env ${ENV_PATH}`; + + const { error, stdout } = await runCLI(args, { env }); + expect(stdout).toContain( + "https://echo.hoppscotch.io/********/********" + ); + expect(error).toBeNull(); + }); + }); + + describe("AWS Signature Authorization type", () => { + test("Successfully translates the authorization information to headers/query params and sends it along with the request", async () => { + const env = { + ...process.env, + secretKey: "test-secret-key", + serviceToken: "test-token", + }; + + const COLL_PATH = getTestJsonFilePath( + "aws-signature-auth-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "aws-signature-auth-envs.json", + "environment" + ); + + const args = `test ${COLL_PATH} -e ${ENV_PATH}`; + const { error } = await runCLI(args, { env }); + + expect(error).toBeNull(); + }); + }); + + describe("Digest Authorization type", () => { + test("Successfully translates the authorization information to headers/query params and sends it along with the request", async () => { + const COLL_PATH = getTestJsonFilePath( + "digest-auth-success-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "digest-auth-envs.json", + "environment" + ); + + const args = `test ${COLL_PATH} -e ${ENV_PATH}`; + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + }); + + test("Supports disabling request retries", async () => { + const COLL_PATH = getTestJsonFilePath( + "digest-auth-failure-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "digest-auth-envs.json", + "environment" + ); + + const args = `test ${COLL_PATH} -e ${ENV_PATH}`; + const { error } = await runCLI(args); + + expect(error).toBeTruthy(); + }); }); describe("Test `hopp test --delay ` command:", () => { @@ -472,11 +618,11 @@ describe("hopp test [options] ", () => { }); test("Supports specifying collection file path along with environment ID", async () => { - const TESTS_PATH = getTestJsonFilePath( + const COLL_PATH = getTestJsonFilePath( "req-body-env-vars-coll.json", "collection" ); - const args = `test ${TESTS_PATH} --env ${REQ_BODY_ENV_VARS_ENVS_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + const args = `test ${COLL_PATH} --env ${REQ_BODY_ENV_VARS_ENVS_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; const { error } = await runCLI(args); expect(error).toBeNull(); @@ -494,7 +640,7 @@ describe("hopp test [options] ", () => { }); test("Supports specifying both collection and environment file paths", async () => { - const TESTS_PATH = getTestJsonFilePath( + const COLL_PATH = getTestJsonFilePath( "req-body-env-vars-coll.json", "collection" ); @@ -502,7 +648,7 @@ describe("hopp test [options] ", () => { "req-body-env-vars-envs.json", "environment" ); - const args = `test ${TESTS_PATH} --env ${ENV_PATH} --token ${PERSONAL_ACCESS_TOKEN}`; + const args = `test ${COLL_PATH} --env ${ENV_PATH} --token ${PERSONAL_ACCESS_TOKEN}`; const { error } = await runCLI(args); expect(error).toBeNull(); @@ -533,7 +679,12 @@ describe("hopp test [options] ", () => { const COLL_PATH = getTestJsonFilePath("passes-coll.json", "collection"); - const args = `test ${COLL_PATH} --reporter-junit /non-existent-path/report.xml`; + const invalidPath = + process.platform === "win32" + ? "Z:/non-existent-path/report.xml" + : "/non-existent/report.xml"; + + const args = `test ${COLL_PATH} --reporter-junit ${invalidPath}`; const { stdout, stderr } = await runCLI(args, { cwd: path.resolve("hopp-cli-test"), @@ -667,4 +818,139 @@ describe("hopp test [options] ", () => { expect(replaceDynamicValuesInStr(fileContents)).toMatchSnapshot(); }); }); + + describe("Test `hopp test --iteration-count ` command:", () => { + const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; + + test("Errors with the code `INVALID_ARGUMENT` on not supplying an iteration count", async () => { + const args = `${VALID_TEST_ARGS} --iteration-count`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Errors with the code `INVALID_ARGUMENT` on supplying an invalid iteration count", async () => { + const args = `${VALID_TEST_ARGS} --iteration-count NaN`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Errors with the code `INVALID_ARGUMENT` on supplying an iteration count below `1`", async () => { + const args = `${VALID_TEST_ARGS} --iteration-count -5`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Successfully executes all requests in the collection iteratively based on the specified iteration count", async () => { + const iterationCount = 3; + const args = `${VALID_TEST_ARGS} --iteration-count ${iterationCount}`; + const { error, stdout } = await runCLI(args); + + // Logs iteration count in each pass + Array.from({ length: 3 }).forEach((_, idx) => + expect(stdout).include(`Iteration: ${idx + 1}/${iterationCount}`) + ); + expect(error).toBeNull(); + }); + + test("Doesn't log iteration count if the value supplied is `1`", async () => { + const args = `${VALID_TEST_ARGS} --iteration-count 1`; + const { error, stdout } = await runCLI(args); + + expect(stdout).not.include(`Iteration: 1/1`); + + expect(error).toBeNull(); + }); + }); + + describe("Test `hopp test --iteration-data ` command:", () => { + describe("Supplied data export file validations", () => { + const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; + + test("Errors with the code `INVALID_ARGUMENT` if no file is supplied", async () => { + const args = `${VALID_TEST_ARGS} --iteration-data`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Errors with the code `INVALID_DATA_FILE_TYPE` if the supplied data file doesn't end with the `.csv` extension", async () => { + const args = `${VALID_TEST_ARGS} --iteration-data ${getTestJsonFilePath( + "notjson-coll.txt", + "collection" + )}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_DATA_FILE_TYPE"); + }); + + test("Errors with the code `FILE_NOT_FOUND` if the supplied data export file doesn't exist", async () => { + const args = `${VALID_TEST_ARGS} --iteration-data notfound.csv`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("FILE_NOT_FOUND"); + }); + }); + + test("Prioritizes values from the supplied data export file over environment variables with relevant fallbacks for missing entries", async () => { + const COLL_PATH = getTestJsonFilePath( + "iteration-data-tests-coll.json", + "collection" + ); + const ITERATION_DATA_PATH = getTestJsonFilePath( + "iteration-data-export.csv", + "environment" + ); + const ENV_PATH = getTestJsonFilePath( + "iteration-data-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --iteration-data ${ITERATION_DATA_PATH} -e ${ENV_PATH}`; + + const { error, stdout } = await runCLI(args); + + const iterationCount = 3; + + // Even though iteration count is not supplied, it will be inferred from the iteration data size + Array.from({ length: iterationCount }).forEach((_, idx) => + expect(stdout).include(`Iteration: ${idx + 1}/${iterationCount}`) + ); + + expect(error).toBeNull(); + }); + + test("Iteration count takes priority if supplied instead of inferring from the iteration data size", async () => { + const COLL_PATH = getTestJsonFilePath( + "iteration-data-tests-coll.json", + "collection" + ); + const ITERATION_DATA_PATH = getTestJsonFilePath( + "iteration-data-export.csv", + "environment" + ); + const ENV_PATH = getTestJsonFilePath( + "iteration-data-envs.json", + "environment" + ); + + const iterationCount = 5; + const args = `test ${COLL_PATH} --iteration-data ${ITERATION_DATA_PATH} -e ${ENV_PATH} --iteration-count ${iterationCount}`; + + const { error, stdout } = await runCLI(args); + + Array.from({ length: iterationCount }).forEach((_, idx) => + expect(stdout).include(`Iteration: ${idx + 1}/${iterationCount}`) + ); + + expect(error).toBeNull(); + }); + }); }); diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/aws-signature-auth-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/aws-signature-auth-coll.json new file mode 100644 index 000000000..eae88b752 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/aws-signature-auth-coll.json @@ -0,0 +1,101 @@ +{ + "v": 3, + "name": "AWS Signature Auth - collection", + "folders": [], + "requests": [ + { + "v": "7", + "id": "cm0dm70cw000687bnxi830zz7", + "auth": { + "addTo": "HEADERS", + "region": "<>", + "authType": "aws-signature", + "accessKey": "<>", + "secretKey": "<>", + "authActive": true, + "serviceName": "<>", + "serviceToken": "", + "grantTypeInfo": { + "token": "", + "isPKCE": false, + "clientID": "", + "grantType": "AUTHORIZATION_CODE", + "authEndpoint": "", + "clientSecret": "", + "tokenEndpoint": "", + "codeVerifierMethod": "S256" + } + }, + "body": { + "body": null, + "contentType": null + }, + "name": "aws-signature-auth-headers", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "<>", + "testScript": "pw.test(\"Successfully sends relevant AWS signature information via headers\", ()=> {\n const { headers } = pw.response.body\n\n // Dynamic values, hence comparing the type.\n pw.expect(headers[\"authorization\"]).toBeType(\"string\");\n pw.expect(headers[\"x-amz-date\"]).toBeType(\"string\");\n \n pw.expect(headers[\"x-amz-content-sha256\"]).toBe(\"UNSIGNED-PAYLOAD\")\n \n // No session token supplied\n pw.expect(headers[\"x-amz-security-token\"]).toBe(undefined)\n \n});", + "preRequestScript": "", + "requestVariables": [ + { + "key": "secretVarKey", + "value": "<>", + "active": true + } + ] + }, + { + "v": "7", + "id": "cm0dm70cw000687bnxi830zz7", + "auth": { + "addTo": "QUERY_PARAMS", + "region": "<>", + "authType": "aws-signature", + "accessKey": "<>", + "secretKey": "<>", + "authActive": true, + "serviceName": "<>", + "serviceToken": "<>", + "grantTypeInfo": { + "token": "", + "isPKCE": false, + "clientID": "", + "grantType": "AUTHORIZATION_CODE", + "authEndpoint": "", + "clientSecret": "", + "tokenEndpoint": "", + "codeVerifierMethod": "S256" + } + }, + "body": { + "body": null, + "contentType": null + }, + "name": "aws-signature-auth-query-params", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "<>", + "testScript": "pw.test(\"Successfully sends relevant AWS signature information via query params\", ()=> {\n const { args } = pw.response.body\n pw.expect(args[\"X-Amz-Algorithm\"]).toBe(\"AWS4-HMAC-SHA256\");\n pw.expect(args[\"X-Amz-Algorithm\"]).toBe(\"AWS4-HMAC-SHA256\");\n pw.expect(args[\"X-Amz-Credential\"]).toInclude(\"test-access-key\");\n pw.expect(args[\"X-Amz-Credential\"]).toInclude(\"eu-west-1/s3\");\n\n // Dynamic values, hence comparing the type.\n pw.expect(args[\"X-Amz-Date\"]).toBeType(\"string\");\n pw.expect(args[\"X-Amz-Signature\"]).toBeType(\"string\");\n\n pw.expect(args[\"X-Amz-Expires\"]).toBe(\"86400\")\n pw.expect(args[\"X-Amz-SignedHeaders\"]).toBe(\"host\")\n pw.expect(args[\"X-Amz-Security-Token\"]).toBe(\"test-token\")\n \n});", + "preRequestScript": "", + "requestVariables": [ + { + "key": "awsRegion", + "value": "eu-west-1", + "active": true + }, + { + "key": "secretKey", + "value": "test-secret-key-overriden", + "active": true + } + ] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/content-type-header-scenarios.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/content-type-header-scenarios.json new file mode 100644 index 000000000..9aea2c0f2 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/content-type-header-scenarios.json @@ -0,0 +1,171 @@ +{ + "v": 2, + "name": "content-type-header-scenarios", + "folders": [], + "requests": [ + { + "v": "6", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "\n\n \n 12345\n John Doe\n john.doe@example.com\n \n \n 98765\n Sample Product\n 2\n \n\n", + "contentType": "text/xml" + }, + "name": "content-type-header-assignment", + "method": "POST", + "params": [], + "headers": [], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"The `Content-Type` header is assigned the content type value set at the request body level\", ()=> {\n pw.expect(pw.response.body.headers[\"content-type\"]).toBe(\"text/xml\");\n});", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "6", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "\n\n \n 12345\n John Doe\n john.doe@example.com\n \n \n 98765\n Sample Product\n 2\n \n\n", + "contentType": "application/json" + }, + "name": "content-type-header-override", + "method": "POST", + "params": [], + "headers": [ + { + "key": "Content-Type", + "value": "application/xml", + "active": true + } + ], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"The `Content-Type` header overrides the content type value set at the request body level\", ()=> {\n pw.expect(pw.response.body.headers[\"content-type\"]).toBe(\"application/xml\");\n});", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "6", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "\n\n \n 12345\n John Doe\n john.doe@example.com\n \n \n 98765\n Sample Product\n 2\n \n\n", + "contentType": "application/json" + }, + "name": "multiple-content-type-headers", + "method": "POST", + "params": [], + "headers": [ + { + "key": "Content-Type", + "value": "text/xml", + "active": true + }, + { + "key": "Content-Type", + "value": "application/json", + "active": true + }, + { + "key": "Content-Type", + "value": "application/xml", + "active": true + } + ], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"The last occurrence will be considered among multiple `Content-Type` headers\", ()=> {\n pw.expect(pw.response.body.headers[\"content-type\"]).toBe(\"application/xml\");\n});", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "6", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "\n\n \n 12345\n John Doe\n john.doe@example.com\n \n \n 98765\n Sample Product\n 2\n \n\n", + "contentType": null + }, + "name": "multiple-content-type-headers-different-casing", + "method": "POST", + "params": [], + "headers": [ + { + "key": "Content-Type", + "value": "text/xml", + "active": true + }, + { + "key": "content-Type", + "value": "application/json", + "active": true + }, + { + "key": "Content-type", + "value": "text/plain", + "active": true + }, + { + "key": "CONTENT-TYPE", + "value": "application/xml", + "active": true + } + ], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"The last occurrence will be considered among multiple `Content-Type` headers following different casing\", ()=> {\n pw.expect(pw.response.body.headers[\"content-type\"]).toBe(\"application/xml\");\n});", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "6", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "\n\n \n 12345\n John Doe\n john.doe@example.com\n \n \n 98765\n Sample Product\n 2\n \n\n", + "contentType": null + }, + "name": "multiple-content-type-headers-different-casing-without-value-set-at-body", + "method": "POST", + "params": [], + "headers": [ + { + "key": "Content-Type", + "value": "text/xml", + "active": true + }, + { + "key": "content-Type", + "value": "application/json", + "active": true + }, + { + "key": "Content-type", + "value": "text/plain", + "active": true + }, + { + "key": "CONTENT-TYPE", + "value": "application/xml", + "active": true + } + ], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"The content type is inferred from the `Content-Type` header if not set at the request body\", ()=> {\n pw.expect(pw.response.body.headers[\"content-type\"]).toBe(\"application/xml\");\n});", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/digest-auth-failure-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/digest-auth-failure-coll.json new file mode 100644 index 000000000..d8ab5ef25 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/digest-auth-failure-coll.json @@ -0,0 +1,43 @@ +{ + "v": 3, + "name": "Digest Auth (failure state) - collection", + "folders": [], + "requests": [ + { + "v": "8", + "id": "cm0dm70cw000687bnxi830zz7", + "auth": { + "authType": "digest", + "authActive": true, + "username": "<>", + "password": "<>", + "realm": "", + "nonce": "", + "algorithm": "MD5", + "qop": "auth", + "nc": "", + "cnonce": "", + "opaque": "", + "disableRetry": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "digest-auth-headers", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "<>", + "testScript": "pw.test(\"Status code is not 200\", ()=> { pw.expect(pw.response.status).not.toBe(200);}); \n pw.test(\"Receives the www-authenticate header\", ()=> { pw.expect(pw.response.headers['www-authenticate']).not.toBeType('string');});", + "preRequestScript": "", + "responses": {}, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/digest-auth-success-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/digest-auth-success-coll.json new file mode 100644 index 000000000..16f0b6fca --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/digest-auth-success-coll.json @@ -0,0 +1,43 @@ +{ + "v": 3, + "name": "Digest Auth (success state) - collection", + "folders": [], + "requests": [ + { + "v": "8", + "id": "cm0dm70cw000687bnxi830zz7", + "auth": { + "authType": "digest", + "authActive": true, + "username": "<>", + "password": "<>", + "realm": "", + "nonce": "", + "algorithm": "MD5", + "qop": "auth", + "nc": "", + "cnonce": "", + "opaque": "", + "disableRetry": false + }, + "body": { + "body": null, + "contentType": null + }, + "name": "digest-auth-headers", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "<>", + "testScript": "pw.test(\"Status code is 200\", ()=> { pw.expect(pw.response.status).toBe(200);}); \n pw.test(\"Receives the www-authenticate header\", ()=> { pw.expect(pw.response.headers['www-authenticate']).toBeType('string');});", + "preRequestScript": "", + "responses": {}, + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/iteration-data-tests-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/iteration-data-tests-coll.json new file mode 100644 index 000000000..d70aa8453 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/iteration-data-tests-coll.json @@ -0,0 +1,23 @@ +{ + "v": 1, + "name": "iteration-data-tests-coll", + "folders": [], + "requests": [ + { + "v": "3", + "endpoint": "<>", + "name": "test1", + "params": [], + "headers": [], + "method": "POST", + "auth": { "authType": "none", "authActive": true }, + "preRequestScript": "", + "testScript": "// Iteration data is prioritised over environment variables \n const { data, headers } = pw.response.body;\n pw.expect(headers['host']).toBe('echo.hoppscotch.io')\n // Falls back to environment variables for missing entries in data export\n pw.expect(data).toInclude('overriden-body-key-at-environment')\n pw.expect(data).toInclude('body_value')", + "body": { + "contentType": "application/json", + "body": "{\n \"<>\":\"<>\"\n}" + }, + "requestVariables": [] + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multipart-form-data-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multipart-form-data-coll.json new file mode 100644 index 000000000..9853183bc --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multipart-form-data-coll.json @@ -0,0 +1,55 @@ +{ + "v": 3, + "name": "Multpart form data content type - Collection", + "folders": [], + "requests": [ + { + "v": "7", + "endpoint": "https://echo.hoppscotch.io", + "name": "multipart-form-data-sample-req", + "params": [], + "headers": [], + "method": "POST", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "HEADERS", + "grantTypeInfo": { + "authEndpoint": "test-authorization-endpoint", + "tokenEndpoint": "test-token-endpont", + "clientID": "test-client-id", + "clientSecret": "test-client-secret", + "isPKCE": true, + "codeVerifierMethod": "S256", + "grantType": "AUTHORIZATION_CODE", + "token": "test-token" + } + }, + "preRequestScript": "", + "testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully derives the relevant headers based on the content type\", () => {\n pw.expect(pw.response.body.headers['content-type']).toInclude(\"multipart/form-data\");\n});\n\npw.test(\"Successfully sends the form data in the request body\", () => {\n // Dynamic value\n pw.expect(pw.response.body.data).toBeType(\"string\");\n});", + "body": { + "contentType": "multipart/form-data", + "body": [ + { + "key": "key1", + "value": "value1", + "active": true, + "isFile": false + }, + { + "key": "key2", + "value": [{}], + "active": true, + "isFile": true + } + ] + }, + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json index 8e94b7df4..5cff6d510 100644 --- a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json @@ -1,655 +1,655 @@ { - "v": 2, - "id": "clx1f86hv000010f8szcfya0t", - "name": "Multiple child collections with authorization & headers set at each level", - "folders": [ + "v": 3, + "id": "clx1f86hv000010f8szcfya0t", + "name": "Multiple child collections with authorization & headers set at each level", + "folders": [ + { + "v": 3, + "id": "clx1fjgah000110f8a5bs68gd", + "name": "folder-1", + "folders": [ { - "v": 2, - "id": "clx1fjgah000110f8a5bs68gd", - "name": "folder-1", - "folders": [ - { - "v": 2, - "id": "clx1fjwmm000410f8l1gkkr1a", - "name": "folder-11", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "inherit", - "password": "testpass", - "username": "testuser", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-11-request", - "method": "GET", - "params": [], - "headers": [], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-1\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "authType": "inherit", - "authActive": true - }, - "headers": [ - { - "key": "key", - "value": "Set at folder-11", - "active": true - } - ] - }, - { - "v": 2, - "id": "clx1fjyxm000510f8pv90dt43", - "name": "folder-12", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "none", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-12-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-12-request", - "active": true - }, - { - "key": "key", - "value": "Overriden at folder-12-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-12-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-12-request\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "authType": "none", - "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-12", - "active": true - }, - { - "key": "key", - "value": "Set at folder-12", - "active": true - } - ] - }, - { - "v": 2, - "id": "clx1fk1cv000610f88kc3aupy", - "name": "folder-13", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "key": "api-key", - "addTo": "HEADERS", - "value": "api-key-value", - "authType": "basic", - "password": "testpass", - "username": "testuser", - "authActive": true, - "grantTypeInfo": { - "token": "", - "isPKCE": true, - "clientID": "sfasfa", - "password": "", - "username": "", - "grantType": "AUTHORIZATION_CODE", - "authEndpoint": "asfafs", - "clientSecret": "sfasfasf", - "tokenEndpoint": "asfa", - "codeVerifierMethod": "S256" - } - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-13-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header-Request-Level", - "value": "New custom header added at the folder-13-request level", - "active": true - }, - { - "key": "key", - "value": "Overriden at folder-13-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-13\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-13-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-13-request level\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "token": "test-token", - "authType": "bearer", - "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-13", - "active": true - }, - { - "key": "key", - "value": "Set at folder-13", - "active": true - } - ] - } - ], - "requests": [ - { - "v": "4", - "auth": { - "authType": "inherit", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-1-request", - "method": "GET", - "params": [], - "headers": [], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-1\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { + "v": 3, + "id": "clx1fjwmm000410f8l1gkkr1a", + "name": "folder-11", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { "authType": "inherit", + "password": "testpass", + "username": "testuser", "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-1", - "active": true - } - ] + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-11-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-1\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "key", + "value": "Set at folder-11", + "active": true + } + ] }, { - "v": 2, - "id": "clx1fjk9o000210f8j0573pls", - "name": "folder-2", - "folders": [ - { - "v": 2, - "id": "clx1fk516000710f87sfpw6bo", - "name": "folder-21", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "inherit", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-21-request", - "method": "GET", - "params": [], - "headers": [], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-2\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "authType": "inherit", - "authActive": true - }, - "headers": [ - { - "key": "key", - "value": "Set at folder-21", - "active": true - } - ] - }, - { - "v": 2, - "id": "clx1fk72t000810f8gfwkpi5y", - "name": "folder-22", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "none", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-22-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-22-request", - "active": true - }, - { - "key": "key", - "value": "Overriden at folder-22-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-22-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-22-request\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "authType": "none", - "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-22", - "active": true - }, - { - "key": "key", - "value": "Set at folder-22", - "active": true - } - ] - }, - { - "v": 2, - "id": "clx1fk95g000910f8bunhaoo8", - "name": "folder-23", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "basic", - "password": "testpass", - "username": "testuser", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-23-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header-Request-Level", - "value": "New custom header added at the folder-23-request level", - "active": true - }, - { - "key": "key", - "value": "Overriden at folder-23-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-23\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-23-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-23-request level\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "token": "test-token", - "authType": "bearer", - "password": "testpass", - "username": "testuser", - "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-23", - "active": true - }, - { - "key": "key", - "value": "Set at folder-23", - "active": true - } - ] - } - ], - "requests": [ - { - "v": "4", - "auth": { - "authType": "none", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-2-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-2-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-2-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { + "v": 3, + "id": "clx1fjyxm000510f8pv90dt43", + "name": "folder-12", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { "authType": "none", "authActive": true - }, - "headers": [ + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-12-request", + "method": "GET", + "params": [], + "headers": [ { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-2", - "active": true + "key": "Custom-Header", + "value": "Custom header value overriden at folder-12-request", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-12-request", + "active": true } - ] + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-12-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-12-request\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-12", + "active": true + }, + { + "key": "key", + "value": "Set at folder-12", + "active": true + } + ] }, { - "v": 2, - "id": "clx1fjmlq000310f86o4d3w2o", - "name": "folder-3", - "folders": [ - { - "v": 2, - "id": "clx1iwq0p003e10f8u8zg0p85", - "name": "folder-31", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "inherit", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-31-request", - "method": "GET", - "params": [], - "headers": [], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-3\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "authType": "inherit", - "authActive": true - }, - "headers": [ - { - "key": "key", - "value": "Set at folder-31", - "active": true - } - ] - }, - { - "v": 2, - "id": "clx1izut7003m10f894ip59zg", - "name": "folder-32", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "none", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-32-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-32-request", - "active": true - }, - { - "key": "key", - "value": "Overriden at folder-32-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-32-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-32-request\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "authType": "none", - "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-32", - "active": true - }, - { - "key": "key", - "value": "Set at folder-32", - "active": true - } - ] - }, - { - "v": 2, - "id": "clx1j2ka9003q10f8cdbzpgpg", - "name": "folder-33", - "folders": [], - "requests": [ - { - "v": "4", - "auth": { - "authType": "basic", - "password": "testpass", - "username": "testuser", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-33-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header-Request-Level", - "value": "New custom header added at the folder-33-request level", - "active": true - }, - { - "key": "key", - "value": "Overriden at folder-33-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-33\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-33-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-33-request level\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "token": "test-token", - "authType": "bearer", - "password": "testpass", - "username": "testuser", - "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-33", - "active": true - }, - { - "key": "key", - "value": "Set at folder-33", - "active": true - } - ] - } - ], - "requests": [ - { - "v": "4", - "auth": { - "authType": "basic", - "password": "testpass", - "username": "testuser", - "authActive": true - }, - "body": { - "body": null, - "contentType": null - }, - "name": "folder-3-request", - "method": "GET", - "params": [], - "headers": [ - { - "key": "Custom-Header-Request-Level", - "value": "New custom header added at the folder-3-request level", - "active": true - }, - { - "key": "key", - "value": "Set at folder-3-request", - "active": true - } - ], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-3\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Set at folder-3-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-3-request level\")\n})", - "preRequestScript": "", - "requestVariables": [] - } - ], - "auth": { - "key": "testuser", + "v": 3, + "id": "clx1fk1cv000610f88kc3aupy", + "name": "folder-13", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "key": "api-key", "addTo": "HEADERS", - "value": "testpass", + "value": "api-key-value", + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true, + "grantTypeInfo": { + "token": "", + "isPKCE": true, + "clientID": "sfasfa", + "password": "", + "username": "", + "grantType": "AUTHORIZATION_CODE", + "authEndpoint": "asfafs", + "clientSecret": "sfasfasf", + "tokenEndpoint": "asfa", + "codeVerifierMethod": "S256" + } + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-13-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-13-request level", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-13-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-13\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-13-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-13-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "token": "test-token", + "authType": "bearer", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-13", + "active": true + }, + { + "key": "key", + "value": "Set at folder-13", + "active": true + } + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-1-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-1\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-1", + "active": true + } + ] + }, + { + "v": 3, + "id": "clx1fjk9o000210f8j0573pls", + "name": "folder-2", + "folders": [ + { + "v": 3, + "id": "clx1fk516000710f87sfpw6bo", + "name": "folder-21", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-21-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-2\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "key", + "value": "Set at folder-21", + "active": true + } + ] + }, + { + "v": 3, + "id": "clx1fk72t000810f8gfwkpi5y", + "name": "folder-22", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-22-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-22-request", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-22-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-22-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-22-request\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-22", + "active": true + }, + { + "key": "key", + "value": "Set at folder-22", + "active": true + } + ] + }, + { + "v": 3, + "id": "clx1fk95g000910f8bunhaoo8", + "name": "folder-23", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { "authType": "basic", "password": "testpass", "username": "testuser", "authActive": true - }, - "headers": [ - { - "key": "Custom-Header", - "value": "Custom header value overriden at folder-3", - "active": true - } - ] - } - ], - "requests": [ - { - "v": "4", - "auth": { - "authType": "inherit", - "authActive": true - }, - "body": { + }, + "body": { "body": null, "contentType": null + }, + "name": "folder-23-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-23-request level", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-23-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-23\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-23-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-23-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "token": "test-token", + "authType": "bearer", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-23", + "active": true }, - "name": "root-collection-request", - "method": "GET", - "params": [], - "headers": [], - "endpoint": "https://httpbin.org/get", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value set at the root collection\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", - "preRequestScript": "", - "requestVariables": [] + { + "key": "key", + "value": "Set at folder-23", + "active": true + } + ] } - ], - "auth": { + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-2-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-2-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-2-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-2", + "active": true + } + ] + }, + { + "v": 3, + "id": "clx1fjmlq000310f86o4d3w2o", + "name": "folder-3", + "folders": [ + { + "v": 3, + "id": "clx1iwq0p003e10f8u8zg0p85", + "name": "folder-31", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-31-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-3\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "key", + "value": "Set at folder-31", + "active": true + } + ] + }, + { + "v": 3, + "id": "clx1izut7003m10f894ip59zg", + "name": "folder-32", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-32-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-32-request", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-32-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-32-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-32-request\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-32", + "active": true + }, + { + "key": "key", + "value": "Set at folder-32", + "active": true + } + ] + }, + { + "v": 3, + "id": "clx1j2ka9003q10f8cdbzpgpg", + "name": "folder-33", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-33-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-33-request level", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-33-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-33\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-33-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-33-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "token": "test-token", + "authType": "bearer", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-33", + "active": true + }, + { + "key": "key", + "value": "Set at folder-33", + "active": true + } + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-3-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-3-request level", + "active": true + }, + { + "key": "key", + "value": "Set at folder-3-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-3\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Set at folder-3-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-3-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "key": "testuser", + "addTo": "HEADERS", + "value": "testpass", "authType": "basic", "password": "testpass", "username": "testuser", "authActive": true - }, - "headers": [ + }, + "headers": [ { - "key": "Custom-Header", - "value": "Custom header value set at the root collection", - "active": true - }, - { - "key": "Inherited-Header", - "value": "Inherited header at all levels", - "active": true + "key": "Custom-Header", + "value": "Custom header value overriden at folder-3", + "active": true } - ] -} \ No newline at end of file + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "root-collection-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value set at the root collection\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value set at the root collection", + "active": true + }, + { + "key": "Inherited-Header", + "value": "Inherited header at all levels", + "active": true + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/oauth2-auth-code-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/oauth2-auth-code-coll.json new file mode 100644 index 000000000..12c91fbd1 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/oauth2-auth-code-coll.json @@ -0,0 +1,72 @@ +{ + "v": 3, + "name": "OAuth2 Authorization Code Grant Type - Collection", + "folders": [], + "requests": [ + { + "v": "7", + "endpoint": "https://echo.hoppscotch.io", + "name": "oauth2-auth-code-sample-req-pass-by-headers", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "oauth-2", + "authActive": true, + "addTo": "HEADERS", + "grantTypeInfo": { + "authEndpoint": "test-authorization-endpoint", + "tokenEndpoint": "test-token-endpont", + "clientID": "test-client-id", + "clientSecret": "test-client-secret", + "isPKCE": true, + "codeVerifierMethod": "S256", + "grantType": "AUTHORIZATION_CODE", + "token": "test-token" + } + }, + "preRequestScript": "", + "testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully derives Authorization header from the supplied fields\", ()=> {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBeType(\"string\");\n});", + "body": { + "contentType": null, + "body": null + }, + "requestVariables": [] + }, + { + "v": "7", + "endpoint": "https://echo.hoppscotch.io", + "name": "oauth2-auth-code-sample-req-pass-by-query-params", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "oauth-2", + "authActive": true, + "addTo": "HEADERS", + "grantTypeInfo": { + "authEndpoint": "test-authorization-endpoint", + "tokenEndpoint": "test-token-endpont", + "clientID": "test-client-id", + "clientSecret": "test-client-secret", + "isPKCE": true, + "codeVerifierMethod": "S256", + "grantType": "AUTHORIZATION_CODE", + "token": "test-token" + } + }, + "preRequestScript": "", + "testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully derives Authorization header from the supplied fields\", ()=> {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBeType(\"string\");\n});", + "body": { + "contentType": null, + "body": null + }, + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/request-vars-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/request-vars-coll.json new file mode 100644 index 000000000..7eff0a5a1 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/request-vars-coll.json @@ -0,0 +1,188 @@ +{ + "v": 2, + "name": "Request variables", + "folders": [], + "requests": [ + { + "v": "6", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "{\n \"<>\": \"<>\"\n}", + "contentType": "application/json" + }, + "name": "request-variables-basic-usage", + "method": "POST", + "params": [ + { + "key": "<>", + "value": "<>", + "active": true + }, + { + "key": "inactive-query-param-key", + "value": "<>", + "active": true + } + ], + "headers": [ + { + "key": "<>", + "value": "<>", + "active": true + }, + { + "key": "inactive-header-key", + "value": "<>", + "active": true + } + ], + "endpoint": "<>", + "testScript": "pw.test(\"Accounts for active request variables\", ()=> {\n pw.expect(pw.response.body.args[\"query-param-key\"]).toBe(\"query-param-value\");\n\n const data = JSON.parse(pw.response.body.data)\n\n pw.expect(data[\"http-body-raw-key\"]).toBe(\"http-body-raw-value\")\n\n pw.expect(pw.response.body.headers[\"custom-header-key\"]).toBe(\"custom-header-value\");\n});\n\npw.test(\"Ignores inactive request variables\", () => {\n pw.expect(pw.response.body.args[\"inactive-query-param-key\"]).toBe(\"\")\n pw.expect(pw.response.body.args[\"inactive-header-key\"]).toBe(undefined)\n})", + "preRequestScript": "", + "requestVariables": [ + { + "key": "url", + "value": "https://echo.hoppscotch.io", + "active": true + }, + { + "key": "method", + "value": "POST", + "active": true + }, + { + "key": "httpBodyRawKey", + "value": "http-body-raw-key", + "active": true + }, + { + "key": "httpBodyRawValue", + "value": "http-body-raw-value", + "active": true + }, + { + "key": "customHeaderKey", + "value": "custom-header-key", + "active": true + }, + { + "key": "customHeaderValue", + "value": "custom-header-value", + "active": true + }, + { + "key": "queryParamKey", + "value": "query-param-key", + "active": true + }, + { + "key": "queryParamValue", + "value": "query-param-value", + "active": true + }, + { + "key": "inactiveQueryParamValue", + "value": "inactive-query-param-value", + "active": false + }, + { + "key": "inactiveHeaderValue", + "value": "inactive-header-value", + "active": false + } + ] + }, + { + "v": "6", + "auth": { + "authType": "none", + "password": "<>", + "username": "<>", + "authActive": true + }, + "body": { + "body": "{\n \"username\": \"<>\",\n \"password\": \"<>\"\n}", + "contentType": "application/json" + }, + "name": "request-variables-alongside-environment-variables", + "method": "POST", + "params": [ + { + "key": "method", + "value": "<>", + "active": true + } + ], + "headers": [ + { + "key": "test-header-key", + "value": "<>", + "active": true + } + ], + "endpoint": "<>/<>", + "testScript": "pw.test(\"The first occurrence is picked for multiple request variable occurrences with the same key.\", () => {\n pw.expect(pw.response.body.args.method).toBe(\"post\");\n});\n\npw.test(\"Request variables support recursive resolution and pick values from secret environment variables\", () => {\n const { username, password } = JSON.parse(pw.response.body.data)\n\n pw.expect(username).toBe(\"username\")\n pw.expect(password).toBe(\"password\")\n\n})\n\npw.test(\"Resolves request variables that are clubbed together\", () => {\n pw.expect(pw.response.body.path).toBe(\"/username/password\")\n})\n\npw.test(\"Request variables are prioritised over environment variables\", () => {\n pw.expect(pw.response.body.headers.host).toBe(\"echo.hoppscotch.io\")\n})\n\npw.test(\"Environment variable is picked if the request variable under the same name is empty\", () => {\n pw.expect(pw.response.body.headers[\"test-header-key\"]).toBe(\"test-header-value\")\n})", + "preRequestScript": "", + "requestVariables": [ + { + "key": "url", + "value": "https://echo.hoppscotch.io", + "active": true + }, + { + "key": "username", + "value": "<>", + "active": true + }, + { + "key": "recursiveBasicAuthUsernameReqVar", + "value": "<>", + "active": true + }, + { + "key": "password", + "value": "<>", + "active": true + }, + { + "key": "recursiveBasicAuthPasswordReqVar", + "value": "<>", + "active": true + }, + { + "key": "method", + "value": "post", + "active": true + }, + { + "key": "method", + "value": "get", + "active": true + }, + { + "key": "method", + "value": "put", + "active": true + }, + { + "key": "path", + "value": "<>/<>", + "active": true + }, + { + "key": "testHeaderValue", + "value": "", + "active": true + } + ] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/aws-signature-auth-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/aws-signature-auth-envs.json new file mode 100644 index 000000000..534bf7113 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/aws-signature-auth-envs.json @@ -0,0 +1,35 @@ +{ + "v": 1, + "id": "cm0dsn3v70004p4qk3l9b7sjm", + "name": "AWS Signature - environments", + "variables": [ + { + "key": "awsRegion", + "value": "us-east-1", + "secret": false + }, + { + "key": "serviceName", + "value": "s3", + "secret": false + }, + { + "key": "accessKey", + "value": "test-access-key", + "secret": true + }, + { + "key": "secretKey", + "secret": true + }, + { + "key": "url", + "value": "https://echo.hoppscotch.io", + "secret": false + }, + { + "key": "serviceToken", + "secret": true + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/digest-auth-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/digest-auth-envs.json new file mode 100644 index 000000000..eacbcbc07 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/digest-auth-envs.json @@ -0,0 +1,21 @@ +{ + "v": 1, + "id": "cm0dsn3v70004p4qk3l9b7sjm", + "name": "Digest Auth - environments", + "variables": [ + { + "key": "username", + "value": "admin", + "secret": true + }, + { + "key": "password", + "value": "admin", + "secret": true + }, + { + "key": "url", + "value": "https://test.insightres.org/digest/" + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/iteration-data-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/iteration-data-envs.json new file mode 100644 index 000000000..bea639355 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/iteration-data-envs.json @@ -0,0 +1,18 @@ +{ + "v": 0, + "name": "Iteration data environments", + "variables": [ + { + "key": "URL", + "value": "https://httpbin.org/get" + }, + { + "key": "BODY_KEY", + "value": "overriden-body-key-at-environment" + }, + { + "key": "BODY_VALUE", + "value": "overriden-body-value-at-environment" + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/iteration-data-export.csv b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/iteration-data-export.csv new file mode 100644 index 000000000..5caf994ed --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/iteration-data-export.csv @@ -0,0 +1,4 @@ +URL,BODY_KEY,BODY_VALUE +https://echo.hoppscotch.io/1,,body_value1 +https://echo.hoppscotch.io/2,,body_value2 +https://echo.hoppscotch.io/3,,body_value3 diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/request-vars-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/request-vars-envs.json new file mode 100644 index 000000000..844af0108 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/request-vars-envs.json @@ -0,0 +1,34 @@ +{ + "v": 1, + "id": "cm00r7kpb0006mbd2nq1560w6", + "name": "Request variables alongside environment variables", + "variables": [ + { + "key": "url", + "value": "https://echo.hoppscotch.io", + "secret": false + }, + { + "key": "secretBasicAuthPasswordEnvVar", + "secret": true + }, + { + "key": "secretBasicAuthUsernameEnvVar", + "value": "username", + "secret": true + }, + { + "key": "username", + "secret": true + }, + { + "key": "password", + "secret": true + }, + { + "key": "testHeaderValue", + "value": "test-header-value", + "secret": false + } + ] +} diff --git a/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts index e1df4a2df..7fb45aca7 100644 --- a/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts @@ -15,7 +15,6 @@ describe("requestRunner", () => { }; beforeEach(() => { - SAMPLE_REQUEST_CONFIG.supported = false; SAMPLE_REQUEST_CONFIG.url = "https://example.com"; SAMPLE_REQUEST_CONFIG.method = "GET"; jest.clearAllMocks(); @@ -70,7 +69,6 @@ describe("requestRunner", () => { it("Should handle axios-error with request info.", () => { jest.spyOn(axios, "isAxiosError").mockReturnValue(true); - SAMPLE_REQUEST_CONFIG.supported = true; (axios as unknown as jest.Mock).mockRejectedValueOnce({ name: "name", message: "message", @@ -91,7 +89,6 @@ describe("requestRunner", () => { }); it("Should successfully execute.", () => { - SAMPLE_REQUEST_CONFIG.supported = true; (axios as unknown as jest.Mock).mockResolvedValue({ data: "data", status: 200, diff --git a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts index e2a7d6bff..5c52c1c8e 100644 --- a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts +++ b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts @@ -3,6 +3,7 @@ import { Environment, EnvironmentSchemaVersion, HoppCollection, + RESTReqSchemaVersion, } from "@hoppscotch/data"; import { @@ -14,7 +15,7 @@ export const WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Workspa [ { id: "clx1ldkzs005t10f8rp5u60q7", - data: '{"auth":{"token":"BearerToken","authType":"bearer","authActive":true},"headers":[{"key":"X-Test-Header","value":"Set at root collection","active":true}]}', + data: '{"auth":{"token":"BearerToken","authType":"bearer","authActive":true},"headers":[{"key":"X-Test-Header","value":"Set at root collection","active":true,"description":""}]}', title: "CollectionA", parentID: null, folders: [ @@ -78,8 +79,7 @@ export const WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Workspa collectionID: "clx1ldkzs005t10f8rp5u60q7", teamID: "clws3hg58000011o8h07glsb1", title: "RequestA", - request: - '{"v":"5","id":"clpttpdq00003qp16kut6doqv","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestA","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the root collection\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Set at root collection\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Bearer BearerToken\\");\\n});","preRequestScript":"","requestVariables":[]}', + request: `{"v":"${RESTReqSchemaVersion}","id":"clpttpdq00003qp16kut6doqv","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestA","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the root collection\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Set at root collection\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Bearer BearerToken\\");\\n});","preRequestScript":"","requestVariables":[],"responses":{}}`, }, ], }, @@ -178,6 +178,7 @@ export const TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppC key: "X-Test-Header", value: "Overriden at FolderB", active: true, + description: "", }, ], }, @@ -214,7 +215,7 @@ export const TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppC ], requests: [ { - v: "5", + v: RESTReqSchemaVersion, id: "clpttpdq00003qp16kut6doqv", auth: { authType: "inherit", @@ -233,6 +234,7 @@ export const TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppC 'pw.test("Correctly inherits auth and headers from the root collection", ()=> {\n pw.expect(pw.response.body.headers["x-test-header"]).toBe("Set at root collection");\n pw.expect(pw.response.body.headers["authorization"]).toBe("Bearer BearerToken");\n});', preRequestScript: "", requestVariables: [], + responses: {}, }, ], auth: { @@ -245,6 +247,7 @@ export const TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppC key: "X-Test-Header", value: "Set at root collection", active: true, + description: "", }, ], }, @@ -473,8 +476,707 @@ export const WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Worksp collectionID: "clx1f86hv000010f8szcfya0t", teamID: "clws3hg58000011o8h07glsb1", title: "root-collection-request", - request: - '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"root-collection-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value set at the root collection\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + request: `{"v":"${RESTReqSchemaVersion}","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"root-collection-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value set at the root collection\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[],"responses":{}}`, + }, + ], + }, + ]; + +export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppCollection[] = + [ + { + v: 5, + id: "clx1f86hv000010f8szcfya0t", + name: "Multiple child collections with authorization & headers set at each level", + folders: [ + { + v: 5, + id: "clx1fjgah000110f8a5bs68gd", + name: "folder-1", + folders: [ + { + v: 5, + id: "clx1fjwmm000410f8l1gkkr1a", + name: "folder-11", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "inherit", + password: "testpass", + username: "testuser", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-11-request", + method: "GET", + params: [], + headers: [], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-1")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [ + { + key: "key", + value: "Set at folder-11", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1fjyxm000510f8pv90dt43", + name: "folder-12", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "none", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-12-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header", + value: + "Custom header value overriden at folder-12-request", + active: true, + }, + { + key: "key", + value: "Overriden at folder-12-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-12-request")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Overriden at folder-12-request")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "none", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-12", + active: true, + description: "", + }, + { + key: "key", + value: "Set at folder-12", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1fk1cv000610f88kc3aupy", + name: "folder-13", + folders: [], + requests: [ + { + v: "4", + auth: { + key: "api-key", + addTo: "HEADERS", + value: "api-key-value", + authType: "basic", + password: "testpass", + username: "testuser", + authActive: true, + grantTypeInfo: { + token: "", + isPKCE: true, + clientID: "sfasfa", + password: "", + username: "", + grantType: "AUTHORIZATION_CODE", + authEndpoint: "asfafs", + clientSecret: "sfasfasf", + tokenEndpoint: "asfa", + codeVerifierMethod: "S256", + }, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-13-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header-Request-Level", + value: + "New custom header added at the folder-13-request level", + active: true, + }, + { + key: "key", + value: "Overriden at folder-13-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level with new header addition", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-13")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Overriden at folder-13-request")\n pw.expect(pw.response.body.headers["Custom-Header-Request-Level"]).toBe("New custom header added at the folder-13-request level")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + token: "test-token", + authType: "bearer", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-13", + active: true, + description: "", + }, + { + key: "key", + value: "Set at folder-13", + active: true, + description: "", + }, + ], + }, + ], + requests: [ + { + v: "4", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-1-request", + method: "GET", + params: [], + headers: [], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-1")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-1", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1fjk9o000210f8j0573pls", + name: "folder-2", + folders: [ + { + v: 5, + id: "clx1fk516000710f87sfpw6bo", + name: "folder-21", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-21-request", + method: "GET", + params: [], + headers: [], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-2")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [ + { + key: "key", + value: "Set at folder-21", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1fk72t000810f8gfwkpi5y", + name: "folder-22", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "none", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-22-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header", + value: + "Custom header value overriden at folder-22-request", + active: true, + }, + { + key: "key", + value: "Overriden at folder-22-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-22-request")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Overriden at folder-22-request")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "none", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-22", + active: true, + description: "", + }, + { + key: "key", + value: "Set at folder-22", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1fk95g000910f8bunhaoo8", + name: "folder-23", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "basic", + password: "testpass", + username: "testuser", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-23-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header-Request-Level", + value: + "New custom header added at the folder-23-request level", + active: true, + }, + { + key: "key", + value: "Overriden at folder-23-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level with new header addition", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-23")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Overriden at folder-23-request")\n pw.expect(pw.response.body.headers["Custom-Header-Request-Level"]).toBe("New custom header added at the folder-23-request level")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + token: "test-token", + authType: "bearer", + password: "testpass", + username: "testuser", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-23", + active: true, + description: "", + }, + { + key: "key", + value: "Set at folder-23", + active: true, + description: "", + }, + ], + }, + ], + requests: [ + { + v: "4", + auth: { + authType: "none", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-2-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-2-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-2-request")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "none", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-2", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1fjmlq000310f86o4d3w2o", + name: "folder-3", + folders: [ + { + v: 5, + id: "clx1iwq0p003e10f8u8zg0p85", + name: "folder-31", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-31-request", + method: "GET", + params: [], + headers: [], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-3")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [ + { + key: "key", + value: "Set at folder-31", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1izut7003m10f894ip59zg", + name: "folder-32", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "none", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-32-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header", + value: + "Custom header value overriden at folder-32-request", + active: true, + }, + { + key: "key", + value: "Overriden at folder-32-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-32-request")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Overriden at folder-32-request")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "none", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-32", + active: true, + description: "", + }, + { + key: "key", + value: "Set at folder-32", + active: true, + description: "", + }, + ], + }, + { + v: 5, + id: "clx1j2ka9003q10f8cdbzpgpg", + name: "folder-33", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "basic", + password: "testpass", + username: "testuser", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-33-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header-Request-Level", + value: + "New custom header added at the folder-33-request level", + active: true, + }, + { + key: "key", + value: "Overriden at folder-33-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level with new header addition", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-33")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Overriden at folder-33-request")\n pw.expect(pw.response.body.headers["Custom-Header-Request-Level"]).toBe("New custom header added at the folder-33-request level")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + token: "test-token", + authType: "bearer", + password: "testpass", + username: "testuser", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-33", + active: true, + description: "", + }, + { + key: "key", + value: "Set at folder-33", + active: true, + description: "", + }, + ], + }, + ], + requests: [ + { + v: "4", + auth: { + authType: "basic", + password: "testpass", + username: "testuser", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "folder-3-request", + method: "GET", + params: [], + headers: [ + { + key: "Custom-Header-Request-Level", + value: + "New custom header added at the folder-3-request level", + active: true, + }, + { + key: "key", + value: "Set at folder-3-request", + active: true, + }, + ], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits/overrides authorization/header set at the parent collection level with new header addition", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value overriden at folder-3")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n pw.expect(pw.response.body.headers["Key"]).toBe("Set at folder-3-request")\n pw.expect(pw.response.body.headers["Custom-Header-Request-Level"]).toBe("New custom header added at the folder-3-request level")\n})', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + key: "testuser", + addTo: "HEADERS", + value: "testpass", + authType: "basic", + password: "testpass", + username: "testuser", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value overriden at folder-3", + active: true, + description: "", + }, + ], + }, + ], + requests: [ + { + v: RESTReqSchemaVersion, + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "root-collection-request", + method: "GET", + params: [], + headers: [], + endpoint: "https://httpbin.org/get", + testScript: + '// Check status code is 200\npw.test("Status code is 200", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test("Successfully inherits authorization/header set at the parent collection level", () => {\n pw.expect(pw.response.body.headers["Authorization"]).toBe("Basic dGVzdHVzZXI6dGVzdHBhc3M=")\n \n pw.expect(pw.response.body.headers["Custom-Header"]).toBe("Custom header value set at the root collection")\n pw.expect(pw.response.body.headers["Inherited-Header"]).toBe("Inherited header at all levels")\n})', + preRequestScript: "", + requestVariables: [], + responses: {}, + }, + ], + auth: { + authType: "basic", + password: "testpass", + username: "testuser", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Custom header value set at the root collection", + active: true, + description: "", + }, + { + key: "Inherited-Header", + value: "Inherited header at all levels", + active: true, + description: "", }, ], }, @@ -547,12 +1249,12 @@ export const WORKSPACE_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK: export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK: HoppCollection[] = [ { - v: 2, + v: CollectionSchemaVersion, id: "clx1kxvao005m10f8luqivrf1", name: "Collection with no authorization/headers set", folders: [ { - v: 2, + v: CollectionSchemaVersion, id: "clx1kygjt005n10f8m1nkhjux", name: "folder-1", folders: [], @@ -584,7 +1286,7 @@ export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK headers: [], }, { - v: 2, + v: CollectionSchemaVersion, id: "clx1kym98005o10f8qg17t9o2", name: "folder-2", folders: [], @@ -618,11 +1320,12 @@ export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK key: "Custom-Header", value: "Set at folder-2", active: true, + description: "", }, ], }, { - v: 2, + v: CollectionSchemaVersion, id: "clx1l2bu6005r10f8daynohge", name: "folder-3", folders: [], @@ -634,7 +1337,7 @@ export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK headers: [], }, { - v: 2, + v: CollectionSchemaVersion, id: "clx1l2eaz005s10f8loetbbeb", name: "folder-4", folders: [], @@ -648,6 +1351,7 @@ export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK key: "Custom-Header", value: "Set at folder-4", active: true, + description: "", }, ], }, diff --git a/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts index ef3141154..3950a9fe6 100644 --- a/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts @@ -4,7 +4,6 @@ import { describe, expect, test, vi } from "vitest"; import { CollectionSchemaVersion, - Environment, HoppCollection, getDefaultRESTRequest, } from "@hoppscotch/data"; @@ -13,6 +12,7 @@ import { DEFAULT_DURATION_PRECISION } from "../../utils/constants"; import { getDurationInSeconds, getEffectiveFinalMetaData, + getResolvedVariables, getResourceContents, } from "../../utils/getters"; import * as mutators from "../../utils/mutators"; @@ -43,13 +43,14 @@ describe("getters", () => { }); describe("getEffectiveFinalMetaData", () => { - const DEFAULT_ENV = { - name: "name", - variables: [{ key: "PARAM", value: "parsed_param" }], - }; + const environmentVariables = [ + { key: "PARAM", value: "parsed_param", secret: false }, + ]; test("Empty list of meta-data", () => { - expect(getEffectiveFinalMetaData([], DEFAULT_ENV)).toSubsetEqualRight([]); + expect( + getEffectiveFinalMetaData([], environmentVariables) + ).toSubsetEqualRight([]); }); test("Non-empty active list of meta-data with unavailable ENV", () => { @@ -60,9 +61,10 @@ describe("getters", () => { active: true, key: "<>", value: "<>", + description: "", }, ], - DEFAULT_ENV + environmentVariables ) ).toSubsetEqualRight([{ active: true, key: "", value: "" }]); }); @@ -70,8 +72,8 @@ describe("getters", () => { test("Inactive list of meta-data", () => { expect( getEffectiveFinalMetaData( - [{ active: false, key: "KEY", value: "<>" }], - DEFAULT_ENV + [{ active: false, key: "KEY", value: "<>", description: "" }], + environmentVariables ) ).toSubsetEqualRight([]); }); @@ -79,8 +81,8 @@ describe("getters", () => { test("Active list of meta-data", () => { expect( getEffectiveFinalMetaData( - [{ active: true, key: "PARAM", value: "<>" }], - DEFAULT_ENV + [{ active: true, key: "PARAM", value: "<>", description: "" }], + environmentVariables ) ).toSubsetEqualRight([ { active: true, key: "PARAM", value: "parsed_param" }, @@ -386,4 +388,101 @@ describe("getters", () => { }); }); }); + + describe("getResolvedVariables", () => { + const requestVariables = [ + { + key: "SHARED_KEY_I", + value: "request-variable-shared-value-I", + active: true, + }, + { + key: "SHARED_KEY_II", + value: "", + active: true, + }, + { + key: "REQUEST_VAR_III", + value: "request-variable-value-III", + active: true, + }, + { + key: "REQUEST_VAR_IV", + value: "request-variable-value-IV", + active: false, + }, + { + key: "REQUEST_VAR_V", + value: "request-variable-value-V", + active: false, + }, + ]; + + const environmentVariables = [ + { + key: "SHARED_KEY_I", + value: "environment-variable-shared-value-I", + secret: false, + }, + { + key: "SHARED_KEY_II", + value: "environment-variable-shared-value-II", + secret: false, + }, + { + key: "ENV_VAR_III", + value: "environment-variable-value-III", + secret: false, + }, + { + key: "ENV_VAR_IV", + value: "environment-variable-value-IV", + secret: false, + }, + { + key: "ENV_VAR_V", + value: "environment-variable-value-V", + secret: false, + }, + ]; + + test("Filters request variables by active status and value fields, then remove environment variables sharing the same keys", () => { + const expected = [ + { + key: "SHARED_KEY_I", + value: "request-variable-shared-value-I", + secret: false, + }, + { + key: "REQUEST_VAR_III", + value: "request-variable-value-III", + secret: false, + }, + { + key: "SHARED_KEY_II", + value: "environment-variable-shared-value-II", + secret: false, + }, + { + key: "ENV_VAR_III", + value: "environment-variable-value-III", + secret: false, + }, + { + key: "ENV_VAR_IV", + value: "environment-variable-value-IV", + secret: false, + }, + { + key: "ENV_VAR_V", + value: "environment-variable-value-V", + secret: false, + }, + ]; + + expect( + getResolvedVariables(requestVariables, environmentVariables) + ).toEqual(expected); + }); + }); }); diff --git a/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts index 3677b6747..51e5dbf2e 100644 --- a/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts @@ -8,14 +8,13 @@ import { TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK, TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, TRANSFORMED_ENVIRONMENT_MOCK, + TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, WORKSPACE_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK, WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, WORKSPACE_ENVIRONMENT_MOCK, WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, } from "./fixtures/workspace-access.mock"; -import TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK from "../e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json"; - describe("workspace-access", () => { describe("transformWorkspaceCollection", () => { test("Successfully transforms collection data with deeply nested collections and authorization/headers set at each level to the `HoppCollection` format", () => { @@ -31,9 +30,7 @@ describe("workspace-access", () => { transformWorkspaceCollections( WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK ) - ).toEqual([ - TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, - ]); + ).toEqual(TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK); }); test("Adds the default value for `auth` & `header` fields while transforming collections without authorization/headers set at certain levels", () => { diff --git a/packages/hoppscotch-cli/src/commands/test.ts b/packages/hoppscotch-cli/src/commands/test.ts index 6143f5baf..0291fa31f 100644 --- a/packages/hoppscotch-cli/src/commands/test.ts +++ b/packages/hoppscotch-cli/src/commands/test.ts @@ -1,7 +1,14 @@ +import fs from "fs"; +import { isSafeInteger } from "lodash-es"; +import Papa from "papaparse"; +import path from "path"; + import { handleError } from "../handlers/error"; import { parseDelayOption } from "../options/test/delay"; import { parseEnvsData } from "../options/test/env"; +import { IterationDataItem } from "../types/collections"; import { TestCmdEnvironmentOptions, TestCmdOptions } from "../types/commands"; +import { error } from "../types/errors"; import { HoppEnvs } from "../types/request"; import { isHoppCLIError } from "../utils/checks"; import { @@ -13,16 +20,79 @@ import { parseCollectionData } from "../utils/mutators"; export const test = (pathOrId: string, options: TestCmdOptions) => async () => { try { - const delay = options.delay ? parseDelayOption(options.delay) : 0; + const { delay, env, iterationCount, iterationData, reporterJunit } = + options; - const envs = options.env + if ( + iterationCount !== undefined && + (iterationCount < 1 || !isSafeInteger(iterationCount)) + ) { + throw error({ + code: "INVALID_ARGUMENT", + data: "The value must be a positive integer", + }); + } + + const resolvedDelay = delay ? parseDelayOption(delay) : 0; + + const envs = env ? await parseEnvsData(options as TestCmdEnvironmentOptions) : { global: [], selected: [] }; + let parsedIterationData: unknown[] | null = null; + let transformedIterationData: IterationDataItem[][] | undefined; + const collections = await parseCollectionData(pathOrId, options); - const report = await collectionsRunner({ collections, envs, delay }); - const hasSucceeded = collectionsRunnerResult(report, options.reporterJunit); + if (iterationData) { + // Check file existence + if (!fs.existsSync(iterationData)) { + throw error({ code: "FILE_NOT_FOUND", path: iterationData }); + } + + // Check the file extension + if (path.extname(iterationData) !== ".csv") { + throw error({ + code: "INVALID_DATA_FILE_TYPE", + data: iterationData, + }); + } + + const csvData = fs.readFileSync(iterationData, "utf8"); + parsedIterationData = Papa.parse(csvData, { header: true }).data; + + // Transform data into the desired format + transformedIterationData = parsedIterationData + .map((item) => { + const iterationDataItem = item as Record; + const keys = Object.keys(iterationDataItem); + + return ( + keys + // Ignore keys with empty string values + .filter((key) => iterationDataItem[key] !== "") + .map( + (key) => + { + key: key, + value: iterationDataItem[key], + secret: false, + } + ) + ); + }) + // Ignore items that result in an empty array + .filter((item) => item.length > 0); + } + + const report = await collectionsRunner({ + collections, + envs, + delay: resolvedDelay, + iterationData: transformedIterationData, + iterationCount, + }); + const hasSucceeded = collectionsRunnerResult(report, reporterJunit); collectionsRunnerExit(hasSucceeded); } catch (e) { diff --git a/packages/hoppscotch-cli/src/handlers/error.ts b/packages/hoppscotch-cli/src/handlers/error.ts index 65918136e..72be3accf 100644 --- a/packages/hoppscotch-cli/src/handlers/error.ts +++ b/packages/hoppscotch-cli/src/handlers/error.ts @@ -65,6 +65,9 @@ export const handleError = (error: HoppError) => { case "INVALID_FILE_TYPE": ERROR_MSG = `Please provide file of extension type .json: ${error.data}`; break; + case "INVALID_DATA_FILE_TYPE": + ERROR_MSG = `Please provide file of extension type .csv: ${error.data}`; + break; case "REQUEST_ERROR": case "TEST_SCRIPT_ERROR": case "PRE_REQUEST_SCRIPT_ERROR": diff --git a/packages/hoppscotch-cli/src/index.ts b/packages/hoppscotch-cli/src/index.ts index ebd20c01f..7b92a47a0 100644 --- a/packages/hoppscotch-cli/src/index.ts +++ b/packages/hoppscotch-cli/src/index.ts @@ -69,6 +69,15 @@ program "--reporter-junit [path]", "generate JUnit report optionally specifying the path" ) + .option( + "--iteration-count ", + "number of iterations to run the test", + parseInt + ) + .option( + "--iteration-data ", + "path to a CSV file for data-driven testing" + ) .allowExcessArguments(false) .allowUnknownOption(false) .description("running hoppscotch collection.json file") diff --git a/packages/hoppscotch-cli/src/interfaces/request.ts b/packages/hoppscotch-cli/src/interfaces/request.ts index 9eb3982f7..4aec5cbc6 100644 --- a/packages/hoppscotch-cli/src/interfaces/request.ts +++ b/packages/hoppscotch-cli/src/interfaces/request.ts @@ -20,8 +20,7 @@ export interface RequestStack { * @property {boolean} supported - Boolean check for supported or unsupported requests. */ export interface RequestConfig extends AxiosRequestConfig { - supported: boolean; - displayUrl?: string + displayUrl?: string; } export interface EffectiveHoppRESTRequest extends HoppRESTRequest { @@ -32,7 +31,17 @@ export interface EffectiveHoppRESTRequest extends HoppRESTRequest { */ effectiveFinalURL: string; effectiveFinalDisplayURL?: string; - effectiveFinalHeaders: { key: string; value: string; active: boolean }[]; - effectiveFinalParams: { key: string; value: string; active: boolean }[]; - effectiveFinalBody: FormData | string | null; + effectiveFinalHeaders: { + key: string; + value: string; + active: boolean; + description: string; + }[]; + effectiveFinalParams: { + key: string; + value: string; + active: boolean; + description: string; + }[]; + effectiveFinalBody: FormData | string | File | null; } diff --git a/packages/hoppscotch-cli/src/options/test/env.ts b/packages/hoppscotch-cli/src/options/test/env.ts index d42450344..cc6746525 100644 --- a/packages/hoppscotch-cli/src/options/test/env.ts +++ b/packages/hoppscotch-cli/src/options/test/env.ts @@ -1,4 +1,4 @@ -import { Environment } from "@hoppscotch/data"; +import { Environment, NonSecretEnvironment } from "@hoppscotch/data"; import { entityReference } from "verzod"; import { z } from "zod"; @@ -63,7 +63,27 @@ export async function parseEnvsData(options: TestCmdEnvironmentOptions) { envPairs.push({ key, value, secret: false }); } } else if (HoppEnvExportObjectResult.type === "ok") { - envPairs.push(...HoppEnvExportObjectResult.value.variables); + // Original environment variables from the supplied export file + const originalEnvVariables = (contents as NonSecretEnvironment).variables; + + // Above environment variables conforming to the latest schema + // `value` fields if specified will be omitted for secret environment variables + const migratedEnvVariables = HoppEnvExportObjectResult.value.variables; + + // The values supplied for secret environment variables have to be considered in the CLI + // For each secret environment variable, include the value in case supplied + const resolvedEnvVariables = migratedEnvVariables.map((variable, idx) => { + if (variable.secret && originalEnvVariables[idx].value) { + return { + ...variable, + value: originalEnvVariables[idx].value, + }; + } + + return variable; + }); + + envPairs.push(...resolvedEnvVariables); } return { global: [], selected: envPairs }; diff --git a/packages/hoppscotch-cli/src/types/collections.ts b/packages/hoppscotch-cli/src/types/collections.ts index f5483e2d4..08c6ccb75 100644 --- a/packages/hoppscotch-cli/src/types/collections.ts +++ b/packages/hoppscotch-cli/src/types/collections.ts @@ -1,10 +1,15 @@ import { HoppCollection } from "@hoppscotch/data"; -import { HoppEnvs } from "./request"; +import { HoppEnvPair, HoppEnvs } from "./request"; export type CollectionRunnerParam = { collections: HoppCollection[]; envs: HoppEnvs; delay?: number; + iterationData?: IterationDataItem[][]; + iterationCount?: number; }; export type HoppCollectionFileExt = "json"; + +// Indicates the shape each iteration data entry gets transformed into +export type IterationDataItem = Extract; diff --git a/packages/hoppscotch-cli/src/types/commands.ts b/packages/hoppscotch-cli/src/types/commands.ts index 71fd51f6b..e353cb7d3 100644 --- a/packages/hoppscotch-cli/src/types/commands.ts +++ b/packages/hoppscotch-cli/src/types/commands.ts @@ -4,6 +4,8 @@ export type TestCmdOptions = { token?: string; server?: string; reporterJunit?: string; + iterationCount?: number; + iterationData?: string; }; // Consumed in the collection `file_path_or_id` argument action handler diff --git a/packages/hoppscotch-cli/src/types/errors.ts b/packages/hoppscotch-cli/src/types/errors.ts index 354b2da4f..6122ff797 100644 --- a/packages/hoppscotch-cli/src/types/errors.ts +++ b/packages/hoppscotch-cli/src/types/errors.ts @@ -26,6 +26,7 @@ type HoppErrors = { MALFORMED_ENV_FILE: HoppErrorPath & HoppErrorData; BULK_ENV_FILE: HoppErrorPath & HoppErrorData; INVALID_FILE_TYPE: HoppErrorData; + INVALID_DATA_FILE_TYPE: HoppErrorData; TOKEN_EXPIRED: HoppErrorData; TOKEN_INVALID: HoppErrorData; INVALID_ID: HoppErrorData; diff --git a/packages/hoppscotch-cli/src/types/request.ts b/packages/hoppscotch-cli/src/types/request.ts index 21dffbd1c..0f6f780f5 100644 --- a/packages/hoppscotch-cli/src/types/request.ts +++ b/packages/hoppscotch-cli/src/types/request.ts @@ -7,6 +7,7 @@ import { HoppCLIError } from "./errors"; export type FormDataEntry = { key: string; value: string | Blob; + contentType?: string; }; export type HoppEnvPair = Environment["variables"][number]; @@ -18,7 +19,7 @@ export type HoppEnvs = { selected: HoppEnvPair[]; }; -export type CollectionStack = { +export type CollectionQueue = { path: string; collection: HoppCollection; }; diff --git a/packages/hoppscotch-cli/src/utils/auth/digest.ts b/packages/hoppscotch-cli/src/utils/auth/digest.ts new file mode 100644 index 000000000..7c8d65ddd --- /dev/null +++ b/packages/hoppscotch-cli/src/utils/auth/digest.ts @@ -0,0 +1,158 @@ +import axios from "axios"; +import { md5 } from "js-md5"; + +import { exceptionColors } from "../getters"; + +export interface DigestAuthParams { + username: string; + password: string; + realm: string; + nonce: string; + endpoint: string; + method: string; + algorithm: string; + qop: string; + nc?: string; + opaque?: string; + cnonce?: string; // client nonce (optional but typically required in qop='auth') + reqBody?: string; +} + +export interface DigestAuthInfo { + realm: string; + nonce: string; + qop: string; + opaque?: string; + algorithm: string; +} + +// Utility function to parse Digest auth header values +const parseDigestAuthHeader = ( + header: string +): { [key: string]: string } | null => { + const matches = header.match(/([a-z0-9]+)="([^"]+)"/gi); + if (!matches) return null; + + const authParams: { [key: string]: string } = {}; + matches.forEach((match) => { + const parts = match.split("="); + authParams[parts[0]] = parts[1].replace(/"/g, ""); + }); + + return authParams; +}; + +// Function to generate Digest Auth Header +export const generateDigestAuthHeader = async (params: DigestAuthParams) => { + const { + username, + password, + realm, + nonce, + endpoint, + method, + algorithm = "MD5", + qop, + nc = "00000001", + opaque, + cnonce, + reqBody = "", + } = params; + + const url = new URL(endpoint); + const uri = url.pathname + url.search; + + // Generate client nonce if not provided + const generatedCnonce = cnonce || md5(`${Math.random()}`); + + // Step 1: Hash the username, realm, password and any additional fields based on the algorithm + const ha1 = + algorithm === "MD5-sess" + ? md5( + `${md5(`${username}:${realm}:${password}`)}:${nonce}:${generatedCnonce}` + ) + : md5(`${username}:${realm}:${password}`); + + // Step 2: Hash the method and URI + const ha2 = + qop === "auth-int" + ? md5(`${method}:${uri}:${md5(reqBody)}`) // Entity body hash for `auth-int` + : md5(`${method}:${uri}`); + + // Step 3: Compute the response hash + const response = md5( + `${ha1}:${nonce}:${nc}:${generatedCnonce}:${qop}:${ha2}` + ); + + // Build the Digest header + let authHeader = `Digest username="${username}", realm="${realm}", nonce="${nonce}", uri="${uri}", algorithm="${algorithm}", response="${response}", qop=${qop}, nc=${nc}, cnonce="${generatedCnonce}"`; + + if (opaque) { + authHeader += `, opaque="${opaque}"`; + } + + return authHeader; +}; + +export const fetchInitialDigestAuthInfo = async ( + url: string, + method: string, + disableRetry: boolean +): Promise => { + try { + const initialResponse = await axios.request({ + url, + method, + validateStatus: () => true, // Allow handling of all status codes + }); + + if (disableRetry) { + throw new Error( + `Received status: ${initialResponse.status}. Retry is disabled as specified, so no further attempts will be made.` + ); + } + + // Check if the response status is 401 (which is expected in Digest Auth flow) + if (initialResponse.status === 401) { + const authHeaderEntry = Object.keys(initialResponse.headers).find( + (header) => header.toLowerCase() === "www-authenticate" + ); + + const authHeader = authHeaderEntry + ? (initialResponse.headers[authHeaderEntry] ?? null) + : null; + + if (authHeader) { + const authParams = parseDigestAuthHeader(authHeader); + if ( + authParams && + authParams.realm && + authParams.nonce && + authParams.qop + ) { + return { + realm: authParams.realm, + nonce: authParams.nonce, + qop: authParams.qop, + opaque: authParams.opaque, + algorithm: authParams.algorithm, + }; + } + } + throw new Error( + "Failed to parse authentication parameters from WWW-Authenticate header" + ); + } + + throw new Error(`Unexpected response: ${initialResponse.status}`); + } catch (error) { + const errMsg = error instanceof Error ? error.message : error; + + console.error( + exceptionColors.FAIL( + `\n Error fetching initial digest auth info: ${errMsg} \n` + ) + ); + throw error; // Re-throw the error to handle it further up the chain if needed + } +}; diff --git a/packages/hoppscotch-cli/src/utils/collections.ts b/packages/hoppscotch-cli/src/utils/collections.ts index 9a6ef5ef1..5c81d5d7d 100644 --- a/packages/hoppscotch-cli/src/utils/collections.ts +++ b/packages/hoppscotch-cli/src/utils/collections.ts @@ -7,7 +7,7 @@ import { round } from "lodash-es"; import { CollectionRunnerParam } from "../types/collections"; import { - CollectionStack, + CollectionQueue, HoppEnvs, ProcessRequestParams, RequestReport, @@ -35,7 +35,7 @@ import { } from "./request"; import { getTestMetrics } from "./test"; -const { WARN, FAIL } = exceptionColors; +const { WARN, FAIL, INFO } = exceptionColors; /** * Processes each requests within collections to prints details of subsequent requests, @@ -43,93 +43,134 @@ const { WARN, FAIL } = exceptionColors; * @param param Data of hopp-collection with hopp-requests, envs to be processed. * @returns List of report for each processed request. */ + export const collectionsRunner = async ( param: CollectionRunnerParam ): Promise => { - const envs: HoppEnvs = param.envs; - const delay = param.delay ?? 0; + const { collections, envs, delay, iterationCount, iterationData } = param; + + const resolvedDelay = delay ?? 0; + const requestsReport: RequestReport[] = []; - const collectionStack: CollectionStack[] = getCollectionStack( - param.collections - ); + const collectionQueue = getCollectionQueue(collections); - while (collectionStack.length) { - // Pop out top-most collection from stack to be processed. - const { collection, path } = collectionStack.pop(); + // If iteration count is not supplied, it should be based on the size of iteration data if in scope + const resolvedIterationCount = iterationCount ?? iterationData?.length ?? 1; - // Processing each request in collection - for (const request of collection.requests) { - const _request = preProcessRequest( - request as HoppRESTRequest, - collection - ); - const requestPath = `${path}/${_request.name}`; - const processRequestParams: ProcessRequestParams = { - path: requestPath, - request: _request, - envs, - delay, - }; + const originalSelectedEnvs = [...envs.selected]; - // Request processing initiated message. - log(WARN(`\nRunning: ${chalk.bold(requestPath)}`)); - - // Processing current request. - const result = await processRequest(processRequestParams)(); - - // Updating global & selected envs with new envs from processed-request output. - const { global, selected } = result.envs; - envs.global = global; - envs.selected = selected; - - // Storing current request's report. - const requestReport = result.report; - requestsReport.push(requestReport); + for (let count = 0; count < resolvedIterationCount; count++) { + if (resolvedIterationCount > 1) { + log(INFO(`\nIteration: ${count + 1}/${resolvedIterationCount}`)); } - // Pushing remaining folders realted collection to stack. - for (const folder of collection.folders) { - const updatedFolder: HoppCollection = { ...folder }; + // Reset `envs` to the original value at the start of each iteration + envs.selected = [...originalSelectedEnvs]; - if (updatedFolder.auth?.authType === "inherit") { - updatedFolder.auth = collection.auth; - } + if (iterationData) { + // Ensure last item is picked if the iteration count exceeds size of the iteration data + const iterationDataItem = + iterationData[Math.min(count, iterationData.length - 1)]; - if (collection.headers?.length) { - // Filter out header entries present in the parent collection under the same name - // This ensures the folder headers take precedence over the collection headers - const filteredHeaders = collection.headers.filter( - (collectionHeaderEntries) => { - return !updatedFolder.headers.some( - (folderHeaderEntries) => - folderHeaderEntries.key === collectionHeaderEntries.key - ); - } - ); - updatedFolder.headers.push(...filteredHeaders); - } + // Ensure iteration data takes priority over supplied environment variables + envs.selected = envs.selected + .filter( + (envPair) => + !iterationDataItem.some((dataPair) => dataPair.key === envPair.key) + ) + .concat(iterationDataItem); + } - collectionStack.push({ - path: `${path}/${updatedFolder.name}`, - collection: updatedFolder, - }); + for (const { collection, path } of collectionQueue) { + await processCollection( + collection, + path, + envs, + resolvedDelay, + requestsReport + ); } } return requestsReport; }; +const processCollection = async ( + collection: HoppCollection, + path: string, + envs: HoppEnvs, + delay: number, + requestsReport: RequestReport[] +) => { + // Process each request in the collection + for (const request of collection.requests) { + const _request = preProcessRequest(request as HoppRESTRequest, collection); + const requestPath = `${path}/${_request.name}`; + const processRequestParams: ProcessRequestParams = { + path: requestPath, + request: _request, + envs, + delay, + }; + + // Request processing initiated message. + log(WARN(`\nRunning: ${chalk.bold(requestPath)}`)); + + // Processing current request. + const result = await processRequest(processRequestParams)(); + + // Updating global & selected envs with new envs from processed-request output. + const { global, selected } = result.envs; + envs.global = global; + envs.selected = selected; + + // Storing current request's report. + const requestReport = result.report; + requestsReport.push(requestReport); + } + + // Process each folder in the collection + for (const folder of collection.folders) { + const updatedFolder: HoppCollection = { ...folder }; + + if (updatedFolder.auth?.authType === "inherit") { + updatedFolder.auth = collection.auth; + } + + if (collection.headers?.length) { + // Filter out header entries present in the parent collection under the same name + // This ensures the folder headers take precedence over the collection headers + const filteredHeaders = collection.headers.filter( + (collectionHeaderEntries) => { + return !updatedFolder.headers.some( + (folderHeaderEntries) => + folderHeaderEntries.key === collectionHeaderEntries.key + ); + } + ); + updatedFolder.headers.push(...filteredHeaders); + } + + await processCollection( + updatedFolder, + `${path}/${updatedFolder.name}`, + envs, + delay, + requestsReport + ); + } +}; /** * Transforms collections to generate collection-stack which describes each collection's * path within collection & the collection itself. * @param collections Hopp-collection objects to be mapped to collection-stack type. * @returns Mapped collections to collection-stack. */ -const getCollectionStack = (collections: HoppCollection[]): CollectionStack[] => +const getCollectionQueue = (collections: HoppCollection[]): CollectionQueue[] => pipe( collections, A.map( - (collection) => { collection, path: collection.name } + (collection) => { collection, path: collection.name } ) ); diff --git a/packages/hoppscotch-cli/src/utils/getters.ts b/packages/hoppscotch-cli/src/utils/getters.ts index 5df063cb1..07fe43fc2 100644 --- a/packages/hoppscotch-cli/src/utils/getters.ts +++ b/packages/hoppscotch-cli/src/utils/getters.ts @@ -1,8 +1,8 @@ import { - Environment, - HoppCollection, + EnvironmentVariable, HoppRESTHeader, HoppRESTParam, + HoppRESTRequestVariables, parseTemplateStringE, } from "@hoppscotch/data"; import axios, { AxiosError } from "axios"; @@ -58,12 +58,12 @@ export const getColorStatusCode = ( * Replaces all template-string with their effective ENV values to generate effective * request headers/parameters meta-data. * @param metaData Headers/parameters on which ENVs will be applied. - * @param environment Provides ENV variables for parsing template-string. + * @param resolvedVariables Provides ENV variables for parsing template-string. * @returns Active, non-empty-key, parsed headers/parameters pairs. */ export const getEffectiveFinalMetaData = ( metaData: HoppRESTHeader[] | HoppRESTParam[], - environment: Environment + resolvedVariables: EnvironmentVariable[] ) => pipe( metaData, @@ -72,11 +72,14 @@ export const getEffectiveFinalMetaData = ( * Selecting only non-empty and active pairs. */ A.filter(({ key, active }) => !S.isEmpty(key) && active), - A.map(({ key, value }) => ({ - active: true, - key: parseTemplateStringE(key, environment.variables), - value: parseTemplateStringE(value, environment.variables), - })), + A.map(({ key, value, description }) => { + return { + active: true, + key: parseTemplateStringE(key, resolvedVariables), + value: parseTemplateStringE(value, resolvedVariables), + description, + }; + }), E.fromPredicate( /** * Check if every key-value is right either. Else return HoppCLIError with @@ -89,9 +92,14 @@ export const getEffectiveFinalMetaData = ( /** * Filtering and mapping only right-eithers for each key-value as [string, string]. */ - A.filterMap(({ key, value }) => + A.filterMap(({ key, value, description }) => E.isRight(key) && E.isRight(value) - ? O.some({ active: true, key: key.right, value: value.right }) + ? O.some({ + active: true, + key: key.right, + value: value.right, + description, + }) : O.none ) ) @@ -253,3 +261,30 @@ export const getResourceContents = async ( return contents; }; + +/** + * Processes incoming request variables and environment variables and returns a list + * where active request variables are picked and prioritised over the supplied environment variables. + * Falls back to environment variables for an empty request variable. + * + * @param {HoppRESTRequestVariables} requestVariables - Incoming request variables. + * @param {EnvironmentVariable[]} environmentVariables - Incoming environment variables. + * @returns {EnvironmentVariable[]} The resolved list of variables that conforms to the shape of environment variables. + */ +export const getResolvedVariables = ( + requestVariables: HoppRESTRequestVariables, + environmentVariables: EnvironmentVariable[] +): EnvironmentVariable[] => { + const activeRequestVariables = requestVariables + .filter(({ active, value }) => active && value) + .map(({ key, value }) => ({ key, value, secret: false })); + + const requestVariableKeys = activeRequestVariables.map(({ key }) => key); + + // Request variables have higher priority, hence filtering out environment variables with the same keys + const filteredEnvironmentVariables = environmentVariables.filter( + ({ key }) => !requestVariableKeys.includes(key) + ); + + return [...activeRequestVariables, ...filteredEnvironmentVariables]; +}; diff --git a/packages/hoppscotch-cli/src/utils/mutators.ts b/packages/hoppscotch-cli/src/utils/mutators.ts index 6c4ac6547..153e66b24 100644 --- a/packages/hoppscotch-cli/src/utils/mutators.ts +++ b/packages/hoppscotch-cli/src/utils/mutators.ts @@ -52,7 +52,21 @@ const getValidRequests = ( export const toFormData = (values: FormDataEntry[]) => { const formData = new FormData(); - values.forEach(({ key, value }) => formData.append(key, value)); + values.forEach(({ key, value, contentType }) => { + if (contentType) { + formData.append( + key, + new Blob([value], { + type: contentType, + }), + key + ); + + return; + } + + formData.append(key, value); + }); return formData; }; diff --git a/packages/hoppscotch-cli/src/utils/pre-request.ts b/packages/hoppscotch-cli/src/utils/pre-request.ts index f39410644..de4563132 100644 --- a/packages/hoppscotch-cli/src/utils/pre-request.ts +++ b/packages/hoppscotch-cli/src/utils/pre-request.ts @@ -1,5 +1,6 @@ import { Environment, + EnvironmentVariable, HoppRESTRequest, parseBodyEnvVariablesE, parseRawKeyValueEntriesE, @@ -15,6 +16,7 @@ import * as TE from "fp-ts/TaskEither"; import { flow, pipe } from "fp-ts/function"; import * as S from "fp-ts/string"; import qs from "qs"; +import { AwsV4Signer } from "aws4fetch"; import { EffectiveHoppRESTRequest } from "../interfaces/request"; import { HoppCLIError, error } from "../types/errors"; @@ -22,8 +24,13 @@ import { HoppEnvs } from "../types/request"; import { PreRequestMetrics } from "../types/response"; import { isHoppCLIError } from "./checks"; import { arrayFlatMap, arraySort, tupleToRecord } from "./functions/array"; -import { getEffectiveFinalMetaData } from "./getters"; +import { getEffectiveFinalMetaData, getResolvedVariables } from "./getters"; import { toFormData } from "./mutators"; +import { + DigestAuthParams, + fetchInitialDigestAuthInfo, + generateDigestAuthHeader, +} from "./auth/digest"; /** * Runs pre-request-script runner over given request which extracts set ENVs and @@ -52,7 +59,13 @@ export const preRequestScriptRunner = ( variables: [...(selected ?? []), ...(global ?? [])], } ), - TE.chainEitherKW((env) => getEffectiveRESTRequest(request, env)), + TE.chainW((env) => + TE.tryCatch( + () => getEffectiveRESTRequest(request, env), + (reason) => error({ code: "PRE_REQUEST_SCRIPT_ERROR", data: reason }) + ) + ), + TE.chainEitherKW((effectiveRequest) => effectiveRequest), TE.mapLeft((reason) => isHoppCLIError(reason) ? reason @@ -71,19 +84,26 @@ export const preRequestScriptRunner = ( * * @returns An object with extra fields defining a complete request */ -export function getEffectiveRESTRequest( +export async function getEffectiveRESTRequest( request: HoppRESTRequest, environment: Environment -): E.Either< - HoppCLIError, - { effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs } +): Promise< + E.Either< + HoppCLIError, + { effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs } + > > { const envVariables = environment.variables; + const resolvedVariables = getResolvedVariables( + request.requestVariables, + envVariables + ); + // Parsing final headers with applied ENVs. const _effectiveFinalHeaders = getEffectiveFinalMetaData( request.headers, - environment + resolvedVariables ); if (E.isLeft(_effectiveFinalHeaders)) { return _effectiveFinalHeaders; @@ -93,30 +113,47 @@ export function getEffectiveRESTRequest( // Parsing final parameters with applied ENVs. const _effectiveFinalParams = getEffectiveFinalMetaData( request.params, - environment + resolvedVariables ); if (E.isLeft(_effectiveFinalParams)) { return _effectiveFinalParams; } const effectiveFinalParams = _effectiveFinalParams.right; + // Parsing final-body with applied ENVs. + const _effectiveFinalBody = getFinalBodyFromRequest( + request, + resolvedVariables + ); + if (E.isLeft(_effectiveFinalBody)) { + return _effectiveFinalBody; + } + // Authentication if (request.auth.authActive) { // TODO: Support a better b64 implementation than btoa ? if (request.auth.authType === "basic") { - const username = parseTemplateString(request.auth.username, envVariables); - const password = parseTemplateString(request.auth.password, envVariables); + const username = parseTemplateString( + request.auth.username, + resolvedVariables + ); + const password = parseTemplateString( + request.auth.password, + resolvedVariables + ); effectiveFinalHeaders.push({ active: true, key: "Authorization", value: `Basic ${btoa(`${username}:${password}`)}`, + description: "", }); } else if (request.auth.authType === "bearer") { effectiveFinalHeaders.push({ active: true, key: "Authorization", - value: `Bearer ${parseTemplateString(request.auth.token, envVariables)}`, + value: `Bearer ${parseTemplateString(request.auth.token, resolvedVariables)}`, + description: "", }); } else if (request.auth.authType === "oauth-2") { const { addTo } = request.auth; @@ -125,7 +162,8 @@ export function getEffectiveRESTRequest( effectiveFinalHeaders.push({ active: true, key: "Authorization", - value: `Bearer ${parseTemplateString(request.auth.grantTypeInfo.token, envVariables)}`, + value: `Bearer ${parseTemplateString(request.auth.grantTypeInfo.token, resolvedVariables)}`, + description: "", }); } else if (addTo === "QUERY_PARAMS") { effectiveFinalParams.push({ @@ -133,8 +171,9 @@ export function getEffectiveRESTRequest( key: "access_token", value: parseTemplateString( request.auth.grantTypeInfo.token, - envVariables + resolvedVariables ), + description: "", }); } } else if (request.auth.authType === "api-key") { @@ -142,37 +181,135 @@ export function getEffectiveRESTRequest( if (addTo === "HEADERS") { effectiveFinalHeaders.push({ active: true, - key: parseTemplateString(key, envVariables), - value: parseTemplateString(value, envVariables), + key: parseTemplateString(key, resolvedVariables), + value: parseTemplateString(value, resolvedVariables), + description: "", }); } else if (addTo === "QUERY_PARAMS") { effectiveFinalParams.push({ active: true, - key: parseTemplateString(key, envVariables), - value: parseTemplateString(value, envVariables), + key: parseTemplateString(key, resolvedVariables), + value: parseTemplateString(value, resolvedVariables), + description: "", }); } + } else if (request.auth.authType === "aws-signature") { + const { addTo } = request.auth; + + const currentDate = new Date(); + const amzDate = currentDate.toISOString().replace(/[:-]|\.\d{3}/g, ""); + const { method, endpoint } = request; + + const signer = new AwsV4Signer({ + method, + datetime: amzDate, + signQuery: addTo === "QUERY_PARAMS", + accessKeyId: parseTemplateString( + request.auth.accessKey, + resolvedVariables + ), + secretAccessKey: parseTemplateString( + request.auth.secretKey, + resolvedVariables + ), + region: + parseTemplateString(request.auth.region, resolvedVariables) ?? + "us-east-1", + service: parseTemplateString( + request.auth.serviceName, + resolvedVariables + ), + url: parseTemplateString(endpoint, resolvedVariables), + sessionToken: + request.auth.serviceToken && + parseTemplateString(request.auth.serviceToken, resolvedVariables), + }); + + const sign = await signer.sign(); + + if (addTo === "HEADERS") { + sign.headers.forEach((value, key) => { + effectiveFinalHeaders.push({ + active: true, + key, + value, + description: "", + }); + }); + } else if (addTo === "QUERY_PARAMS") { + sign.url.searchParams.forEach((value, key) => { + effectiveFinalParams.push({ + active: true, + key, + value, + description: "", + }); + }); + } + } else if (request.auth.authType === "digest") { + const { method, endpoint } = request as HoppRESTRequest; + + // Step 1: Fetch the initial auth info (nonce, realm, etc.) + const authInfo = await fetchInitialDigestAuthInfo( + parseTemplateString(endpoint, resolvedVariables), + method, + request.auth.disableRetry + ); + + // Step 2: Set up the parameters for the digest authentication header + const digestAuthParams: DigestAuthParams = { + username: parseTemplateString(request.auth.username, resolvedVariables), + password: parseTemplateString(request.auth.password, resolvedVariables), + realm: request.auth.realm + ? parseTemplateString(request.auth.realm, resolvedVariables) + : authInfo.realm, + nonce: request.auth.nonce + ? parseTemplateString(authInfo.nonce, resolvedVariables) + : authInfo.nonce, + endpoint: parseTemplateString(endpoint, resolvedVariables), + method, + algorithm: request.auth.algorithm ?? authInfo.algorithm, + qop: request.auth.qop + ? parseTemplateString(request.auth.qop, resolvedVariables) + : authInfo.qop, + opaque: request.auth.opaque + ? parseTemplateString(request.auth.opaque, resolvedVariables) + : authInfo.opaque, + reqBody: typeof request.body.body === "string" ? request.body.body : "", + }; + + // Step 3: Generate the Authorization header + const authHeaderValue = await generateDigestAuthHeader(digestAuthParams); + + effectiveFinalHeaders.push({ + active: true, + key: "Authorization", + value: authHeaderValue, + description: "", + }); } } - // Parsing final-body with applied ENVs. - const _effectiveFinalBody = getFinalBodyFromRequest(request, envVariables); - if (E.isLeft(_effectiveFinalBody)) { - return _effectiveFinalBody; - } const effectiveFinalBody = _effectiveFinalBody.right; - if (request.body.contentType) + if ( + request.body.contentType && + !effectiveFinalHeaders.some( + ({ key }) => key.toLowerCase() === "content-type" + ) + ) { effectiveFinalHeaders.push({ active: true, - key: "content-type", + key: "Content-Type", value: request.body.contentType, + description: "", }); + } - // Parsing final-endpoint with applied ENVs. + // Parsing final-endpoint with applied ENVs (environment + request variables). const _effectiveFinalURL = parseTemplateStringE( request.endpoint, - envVariables + resolvedVariables ); if (E.isLeft(_effectiveFinalURL)) { return E.left( @@ -189,7 +326,7 @@ export function getEffectiveRESTRequest( if (envVariables.some(({ secret }) => secret)) { const _effectiveFinalDisplayURL = parseTemplateStringE( request.endpoint, - envVariables, + resolvedVariables, true ); @@ -207,7 +344,7 @@ export function getEffectiveRESTRequest( effectiveFinalParams, effectiveFinalBody, }, - updatedEnvs: { global: [], selected: envVariables }, + updatedEnvs: { global: [], selected: resolvedVariables }, }); } @@ -215,15 +352,15 @@ export function getEffectiveRESTRequest( * Replaces template variables in request's body from the given set of ENVs, * to generate final request body without any template variables. * @param request Provides request's body, on which ENVs has to be applied. - * @param envVariables Provides set of key-value pairs (environment variables), + * @param resolvedVariables Provides set of key-value pairs (request + environment variables), * used to parse-out template variables. * @returns Final request body without any template variables as value. * Or, HoppCLIError in case of error while parsing. */ function getFinalBodyFromRequest( request: HoppRESTRequest, - envVariables: Environment["variables"] -): E.Either { + resolvedVariables: EnvironmentVariable[] +): E.Either { if (request.body.contentType === null) { return E.right(null); } @@ -246,8 +383,8 @@ function getFinalBodyFromRequest( * which will be resolved in further steps. */ A.map(({ key, value }) => [ - parseTemplateStringE(key, envVariables), - parseTemplateStringE(value, envVariables), + parseTemplateStringE(key, resolvedVariables), + parseTemplateStringE(value, resolvedVariables), ]), /** @@ -283,13 +420,15 @@ function getFinalBodyFromRequest( arrayFlatMap((x) => x.isFile ? x.value.map((v) => ({ - key: parseTemplateString(x.key, envVariables), + key: parseTemplateString(x.key, resolvedVariables), value: v as string | Blob, + contentType: x.contentType, })) : [ { - key: parseTemplateString(x.key, envVariables), - value: parseTemplateString(x.value, envVariables), + key: parseTemplateString(x.key, resolvedVariables), + value: parseTemplateString(x.value, resolvedVariables), + contentType: x.contentType, }, ] ), @@ -298,8 +437,22 @@ function getFinalBodyFromRequest( ); } + if (request.body.contentType === "application/octet-stream") { + const body = request.body.body; + + if (!body) { + return E.right(null); + } + + if (!(body instanceof File)) { + return E.right(null); + } + + return E.right(body); + } + return pipe( - parseBodyEnvVariablesE(request.body.body, envVariables), + parseBodyEnvVariablesE(request.body.body, resolvedVariables), E.mapLeft((e) => error({ code: "PARSING_ERROR", diff --git a/packages/hoppscotch-cli/src/utils/request.ts b/packages/hoppscotch-cli/src/utils/request.ts index 16a02c519..a46cd0694 100644 --- a/packages/hoppscotch-cli/src/utils/request.ts +++ b/packages/hoppscotch-cli/src/utils/request.ts @@ -1,4 +1,9 @@ -import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; +import { + Environment, + HoppCollection, + HoppRESTRequest, + RESTReqSchemaVersion, +} from "@hoppscotch/data"; import axios, { Method } from "axios"; import * as A from "fp-ts/Array"; import * as E from "fp-ts/Either"; @@ -27,8 +32,6 @@ import { getDurationInSeconds, getMetaDataPairs } from "./getters"; import { preRequestScriptRunner } from "./pre-request"; import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test"; -// !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported - /** * Processes given variable, which includes checking for secret variables * and getting value from system environment @@ -55,8 +58,8 @@ const processVariables = (variable: Environment["variables"][number]) => { const processEnvs = (envs: Partial) => { // This can take the shape `{ global: undefined, selected: undefined }` when no environment is supplied const processedEnvs = { - global: envs.global?.map(processVariables), - selected: envs.selected?.map(processVariables), + global: envs.global?.map(processVariables) ?? [], + selected: envs.selected?.map(processVariables) ?? [], }; return processedEnvs; @@ -70,43 +73,20 @@ const processEnvs = (envs: Partial) => { */ export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => { const config: RequestConfig = { - supported: true, displayUrl: req.effectiveFinalDisplayURL, }; + const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest; + const reqParams = finalParams(req); const reqHeaders = finalHeaders(req); + config.url = finalEndpoint(req); config.method = req.method as Method; config.params = getMetaDataPairs(reqParams); config.headers = getMetaDataPairs(reqHeaders); - if (req.auth.authActive) { - switch (req.auth.authType) { - case "oauth-2": { - // TODO: OAuth2 Request Parsing - // !NOTE: Temporary `config.supported` check - config.supported = false; - } - default: { - break; - } - } - } - if (req.body.contentType) { - config.headers["Content-Type"] = req.body.contentType; - switch (req.body.contentType) { - case "multipart/form-data": { - // TODO: Parse Multipart Form Data - // !NOTE: Temporary `config.supported` check - config.supported = false; - break; - } - default: { - config.data = finalBody(req); - break; - } - } - } + + config.data = finalBody(req); return config; }; @@ -141,13 +121,6 @@ export const requestRunner = duration: 0, }; - // !NOTE: Temporary `config.supported` check - if ((config as RequestConfig).supported === false) { - status = 501; - runnerResponse.status = status; - runnerResponse.statusText = responseErrors[status]; - } - const end = hrtime(start); const duration = getDurationInSeconds(end); runnerResponse.duration = duration; @@ -166,7 +139,7 @@ export const requestRunner = }; if (axios.isAxiosError(e)) { - runnerResponse.endpoint = e.config.url ?? ""; + runnerResponse.endpoint = e.config?.url ?? ""; if (e.response) { const { data, status, statusText, headers } = e.response; @@ -174,10 +147,6 @@ export const requestRunner = runnerResponse.statusText = statusText; runnerResponse.status = status; runnerResponse.headers = headers; - } else if ((e.config as RequestConfig).supported === false) { - status = 501; - runnerResponse.status = status; - runnerResponse.statusText = responseErrors[status]; } else if (e.request) { return E.left(error({ code: "REQUEST_ERROR", data: E.toError(e) })); } @@ -271,7 +240,9 @@ export const processRequest = // Updating report for errors & current result report.errors.push(preRequestRes.left); - report.result = report.result; + + // Ensure, the CLI fails with a non-zero exit code if there are any errors + report.result = false; } else { // Updating effective-request and consuming updated envs after pre-request script execution ({ effectiveRequest, updatedEnvs } = preRequestRes.right); @@ -299,7 +270,9 @@ export const processRequest = if (E.isLeft(requestRunnerRes)) { // Updating report for errors & current result report.errors.push(requestRunnerRes.left); - report.result = report.result; + + // Ensure, the CLI fails with a non-zero exit code if there are any errors + report.result = false; printRequestRunner.fail(); } else { @@ -322,7 +295,9 @@ export const processRequest = // Updating report with current errors & result. report.errors.push(testRunnerRes.left); - report.result = report.result; + + // Ensure, the CLI fails with a non-zero exit code if there are any errors + report.result = false; } else { const { envs, testsReport, duration } = testRunnerRes.right; const _hasFailedTestCases = hasFailedTestCases(testsReport); @@ -358,7 +333,7 @@ export const preProcessRequest = ( const { headers: parentHeaders, auth: parentAuth } = collection; if (!tempRequest.v) { - tempRequest.v = "1"; + tempRequest.v = RESTReqSchemaVersion; } if (!tempRequest.name) { tempRequest.name = "Untitled Request"; diff --git a/packages/hoppscotch-cli/src/utils/workspace-access.ts b/packages/hoppscotch-cli/src/utils/workspace-access.ts index 063907596..901021c99 100644 --- a/packages/hoppscotch-cli/src/utils/workspace-access.ts +++ b/packages/hoppscotch-cli/src/utils/workspace-access.ts @@ -3,6 +3,8 @@ import { Environment, EnvironmentSchemaVersion, HoppCollection, + HoppRESTAuth, + HoppRESTHeaders, HoppRESTRequest, } from "@hoppscotch/data"; @@ -34,6 +36,7 @@ interface WorkspaceRequest { /** * Transforms the incoming list of workspace requests by applying `JSON.parse` to the `request` field. + * It includes the `v` field indicating the schema version, but migration is handled already at the `parseCollectionData()` helper function. * * @param {WorkspaceRequest[]} requests - An array of workspace request objects to be transformed. * @returns {HoppRESTRequest[]} The transformed array of requests conforming to the `HoppRESTRequest` type. @@ -65,6 +68,8 @@ export const transformWorkspaceEnvironment = ( return variable; }); + // The response doesn't include a way to infer the schema version, so it's set to the latest version + // Any relevant migrations have to be accounted here return { v: EnvironmentSchemaVersion, variables: transformedEnvVars, @@ -84,10 +89,19 @@ export const transformWorkspaceCollections = ( return collections.map((collection) => { const { id, title, data, requests, folders } = collection; - const parsedData = data ? JSON.parse(data) : {}; + const parsedData: { auth?: HoppRESTAuth; headers?: HoppRESTHeaders } = data + ? JSON.parse(data) + : {}; + const { auth = { authType: "inherit", authActive: true }, headers = [] } = parsedData; + const migratedHeaders = headers.map((header) => + header.description ? header : { ...header, description: "" } + ); + + // The response doesn't include a way to infer the schema version, so it's set to the latest version + // Any relevant migrations have to be accounted here return { v: CollectionSchemaVersion, id, @@ -95,7 +109,7 @@ export const transformWorkspaceCollections = ( folders: transformWorkspaceCollections(folders), requests: transformWorkspaceRequests(requests), auth, - headers, + headers: migratedHeaders, }; }); }; diff --git a/packages/hoppscotch-cli/tsup.config.ts b/packages/hoppscotch-cli/tsup.config.ts index 126507ad0..d6e19c127 100644 --- a/packages/hoppscotch-cli/tsup.config.ts +++ b/packages/hoppscotch-cli/tsup.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ - entry: [ "./src/index.ts" ], + entry: ["./src/index.ts"], outDir: "./dist/", format: ["esm"], platform: "node", @@ -10,7 +10,7 @@ export default defineConfig({ target: "esnext", skipNodeModulesBundle: false, esbuildOptions(options) { - options.bundle = true + options.bundle = true; }, clean: true, }); diff --git a/packages/hoppscotch-common/assets/scss/styles.scss b/packages/hoppscotch-common/assets/scss/styles.scss index 258992de7..e2b37d80a 100644 --- a/packages/hoppscotch-common/assets/scss/styles.scss +++ b/packages/hoppscotch-common/assets/scss/styles.scss @@ -344,26 +344,44 @@ pre.ace_editor { .info-response { color: var(--status-info-color); + &.outlined { + border: 1px solid var(--status-info-color); + } } .success-response { color: var(--status-success-color); + &.outlined { + border: 1px solid var(--status-success-color); + } } .redirect-response { color: var(--status-redirect-color); + &.outlined { + border: 1px solid var(--status-redirect-color); + } } .critical-error-response { color: var(--status-critical-error-color); + &.outlined { + border: 1px solid var(--status-critical-error-color); + } } .server-error-response { color: var(--status-server-error-color); + &.outlined { + border: 1px solid var(--status-server-error-color); + } } .missing-data-response { color: var(--status-missing-data-color); + &.outlined { + border: 1px solid var(--status-missing-data-color); + } } .toasted-container { @@ -560,9 +578,24 @@ details[open] summary .indicator { } } -.env-highlight { - @apply text-accentContrast; +.env-highlight, +.predefined-variable-highlight { + // forcing the text colour to be white inside a higlighted environment variable and predefined variable + @apply text-accentContrast #{!important}; + span { + @apply text-accentContrast #{!important}; + } + // setting the text colour to be visible when it's selected and common item is highlighted + .cm-selectionMatch { + @apply text-secondaryDark #{!important}; + span { + @apply text-secondaryDark #{!important}; + } + } +} + +.env-highlight { &.request-variable-highlight { @apply bg-amber-500; @apply hover:bg-amber-600; @@ -584,6 +617,18 @@ details[open] summary .indicator { } } +.predefined-variable-highlight { + &.predefined-variable-valid { + @apply bg-yellow-500; + @apply hover:bg-yellow-600; + } + + &.predefined-variable-invalid { + @apply hover:bg-red-300; + @apply bg-red-300; + } +} + #nprogress .bar { @apply bg-accent #{!important}; } diff --git a/packages/hoppscotch-common/assets/themes/base-themes.scss b/packages/hoppscotch-common/assets/themes/base-themes.scss index 9bc0a4f30..b5efbcec3 100644 --- a/packages/hoppscotch-common/assets/themes/base-themes.scss +++ b/packages/hoppscotch-common/assets/themes/base-themes.scss @@ -5,7 +5,9 @@ --font-size-tiny: 0.625rem; --line-height-body: 1rem; --upper-primary-sticky-fold: 4.125rem; + --upper-runner-sticky-fold: 4.125rem; --upper-secondary-sticky-fold: 6.188rem; + --upper-runner-sticky-fold: 4.5rem; --upper-tertiary-sticky-fold: 8.25rem; --upper-fourth-sticky-fold: 10.2rem; --upper-mobile-primary-sticky-fold: 6.75rem; diff --git a/packages/hoppscotch-common/locales/ar.json b/packages/hoppscotch-common/locales/ar.json index 02a8c40b4..8fa69a5c0 100644 --- a/packages/hoppscotch-common/locales/ar.json +++ b/packages/hoppscotch-common/locales/ar.json @@ -1,60 +1,60 @@ { "action": { - "add": "Add", - "autoscroll": "Autoscroll", - "cancel": "الغاء", - "choose_file": "اختيار ملف", - "clear": "امسح", - "clear_all": "امسح الكل", - "clear_history": "Clear all History", - "close": "Close", - "connect": "الاتصال", - "connecting": "Connecting", + "add": "إضافة", + "autoscroll": "التمرير التلقائي", + "cancel": "إلغاء", + "choose_file": "اختر ملف", + "clear": "محو", + "clear_all": "محو الكل", + "clear_history": "محو السجل", + "close": "إغلاق", + "connect": "اتصال", + "connecting": "جارٍ الاتصال", "copy": "نسخ", - "create": "Create", + "create": "إنشاء", "delete": "حذف", "disconnect": "قطع الاتصال", - "dismiss": "رفض", - "dont_save": "Don't save", + "dismiss": "تجاهل", + "dont_save": "لا تحفظ", "download_file": "تحميل الملف", - "drag_to_reorder": "Drag to reorder", - "duplicate": "كرر", - "edit": "يحرر", - "filter": "Filter", - "go_back": "عد", - "go_forward": "Go forward", - "group_by": "Group by", - "hide_secret": "Hide secret", + "drag_to_reorder": "سحب لإعادة الترتيب", + "duplicate": "تكرار", + "edit": "تعديل", + "filter": "تصفية", + "go_back": "رجوع", + "go_forward": "تقدم", + "group_by": "تجميع حسب", + "hide_secret": "إخفاء السر", "label": "ملصق", - "learn_more": "اقرأ أكثر", - "download_here": "Download here", - "less": "اقل", - "more": "أكثر", + "learn_more": "اقرأ المزيد", + "download_here": "تنزيل هنا", + "less": "أقل", + "more": "المزيد", "new": "جديد", - "no": "رقم", - "open_workspace": "Open workspace", + "no": "لا", + "open_workspace": "افتح مساحة العمل", "paste": "لصق", - "prettify": "جمال", - "properties": "Properties", - "remove": "ازالة", - "rename": "Rename", - "restore": "اعادة", + "prettify": "تجميل", + "properties": "خصائص", + "remove": "إزالة", + "rename": "إعادة تسمية", + "restore": "استعادة", "save": "حفظ", - "scroll_to_bottom": "Scroll to bottom", - "scroll_to_top": "Scroll to top", + "scroll_to_bottom": "التمرير إلى الأسفل", + "scroll_to_top": "التمرير إلى الأعلى", "search": "بحث", - "send": "ارسل", - "share": "Share", - "show_secret": "Show secret", + "send": "إرسال", + "share": "مشاركة", + "show_secret": "عرض السر", "start": "ابدأ", - "starting": "Starting", - "stop": "قف", - "to_close": "لإغلاء", - "to_navigate": "للإنتقال", - "to_select": "للإختيار", - "turn_off": "اطفئه", - "turn_on": "شغله", - "undo": "الغاء التحميل", + "starting": "جارٍ البدء", + "stop": "إيقاف", + "to_close": "للإغلاق", + "to_navigate": "للتنقل", + "to_select": "للاختيار", + "turn_off": "إيقاف التشغيل", + "turn_on": "تشغيل", + "undo": "تراجع", "yes": "نعم" }, "add": { @@ -64,59 +64,59 @@ "app": { "chat_with_us": "دردش معنا", "contact_us": "اتصل بنا", - "cookies": "Cookies", - "copy": "انسخ", - "copy_interface_type": "Copy interface type", - "copy_user_id": "Copy User Auth Token", - "developer_option": "Developer options", - "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", + "cookies": "ملفات تعريف الارتباط", + "copy": "نسخ", + "copy_interface_type": "نسخ نوع الواجهة", + "copy_user_id": "نسخ رمز تعريف المستخدم", + "developer_option": "خيارات المطور", + "developer_option_description": "أدوات المطور التي تساعد في تطوير وصيانة هوبسكوتش.", "discord": "Discord", "documentation": "توثيق", "github": "GitHub", - "help": "المساعدة والتعليقات ووثائق", + "help": "المساعدة والتعليقات والتوثيق", "home": "الصفحة الرئيسية", "invite": "دعوة", - "invite_description": "في Hoppscotch ، قمنا بتصميم واجهة بسيطة وبديهية لإنشاء وإدارة واجهات برمجة التطبيقات الخاصة بك. Hoppscotch هي أداة تساعدك على بناء واختبار وتوثيق ومشاركة واجهات برمجة التطبيقات الخاصة بك.", - "invite_your_friends": "اعزم أصحابك", + "invite_description": "هوبسكوتش هو نظام بيئي مفتوح المصدر لتطوير API. قمنا بتصميم واجهة بسيطة وبديهية لإنشاء وإدارة API الخاصة بك. هوبسكوتش هي أداة تساعدك على بناء واختبار وتوثيق ومشاركة API الخاصة بك.", + "invite_your_friends": "ادعو أصدقائك", "join_discord_community": "انضم إلى مجتمع Discord الخاص بنا", "keyboard_shortcuts": "اختصارات لوحة المفاتيح", "name": "هوبسكوتش", - "new_version_found": "تم العثور على نسخة جديدة. قم بالتحديث للتحديث.", - "open_in_hoppscotch": "Open in Hoppscotch", - "options": "Options", + "new_version_found": "تم العثور على نسخة جديدة. قم بالتحديث الآن.", + "open_in_hoppscotch": "افتح في هوبسكوتش", + "options": "خيارات", "proxy_privacy_policy": "سياسة خصوصية الوكيل", "reload": "إعادة تحميل", "search": "بحث", - "share": "يشارك", + "share": "مشاركة", "shortcuts": "الاختصارات", - "social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", - "social_links": "Social links", + "social_description": "تابعنا على وسائل التواصل الاجتماعي للبقاء على اطلاع بآخر الأخبار والتحديثات والإصدارات.", + "social_links": "روابط اجتماعية", "spotlight": "أضواء كاشفة", "status": "حالة", - "status_description": "Check the status of the website", + "status_description": "تحقق من حالة الموقع", "terms_and_privacy": "الشروط والخصوصية", "twitter": "Twitter", - "type_a_command_search": "اكتب أمرًا أو ابحث ...", + "type_a_command_search": "اكتب أمرًا أو ابحث...", "we_use_cookies": "نحن نستخدم ملفات تعريف الارتباط", - "updated_text": "Hoppscotch has been updated to v{version} 🎉", - "whats_new": "ما هو الجديد؟", - "see_whats_new": "See what’s new", + "updated_text": "تم تحديث هوبسكوتش إلى الإصدار {version} 🎉", + "whats_new": "ما الجديد؟", + "see_whats_new": "شاهد ما الجديد", "wiki": "ويكي" }, "auth": { "account_exists": "الحساب موجود ببيانات اعتماد مختلفة - تسجيل الدخول لربط كلا الحسابين", "all_sign_in_options": "كل خيارات تسجيل الدخول", - "continue_with_auth_provider": "Continue with {provider}", + "continue_with_auth_provider": "المواصلة من خلال {provider}", "continue_with_email": "تواصل مع البريد الإلكتروني", - "continue_with_github": "تواصل مع جيثب", - "continue_with_github_enterprise": "Continue with GitHub Enterprise", - "continue_with_google": "تواصل مع جوجل", - "continue_with_microsoft": "Continue with Microsoft", + "continue_with_github": "المواصلة من خلال جيت هاب", + "continue_with_github_enterprise": "المواصلة من خلال جيت هاب Enterprise", + "continue_with_google": "المواصلة من خلال جوجل", + "continue_with_microsoft": "المواصلة من خلال مايكروسوفت", "email": "بريد إلكتروني", "logged_out": "تسجيل الخروج", "login": "تسجيل الدخول", "login_success": "تم تسجيل الدخول بنجاح", - "login_to_hoppscotch": "تسجيل الدخول إلى Hoppscotch", + "login_to_hoppscotch": "تسجيل الدخول إلى هوبسكوتش", "logout": "تسجيل خروج", "re_enter_email": "أعد إدخال البريد الإلكتروني", "send_magic_link": "أرسل رابطًا سحريًا", @@ -125,82 +125,82 @@ "we_sent_magic_link_description": "تحقق من بريدك الوارد - لقد أرسلنا بريدًا إلكترونيًا إلى {email}. يحتوي على رابط سحري سيسجل دخولك." }, "authorization": { - "generate_token": "توليد رمز", - "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", + "generate_token": "توليد رمز الوصول", + "graphql_headers": "تُرسل رؤوس التفويض كجزء من الحمولة إلى `connection_init`", "include_in_url": "تضمين في URL", - "inherited_from": "Inherited from {auth} from Parent Collection {collection} ", + "inherited_from": "موروثة من {auth} من المجموعة الأصلية {collection}", "learn": "تعلم كيف", "oauth": { - "redirect_auth_server_returned_error": "Auth Server returned an error state", - "redirect_auth_token_request_failed": "Request to get the auth token failed", - "redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token", - "redirect_invalid_state": "Invalid State value present in the redirect", - "redirect_no_auth_code": "No Authorization Code present in the redirect", - "redirect_no_client_id": "No Client ID defined", - "redirect_no_client_secret": "No Client Secret Defined", - "redirect_no_code_verifier": "No Code Verifier Defined", - "redirect_no_token_endpoint": "No Token Endpoint Defined", - "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", - "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", - "grant_type": "Grant Type", - "grant_type_auth_code": "Authorization Code", - "token_fetched_successfully": "Token fetched successfully", - "token_fetch_failed": "Failed to fetch token", - "validation_failed": "Validation Failed, please check the form fields", - "label_authorization_endpoint": "Authorization Endpoint", - "label_client_id": "Client ID", - "label_client_secret": "Client Secret", - "label_code_challenge": "Code Challenge", - "label_code_challenge_method": "Code Challenge Method", - "label_code_verifier": "Code Verifier", - "label_scopes": "Scopes", - "label_token_endpoint": "Token Endpoint", - "label_use_pkce": "Use PKCE", - "label_implicit": "Implicit", - "label_password": "Password", - "label_username": "Username", - "label_auth_code": "Authorization Code", - "label_client_credentials": "Client Credentials" + "redirect_auth_server_returned_error": "عاد خادم Authorization بحالة خطأ", + "redirect_auth_token_request_failed": "فشل الطلب للحصول على رمز Authorization", + "redirect_auth_token_request_invalid_response": "استجابة غير صالحة من الرمز عند طلب رمز Authorization endpoint", + "redirect_invalid_state": "قيمة حالة غير صالحة موجودة في إعادة التوجيه", + "redirect_no_auth_code": "لا يوجد رمز تفويض في إعادة التوجيه", + "redirect_no_client_id": "لم يتم تعريف رقم العميل", + "redirect_no_client_secret": "لم يتم تعريف سر العميل", + "redirect_no_code_verifier": "لم يتم تعريف محقق الشيفرة", + "redirect_no_token_endpoint": "لم يتم تعريف endpoint الرمز", + "something_went_wrong_on_oauth_redirect": "حدث خطأ أثناء إعادة توجيه OAuth", + "something_went_wrong_on_token_generation": "حدث خطأ أثناء توليد الرمز", + "token_generation_oidc_discovery_failed": "فشل توليد الرمز: فشل اكتشاف OpenID Connect", + "grant_type": "نوع المنح", + "grant_type_auth_code": "رمز Authorization", + "token_fetched_successfully": "تم الحصول على الرمز بنجاح", + "token_fetch_failed": "فشل الحصول على الرمز", + "validation_failed": "فشل التحقق، يرجى التحقق من حقول النموذج", + "label_authorization_endpoint": "Authorization endpoint", + "label_client_id": "معرف العميل", + "label_client_secret": "سر العميل", + "label_code_challenge": "تحدي الشيفرة", + "label_code_challenge_method": "طريقة تحدي الشيفرة", + "label_code_verifier": "محقق الشيفرة", + "label_scopes": "النطاقات", + "label_token_endpoint": "endpoint الرمز", + "label_use_pkce": "استخدم PKCE", + "label_implicit": "ضمني", + "label_password": "كلمة المرور", + "label_username": "اسم المستخدم", + "label_auth_code": "رمز Authorization", + "label_client_credentials": "بيانات اعتماد العميل" }, - "pass_key_by": "Pass by", - "pass_by_query_params_label": "Query Parameters", - "pass_by_headers_label": "Headers", + "pass_key_by": "تمرير بواسطة", + "pass_by_query_params_label": "معلمات الاستعلام", + "pass_by_headers_label": "الرؤوس", "password": "كلمة المرور", - "save_to_inherit": "Please save this request in any collection to inherit the authorization", - "token": "رمز", - "type": "نوع التفويض", + "save_to_inherit": "يرجى حفظ هذا الطلب في أي مجموعة لتوريث Authorization", + "token": "رمز الوصول", + "type": "نوع Authorization", "username": "اسم المستخدم" }, "collection": { "created": "تم إنشاء المجموعة", - "different_parent": "Cannot reorder collection with different parent", + "different_parent": "لا يمكن إعادة ترتيب المجموعة مع والد مختلف", "edit": "تحرير المجموعة", - "import_or_create": "Import or create a collection", - "import_collection": "Import Collection", + "import_or_create": "استيراد أو إنشاء مجموعة", + "import_collection": "استيراد مجموعة", "invalid_name": "الرجاء تقديم اسم صالح للمجموعة", - "invalid_root_move": "Collection already in the root", - "moved": "Moved Successfully", + "invalid_root_move": "المجموعة موجودة بالفعل في الجذر", + "moved": "تم النقل بنجاح", "my_collections": "مجموعاتي", "name": "مجموعتي الجديدة", - "name_length_insufficient": "اسم المجموعة يجب ان لايقل على 3 رموز", + "name_length_insufficient": "يجب أن لا يقل اسم المجموعة عن 3 رموز", "new": "مجموعة جديدة", - "order_changed": "Collection Order Updated", - "properties": "Collection Properties", - "properties_updated": "Collection Properties Updated", + "order_changed": "تم تحديث ترتيب المجموعة", + "properties": "خصائص المجموعة", + "properties_updated": "تم تحديث خصائص المجموعة", "renamed": "تمت إعادة تسمية المجموعة", - "request_in_use": "Request in use", + "request_in_use": "الطلب قيد الاستخدام", "save_as": "حفظ باسم", - "save_to_collection": "Save to Collection", + "save_to_collection": "حفظ إلى المجموعة", "select": "حدد مجموعة", "select_location": "اختر موقعا", - "details": "Details", + "details": "تفاصيل", "select_team": "اختر فريقًا", "team_collections": "مجموعات الفريق" }, "confirm": { - "close_unsaved_tab": "Are you sure you want to close this tab?", - "close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", + "close_unsaved_tab": "هل أنت متأكد أنك تريد إغلاق هذه التبويبة؟", + "close_unsaved_tabs": "هل أنت متأكد أنك تريد إغلاق جميع علامات التبويب سيتم فقدان {count} من التبويبات غير المحفوظة.", "exit_team": "هل أنت متأكد أنك تريد مغادرة هذا الفريق؟", "logout": "هل أنت متأكد أنك تريد تسجيل الخروج؟", "remove_collection": "هل أنت متأكد أنك تريد حذف هذه المجموعة نهائيًا؟", @@ -208,59 +208,59 @@ "remove_folder": "هل أنت متأكد أنك تريد حذف هذا المجلد نهائيًا؟", "remove_history": "هل أنت متأكد أنك تريد حذف كل المحفوظات بشكل دائم؟", "remove_request": "هل أنت متأكد أنك تريد حذف هذا الطلب نهائيًا؟", - "remove_shared_request": "Are you sure you want to permanently delete this shared request?", + "remove_shared_request": "هل أنت متأكد أنك تريد حذف هذا الطلب المشترك نهائيًا؟", "remove_team": "هل أنت متأكد أنك تريد حذف هذا الفريق؟", "remove_telemetry": "هل أنت متأكد أنك تريد الانسحاب من القياس عن بعد؟", - "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", - "save_unsaved_tab": "Do you want to save changes made in this tab?", + "request_change": "هل أنت متأكد أنك تريد تجاهل الطلب الحالي؟ سيتم فقدان التغييرات غير المحفوظة.", + "save_unsaved_tab": "هل تريد حفظ التغييرات التي أجريتها في هذه التبويبة؟", "sync": "هل أنت متأكد أنك تريد مزامنة مساحة العمل هذه؟", - "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" + "delete_access_token": "هل أنت متأكد أنك تريد حذف رمز الوصول {tokenLabel}؟" }, "context_menu": { - "add_parameters": "Add to parameters", - "open_request_in_new_tab": "Open request in new tab", - "set_environment_variable": "Set as variable" + "add_parameters": "إضافة إلى parameters", + "open_request_in_new_tab": "فتح الطلب في تبويبة جديدة", + "set_environment_variable": "تعيين كمتغير" }, "cookies": { "modal": { - "cookie_expires": "Expires", - "cookie_name": "Name", - "cookie_path": "Path", - "cookie_string": "Cookie string", - "cookie_value": "Value", - "empty_domain": "Domain is empty", - "empty_domains": "Domain list is empty", - "enter_cookie_string": "Enter cookie string", - "interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", - "managed_tab": "Managed", - "new_domain_name": "New domain name", - "no_cookies_in_domain": "No cookies set for this domain", - "raw_tab": "Raw", - "set": "Set a cookie" + "cookie_expires": "تنتهي في", + "cookie_name": "الاسم", + "cookie_path": "المسار", + "cookie_string": "نص الكوكي", + "cookie_value": "القيمة", + "empty_domain": "النطاق فارغ", + "empty_domains": "قائمة النطاقات فارغة", + "enter_cookie_string": "أدخل نص الكوكي", + "interceptor_no_support": "الاعتراض المحدد حاليًا لا يدعم الكوكيز. اختر معترضًا مختلفًا وحاول مرة أخرى.", + "managed_tab": "مدار", + "new_domain_name": "اسم نطاق جديد", + "no_cookies_in_domain": "لا توجد كوكيز محددة لهذا النطاق", + "raw_tab": "خام", + "set": "تعيين كوكي" } }, "count": { "header": "رأس {count}", - "message": "الرسالة {count}", + "message": "رسالة {count}", "parameter": "المعلمة {count}", "protocol": "البروتوكول {count}", - "value": "القيمة {count}", + "value": "قيمة {count}", "variable": "متغير {count}" }, "documentation": { - "generate": "توليد الوثائق", - "generate_message": "قم باستيراد أي مجموعة Hoppscotch لإنشاء وثائق API أثناء التنقل." + "generate": "توليد docs", + "generate_message": "قم باستيراد أي مجموعة هوبسكوتش لإنشاء API docs أثناء التنقل." }, "empty": { "authorization": "هذا الطلب لا يستخدم أي إذن", "body": "هذا الطلب ليس له هيئة", "collection": "المجموعة فارغة", "collections": "المجموعات فارغة", - "documentation": "Connect to a GraphQL endpoint to view documentation", - "endpoint": "Endpoint cannot be empty", + "documentation": "اتصل ب GraphQL endpoint لعرض الوثائق", + "endpoint": "لا يمكن أن تكون endpoint فارغة", "environments": "البيئات فارغة", "folder": "مجلد فارغ", - "headers": "لا يحتوي هذا الطلب على أية رؤوس", + "headers": "لا يحتوي هذا الطلب على أية Headers", "history": "التاريخ فارغ", "invites": "قائمة الدعوات فارغة", "members": "الفريق فارغ", @@ -268,135 +268,135 @@ "pending_invites": "لاتوجد اي دعوات معلقة لهذا الفريق", "profile": "سجل الدخول لرؤية فريقك", "protocols": "البروتوكولات فارغة", - "request_variables": "This request does not have any request variables", - "schema": "اتصل بنقطة نهاية GraphQL", - "secret_environments": "Secrets are not synced to Hoppscotch", - "shared_requests": "Shared requests are empty", - "shared_requests_logout": "Login to view your shared requests or create a new one", - "subscription": "Subscriptions are empty", + "request_variables": "لا يحتوي هذا الطلب على أي متغيرات ", + "schema": " اتصل ب GraphQL endpoint", + "secret_environments": "الأسرار غير متزامنة مع هوبسكوتش", + "shared_requests": "الطلبات المشتركة فارغة", + "shared_requests_logout": "سجل الدخول لعرض طلباتك المشتركة أو لإنشاء طلب جديد", + "subscription": "الاشتراكات فارغة", "team_name": "اسم الفريق فارغ", "teams": "الفرق فارغة", - "tests": "لا توجد اختبارات لهذا الطلب", - "access_tokens": "Access tokens are empty", - "shortcodes": "Shortcodes are empty" + "tests": "لا توجد tests لهذا الطلب", + "access_tokens": "رموز الوصول فارغة", + "shortcodes": "الرموز القصيرة فارغة" }, "environment": { - "add_to_global": "Add to Global", - "added": "Environment addition", - "create_new": "انشاء بيئة جديدة", - "created": "Environment created", - "deleted": "حذف بيئة العمل", - "duplicated": "Environment duplicated", + "add_to_global": "أضف إلى العام", + "added": "تمت إضافة البيئة", + "create_new": "إنشاء بيئة جديدة", + "created": "تم إنشاء البيئة", + "deleted": "تم حذف البيئة", + "duplicated": "تم تكرار البيئة", "edit": "تحرير البيئة", - "empty_variables": "No variables", - "global": "Global", - "global_variables": "Global variables", - "import_or_create": "Import or create a environment", - "invalid_name": "الرجاء تقديم اسم صالح للبيئة", - "list": "Environment variables", - "my_environments": "My Environments", - "name": "Name", - "nested_overflow": "nested environment variables are limited to 10 levels", + "empty_variables": "لا توجد متغيرات", + "global": "عام", + "global_variables": "المتغيرات العامة", + "import_or_create": "استيراد أو إنشاء بيئة", + "invalid_name": "يرجى كنابة اسم صحيح للبيئة", + "list": "متغيرات البيئة", + "my_environments": "البيئات الخاصة بي", + "name": "الاسم", + "nested_overflow": "المتغيرات البيئية المتداخلة محدودة بـ 10 مستويات", "new": "بيئة جديدة", - "no_active_environment": "No active environment", - "no_environment": "لا بيئة", - "no_environment_description": "لم يتم اختيار أي بيئة عمل. اختر ما تريد فعله بالمتغيرات التالية.", - "quick_peek": "Environment Quick Peek", - "replace_with_variable": "Replace with variable", - "scope": "Scope", - "secrets": "Secrets", - "secret_value": "Secret value", - "select": "حدد البيئة", - "set": "Set environment", - "set_as_environment": "Set as environment", - "team_environments": "Team Environments", + "no_active_environment": "لا توجد بيئة نشطة", + "no_environment": "لا توجد بيئة", + "no_environment_description": "لم يتم اختيار أي بيئات. اختر ما تريد القيام به مع المتغيرات التالية.", + "quick_peek": "نظرة سريعة على البيئة", + "replace_with_variable": "استبدال بمتغير", + "scope": "نطاق", + "secrets": "الأسرار", + "secret_value": "القيمة السرية", + "select": "اختر البيئة", + "set": "تعيين البيئة", + "set_as_environment": "تعيين كبيئة", + "team_environments": "بيئات العمل", "title": "البيئات", - "updated": "تحديث بيئة العمل", - "value": "Value", - "variable": "Variable", - "variables": "Variables", - "variable_list": "قائمة متغيرة", - "properties": "Environment Properties", - "details": "Details" + "updated": "تم تحديث البيئة", + "value": "القيمة", + "variable": "متغير", + "variables": "المتغيرات", + "variable_list": "قائمة المتغيرات", + "properties": "خصائص البيئة", + "details": "التفاصيل" }, "error": { - "authproviders_load_error": "Unable to load auth providers", - "browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.", + "authproviders_load_error": "غير قادر على تحميل مقدمي Authorization", + "browser_support_sse": "يبدو أن هذا المتصفح لا يدعم Server Sent Events.", "check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.", - "check_how_to_add_origin": "Check how you can add an origin", - "curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح", - "danger_zone": "Danger zone", - "delete_account": "Your account is currently an owner in these teams:", - "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", - "empty_profile_name": "Profile name cannot be empty", + "check_how_to_add_origin": "تحقق من كيفية إضافة مصدر", + "curl_invalid_format": "صيغة cURL غير صحيحة", + "danger_zone": "منطقة الخطر", + "delete_account": "حسابك حاليًا هو مالك في مساحات العمل التالية:", + "delete_account_description": "يجب عليك إما إزالة نفسك أو نقل الملكية أو حذف هذه مساحات العمل قبل أن تتمكن من حذف حسابك.", + "empty_profile_name": "لا يمكن أن يكون اسم الملف الشخصي فارغًا", "empty_req_name": "اسم الطلب فارغ", - "f12_details": "(للحصول على تفاصيل F12)", - "gql_prettify_invalid_query": "تعذر تحسين استعلام غير صالح وحل أخطاء بنية الاستعلام وحاول مرة أخرى", - "incomplete_config_urls": "Incomplete configuration URLs", - "incorrect_email": "Incorrect email", - "invalid_link": "Invalid link", - "invalid_link_description": "The link you clicked is invalid or expired.", - "invalid_embed_link": "The embed does not exist or is invalid.", - "json_parsing_failed": "Invalid JSON", - "json_prettify_invalid_body": "تعذر تجميل جسم غير صالح وحل أخطاء بناء جملة json وحاول مرة أخرى", - "network_error": "There seems to be a network error. Please try again.", - "network_fail": "تعذر إرسال الطلب", - "no_collections_to_export": "No collections to export. Please create a collection to get started.", - "no_duration": "لا مدة", - "no_environments_to_export": "No environments to export. Please create an environment to get started.", - "no_results_found": "No matches found", - "page_not_found": "This page could not be found", - "please_install_extension": "Please install the extension and add origin to the extension.", - "proxy_error": "Proxy error", - "same_profile_name": "Updated profile name is same as the current profile name", - "script_fail": "تعذر تنفيذ نص الطلب المسبق", - "something_went_wrong": "هناك خطأ ما", - "test_script_fail": "Could not execute post-request script", - "reading_files": "Error while reading one or more files.", - "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", - "generate_access_token": "Something went wrong while generating the access token", - "delete_access_token": "Something went wrong while deleting the access token" + "f12_details": "(F12 للتفاصيل)", + "gql_prettify_invalid_query": "لم يتمكن من تجميل استعلام غير صالح، حل أخطاء بناء الجملة وحاول مرة أخرى", + "incomplete_config_urls": "عناوين URL الخاصة بالتكوين غير مكتملة", + "incorrect_email": "البريد الإلكتروني غير صحيح", + "invalid_link": "الرابط غير صالح", + "invalid_link_description": "الرابط الذي نقرت عليه غير صالح أو منتهي الصلاحية.", + "invalid_embed_link": "التضمين غير موجود أو غير صالح.", + "json_parsing_failed": "JSON غير صالح", + "json_prettify_invalid_body": "لم يتمكن من تجميل نص غير صالح، حل أخطاء بناء الجملة في JSON وحاول مرة أخرى", + "network_error": "يبدو أن هناك خطأ في الشبكة. حاول مرة أخرى.", + "network_fail": "لم يتمكن من إرسال الطلب", + "no_collections_to_export": "لا توجد مجموعات للتصدير. يرجى إنشاء مجموعة للبدء.", + "no_duration": "لا يوجد مدة", + "no_environments_to_export": "لا توجد بيئات للتصدير. يرجى إنشاء بيئة للبدء.", + "no_results_found": "لم يتم العثور على نتائج", + "page_not_found": "لا يمكن العثور على هذه الصفحة", + "please_install_extension": "يرجى تثبيت الإضافة وإضافة المصدر إلى الإضافة.", + "proxy_error": "خطأ في الوكيل", + "same_profile_name": "اسم الملف الشخصي المحدث هو نفسه اسم الملف الشخصي الحالي", + "script_fail": "لم يتمكن من تنفيذ نص الطلب المسبق", + "something_went_wrong": "حدث خطأ ما", + "test_script_fail": "لم يتمكن من تنفيذ نص ما بعد الطلب", + "reading_files": "حدث خطأ أثناء قراءة واحد أو أكثر من الملفات.", + "fetching_access_tokens_list": "حدث خطأ أثناء جلب قائمة الرموز", + "generate_access_token": "حدث خطأ أثناء توليد رمز الوصول", + "delete_access_token": "حدث خطأ أثناء حذف رمز الوصول" }, "export": { - "as_json": "تصدير بتنسيق JSON", - "create_secret_gist": "إنشاء جوهر سري", - "create_secret_gist_tooltip_text": "Export as secret Gist", - "failed": "Something went wrong while exporting", - "secret_gist_success": "Successfully exported as secret Gist", - "require_github": "تسجيل الدخول باستخدام GitHub لإنشاء جوهر سري", - "title": "Export", - "success": "Successfully exported", - "gist_created": "خلقت الجست" + "as_json": "تصدير كملف JSON", + "create_secret_gist": "إنشاء Gist سري", + "create_secret_gist_tooltip_text": "تصدير كـ Gist سري", + "failed": "حدث خطأ أثناء التصدير", + "secret_gist_success": "تم التصدير بنجاح كـ Gist سري", + "require_github": "تسجيل الدخول باستخدام GitHub لإنشاء Gist سري", + "title": "تصدير", + "success": "تم التصدير بنجاح", + "gist_created": "تم إنشاء Gist" }, "filter": { - "all": "All", - "none": "None", - "starred": "Starred" + "all": "الكل", + "none": "لا شيء", + "starred": "المفضلة" }, "folder": { "created": "تم إنشاء المجلد", "edit": "تحرير المجلد", - "invalid_name": "الرجاء توفير اسم للمجلد", - "name_length_insufficient": "اسم المجلد يحب ان يكون على اقل 3 رموز", - "new": "ملف جديد", + "invalid_name": "يرجى تقديم اسم للمجلد", + "name_length_insufficient": "يجب أن يتكون اسم المجلد من 3 أحرف على الأقل", + "new": "مجلد جديد", "renamed": "تمت إعادة تسمية المجلد" }, "graphql": { - "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", - "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", - "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", - "mutations": "الطفرات", - "schema": "مخطط", + "connection_switch_confirm": "هل تريد الاتصال بنقطة النهاية الأخيرة لـ GraphQL؟", + "connection_switch_new_url": "التبديل إلى علامة تبويب أخرى سيفصلك عن الاتصال النشط بـ GraphQL. عنوان URL للاتصال الجديد هو", + "connection_switch_url": "أنت متصل بنقطة نهاية GraphQL وعنوان URL للاتصال هو", + "mutations": "التغييرات", + "schema": "المخطط", "subscriptions": "الاشتراكات", - "switch_connection": "Switch connection", - "url_placeholder": "Enter a GraphQL endpoint URL" + "switch_connection": "تبديل الاتصال", + "url_placeholder": "أدخل عنوان URL لنقطة نهاية GraphQL" }, "graphql_collections": { - "title": "GraphQL Collections" + "title": "مجموعات GraphQL" }, "group": { - "time": "Time", - "url": "URL" + "time": "الوقت", + "url": "الرابط" }, "header": { "install_pwa": "تثبيت التطبيق", @@ -405,128 +405,128 @@ }, "helpers": { "authorization": "سيتم إنشاء رأس التفويض تلقائيًا عند إرسال الطلب.", - "collection_properties_authorization": " This authorization will be set for every request in this collection.", - "collection_properties_header": "This header will be set for every request in this collection.", + "collection_properties_authorization": "سيتم تعيين هذا التفويض لكل طلب في هذه المجموعة.", + "collection_properties_header": "سيتم تعيين هذا الرأس لكل طلب في هذه المجموعة.", "generate_documentation_first": "قم بإنشاء الوثائق أولاً", - "network_fail": "تعذر الوصول إلى نقطة نهاية API. تحقق من اتصالك بالشبكة وحاول مرة أخرى.", - "offline": "يبدو أنك غير متصل بالإنترنت. قد لا تكون البيانات الموجودة في مساحة العمل هذه محدثة.", - "offline_short": "يبدو أنك غير متصل بالإنترنت.", - "post_request_tests": "تتم كتابة نصوص الاختبار بلغة JavaScript ، ويتم تشغيلها بعد تلقي الاستجابة.", - "pre_request_script": "تتم كتابة البرامج النصية للطلب المسبق بلغة JavaScript ، ويتم تشغيلها قبل إرسال الطلب.", - "script_fail": "يبدو أن هناك خللًا في نص الطلب المسبق. تحقق من الخطأ أدناه وقم بإصلاح البرنامج النصي وفقًا لذلك.", - "test_script_fail": "There seems to be an error with test script. Please fix the errors and run tests again", - "tests": "اكتب نص اختبار لأتمتة تصحيح الأخطاء." + "network_fail": "تعذر الوصول إلى نقطة النهاية الخاصة بـ API. تحقق من اتصال الشبكة الخاص بك أو اختر Interceptor مختلفًا وحاول مرة أخرى.", + "offline": "أنت تستخدم Hoppscotch دون اتصال. سيتم مزامنة التحديثات عندما تكون متصلاً بالإنترنت، بناءً على إعدادات مساحة العمل.", + "offline_short": "أنت تستخدم Hoppscotch دون اتصال.", + "post_request_tests": "يتم كتابة نصوص الاختبار بلغة JavaScript، ويتم تشغيلها بعد استلام الاستجابة.", + "pre_request_script": "يتم كتابة نصوص ما قبل الطلب بلغة JavaScript، ويتم تشغيلها قبل إرسال الطلب.", + "script_fail": "يبدو أن هناك خللاً في نص ما قبل الطلب. تحقق من الخطأ أدناه وقم بإصلاح النص وفقًا لذلك.", + "test_script_fail": "يبدو أن هناك خطأ في نص الاختبار. يرجى إصلاح الأخطاء وإعادة تشغيل الاختبارات مرة أخرى", + "tests": "اكتب نص اختبار لأتمتة التصحيح." }, "hide": { - "collection": "Collapse Collection Panel", + "collection": "طي لوحة المجموعة", "more": "إخفاء المزيد", "preview": "إخفاء المعاينة", - "sidebar": "اخفاء الشريط الجانبي" + "sidebar": "طي الشريط الجانبي" }, "import": { - "collections": "مجموعات الاستيراد", + "collections": "استيراد المجموعات", "curl": "استيراد cURL", - "environments_from_gist": "Import From Gist", - "environments_from_gist_description": "Import Hoppscotch Environments From Gist", - "failed": "فشل الاستيراد", - "from_file": "Import from File", - "from_gist": "الاستيراد من Gist", - "from_gist_description": "استيراد من Gist URL", + "environments_from_gist": "استيراد من Gist", + "environments_from_gist_description": "استيراد بيئات Hoppscotch من Gist", + "failed": "حدث خطأ أثناء الاستيراد: التنسيق غير معترف به", + "from_file": "استيراد من ملف", + "from_gist": "استيراد من Gist", + "from_gist_description": "استيراد من رابط Gist", "from_insomnia": "استيراد من Insomnia", "from_insomnia_description": "استيراد من مجموعة Insomnia", - "from_json": "Import from Hoppscotch", - "from_json_description": "استيراد من ملف مجموعة Hoppscotch", - "from_my_collections": "الاستيراد من \"مجموعاتي\"", - "from_my_collections_description": "استيراد من ملف مجموعاتي", + "from_json": "استيراد من Hoppscotch", + "from_json_description": "استيراد من ملف مجموعة Hoppscotch", + "from_my_collections": "استيراد من مجموعاتي الشخصية", + "from_my_collections_description": "استيراد من ملف مجموعاتي الشخصية", "from_openapi": "استيراد من OpenAPI", - "from_openapi_description": "استيراد من ملف (YML/JSON) ل OpenAPI", + "from_openapi_description": "استيراد من ملف مواصفات OpenAPI (YML/JSON)", "from_postman": "استيراد من Postman", "from_postman_description": "استيراد من مجموعة Postman", - "from_url": "استيراد من رابط", - "gist_url": "أدخل عنوان URL لـ Gist", - "gql_collections_from_gist_description": "Import GraphQL Collections From Gist", - "hoppscotch_environment": "Hoppscotch Environment", - "hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", - "import_from_url_invalid_fetch": "Couldn't get data from the url", - "import_from_url_invalid_file_format": "Error while importing collections", - "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", - "import_from_url_success": "Collections Imported", - "insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", - "json_description": "استيراد مجموعة من ملفHoppscotch Collections JSON file", - "postman_environment": "Postman Environment", - "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "يستورد", - "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", - "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", - "success": "Successfully imported" + "from_url": "استيراد من رابط URL", + "gist_url": "أدخل رابط Gist", + "gql_collections_from_gist_description": "استيراد مجموعات GraphQL من Gist", + "hoppscotch_environment": "بيئة Hoppscotch", + "hoppscotch_environment_description": "استيراد ملف JSON لبيئة Hoppscotch", + "import_from_url_invalid_fetch": "تعذر الحصول على البيانات من الرابط", + "import_from_url_invalid_file_format": "حدث خطأ أثناء استيراد المجموعات", + "import_from_url_invalid_type": "نوع غير مدعوم. القيم المقبولة هي 'hoppscotch'، 'openapi'، 'postman'، 'insomnia'", + "import_from_url_success": "تم استيراد المجموعات بنجاح", + "insomnia_environment_description": "استيراد بيئة Insomnia من ملف JSON/YAML", + "json_description": "استيراد المجموعات من ملف JSON الخاص بـ Hoppscotch", + "postman_environment": "بيئة Postman", + "postman_environment_description": "استيراد بيئة Postman من ملف JSON", + "title": "استيراد", + "file_size_limit_exceeded_warning_multiple_files": "الملفات المختارة تتجاوز الحد الموصى به وهو 10MB. سيتم استيراد الملفات {files} الأولى المختارة فقط", + "file_size_limit_exceeded_warning_single_file": "الملف المختار حاليًا يتجاوز الحد الموصى به وهو 10MB. يرجى اختيار ملف آخر.", + "success": "تم الاستيراد بنجاح" }, "inspections": { - "description": "Inspect possible errors", + "description": "فحص الأخطاء المحتملة", "environment": { - "add_environment": "Add to Environment", - "add_environment_value": "Add value", - "empty_value": "Environment value is empty for the variable '{variable}' ", - "not_found": "Environment variable “{environment}” not found." + "add_environment": "إضافة إلى البيئة", + "add_environment_value": "إضافة قيمة", + "empty_value": "قيمة البيئة فارغة للمتغير '{variable}'", + "not_found": "لم يتم العثور على متغير البيئة “{environment}”." }, "header": { - "cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." + "cookie": "لا يسمح المتصفح لـ هوبسكوتش بتعيين عنوان كوكي. بينما نعمل على تطبيق هوبسكوتش لسطح المكتب (قريبًا)، يرجى استخدام عنوان Authorization بدلاً من ذلك." }, "response": { - "401_error": "Please check your authentication credentials.", - "404_error": "Please check your request URL and method type.", - "cors_error": "Please check your Cross-Origin Resource Sharing configuration.", - "default_error": "Please check your request.", - "network_error": "Please check your network connection." + "401_error": "يرجى التحقق من بيانات اعتماد المصادقة الخاصة بك.", + "404_error": "يرجى التحقق من عنوان URL للطلب ونوع الطريقة.", + "cors_error": "يرجى التحقق من تكوين مشاركة الموارد عبر المواقع.", + "default_error": "يرجى التحقق من طلبك.", + "network_error": "يرجى التحقق من اتصال الشبكة الخاص بك." }, "title": "Inspector", "url": { - "extension_not_installed": "Extension not installed.", - "extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", - "extention_enable_action": "Enable Browser Extension", - "extention_not_enabled": "Extension not enabled." + "extension_not_installed": "الاضافة غير مثبتة", + "extension_unknown_origin": "تأكد من أنك أضفت أصل نقطة نهاية API إلى قائمة ملحق متصفح هوبسكوتش.", + "extention_enable_action": "تفعيل الاضافة", + "extention_not_enabled": "الاضافة غير مفعلة" } }, "layout": { - "collapse_collection": "Collapse or Expand Collections", - "collapse_sidebar": "Collapse or Expand the sidebar", - "column": "تصيم عمودي", - "name": "Layout", - "row": "تصميم افقي" + "collapse_collection": "طي أو توسيع المجموعات", + "collapse_sidebar": "طي أو توسيع الشريط الجانبي", + "column": "تصميم عمودي", + "name": "التصميم", + "row": "تصميم أفقي" }, "modal": { - "close_unsaved_tab": "You have unsaved changes", + "close_unsaved_tab": "لديك تغييرات غير محفوظة", "collections": "المجموعات", - "confirm": "يتأكد", - "customize_request": "Customize Request", + "confirm": "تأكيد", + "customize_request": "تخصيص الطلب", "edit_request": "تحرير الطلب", - "import_export": "استيراد و تصدير", - "share_request": "Share Request" + "import_export": "استيراد وتصدير", + "share_request": "مشاركة الطلب" }, "mqtt": { - "already_subscribed": "You are already subscribed to this topic.", - "clean_session": "Clean Session", - "clear_input": "Clear input", - "clear_input_on_send": "Clear input on send", - "client_id": "Client ID", - "color": "Pick a color", + "already_subscribed": "أنت مشترك بالفعل في هذا الموضوع.", + "clean_session": "جلسة نظيفة", + "clear_input": "مسح المدخلات", + "clear_input_on_send": "مسح المدخلات عند الإرسال", + "client_id": "رقم العميل", + "color": "اختر لونًا", "communication": "تواصل", - "connection_config": "Connection Config", - "connection_not_authorized": "This MQTT connection does not use any authentication.", - "invalid_topic": "Please provide a topic for the subscription", - "keep_alive": "Keep Alive", + "connection_config": "تكوين الاتصال", + "connection_not_authorized": "هذا الاتصال بـ MQTT لا يستخدم أي مصادقة.", + "invalid_topic": "يرجى تقديم موضوع للاشتراك", + "keep_alive": "الحفاظ على الاتصال", "log": "سجل", - "lw_message": "Last-Will Message", - "lw_qos": "Last-Will QoS", - "lw_retain": "Last-Will Retain", - "lw_topic": "Last-Will Topic", + "lw_message": "رسالة آخر مشيئة", + "lw_qos": "آخر مشيئة QoS", + "lw_retain": "آخر مشيئة احتفاظ", + "lw_topic": "موضوع آخر مشيئة", "message": "رسالة", - "new": "New Subscription", - "not_connected": "Please start a MQTT connection first.", - "publish": "ينشر", + "new": "اشتراك جديد", + "not_connected": "يرجى بدء اتصال MQTT أولاً.", + "publish": "نشر", "qos": "QoS", "ssl": "SSL", - "subscribe": "يشترك", - "topic": "عنوان", + "subscribe": "اشتراك", + "topic": "موضوع", "topic_name": "اسم الموضوع", "topic_title": "نشر / اشتراك الموضوع", "unsubscribe": "إلغاء الاشتراك", @@ -548,7 +548,7 @@ }, "profile": { "app_settings": "إعدادات التطبيق", - "default_hopp_displayname": "Unnamed User", + "default_hopp_displayname": "مستخدم بلا اسم", "editor": "محرر", "editor_description": "المحررين يمكنهم اضافة و تعديل و حذف الطلبات.", "email_verification_mail": "تم إرسال رابط التحقق إلى بريدك الإلكتروني. الرجاء الضغط على الرابط لتأكيد بريدك الإلكتروني.", @@ -566,85 +566,85 @@ }, "request": { "added": "تمت إضافة الطلب", - "authorization": "تفويض", - "body": "طلب الهيئة", + "authorization": "Authorization", + "body": "Body", "choose_language": "اختر اللغة", - "content_type": "نوع المحتوى", + "content_type": "Content-Type", "content_type_titles": { - "others": "Others", + "others": "أخرى", "structured": "Structured", - "text": "Text" + "text": "نص" }, - "different_collection": "Cannot reorder requests from different collections", - "duplicated": "Request duplicated", + "different_collection": "لا يمكن إعادة ترتيب الطلبات من مجموعات مختلفة", + "duplicated": "تم تكرار الطلب", "duration": "مدة", "enter_curl": "أدخل cURL", - "generate_code": "إنشاء التعليمات البرمجية", + "generate_code": "إنشاء الكود", "generated_code": "رمز تم إنشاؤه", - "go_to_authorization_tab": "Go to Authorization tab", - "go_to_body_tab": "Go to Body tab", - "header_list": "قائمة الرأس", + "go_to_authorization_tab": "الانتقال إلى علامة التبويب Authorization", + "go_to_body_tab": "الانتقال إلى علامة التبويب Body", + "header_list": "قائمة Header", "invalid_name": "يرجى تقديم اسم للطلب", "method": "طريقة", - "moved": "Request moved", + "moved": "تم نقل الطلب", "name": "اسم الطلب", - "new": "New Request", - "order_changed": "Request Order Updated", + "new": "طلب جديد", + "order_changed": "تم تحديث ترتيب الطلبات", "override": "Override", "override_help": "Set Content-Type in Headers", "overriden": "Overridden", - "parameter_list": "معلمات الاستعلام", - "parameters": "حدود", + "parameter_list": "قائمة Parameters", + "parameters": "Parameters", "path": "طريق", - "payload": "الحمولة", - "query": "استفسار", + "payload": "Payload", + "query": "Query", "raw_body": "نص طلب خام", - "rename": "Rename Request", + "rename": "إعادة تسمية الطلب", "renamed": "تمت إعادة تسمية الطلب", - "request_variables": "Request variables", - "run": "يركض", - "save": "يحفظ", + "request_variables": "متغيرات الطلب", + "run": "تشغيل", + "save": "حفظ", "save_as": "حفظ باسم", "saved": "تم حفظ الطلب", - "share": "يشارك", - "share_description": "Share Hoppscotch with your friends", - "share_request": "Share Request", - "stop": "Stop", + "share": "مشاركة", + "share_description": "شارك هوبسكوتش مع أصدقائك", + "share_request": "مشاركة الطلب", + "stop": "إيقاف", "title": "طلب", "type": "نوع الطلب", "url": "URL", - "url_placeholder": "Enter a URL or paste a cURL command", + "url_placeholder": "أدخل URL أو الصق أمر cURL", "variables": "المتغيرات", - "view_my_links": "View my links", - "copy_link": "نسخ الوصلة" + "view_my_links": "عرض روابطى", + "copy_link": "نسخ الرابط" }, "response": { - "audio": "Audio", - "body": "هيئة الاستجابة", + "audio": "صوت", + "body": "Body", "filter_response_body": "Filter JSON response body (uses JSONPath syntax)", - "headers": "الرؤوس", - "html": "لغة البرمجة", + "headers": "Headers", + "html": "HTML", "image": "صورة", - "json": "جسون", + "json": "JSON", "pdf": "PDF", "preview_html": "معاينة HTML", - "raw": "الخام", + "raw": "خام", "size": "مقاس", "status": "حالة", "time": "وقت", - "title": "إجابة", - "video": "Video", + "title": "الاستجابة", + "video": "فيديو", "waiting_for_connection": "في انتظار الاتصال", "xml": "XML" }, "settings": { "accent_color": "لون التمييز", "account": "حساب", - "account_deleted": "Your account has been deleted", + "account_deleted": "تم حذف الحساب", "account_description": "تخصيص إعدادات حسابك.", "account_email_description": "عنوان بريدك الإلكتروني الأساسي.", "account_name_description": "هذا هو اسم العرض الخاص بك.", - "additional": "Additional Settings", + "additional": "المزيد من الإعدادات", "background": "خلفية", "black_mode": "أسود", "choose_language": "اختر اللغة", @@ -656,65 +656,65 @@ "experiments_notice": "هذه مجموعة من التجارب التي نعمل عليها والتي قد تكون مفيدة ، أو ممتعة ، أو كليهما ، أو لا شيء. إنها ليست نهائية وقد لا تكون مستقرة ، لذلك إذا حدث شيء غريب للغاية ، فلا داعي للذعر. فقط قم بإيقاف تشغيل الشيء. النكات جانبا،", "extension_ver_not_reported": "لم يبلغ عنها", "extension_version": "نسخة التمديد", - "extensions": "ملحقات", - "extensions_use_toggle": "استخدم امتداد المتصفح لإرسال الطلبات (إن وجدت)", - "follow": "Follow Us", + "extensions": "الاضافات", + "extensions_use_toggle": "استخدم اضافة المتصفح لإرسال الطلبات (إن وجدت)", + "follow": "تابعنا", "interceptor": "المعترض", "interceptor_description": "البرامج الوسيطة بين التطبيق وواجهات برمجة التطبيقات.", - "language": "لغة", + "language": "اللغة", "light_mode": "الوضع الفاتح", - "official_proxy_hosting": "يستضيف Hoppscotch الوكيل الرسمي.", - "profile": "Profile", + "official_proxy_hosting": "يستضيف هوبسكوتش الوكيل الرسمي.", + "profile": "الملف الشخصي", "profile_description": "قم بتحديث بيانات ملفك الشخصي", "profile_email": "البريد الإلكتروني", - "profile_name": "الإسم", + "profile_name": "الاسم", "proxy": "الوكيل", "proxy_url": "وكيل URL", "proxy_use_toggle": "استخدم البرنامج الوسيط الوكيل لإرسال الطلبات", "read_the": "إقرأ ال", - "reset_default": "إعادة تعيين إلى الافتراضي", - "short_codes": "Short codes", - "short_codes_description": "Short codes which were created by you.", - "sidebar_on_left": "Sidebar on left", + "reset_default": "الرجوع إلى الإعدادات الافتراضية", + "short_codes": "الرموز القصيرة", + "short_codes_description": "الرموز القصيرة التي أنشأتها.", + "sidebar_on_left": "الشريط الجانبي على اليسار", "sync": "تزامن", "sync_collections": "المجموعات", "sync_description": "تتم مزامنة هذه الإعدادات مع السحابة.", "sync_environments": "البيئات", "sync_history": "تاريخ", - "system_mode": "نظام", + "system_mode": "وضع النظام", "telemetry": "القياس عن بعد", "telemetry_helps_us": "يساعدنا القياس عن بعد على تخصيص عملياتنا وتقديم أفضل تجربة لك.", "theme": "سمة", "theme_description": "تخصيص موضوع التطبيق الخاص بك.", "use_experimental_url_bar": "استخدم شريط URL التجريبي مع تمييز البيئة", "user": "المستخدم", - "verified_email": "Verified email", + "verified_email": "بريد إلكتروني موثق", "verify_email": "تأكيد البريد الإلكتروني" }, "shared_requests": { - "button": "Button", - "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", - "copy_html": "Copy HTML", - "copy_link": "Copy Link", - "copy_markdown": "Copy Markdown", - "creating_widget": "Creating widget", - "customize": "Customize", - "deleted": "Shared request deleted", - "description": "Select a widget, you can change and customize this later", - "embed": "Embed", - "embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", - "link": "Link", - "link_info": "Create a shareable link to share with anyone on the internet with view access.", - "modified": "Shared request modified", - "not_found": "Shared request not found", - "open_new_tab": "Open in new tab", - "preview": "Preview", - "run_in_hoppscotch": "Run in Hoppscotch", + "button": "زر", + "button_info": "أنشئ زر 'تشغيل في هوبسكوتش' لموقعك الإلكتروني أو مدونتك أو ملف README.", + "copy_html": "نسخ HTML", + "copy_link": "نسخ الرابط", + "copy_markdown": "نسخ Markdown", + "creating_widget": "إنشاء الواجهة", + "customize": "تخصيص", + "deleted": "تم حذف الطلب المشترك", + "description": "اختر واجهة، يمكنك تغييرها وتخصيصها لاحقًا", + "embed": "تضمين", + "embed_info": "أضف 'ملعب API في هوبسكوتش' مصغر إلى موقعك الإلكتروني أو مدونتك أو مستنداتك.", + "link": "رابط", + "link_info": "أنشئ رابطًا قابلًا للمشاركة لتشاركه مع أي شخص على الإنترنت مع إمكانية العرض.", + "modified": "تم تعديل الطلب المشترك", + "not_found": "لم يتم العثور على الطلب المشترك", + "open_new_tab": "فتح في تبويبة جديدة", + "preview": "معاينة", + "run_in_hoppscotch": "تشغيل في هوبسكوتش", "theme": { - "dark": "Dark", - "light": "Light", - "system": "System", - "title": "Theme" + "dark": "داكن", + "light": "فاتح", + "system": "الوضع الإفتراضي للنظام", + "title": "الموضوع" } }, "shortcut": { @@ -726,7 +726,7 @@ "title": "عام" }, "miscellaneous": { - "invite": "ادعُ الناس إلى Hoppscotch", + "invite": "دعوة الناس إلى هوبسكوتش", "title": "متفرقات" }, "navigation": { @@ -734,7 +734,7 @@ "documentation": "انتقل إلى صفحة التوثيق", "forward": "انتقل إلى الصفحة التالية", "graphql": "انتقل إلى صفحة GraphQL", - "profile": "Go to Profile page", + "profile": "انتقل إلى صفحة الملف الشخصي", "realtime": "انتقل إلى صفحة الوقت الفعلي", "rest": "انتقل إلى صفحة REST", "settings": "انتقل إلى صفحة الإعدادات", @@ -742,10 +742,10 @@ }, "others": { "prettify": "Prettify Editor's Content", - "title": "Others" + "title": "أخرى" }, "request": { - "delete_method": "حدد طريقة الحذف", + "delete_method": "حدد طريقة DELETE", "get_method": "حدد طريقة GET", "head_method": "حدد طريقة HEAD", "import_curl": "Import cURL", @@ -754,132 +754,132 @@ "post_method": "حدد طريقة POST", "previous_method": "حدد الطريقة السابقة", "put_method": "حدد طريقة PUT", - "rename": "Rename Request", + "rename": "إعادة تسمية الطلب", "reset_request": "طلب إعادة التعيين", - "save_request": "Save Request", + "save_request": "حفظ الطلب", "save_to_collections": "حفظ في المجموعات", "send_request": "ارسل طلب", - "share_request": "Share Request", - "show_code": "Generate code snippet", + "share_request": "مشاركة الطلب", + "show_code": "توليد الكود", "title": "طلب", - "copy_request_link": "نسخ ارتباط الطلب" + "copy_request_link": "نسخ رابط الطلب" }, "response": { - "copy": "Copy response to clipboard", - "download": "Download response as file", - "title": "Response" + "copy": "نسخ الاستجابة إلى الحافظة", + "download": "تنزيل الاستجابة كملف", + "title": "الاستجابة" }, "theme": { "black": "انتقل الى الوضع الأسود", "dark": "انتقل الى الوضع الليلي", "light": "انتقل الى الوضح الفاتح", "system": "انتقل الى الوضع الإفتراضي للنظام", - "title": "Theme" + "title": "السمة" } }, "show": { - "code": "أظهر الكود", - "collection": "Expand Collection Panel", - "more": "أظهر المزيد", + "code": "عرض الكود", + "collection": "توسيع لوحة المجموعة", + "more": "عرض المزيد", "sidebar": "عرض الشريط الجانبي" }, "socketio": { "communication": "تواصل", - "connection_not_authorized": "This SocketIO connection does not use any authentication.", + "connection_not_authorized": "اتصال SocketIO هذا لا يستخدم أي مصادقة.", "event_name": "اسم الحدث", "events": "الأحداث", "log": "سجل", "url": "URL" }, "spotlight": { - "change_language": "Change Language", + "change_language": "تغيير اللغة", "environments": { - "delete": "Delete current environment", - "duplicate": "Duplicate current environment", - "duplicate_global": "Duplicate global environment", - "edit": "Edit current environment", - "edit_global": "Edit global environment", - "new": "Create new environment", - "new_variable": "Create a new environment variable", - "title": "Environments" + "delete": "حذف البيئة الحالية", + "duplicate": "تكرار البيئة الحالية", + "duplicate_global": "تكرار البيئة العامة", + "edit": "تحرير البيئة الحالية", + "edit_global": "تحرير البيئة العامة", + "new": "إنشاء بيئة جديدة", + "new_variable": "إنشاء متغير بيئة جديد", + "title": "البيئات" }, "general": { - "chat": "Chat with support", - "help_menu": "Help and support", - "open_docs": "Read Documentation", - "open_github": "Open GitHub repository", - "open_keybindings": "Keyboard shortcuts", - "social": "Social", - "title": "General" + "chat": "الدردشة مع الدعم", + "help_menu": "المساعدة والدعم", + "open_docs": "قراءة المستندات", + "open_github": "فتح مستودع جيت هاب", + "open_keybindings": "اختصارات لوحة المفاتيح", + "social": "وسائل التواصل الاجتماعي", + "title": "عام" }, "graphql": { - "connect": "Connect to server", - "disconnect": "Disconnect from server" + "connect": "الاتصال بالخادم", + "disconnect": "انهاء الاتصال بالخادم" }, "miscellaneous": { - "invite": "Invite your friends to Hoppscotch", - "title": "Miscellaneous" + "invite": "دعوة أصدقائك إلى هوبسكوتش", + "title": "متنوع" }, "request": { - "save_as_new": "Save as new request", - "select_method": "Select method", - "switch_to": "Switch to", - "tab_authorization": "Authorization tab", - "tab_body": "Body tab", - "tab_headers": "Headers tab", - "tab_parameters": "Parameters tab", - "tab_pre_request_script": "Pre-request script tab", - "tab_query": "Query tab", - "tab_tests": "Tests tab", - "tab_variables": "Variables tab" + "save_as_new": "حفظ كطلب جديد", + "select_method": "اختيار الطريقة", + "switch_to": "الانتقال إلى", + "tab_authorization": "تبويب Authorization", + "tab_body": "تبويب Body", + "tab_headers": "تبويب Headers", + "tab_parameters": "تبويب Parameters", + "tab_pre_request_script": "تبويب السكربت قبل الطلب", + "tab_query": "تبويب Query", + "tab_tests": "تبويب Tests", + "tab_variables": "تبويب Variables" }, "response": { - "copy": "Copy response", - "download": "Download response as file", - "title": "Response" + "copy": "نسخ الاستجابة", + "download": "تحميل الاستجابة كملف", + "title": "الاستجابة" }, "section": { - "interceptor": "Interceptor", - "interface": "Interface", - "theme": "Theme", - "user": "User" + "interceptor": "القاطع", + "interface": "الواجهة", + "theme": "السمة", + "user": "المستخدم" }, "settings": { - "change_interceptor": "Change Interceptor", - "change_language": "Change Language", + "change_interceptor": "تغيير القاطع", + "change_language": "تغيير اللغة", "theme": { - "black": "Black", - "dark": "Dark", - "light": "Light", - "system": "System preference" + "black": "أسود", + "dark": "داكن", + "light": "فاتح", + "system": "الوضع الإفتراضي للنظام" } }, "tab": { - "close_current": "Close current tab", - "close_others": "Close all other tabs", - "duplicate": "Duplicate current tab", - "new_tab": "Open a new tab", - "title": "Tabs" + "close_current": "إغلاق علامة التبويب الحالية", + "close_others": "إغلاق كل علامات التبويب الأخرى", + "duplicate": "تكرار علامة التبويب الحالية", + "new_tab": "فتح تبويب جديد", + "title": "علامات التبويب" }, "workspace": { - "delete": "Delete current team", - "edit": "Edit current team", - "invite": "Invite people to team", - "new": "Create new team", - "switch_to_personal": "Switch to your personal workspace", - "title": "Teams" + "delete": "حذف الفريق الحالي", + "edit": "تحرير الفريق الحالي", + "invite": "دعوة أشخاص إلى الفريق", + "new": "إنشاء فريق جديد", + "switch_to_personal": "الانتقال إلى مساحة العمل الشخصية", + "title": "الفرق" }, "phrases": { - "try": "Try", - "import_collections": "Import collections", - "create_environment": "Create environment", - "create_workspace": "Create workspace", - "share_request": "Share request" + "try": "جرب", + "import_collections": "استيراد المجموعات", + "create_environment": "إنشاء بيئة", + "create_workspace": "إنشاء مساحة عمل", + "share_request": "مشاركة الطلب" } }, "sse": { "event_type": "نوع الحدث", - "log": "سجل", + "log": "السجل", "url": "URL" }, "state": { @@ -889,114 +889,114 @@ "connected": "متصل", "connected_to": "متصل بـ {name}", "connecting_to": "جارٍ الاتصال بـ {name} ...", - "connection_error": "Failed to connect", - "connection_failed": "Connection failed", - "connection_lost": "Connection lost", - "copied_interface_to_clipboard": "Copied {language} interface type to clipboard", + "connection_error": "فشل الاتصال", + "connection_failed": "فشل الاتصال", + "connection_lost": "انقطع الاتصال", + "copied_interface_to_clipboard": "تم نسخ نوع واجهة {language} إلى الحافظة", "copied_to_clipboard": "نسخ إلى الحافظة", "deleted": "تم الحذف", - "deprecated": "إهمال", - "disabled": "معاق", + "deprecated": "مهمل", + "disabled": "معطل", "disconnected": "انقطع الاتصال", "disconnected_from": "انقطع الاتصال بـ {name}", "docs_generated": "تم إنشاء الوثائق", - "download_failed": "Download failed", + "download_failed": "فشل التنزيل", "download_started": "بدأ التنزيل", - "enabled": "ممكن", + "enabled": "ممكّن", "file_imported": "تم استيراد الملف", "finished_in": "انتهى في {duration} مللي ثانية", - "hide": "Hide", + "hide": "إخفاء", "history_deleted": "تم حذف السجل", - "linewrap": "خطوط الالتفاف", + "linewrap": "التفاف السطور", "loading": "تحميل...", - "message_received": "Message: {message} arrived on topic: {topic}", - "mqtt_subscription_failed": "Something went wrong while subscribing to topic: {topic}", - "none": "لا شيئ", - "nothing_found": "لم يتم العثور على أي شيئ", - "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", - "published_message": "Published message: {message} to topic: {topic}", - "reconnection_error": "Failed to reconnect", - "show": "Show", - "subscribed_failed": "Failed to subscribe to topic: {topic}", - "subscribed_success": "Successfully subscribed to topic: {topic}", - "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", - "unsubscribed_success": "Successfully unsubscribed from topic: {topic}", + "message_received": "الرسالة: {message} وصلت على الموضوع: {topic}", + "mqtt_subscription_failed": "حدث خطأ ما أثناء الاشتراك في الموضوع: {topic}", + "none": "لا شيء", + "nothing_found": "لم يتم العثور على شيء", + "published_error": "حدث خطأ ما أثناء نشر الرسالة: {message} على الموضوع: {topic}", + "published_message": "تم نشر الرسالة: {message} على الموضوع: {topic}", + "reconnection_error": "فشل إعادة الاتصال", + "show": "إظهار", + "subscribed_failed": "فشل الاشتراك في الموضوع: {topic}", + "subscribed_success": "تم الاشتراك بنجاح في الموضوع: {topic}", + "unsubscribed_failed": "فشل إلغاء الاشتراك من الموضوع: {topic}", + "unsubscribed_success": "تم إلغاء الاشتراك بنجاح من الموضوع: {topic}", "waiting_send_request": "في انتظار إرسال الطلب" }, "support": { "changelog": "اقرأ المزيد عن أحدث الإصدارات", "chat": "أسئلة؟ دردش معنا!", "community": "اطرح الأسئلة وساعد الآخرين", - "documentation": "اقرأ المزيد عن Hoppscotch", - "forum": "اسأل سؤالا وتلقى جوابا", - "github": "Follow us on Github", + "documentation": "اقرأ المزيد عن هوبسكوتش", + "forum": "اسأل سؤالاً وتلقَّ جواباً", + "github": "تابعنا على جيت هاب", "shortcuts": "تصفح التطبيق بشكل أسرع", - "title": "يدعم", + "title": "الدعم", "twitter": "تابعنا على تويتر", "team": "تواصل مع الفريق" }, "tab": { - "authorization": "تفويض", - "body": "الجسم", - "close": "Close Tab", - "close_others": "Close other Tabs", + "authorization": "Authorization", + "body": "Body", + "close": "إغلاق", + "close_others": "إغلاق كل التبويبات الأخرى", "collections": "المجموعات", - "documentation": "توثيق", - "duplicate": "Duplicate Tab", - "environments": "Environments", - "headers": "الرؤوس", - "history": "تاريخ", + "documentation": "المستندات", + "duplicate": "تكرار التبويب", + "environments": "البيئات", + "headers": "Headers", + "history": "السجل", "mqtt": "MQTT", - "parameters": "حدود", + "parameters": "Parameters", "pre_request_script": "البرنامج النصي للطلب المسبق", - "queries": "استفسارات", - "query": "استفسار", - "schema": "مخطط", - "shared_requests": "Shared Requests", - "codegen": "Generate Code", - "code_snippet": "Code snippet", - "share_tab_request": "Share tab request", + "queries": "Queries", + "query": "Query", + "schema": "Schema", + "shared_requests": "الطلبات المشتركة", + "codegen": "توليد الكود", + "code_snippet": "مقتطف الكود", + "share_tab_request": "مشاركة طلب التبويب", "socketio": "مقبس", "sse": "SSE", "tests": "الاختبارات", "types": "أنواع", "variables": "المتغيرات", - "websocket": "مقبس الويب" + "websocket": "WebSocket" }, "team": { - "already_member": "انت بالفعل عضو في هذا الفريق! اتصل بمدير الفريق.", + "already_member": "أنت بالفعل عضو في هذا الفريق! اتصل بمدير الفريق.", "create_new": "أنشئ فريقًا جديدًا", "deleted": "تم حذف الفريق", "edit": "تحرير الفريق", "email": "بريد إلكتروني", - "email_do_not_match": "البريد الإلكتروني لا يتوافق مع معلومات حسابك. اتصل بمدير الفريق.", - "exit": "فريق الخروج", - "exit_disabled": "فقط المالك لا يمكنه الخروج من الفريق", - "failed_invites": "Failed invites", - "invalid_coll_id": "Invalid collection ID", + "email_do_not_match": "البريد الإلكتروني لا يتطابق مع معلومات حسابك. اتصل بمدير الفريق.", + "exit": "الخروج من الفريق", + "exit_disabled": "فقط المالك يمكنه الخروج من الفريق", + "failed_invites": "الدعوات الفاشلة", + "invalid_coll_id": "رقم المجموعة غير صالح", "invalid_email_format": "تنسيق البريد الإلكتروني غير صالح", - "invalid_id": "معرف الفريق غير صالح. اتصل بمدير الفريق.", + "invalid_id": "رقم الفريق غير صالح. اتصل بمدير الفريق.", "invalid_invite_link": "رابط الدعوة غير صالح", - "invalid_invite_link_description": "الرابط غير صالح. اتصل بمدير الفريق.", + "invalid_invite_link_description": "الرابط غير صالح أو منتهي. اتصل بمدير الفريق.", "invalid_member_permission": "يرجى تقديم إذن صالح لعضو الفريق", "invite": "دعوة", - "invite_more": "ادعو المزيد", - "invite_tooltip": "ادعو الناس الى العمل", - "invited_to_team": "{owner} قادم بدعوتك للإنضمام الى {team}", + "invite_more": "دعوة المزيد", + "invite_tooltip": "دعوة الأشخاص للانضمام", + "invited_to_team": "{owner} دعاك للانضمام إلى فريق {team}", "join": "تم قبول الدعوة", - "join_team": "انصم الى فريق {team}", - "joined_team": "لقد انضممت الى فريق {team}", - "joined_team_description": "انت الآن عضو في الفريق", + "join_team": "انضم إلى فريق {team}", + "joined_team": "لقد انضممت إلى فريق {team}", + "joined_team_description": "أنت الآن عضو في الفريق", "left": "لقد تركت الفريق", "login_to_continue": "سجل الدخول للإكمال", - "login_to_continue_description": "عليك تسجيل الدخول لكي تنضم الى فريق", - "logout_and_try_again": "سجل الخروج و ادخل بحساب آخر", - "member_has_invite": "معرف البريد الإلكتروني لديه دعوة بالفعل. اتصل بمدير الفريق.", + "login_to_continue_description": "يجب عليك تسجيل الدخول لكي تنضم إلى الفريق", + "logout_and_try_again": "سجل الخروج وادخل بحساب آخر", + "member_has_invite": "البريد الإلكتروني لديه دعوة بالفعل. اتصل بمدير الفريق.", "member_not_found": "لم يتم العثور على العضو. اتصل بمدير الفريق.", "member_removed": "تمت إزالة المستخدم", "member_role_updated": "تم تحديث أدوار المستخدم", - "members": "أعضاء", - "more_members": "+{count} more", + "members": "الأعضاء", + "more_members": "+{count} أعضاء آخرين", "name_length_insufficient": "يجب أن يتكون اسم الفريق من 6 أحرف على الأقل", "name_updated": "تم تحديث اسم الفريق", "new": "فريق جديد", @@ -1004,38 +1004,38 @@ "new_name": "فريقي الجديد", "no_access": "ليس لديك حق التعديل في هذه المجموعات", "no_invite_found": "لم يتم العثور على الدعوة. اتصل بمدير الفريق", - "no_request_found": "Request not found.", - "not_found": "Team not found. Contact your team owner.", - "not_valid_viewer": "You are not a valid viewer. Contact your team owner.", - "parent_coll_move": "Cannot move collection to a child collection", + "no_request_found": "لم يتم العثور على الطلب.", + "not_found": "الفريق غير موجود. اتصل بمالك الفريق.", + "not_valid_viewer": "أنت لست مشاهدًا صالحًا. اتصل بمالك الفريق.", + "parent_coll_move": "لا يمكن نقل المجموعة إلى مجموعة فرعية", "pending_invites": "دعوات معلقة", - "permissions": "أذونات", - "same_target_destination": "Same target and destination", - "saved": "فريق حفظ", - "select_a_team": "اختر فريق", - "success_invites": "Success invites", - "title": "فرق", - "we_sent_invite_link": "لقد أرسلنا رابط دعوة لجميع المدعوين!", - "invite_sent_smtp_disabled": "Invite links generated", + "permissions": "الأذونات", + "same_target_destination": "نفس الهدف والوجهة", + "saved": "تم حفظ الفريق", + "select_a_team": "اختر فريقًا", + "success_invites": "الدعوات الناجحة", + "title": "الفرق", + "we_sent_invite_link": "لقد أرسلنا رابط الدعوة لجميع المدعوين!", + "invite_sent_smtp_disabled": "تم إنشاء روابط الدعوة", "we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق.", - "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", - "copy_invite_link": "Copy Invite Link", - "search_title": "Team Requests", + "invite_sent_smtp_disabled_description": "إرسال رسائل الدعوة معطل لهذه النسخة من هوبسكوتش. يرجى استخدام زر نسخ الرابط لنسخ الرابط ومشاركته يدويًا.", + "copy_invite_link": "نسخ رابط الدعوة", + "search_title": "طلبات الفريق", "join_beta": "انضم إلى برنامج بيتا للوصول إلى الفرق." }, "team_environment": { - "deleted": "Environment Deleted", - "duplicate": "Environment Duplicated", - "not_found": "Environment not found." + "deleted": "تم حذف البيئة", + "duplicate": "تم تكرار البيئة", + "not_found": "البيئة غير موجودة" }, "test": { - "failed": "فشل التجربة", + "failed": "فشل test", "javascript_code": "كود جافا سكريبت", "learn": "اقرأ الوثائق", - "passed": "نجاح التجربة", - "report": "تقرير الاختبار", - "results": "نتائج الإختبار", - "script": "النصي", + "passed": "نجاح test", + "report": "تقرير test", + "results": "نتائج test", + "script": "السكربت", "snippets": "المقتطفات" }, "websocket": { @@ -1046,59 +1046,59 @@ "url": "URL" }, "workspace": { - "change": "Change workspace", - "personal": "My Workspace", - "other_workspaces": "My Workspaces", - "team": "Team Workspace", - "title": "Workspaces" + "change": "تغيير مساحة العمل", + "personal": "مساحتي الشخصية", + "other_workspaces": "مساحاتي", + "team": "مساحة عمل الفريق", + "title": "مساحات العمل" }, "site_protection": { - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", - "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + "login_to_continue": "تسجيل الدخول للمتابعة", + "login_to_continue_description": "تحتاج إلى تسجيل الدخول للوصول إلى هذه النسخة من هوبسكوتش للمؤسسات.", + "error_fetching_site_protection_status": "حدث خطأ أثناء جلب حالة حماية الموقع" }, "access_tokens": { - "tab_title": "Tokens", - "section_title": "Personal Access Tokens", - "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", - "last_used_on": "Last used on", - "expires_on": "Expires on", - "no_expiration": "No expiration", - "expired": "Expired", - "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", - "token_purpose": "What's this token for?", - "expiration_label": "Expiration", - "scope_label": "Scope", - "workspace_read_only_access": "Read-only access to workspace data.", - "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", - "generate_token": "Generate Token", - "invalid_label": "Please provide a label for the token", - "no_expiration_verbose": "This token will never expire!", - "token_expires_on": "This token will expire on", - "generate_new_token": "Generate new token", - "generate_modal_title": "New Personal Access Token", - "deletion_success": "The access token {label} has been deleted" + "tab_title": "الرموز", + "section_title": "الرموز الشخصية", + "section_description": "تساعدك الرموز الشخصية على ربط CLI بحسابك في هوبسكوتش", + "last_used_on": "آخر استخدام في", + "expires_on": "تنتهي في", + "no_expiration": "بدون انتهاء", + "expired": "منتهية الصلاحية", + "copy_token_warning": "تأكد من نسخ الرمز الشخصي الآن. لن تتمكن من رؤيته مرة أخرى!", + "token_purpose": "ما الغرض من هذا الرمز؟", + "expiration_label": "تاريخ الانتهاء", + "scope_label": "نطاق", + "workspace_read_only_access": "وصول للقراءة فقط إلى بيانات مساحة العمل.", + "personal_workspace_access_limitation": "لا يمكن للرموز الشخصية الوصول إلى مساحة العمل الشخصية.", + "generate_token": "توليد رمز", + "invalid_label": "يرجى تقديم تسمية صالحة للرمز", + "no_expiration_verbose": "هذا الرمز لن ينتهي أبدًا!", + "token_expires_on": "سينتهي هذا الرمز في", + "generate_new_token": "توليد رمز جديد", + "generate_modal_title": "رمز وصول شخصي جديد", + "deletion_success": "تم حذف رمز الوصول {label}" }, "collection_runner": { - "collection_id": "Collection ID", - "environment_id": "Environment ID", - "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", - "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", - "include_active_environment": "Include active environment:", + "collection_id": "رقم المجموعة", + "environment_id": "رقم البيئة", + "cli_collection_id_description": "سيتم استخدام رقم المجموعة هذا بواسطة مشغل المجموعة عبر CLI لـ هوبسكوتش.", + "cli_environment_id_description": "سيتم استخدام رقم البيئة هذا بواسطة مشغل المجموعة عبر CLI لـ هوبسكوتش.", + "include_active_environment": "تضمين البيئة النشطة:", "cli": "CLI", - "ui": "Runner (coming soon)", - "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", - "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", - "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "ui": "المشغل (قريبًا)", + "cli_command_generation_description_cloud": "انسخ الأمر أدناه وقم بتشغيله من CLI. يرجى تحديد رمز الوصول الشخصي.", + "cli_command_generation_description_sh": "انسخ الأمر أدناه وقم بتشغيله من CLI. يرجى تحديد رمز الوصول الشخصي والتحقق من عنوان URL لخادم مثيل SH المُولّد.", + "cli_command_generation_description_sh_with_server_url_placeholder": "انسخ الأمر أدناه وقم بتشغيله من CLI. يرجى تحديد رمز الوصول الشخصي وعنوان URL لخادم مثيل SH.", + "run_collection": "تشغيل المجموعة" }, "shortcodes": { - "actions": "Actions", - "created_on": "Created on", - "deleted": "Shortcode deleted", - "method": "Method", - "not_found": "Shortcode not found", - "short_code": "Short code", - "url": "URL" + "actions": "الإجراءات", + "created_on": "تم الإنشاء في", + "deleted": "تم حذف الرمز القصير", + "method": "الطريقة", + "not_found": "الرمز القصير غير موجود", + "short_code": "رمز قصير", + "url": "الرابط" } } diff --git a/packages/hoppscotch-common/locales/cn.json b/packages/hoppscotch-common/locales/cn.json index d3349ba2f..dc27544ba 100644 --- a/packages/hoppscotch-common/locales/cn.json +++ b/packages/hoppscotch-common/locales/cn.json @@ -1,6 +1,6 @@ { "action": { - "add": "Add", + "add": "新增", "autoscroll": "自动滚动", "cancel": "取消", "choose_file": "选择文件", @@ -11,7 +11,7 @@ "connect": "连接", "connecting": "连接中", "copy": "复制", - "create": "Create", + "create": "新增", "delete": "删除", "disconnect": "断开连接", "dismiss": "忽略", @@ -24,10 +24,10 @@ "go_back": "返回", "go_forward": "前进", "group_by": "分组方式", - "hide_secret": "Hide secret", + "hide_secret": "隐藏密钥", "label": "标签", "learn_more": "了解更多", - "download_here": "Download here", + "download_here": "下载到此处", "less": "更少", "more": "更多", "new": "新增", @@ -35,7 +35,7 @@ "open_workspace": "打开工作区", "paste": "粘贴", "prettify": "美化", - "properties": "Properties", + "properties": "属性", "remove": "移除", "rename": "重命名", "restore": "恢复", @@ -44,8 +44,8 @@ "scroll_to_top": "滚动至顶部", "search": "搜索", "send": "发送", - "share": "Share", - "show_secret": "Show secret", + "share": "分享", + "show_secret": "显示密钥", "start": "开始", "starting": "正在开始", "stop": "停止", @@ -66,7 +66,7 @@ "contact_us": "联系我们", "cookies": "Cookies", "copy": "复制", - "copy_interface_type": "Copy interface type", + "copy_interface_type": "复制接口类型", "copy_user_id": "复制认证 Token", "developer_option": "开发者选项", "developer_option_description": "开发者工具,有助于开发和维护 Hoppscotch。", @@ -82,7 +82,7 @@ "keyboard_shortcuts": "键盘快捷键", "name": "Hoppscotch", "new_version_found": "已发现新版本。刷新页面以更新。", - "open_in_hoppscotch": "Open in Hoppscotch", + "open_in_hoppscotch": "在 Hoppscotch 中打开", "options": "选项", "proxy_privacy_policy": "代理隐私政策", "reload": "重新加载", @@ -98,18 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "输入命令或搜索内容……", "we_use_cookies": "我们使用 cookies", - "updated_text": "Hoppscotch has been updated to v{version} 🎉", + "updated_text": "Hoppscotch 已更新至 v{version} 🎉", "whats_new": "新增内容", - "see_whats_new": "See what’s new", + "see_whats_new": "查看更新内容", "wiki": "帮助" }, "auth": { "account_exists": "当前帐号已存在 - 登录以链接两个帐号", "all_sign_in_options": "所有登录选项", - "continue_with_auth_provider": "Continue with {provider}", + "continue_with_auth_provider": "使用 {provider} 登录", "continue_with_email": "使用电子邮箱登录", "continue_with_github": "使用 GitHub 登录", - "continue_with_github_enterprise": "Continue with GitHub Enterprise", + "continue_with_github_enterprise": "使用 GitHub 企业版登录", "continue_with_google": "使用 Google 登录", "continue_with_microsoft": "使用 Microsoft 登录", "email": "电子邮箱地址", @@ -126,48 +126,48 @@ }, "authorization": { "generate_token": "生成令牌", - "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", + "graphql_headers": "将 Authorization 请求头作为 payload 发送到 connection_init", "include_in_url": "包含在 URL 内", - "inherited_from": "Inherited {auth} from parent collection {collection} ", + "inherited_from": "从父级集合 {collection} 继承 {auth}", "learn": "了解更多", "oauth": { - "redirect_auth_server_returned_error": "Auth Server returned an error state", - "redirect_auth_token_request_failed": "Request to get the auth token failed", - "redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token", - "redirect_invalid_state": "Invalid State value present in the redirect", - "redirect_no_auth_code": "No Authorization Code present in the redirect", - "redirect_no_client_id": "No Client ID defined", - "redirect_no_client_secret": "No Client Secret Defined", - "redirect_no_code_verifier": "No Code Verifier Defined", - "redirect_no_token_endpoint": "No Token Endpoint Defined", - "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", - "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", - "grant_type": "Grant Type", - "grant_type_auth_code": "Authorization Code", - "token_fetched_successfully": "Token fetched successfully", - "token_fetch_failed": "Failed to fetch token", - "validation_failed": "Validation Failed, please check the form fields", - "label_authorization_endpoint": "Authorization Endpoint", - "label_client_id": "Client ID", - "label_client_secret": "Client Secret", + "redirect_auth_server_returned_error": "鉴权服务器返回了一个错误状态", + "redirect_auth_token_request_failed": "获取鉴权令牌失败", + "redirect_auth_token_request_invalid_response": "请求鉴权令牌时,令牌端点返回了无效响应", + "redirect_invalid_state": "重定向中存在无效的状态值", + "redirect_no_auth_code": "重定向中不存在授权码", + "redirect_no_client_id": "未定义客户端ID", + "redirect_no_client_secret": "未定义客户端密钥", + "redirect_no_code_verifier": "未定义代码验证器", + "redirect_no_token_endpoint": "未定义令牌端点", + "something_went_wrong_on_oauth_redirect": "OAuth 重定向过程中出现问题", + "something_went_wrong_on_token_generation": "令牌生成过程中出现问题", + "token_generation_oidc_discovery_failed": "令牌生成失败: OpenID Connect 发现失败", + "grant_type": "授权类型", + "grant_type_auth_code": "授权码", + "token_fetched_successfully": "令牌获取成功", + "token_fetch_failed": "令牌获取失败", + "validation_failed": "验证失败,请检查表单字段", + "label_authorization_endpoint": "授权端点", + "label_client_id": "客户端 ID", + "label_client_secret": "客户端密钥", "label_code_challenge": "Code Challenge", - "label_code_challenge_method": "Code Challenge Method", + "label_code_challenge_method": "Code Challenge 方法", "label_code_verifier": "Code Verifier", - "label_scopes": "Scopes", - "label_token_endpoint": "Token Endpoint", - "label_use_pkce": "Use PKCE", - "label_implicit": "Implicit", - "label_password": "Password", - "label_username": "Username", - "label_auth_code": "Authorization Code", - "label_client_credentials": "Client Credentials" + "label_scopes": "范围", + "label_token_endpoint": "令牌端点", + "label_use_pkce": "使用 PKCE", + "label_implicit": "隐式", + "label_password": "密码", + "label_username": "用户名", + "label_auth_code": "授权码", + "label_client_credentials": "客户端凭证" }, "pass_key_by": "传递方式", - "pass_by_query_params_label": "Query Parameters", + "pass_by_query_params_label": "请求参数", "pass_by_headers_label": "Headers", "password": "密码", - "save_to_inherit": "Please save this request in any collection to inherit the authorization", + "save_to_inherit": "为了继承鉴权信息,请保存这个请求到任意集合中", "token": "令牌", "type": "授权类型", "username": "用户名" @@ -176,8 +176,8 @@ "created": "集合已创建", "different_parent": "不能用不同的父类来重新排序集合", "edit": "编辑集合", - "import_or_create": "Import or create a collection", - "import_collection": "Import Collection", + "import_or_create": "导入或创建一个集合", + "import_collection": "导入集合", "invalid_name": "请提供有效的集合名称", "invalid_root_move": "该集合已经在根级了", "moved": "移动完成", @@ -186,8 +186,8 @@ "name_length_insufficient": "集合名字至少需要 3 个字符", "new": "新建集合", "order_changed": "集合顺序已更新", - "properties": "Collection Properties", - "properties_updated": "Collection Properties Updated", + "properties": "集合属性", + "properties_updated": "集合属性已更新", "renamed": "集合已更名", "request_in_use": "请求正在使用中", "save_as": "另存为", @@ -208,13 +208,13 @@ "remove_folder": "你确定要永久删除该文件夹吗?", "remove_history": "你确定要永久删除全部历史记录吗?", "remove_request": "你确定要永久删除该请求吗?", - "remove_shared_request": "Are you sure you want to permanently delete this shared request?", + "remove_shared_request": "你确定要永久删除该共享请求吗?", "remove_team": "你确定要删除该团队吗?", "remove_telemetry": "你确定要退出遥测服务吗?", "request_change": "你确定你要放弃当前的请求,未保存的修改将被丢失。", "save_unsaved_tab": "你想保存在此标签页中所作的修改吗?", "sync": "您确定要同步该工作区吗?", - "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" + "delete_access_token": "你确定要删除这个授权令牌 {tokenLabel} 吗?" }, "context_menu": { "add_parameters": "添加至参数", @@ -223,20 +223,20 @@ }, "cookies": { "modal": { - "cookie_expires": "Expires", - "cookie_name": "Name", - "cookie_path": "Path", - "cookie_string": "Cookie string", - "cookie_value": "Value", - "empty_domain": "Domain is empty", - "empty_domains": "Domain list is empty", - "enter_cookie_string": "Enter cookie string", - "interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", - "managed_tab": "Managed", - "new_domain_name": "New domain name", - "no_cookies_in_domain": "No cookies set for this domain", - "raw_tab": "Raw", - "set": "Set a cookie" + "cookie_expires": "过期时间", + "cookie_name": "名称", + "cookie_path": "路径", + "cookie_string": "Cookie 字符串", + "cookie_value": "值", + "empty_domain": "域名为空", + "empty_domains": "域名列表为空", + "enter_cookie_string": "输入 Cookie 字符串", + "interceptor_no_support": "你当前选择的中间件不支持 Cookie ,请选择一个其他的中间件并重试。", + "managed_tab": "管理", + "new_domain_name": "新域名", + "no_cookies_in_domain": "该域名没有 Cookie", + "raw_tab": "原始内容", + "set": "设置一个 Cookie" } }, "count": { @@ -268,16 +268,16 @@ "pending_invites": "此团队无待办邀请", "profile": "登录以查看你的个人资料", "protocols": "协议为空", - "request_variables": "This request does not have any request variables", + "request_variables": "这个请求没有任何请求变量", "schema": "连接至 GraphQL 端点", - "secret_environments": "Secrets are not synced to Hoppscotch", - "shared_requests": "Shared requests are empty", - "shared_requests_logout": "Login to view your shared requests or create a new one", + "secret_environments": "密钥不会被 Hoppscotch 同步", + "shared_requests": "共享请求为空", + "shared_requests_logout": "登录并查看你的共享请求或创建一个新的共享请求", "subscription": "订阅为空", "team_name": "团队名称为空", "teams": "团队为空", "tests": "没有针对该请求的测试", - "access_tokens": "Access tokens are empty", + "access_tokens": "鉴权令牌为空", "shortcodes": "短链接为空" }, "environment": { @@ -291,7 +291,7 @@ "empty_variables": "没有变量", "global": "全局", "global_variables": "全局变量", - "import_or_create": "Import or create a environment", + "import_or_create": "导入或创建一个环境", "invalid_name": "请提供有效的环境名称", "list": "环境变量", "my_environments": "我的环境", @@ -304,8 +304,8 @@ "quick_peek": "快速浏览环境", "replace_with_variable": "替换为变量", "scope": "范围", - "secrets": "Secrets", - "secret_value": "Secret value", + "secrets": "密钥", + "secret_value": "密钥值", "select": "选择环境", "set": "设置环境", "set_as_environment": "设置为环境", @@ -316,14 +316,14 @@ "variable": "变量", "variables": "Variables", "variable_list": "变量列表", - "properties": "Environment Properties", - "details": "Details" + "properties": "环境属性", + "details": "详情" }, "error": { - "authproviders_load_error": "Unable to load auth providers", + "authproviders_load_error": "无法加载鉴权提供者", "browser_support_sse": "该浏览器似乎不支持 SSE。", "check_console_details": "检查控制台日志以获悉详情", - "check_how_to_add_origin": "Check how you can add an origin", + "check_how_to_add_origin": "检查如何添加源", "curl_invalid_format": "cURL 格式不正确", "danger_zone": "危险区域", "delete_account": "您的帐号目前为这些团队的拥有者:", @@ -336,42 +336,42 @@ "incorrect_email": "电子邮箱错误", "invalid_link": "无效链接", "invalid_link_description": "你点击的链接无效或已过期。", - "invalid_embed_link": "The embed does not exist or is invalid.", + "invalid_embed_link": "内嵌内容不存在或者失效。", "json_parsing_failed": "不合法的 JSON", "json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试", "network_error": "好像发生了网络错误,请重试。", "network_fail": "无法发送请求", - "no_collections_to_export": "No collections to export. Please create a collection to get started.", + "no_collections_to_export": "导出集合为空,请先创建一个集合。", "no_duration": "无持续时间", - "no_environments_to_export": "No environments to export. Please create an environment to get started.", + "no_environments_to_export": "导出集合环境,请先创建一个环境。", "no_results_found": "找不到结果", "page_not_found": "找不到此頁面", - "please_install_extension": "Please install the extension and add origin to the extension.", + "please_install_extension": "请安装扩展并将源添加至扩展。", "proxy_error": "代理错误", - "same_profile_name": "Updated profile name is same as the current profile name", + "same_profile_name": "更新后的配置文件名称与当前配置文件名称相同", "script_fail": "无法执行预请求脚本", "something_went_wrong": "发生了一些错误", "test_script_fail": "无法执行请求脚本", - "reading_files": "Error while reading one or more files.", - "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", - "generate_access_token": "Something went wrong while generating the access token", - "delete_access_token": "Something went wrong while deleting the access token" + "reading_files": "读取一个或多个文件时出错。", + "fetching_access_tokens_list": "获取令牌列表时出错", + "generate_access_token": "生成访问令牌时出错", + "delete_access_token": "删除访问令牌时出错" }, "export": { "as_json": "导出为 JSON", "create_secret_gist": "创建私密 Gist", - "create_secret_gist_tooltip_text": "Export as secret Gist", - "failed": "Something went wrong while exporting", - "secret_gist_success": "Successfully exported as secret Gist", + "create_secret_gist_tooltip_text": "导出为私密 Gist", + "failed": "导出时发生了错误", + "secret_gist_success": "成功导出为私密 Gist", "require_github": "使用 GitHub 登录以创建私密 Gist", "title": "导出", - "success": "Successfully exported", + "success": "成功导出", "gist_created": "已创建 Gist" }, "filter": { "all": "全部", "none": "无", - "starred": "已加星号" + "starred": "已加星标" }, "folder": { "created": "已创建文件夹", @@ -389,10 +389,10 @@ "schema": "模式", "subscriptions": "订阅", "switch_connection": "切换连接", - "url_placeholder": "Enter a GraphQL endpoint URL" + "url_placeholder": "输入一个 GraphQL 端点 URL" }, "graphql_collections": { - "title": "GraphQL Collections" + "title": "GraphQL 集合" }, "group": { "time": "时间", @@ -405,8 +405,8 @@ }, "helpers": { "authorization": "授权头将会在你发送请求时自动生成。", - "collection_properties_authorization": " This authorization will be set for every request in this collection.", - "collection_properties_header": "This header will be set for every request in this collection.", + "collection_properties_authorization": "这个授权将被应用在当前集合下的所有请求。", + "collection_properties_header": "这个请求头将被应用在当前集合下的所有请求。", "generate_documentation_first": "请先生成文档", "network_fail": "无法到达 API 端点。请检查网络连接并重试。", "offline": "你似乎处于离线状态,该工作区中的数据可能不是最新。", @@ -426,10 +426,10 @@ "import": { "collections": "导入集合", "curl": "导入 cURL", - "environments_from_gist": "Import From Gist", - "environments_from_gist_description": "Import Hoppscotch Environments From Gist", + "environments_from_gist": "从 Gist 导入", + "environments_from_gist_description": "从 Gist 导入 Hoppscotch 环境", "failed": "导入失败", - "from_file": "Import from File", + "from_file": "从文件导入", "from_gist": "从 Gist 导入", "from_gist_description": "从 Gist URL 导入", "from_insomnia": "从 Insomnia 导入", @@ -444,28 +444,28 @@ "from_postman_description": "从 Postman 集合中导入", "from_url": "从 URL 导入", "gist_url": "输入 Gist URL", - "gql_collections_from_gist_description": "Import GraphQL Collections From Gist", - "hoppscotch_environment": "Hoppscotch Environment", - "hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", + "gql_collections_from_gist_description": "从 Gist 导入 GraphQL 集合", + "hoppscotch_environment": "Hoppscotch 环境", + "hoppscotch_environment_description": "导入 Hoppscotch 环境 JSON 文件", "import_from_url_invalid_fetch": "无法从网址取得资料", "import_from_url_invalid_file_format": "导入组合时发生错误", "import_from_url_invalid_type": "不支持此类型。可接受的值为 'hoppscotch'、'openapi'、'postman'、'insomnia'", "import_from_url_success": "已导入组合", - "insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", + "insomnia_environment_description": "从一个 JSON/YAML 文件中导入 Insomnia 环境", "json_description": "从 Hoppscotch 的集合文件导入(JSON)", - "postman_environment": "Postman Environment", - "postman_environment_description": "Import Postman Environment from a JSON file", + "postman_environment": "Postman 环境", + "postman_environment_description": "从一个 JSON 文件中导入 Postman 环境", "title": "导入", - "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", - "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", - "success": "Successfully imported" + "file_size_limit_exceeded_warning_multiple_files": "当前选择的文件大小超过了推荐的 10MB,只导入第一个被选择的 {files}。", + "file_size_limit_exceeded_warning_single_file": "当前选择的文件大小超过了推荐的 10MB,请选择其他文件", + "success": "成功导入" }, "inspections": { "description": "查可能的错误", "environment": { "add_environment": "添加到环境", - "add_environment_value": "Add value", - "empty_value": "Environment value is empty for the variable '{variable}' ", + "add_environment_value": "添加值", + "empty_value": "环境变量“{variable}”的值为空", "not_found": "环境变量“{environment}”未找到。" }, "header": { @@ -497,10 +497,10 @@ "close_unsaved_tab": "有未保存的变更", "collections": "集合", "confirm": "确认", - "customize_request": "Customize Request", + "customize_request": "自定义请求", "edit_request": "编辑请求", "import_export": "导入/导出", - "share_request": "Share Request" + "share_request": "分享请求" }, "mqtt": { "already_subscribed": "您已经订阅了此主题。", @@ -516,12 +516,12 @@ "keep_alive": "Keep Alive", "log": "日志", "lw_message": "遗嘱消息", - "lw_qos": "遗嘱消息QoS", + "lw_qos": "遗嘱消息 QoS", "lw_retain": "遗嘱消息保留", "lw_topic": "遗嘱消息主题", "message": "消息", "new": "新订阅", - "not_connected": "请先启动MQTT连接。", + "not_connected": "请先启动 MQTT 连接。", "publish": "发布", "qos": "QoS", "ssl": "SSL", @@ -581,8 +581,8 @@ "enter_curl": "输入 cURL", "generate_code": "生成代码", "generated_code": "已生成代码", - "go_to_authorization_tab": "Go to Authorization tab", - "go_to_body_tab": "Go to Body tab", + "go_to_authorization_tab": "前往鉴权标签", + "go_to_body_tab": "前往请求体标签", "header_list": "请求头列表", "invalid_name": "请提供请求名称", "method": "方法", @@ -601,19 +601,19 @@ "raw_body": "原始请求体", "rename": "重命名请求", "renamed": "请求重命名", - "request_variables": "Request variables", + "request_variables": "请求变量", "run": "运行", "save": "保存", "save_as": "另存为", "saved": "请求已保存", "share": "分享", "share_description": "分享 Hoppscotch 给你的朋友", - "share_request": "Share Request", + "share_request": "分享请求", "stop": "停止", "title": "请求", "type": "请求类型", "url": "URL", - "url_placeholder": "Enter a URL or paste a cURL command", + "url_placeholder": "输入一个 URL 或者粘贴一个 cURL 命令", "variables": "变量", "view_my_links": "查看我的链接", "copy_link": "复制链接" @@ -633,7 +633,7 @@ "status": "状态", "time": "时间", "title": "响应", - "video": "Video", + "video": "视频", "waiting_for_connection": "等待连接", "xml": "XML" }, @@ -644,7 +644,7 @@ "account_description": "自定义您的帐户设置。", "account_email_description": "您的主要电子邮箱地址。", "account_name_description": "这是您的显示名称。", - "additional": "Additional Settings", + "additional": "其他设置", "background": "背景", "black_mode": "黑色", "choose_language": "选择语言", @@ -692,29 +692,29 @@ "verify_email": "验证电子邮箱" }, "shared_requests": { - "button": "Button", - "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", - "copy_html": "Copy HTML", - "copy_link": "Copy Link", - "copy_markdown": "Copy Markdown", - "creating_widget": "Creating widget", - "customize": "Customize", - "deleted": "Shared request deleted", - "description": "Select a widget, you can change and customize this later", - "embed": "Embed", - "embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", - "link": "Link", - "link_info": "Create a shareable link to share with anyone on the internet with view access.", - "modified": "Shared request modified", - "not_found": "Shared request not found", - "open_new_tab": "Open in new tab", - "preview": "Preview", - "run_in_hoppscotch": "Run in Hoppscotch", + "button": "按钮", + "button_info": "为你的网站、博客或者 README 创建一个“Run in Hoppscotch”按钮。", + "copy_html": "复制 HTML", + "copy_link": "复制链接", + "copy_markdown": "复制 Markdown", + "creating_widget": "创建控件", + "customize": "自定义", + "deleted": "共享请求已删除", + "description": "选择一个控件,之后你可以更改或者自定义", + "embed": "内嵌", + "embed_info": "为你的网站、博客或者文档添加一个小的“Hoppscotch API Playground”。", + "link": "链接", + "link_info": "创建一个仅查看的分享链接给任何人", + "modified": "共享请求与修改", + "not_found": "未找到共享请求", + "open_new_tab": "在新标签中打开", + "preview": "预览", + "run_in_hoppscotch": "在 Hoppscotch 中运行", "theme": { - "dark": "Dark", - "light": "Light", - "system": "System", - "title": "Theme" + "dark": "深色", + "light": "浅色", + "system": "系统", + "title": "主题" } }, "shortcut": { @@ -759,7 +759,7 @@ "save_request": "保存请求", "save_to_collections": "保存到集合", "send_request": "发送请求", - "share_request": "Share Request", + "share_request": "共享请求", "show_code": "生成代码片段", "title": "请求", "copy_request_link": "复制请求链接" @@ -830,7 +830,7 @@ "tab_parameters": "参数标签页", "tab_pre_request_script": "预请求脚本标签页", "tab_query": "查询标签页", - "tab_tests": "测试标签页b", + "tab_tests": "测试标签页", "tab_variables": "变量标签页" }, "response": { @@ -871,10 +871,10 @@ }, "phrases": { "try": "Try", - "import_collections": "Import collections", - "create_environment": "Create environment", - "create_workspace": "Create workspace", - "share_request": "Share request" + "import_collections": "导入集合", + "create_environment": "创建环境", + "create_workspace": "创建工作区", + "share_request": "分享请求" } }, "sse": { @@ -892,7 +892,7 @@ "connection_error": "连接错误", "connection_failed": "连接失败", "connection_lost": "连接丢失", - "copied_interface_to_clipboard": "Copied {language} interface type to clipboard", + "copied_interface_to_clipboard": "复制 {language} 接口类型到剪贴板", "copied_to_clipboard": "已复制到剪贴板", "deleted": "已删除", "deprecated": "已弃用", @@ -900,7 +900,7 @@ "disconnected": "断开连接", "disconnected_from": "与 {name} 断开连接", "docs_generated": "已生成文档", - "download_failed": "Download failed", + "download_failed": "下载失败", "download_started": "开始下载", "enabled": "启用", "file_imported": "文件已导入", @@ -938,11 +938,11 @@ "tab": { "authorization": "授权", "body": "请求体", - "close": "Close Tab", - "close_others": "Close other Tabs", + "close": "关闭标签页", + "close_others": "关闭所有其他标签页", "collections": "集合", "documentation": "帮助文档", - "duplicate": "Duplicate Tab", + "duplicate": "复制当前标签页", "environments": "环境", "headers": "请求头", "history": "历史记录", @@ -951,11 +951,11 @@ "pre_request_script": "预请求脚本", "queries": "查询", "query": "查询", - "schema": "Schema", - "shared_requests": "Shared Requests", - "codegen": "Generate Code", - "code_snippet": "Code snippet", - "share_tab_request": "Share tab request", + "schema": "模式", + "shared_requests": "共享请求", + "codegen": "生成代码", + "code_snippet": "代码片段", + "share_tab_request": "分享标签页请求", "socketio": "Socket.IO", "sse": "SSE", "tests": "测试", @@ -972,7 +972,7 @@ "email_do_not_match": "邮箱无法与你的帐户信息匹配。请联系你的团队者。", "exit": "退出团队", "exit_disabled": "团队所有者无法退出团队", - "failed_invites": "Failed invites", + "failed_invites": "邀请失败", "invalid_coll_id": "无效的集合 ID", "invalid_email_format": "电子邮箱格式无效", "invalid_id": "无效的团队 ID,请联系你的团队者。", @@ -1013,14 +1013,14 @@ "same_target_destination": "目标相同", "saved": "团队已保存", "select_a_team": "选择团队", - "success_invites": "Success invites", + "success_invites": "邀请成功", "title": "团队", "we_sent_invite_link": "我们向所有受邀者发送了邀请链接!", - "invite_sent_smtp_disabled": "Invite links generated", + "invite_sent_smtp_disabled": "邀请链接已生成", "we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。", - "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", - "copy_invite_link": "Copy Invite Link", - "search_title": "Team Requests", + "invite_sent_smtp_disabled_description": "发送邀请邮件在此 Hoppscotch 实例中已禁用。请使用“复制链接”按钮手动复制并分享邀请链接。", + "copy_invite_link": "复制邀请链接", + "search_title": "团队请求", "join_beta": "加入 Beta 计划以访问团队。" }, "team_environment": { @@ -1053,44 +1053,44 @@ "title": "工作空间" }, "site_protection": { - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", - "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + "login_to_continue": "登录以继续", + "login_to_continue_description": "您需要登录才能访问此 Hoppscotch 企业实例。", + "error_fetching_site_protection_status": "获取站点保护状态时出现问题" }, "access_tokens": { - "tab_title": "Tokens", - "section_title": "Personal Access Tokens", - "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", - "last_used_on": "Last used on", - "expires_on": "Expires on", - "no_expiration": "No expiration", - "expired": "Expired", - "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", - "token_purpose": "What's this token for?", - "expiration_label": "Expiration", - "scope_label": "Scope", - "workspace_read_only_access": "Read-only access to workspace data.", - "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", - "generate_token": "Generate Token", - "invalid_label": "Please provide a label for the token", - "no_expiration_verbose": "This token will never expire!", - "token_expires_on": "This token will expire on", - "generate_new_token": "Generate new token", - "generate_modal_title": "New Personal Access Token", - "deletion_success": "The access token {label} has been deleted" + "tab_title": "令牌", + "section_title": "个人访问令牌", + "section_description": "个人访问令牌目前帮助您将 CLI 连接到您的 Hoppscotch 账户", + "last_used_on": "最后使用于", + "expires_on": "过期于", + "no_expiration": "未过期", + "expired": "已过期", + "copy_token_warning": "请确保现在复制您的个人访问令牌。您将无法再次看到它!", + "token_purpose": "此令牌的用途是什么?", + "expiration_label": "到期", + "scope_label": "范围", + "workspace_read_only_access": "对工作区数据的只读访问权限。", + "personal_workspace_access_limitation": "个人访问令牌无法访问您的个人工作区。", + "generate_token": "生成令牌", + "invalid_label": "请为令牌提供一个标签", + "no_expiration_verbose": "此令牌永不过期!", + "token_expires_on": "此令牌将在", + "generate_new_token": "生成新令牌", + "generate_modal_title": "新个人访问令牌", + "deletion_success": "访问令牌 {label} 已被删除" }, "collection_runner": { - "collection_id": "Collection ID", - "environment_id": "Environment ID", - "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", - "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", - "include_active_environment": "Include active environment:", + "collection_id": "集合ID", + "environment_id": "环境ID", + "cli_collection_id_description": "此集合 ID 将由 Hoppscotch 的 CLI 集合运行器使用。", + "cli_environment_id_description": "此环境 ID 将由 Hoppscotch 的 CLI 集合运行器使用。", + "include_active_environment": "包含活动环境:", "cli": "CLI", - "ui": "Runner (coming soon)", - "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", - "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", - "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "ui": "运行器(即将推出)", + "cli_command_generation_description_cloud": "复制以下命令并在 CLI 中运行。请指定个人访问令牌。", + "cli_command_generation_description_sh": "复制以下命令并在 CLI 中运行。请指定个人访问令牌并验证生成的SH 实例服务器 URL。", + "cli_command_generation_description_sh_with_server_url_placeholder": "复制以下命令并在 CLI 中运行。请指定个人访问令牌和 SH 实例服务器 URL。", + "run_collection": "运行集合" }, "shortcodes": { "actions": "操作", diff --git a/packages/hoppscotch-common/locales/da.json b/packages/hoppscotch-common/locales/da.json index 500187f98..1c91e7280 100644 --- a/packages/hoppscotch-common/locales/da.json +++ b/packages/hoppscotch-common/locales/da.json @@ -1,59 +1,59 @@ { "action": { - "add": "Add", - "autoscroll": "Autoscroll", - "cancel": "Afbestille", + "add": "Tilføj", + "autoscroll": "Automatisk rulning", + "cancel": "Annuller", "choose_file": "Vælg en fil", - "clear": "Klar", - "clear_all": "Slet alt", - "clear_history": "Clear all History", - "close": "Close", - "connect": "Opret forbindelse", - "connecting": "Connecting", - "copy": "Kopi", - "create": "Create", + "clear": "Ryd", + "clear_all": "Ryd alt", + "clear_history": "Ryd al historik", + "close": "Luk", + "connect": "Forbind", + "connecting": "Forbinder", + "copy": "Kopiér", + "create": "Opret", "delete": "Slet", - "disconnect": "Koble fra", - "dismiss": "Afskedige", - "dont_save": "Don't save", + "disconnect": "Afbryd forbindelse", + "dismiss": "Afvis", + "dont_save": "Gem ikke", "download_file": "Download fil", - "drag_to_reorder": "Drag to reorder", - "duplicate": "Duplicate", - "edit": "Redigere", - "filter": "Filter", + "drag_to_reorder": "Træk for at omarrangere", + "duplicate": "Duplikér", + "edit": "Redigér", + "filter": "Filtrer", "go_back": "Gå tilbage", - "go_forward": "Go forward", - "group_by": "Group by", - "hide_secret": "Hide secret", + "go_forward": "Gå fremad", + "group_by": "Gruppér efter", + "hide_secret": "Skjul hemmelighed", "label": "Etiket", "learn_more": "Lær mere", - "download_here": "Download here", - "less": "Less", + "download_here": "Download her", + "less": "Mindre", "more": "Mere", "new": "Ny", - "no": "Ingen", - "open_workspace": "Open workspace", - "paste": "Paste", - "prettify": "Prettify", - "properties": "Properties", - "remove": "Fjerne", - "rename": "Rename", + "no": "Nej", + "open_workspace": "Åbn arbejdsområde", + "paste": "Indsæt", + "prettify": "Forskøn", + "properties": "Egenskaber", + "remove": "Fjern", + "rename": "Omdøb", "restore": "Gendan", - "save": "Gemme", - "scroll_to_bottom": "Scroll to bottom", - "scroll_to_top": "Scroll to top", + "save": "Gem", + "scroll_to_bottom": "Rul til bunden", + "scroll_to_top": "Rul til toppen", "search": "Søg", - "send": "Sende", - "share": "Share", - "show_secret": "Show secret", + "send": "Send", + "share": "Del", + "show_secret": "Vis hemmelighed", "start": "Start", - "starting": "Starting", - "stop": "Hold op", - "to_close": "to close", - "to_navigate": "to navigate", - "to_select": "to select", + "starting": "Starter", + "stop": "Stop", + "to_close": "for at lukke", + "to_navigate": "for at navigere", + "to_select": "for at vælge", "turn_off": "Sluk", - "turn_on": "Tænde for", + "turn_on": "Tænd", "undo": "Fortryd", "yes": "Ja" }, @@ -65,182 +65,181 @@ "chat_with_us": "Chat med os", "contact_us": "Kontakt os", "cookies": "Cookies", - "copy": "Kopi", - "copy_interface_type": "Copy interface type", - "copy_user_id": "Copy User Auth Token", - "developer_option": "Developer options", - "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", + "copy": "Kopiér", + "copy_interface_type": "Kopiér interfacetype", + "copy_user_id": "Kopiér brugerautentifikationstoken", + "developer_option": "Udviklermuligheder", + "developer_option_description": "Udviklingsværktøjer, der hjælper med udvikling og vedligeholdelse af Hoppscotch.", "discord": "Discord", "documentation": "Dokumentation", "github": "GitHub", - "help": "Hjælp, feedback og dokumentation", + "help": "Hjælp og feedback", "home": "Hjem", - "invite": "Invitere", - "invite_description": "I Hoppscotch har vi designet en enkel og intuitiv grænseflade til oprettelse og administration af dine API'er. Hoppscotch er et værktøj, der hjælper dig med at opbygge, teste, dokumentere og dele dine API'er.", - "invite_your_friends": "Inviter dine venner", - "join_discord_community": "Deltag i vores Discord -fællesskab", + "invite": "Invitér", + "invite_description": "Hoppscotch er et open source API-udviklingsøkosystem. Vi har designet en simpel og intuitiv brugerflade til at oprette og administrere dine API'er. Hoppscotch er et værktøj, der hjælper dig med at bygge, teste, dokumentere og dele dine API'er.", + "invite_your_friends": "Invitér dine venner", + "join_discord_community": "Tilslut dig vores Discord-fællesskab", "keyboard_shortcuts": "Tastaturgenveje", "name": "Hoppscotch", - "new_version_found": "Ny version fundet. Opdater for at opdatere.", - "open_in_hoppscotch": "Open in Hoppscotch", - "options": "Options", - "proxy_privacy_policy": "Politik til beskyttelse af personlige oplysninger i proxy", + "new_version_found": "Ny version fundet. Opdatér for at opdatere.", + "open_in_hoppscotch": "Åbn i Hoppscotch", + "options": "Indstillinger", + "proxy_privacy_policy": "Proxy privatlivspolitik", "reload": "Genindlæs", "search": "Søg", "share": "Del", "shortcuts": "Genveje", - "social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", - "social_links": "Social links", + "social_description": "Følg os på sociale medier for at holde dig opdateret med de seneste nyheder, opdateringer og udgivelser.", + "social_links": "Sociale links", "spotlight": "Spotlight", "status": "Status", - "status_description": "Check the status of the website", - "terms_and_privacy": "Vilkår og fortrolighed", + "status_description": "Tjek hjemmesidens status", + "terms_and_privacy": "Vilkår og privatlivspolitik", "twitter": "Twitter", - "type_a_command_search": "Skriv en kommando eller søg ...", + "type_a_command_search": "Skriv en kommando eller søg...", "we_use_cookies": "Vi bruger cookies", - "updated_text": "Hoppscotch has been updated to v{version} 🎉", + "updated_text": "Hoppscotch er blevet opdateret til v{version} 🎉", "whats_new": "Hvad er nyt?", - "see_whats_new": "See what’s new", + "see_whats_new": "Se hvad der er nyt", "wiki": "Wiki" }, "auth": { - "account_exists": "Kontoen findes med forskellige legitimationsoplysninger - Log ind for at linke begge konti", - "all_sign_in_options": "Alle muligheder for login", - "continue_with_auth_provider": "Continue with {provider}", - "continue_with_email": "Fortsæt med e -mail", + "account_exists": "Konto eksisterer med andre legitimationsoplysninger - Log ind for at forbinde begge konti", + "all_sign_in_options": "Alle loginmuligheder", + "continue_with_auth_provider": "Fortsæt med {provider}", + "continue_with_email": "Fortsæt med e-mail", "continue_with_github": "Fortsæt med GitHub", - "continue_with_github_enterprise": "Continue with GitHub Enterprise", + "continue_with_github_enterprise": "Fortsæt med GitHub Enterprise", "continue_with_google": "Fortsæt med Google", - "continue_with_microsoft": "Continue with Microsoft", - "email": "E -mail", + "continue_with_microsoft": "Fortsæt med Microsoft", + "email": "E-mail", "logged_out": "Logget ud", - "login": "Log på", - "login_success": "Logget ind", + "login": "Log ind", + "login_success": "Logget ind med succes", "login_to_hoppscotch": "Log ind på Hoppscotch", "logout": "Log ud", - "re_enter_email": "Genindtast email", + "re_enter_email": "Indtast e-mail igen", "send_magic_link": "Send et magisk link", - "sync": "Synkronisere", + "sync": "Synkroniser", "we_sent_magic_link": "Vi har sendt dig et magisk link!", - "we_sent_magic_link_description": "Tjek din indbakke - vi sendte en e -mail til {email}. Den indeholder et magisk link, der logger dig ind." + "we_sent_magic_link_description": "Tjek din indbakke - vi har sendt en e-mail til {email}. Den indeholder et magisk link, der vil logge dig ind." }, "authorization": { - "generate_token": "Generer Token", - "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", + "generate_token": "Generer token", + "graphql_headers": "Autorisationsheadere sendes som en del af nyttelasten til connection_init", "include_in_url": "Inkluder i URL", - "inherited_from": "Inherited from {auth} from Parent Collection {collection} ", + "inherited_from": "Nedarvet {auth} fra overordnet samling {collection}", "learn": "Lær hvordan", "oauth": { - "redirect_auth_server_returned_error": "Auth Server returned an error state", - "redirect_auth_token_request_failed": "Request to get the auth token failed", - "redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token", - "redirect_invalid_state": "Invalid State value present in the redirect", - "redirect_no_auth_code": "No Authorization Code present in the redirect", - "redirect_no_client_id": "No Client ID defined", - "redirect_no_client_secret": "No Client Secret Defined", - "redirect_no_code_verifier": "No Code Verifier Defined", - "redirect_no_token_endpoint": "No Token Endpoint Defined", - "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", - "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", - "grant_type": "Grant Type", - "grant_type_auth_code": "Authorization Code", - "token_fetched_successfully": "Token fetched successfully", - "token_fetch_failed": "Failed to fetch token", - "validation_failed": "Validation Failed, please check the form fields", - "label_authorization_endpoint": "Authorization Endpoint", - "label_client_id": "Client ID", - "label_client_secret": "Client Secret", - "label_code_challenge": "Code Challenge", - "label_code_challenge_method": "Code Challenge Method", - "label_code_verifier": "Code Verifier", - "label_scopes": "Scopes", - "label_token_endpoint": "Token Endpoint", - "label_use_pkce": "Use PKCE", + "redirect_auth_server_returned_error": "Auth-server returnerede en fejltilstand", + "redirect_auth_token_request_failed": "Anmodning om at få auth-token mislykkedes", + "redirect_auth_token_request_invalid_response": "Ugyldigt svar fra Token-endepunktet ved anmodning om et auth-token", + "redirect_invalid_state": "Ugyldig tilstandsværdi til stede i omdirigeringen", + "redirect_no_auth_code": "Ingen autorisationskode til stede i omdirigeringen", + "redirect_no_client_id": "Ingen klient-id defineret", + "redirect_no_client_secret": "Ingen klienthemmelighed defineret", + "redirect_no_code_verifier": "Ingen kodeverifikator defineret", + "redirect_no_token_endpoint": "Intet token-endepunkt defineret", + "something_went_wrong_on_oauth_redirect": "Noget gik galt under OAuth-omdirigering", + "something_went_wrong_on_token_generation": "Noget gik galt ved tokengenerering", + "token_generation_oidc_discovery_failed": "Fejl ved tokengenerering: OpenID Connect-opdagelse mislykkedes", + "grant_type": "Tilladelsestype", + "grant_type_auth_code": "Autorisationskode", + "token_fetched_successfully": "Token hentet med succes", + "token_fetch_failed": "Kunne ikke hente token", + "validation_failed": "Validering mislykkedes, tjek venligst formularfelterne", + "label_authorization_endpoint": "Autorisationsendepunkt", + "label_client_id": "Klient-id", + "label_client_secret": "Klienthemmelighed", + "label_code_challenge": "Kodeudfordring", + "label_code_challenge_method": "Kodeudfordringsmetode", + "label_code_verifier": "Kodeverifikator", + "label_scopes": "Områder", + "label_token_endpoint": "Token-endepunkt", + "label_use_pkce": "Brug PKCE", "label_implicit": "Implicit", - "label_password": "Password", - "label_username": "Username", - "label_auth_code": "Authorization Code", - "label_client_credentials": "Client Credentials" + "label_password": "Adgangskode", + "label_username": "Brugernavn", + "label_auth_code": "Autorisationskode", + "label_client_credentials": "Klientlegitimationsoplysninger" }, - "pass_key_by": "Pass by", - "pass_by_query_params_label": "Query Parameters", + "pass_key_by": "Send via", + "pass_by_query_params_label": "Forespørgselsparametre", "pass_by_headers_label": "Headers", "password": "Adgangskode", - "save_to_inherit": "Please save this request in any collection to inherit the authorization", - "token": "Polet", - "type": "Godkendelse Type", + "save_to_inherit": "Gem venligst denne anmodning i en samling for at arve autorisationen", + "token": "Token", + "type": "Autorisationstype", "username": "Brugernavn" }, "collection": { - "created": "Samlingen er oprettet", - "different_parent": "Cannot reorder collection with different parent", - "edit": "Rediger samling", - "import_or_create": "Import or create a collection", - "import_collection": "Import Collection", - "invalid_name": "Angiv et gyldigt navn til samlingen", - "invalid_root_move": "Collection already in the root", - "moved": "Moved Successfully", - "my_collections": "Mine samlinger", + "created": "Samling oprettet", + "different_parent": "Kan ikke omarrangere samling med forskellig overordnet", + "edit": "Redigér samling", + "import_or_create": "Importér eller opret en samling", + "import_collection": "Importér samling", + "invalid_name": "Angiv venligst et navn til samlingen", + "invalid_root_move": "Samling allerede i roden", + "moved": "Flyttet med succes", + "my_collections": "Personlige samlinger", "name": "Min nye samling", - "name_length_insufficient": "Collection name should be at least 3 characters long", - "new": "Ny kollektion", - "order_changed": "Collection Order Updated", - "properties": "Collection Properties", - "properties_updated": "Collection Properties Updated", + "name_length_insufficient": "Samlingens navn skal være mindst 3 tegn langt", + "new": "Ny samling", + "order_changed": "Samlingsrækkefølge opdateret", + "properties": "Samlingsegenskaber", + "properties_updated": "Samlingsegenskaber opdateret", "renamed": "Samling omdøbt", - "request_in_use": "Request in use", + "request_in_use": "Anmodning i brug", "save_as": "Gem som", - "save_to_collection": "Save to Collection", + "save_to_collection": "Gem i samling", "select": "Vælg en samling", "select_location": "Vælg placering", - "details": "Details", - "select_team": "Vælg et hold", - "team_collections": "Teamsamlinger" + "details": "Detaljer", + "duplicated": "Samling duplikeret" }, "confirm": { - "close_unsaved_tab": "Are you sure you want to close this tab?", - "close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", - "exit_team": "Are you sure you want to leave this team?", - "logout": "Er du sikker på, at du vil logge af?", - "remove_collection": "Er du sikker på, at du vil slette denne samling permanent?", - "remove_environment": "Er du sikker på, at du vil slette dette miljø permanent?", - "remove_folder": "Er du sikker på, at du vil slette denne mappe permanent?", - "remove_history": "Er du sikker på, at du vil slette hele historikken permanent?", - "remove_request": "Er du sikker på, at du vil slette denne anmodning permanent?", - "remove_shared_request": "Are you sure you want to permanently delete this shared request?", - "remove_team": "Er du sikker på, at du vil slette dette hold?", + "close_unsaved_tab": "Er du sikker på, at du vil lukke denne fane?", + "close_unsaved_tabs": "Er du sikker på, at du vil lukke alle faner? {count} ikke-gemte faner vil gå tabt.", + "exit_team": "Er du sikker på, at du vil forlade dette arbejdsområde?", + "logout": "Er du sikker på, at du vil logge ud?", + "remove_collection": "Er du sikker på, at du permanent vil slette denne samling?", + "remove_environment": "Er du sikker på, at du permanent vil slette dette miljø?", + "remove_folder": "Er du sikker på, at du permanent vil slette denne mappe?", + "remove_history": "Er du sikker på, at du permanent vil slette al historik?", + "remove_request": "Er du sikker på, at du permanent vil slette denne anmodning?", + "remove_shared_request": "Er du sikker på, at du permanent vil slette denne delte anmodning?", + "remove_team": "Er du sikker på, at du vil slette dette arbejdsområde?", "remove_telemetry": "Er du sikker på, at du vil fravælge telemetri?", - "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", - "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Er du sikker på, at du vil synkronisere dette arbejdsområde?", - "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" + "request_change": "Er du sikker på, at du vil kassere den nuværende anmodning, ikke-gemte ændringer vil gå tabt.", + "save_unsaved_tab": "Vil du gemme ændringer foretaget i denne fane?", + "sync": "Vil du gendanne dit arbejdsområde fra skyen? Dette vil kassere dit lokale fremskridt.", + "delete_access_token": "Er du sikker på, at du vil slette adgangstokenet {tokenLabel}?" }, "context_menu": { - "add_parameters": "Add to parameters", - "open_request_in_new_tab": "Open request in new tab", - "set_environment_variable": "Set as variable" + "add_parameters": "Tilføj til parametre", + "open_request_in_new_tab": "Åbn anmodning i ny fane", + "set_environment_variable": "Indstil som variabel" }, "cookies": { "modal": { - "cookie_expires": "Expires", - "cookie_name": "Name", - "cookie_path": "Path", - "cookie_string": "Cookie string", - "cookie_value": "Value", - "empty_domain": "Domain is empty", - "empty_domains": "Domain list is empty", - "enter_cookie_string": "Enter cookie string", - "interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", - "managed_tab": "Managed", - "new_domain_name": "New domain name", - "no_cookies_in_domain": "No cookies set for this domain", - "raw_tab": "Raw", - "set": "Set a cookie" + "cookie_expires": "Udløber", + "cookie_name": "Navn", + "cookie_path": "Sti", + "cookie_string": "Cookie-streng", + "cookie_value": "Værdi", + "empty_domain": "Domæne er tomt", + "empty_domains": "Domæneliste er tom", + "enter_cookie_string": "Indtast cookie-streng", + "interceptor_no_support": "Din aktuelt valgte interceptor understøtter ikke cookies. Vælg en anden interceptor og prøv igen.", + "managed_tab": "Administreret", + "new_domain_name": "Nyt domænenavn", + "no_cookies_in_domain": "Ingen cookies indstillet for dette domæne", + "raw_tab": "Rå", + "set": "Indstil en cookie" } }, "count": { - "header": "Overskrift {count}", + "header": "Header {count}", "message": "Besked {count}", "parameter": "Parameter {count}", "protocol": "Protokol {count}", @@ -249,380 +248,378 @@ }, "documentation": { "generate": "Generer dokumentation", - "generate_message": "Importer enhver Hoppscotch-samling for at generere API-dokumentation, når du er på farten." + "generate_message": "Importér enhver Hoppscotch-samling for at generere API-dokumentation on-the-go." }, "empty": { - "authorization": "Denne anmodning anvender ikke nogen autorisation", - "body": "Denne anmodning har ikke et organ", + "authorization": "Denne anmodning bruger ingen autorisation", + "body": "Denne anmodning har ingen meddelelse", "collection": "Samlingen er tom", "collections": "Samlinger er tomme", - "documentation": "Connect to a GraphQL endpoint to view documentation", - "endpoint": "Endpoint cannot be empty", + "documentation": "Forbind til et GraphQL-endepunkt for at se dokumentation", + "endpoint": "Endepunkt kan ikke være tomt", "environments": "Miljøer er tomme", - "folder": "Mappen er tom", - "headers": "Denne anmodning har ingen overskrifter", - "history": "Historien er tom", - "invites": "Invite list is empty", - "members": "Holdet er tomt", + "folder": "Mappe er tom", + "headers": "Denne anmodning har ingen headers", + "history": "Historik er tom", + "invites": "Invitationsliste er tom", + "members": "Arbejdsområde er tomt", "parameters": "Denne anmodning har ingen parametre", - "pending_invites": "There are no pending invites for this team", - "profile": "Login to view your profile", + "pending_invites": "Der er ingen ventende invitationer til dette arbejdsområde", + "profile": "Log ind for at se din profil", "protocols": "Protokoller er tomme", - "request_variables": "This request does not have any request variables", - "schema": "Opret forbindelse til et GraphQL -slutpunkt", - "secret_environments": "Secrets are not synced to Hoppscotch", - "shared_requests": "Shared requests are empty", - "shared_requests_logout": "Login to view your shared requests or create a new one", - "subscription": "Subscriptions are empty", - "team_name": "Teamnavn er tomt", - "teams": "Hold er tomme", - "tests": "Der er ingen test for denne anmodning", - "access_tokens": "Access tokens are empty", - "shortcodes": "Shortcodes are empty" + "request_variables": "Denne anmodning har ingen anmodningsvariabler", + "schema": "Forbind til et GraphQL-endepunkt for at se skema", + "secret_environments": "Hemmeligheder synkroniseres ikke til Hoppscotch", + "shared_requests": "Delte anmodninger er tomme", + "shared_requests_logout": "Log ind for at se dine delte anmodninger eller opret en ny", + "subscription": "Abonnementer er tomme", + "team_name": "Arbejdsområdenavn tomt", + "teams": "Du tilhører ikke nogen arbejdsområder", + "tests": "Der er ingen tests for denne anmodning", + "access_tokens": "Adgangstokens er tomme" }, "environment": { - "add_to_global": "Add to Global", - "added": "Environment addition", - "create_new": "Skab nyt miljø", - "created": "Environment created", - "deleted": "Environment deletion", - "duplicated": "Environment duplicated", - "edit": "Rediger miljø", - "empty_variables": "No variables", + "add_to_global": "Tilføj til global", + "added": "Miljø tilføjet", + "create_new": "Opret nyt miljø", + "created": "Miljø oprettet", + "deleted": "Miljø slettet", + "duplicated": "Miljø duplikeret", + "edit": "Redigér miljø", + "empty_variables": "Ingen variabler", "global": "Global", - "global_variables": "Global variables", - "import_or_create": "Import or create a environment", - "invalid_name": "Angiv et gyldigt navn på miljøet", - "list": "Environment variables", - "my_environments": "My Environments", - "name": "Name", - "nested_overflow": "nested environment variables are limited to 10 levels", + "global_variables": "Globale variabler", + "import_or_create": "Importér eller opret et miljø", + "invalid_name": "Angiv venligst et navn til miljøet", + "list": "Miljøvariabler", + "my_environments": "Personlige miljøer", + "name": "Navn", + "nested_overflow": "Indlejrede miljøvariabler er begrænset til 10 niveauer", "new": "Nyt miljø", - "no_active_environment": "No active environment", + "no_active_environment": "Intet aktivt miljø", "no_environment": "Intet miljø", - "no_environment_description": "No environments were selected. Choose what to do with the following variables.", - "quick_peek": "Environment Quick Peek", - "replace_with_variable": "Replace with variable", - "scope": "Scope", - "secrets": "Secrets", - "secret_value": "Secret value", + "no_environment_description": "Ingen miljøer blev valgt. Vælg hvad der skal gøres med følgende variabler.", + "quick_peek": "Miljø hurtig kig", + "replace_with_variable": "Erstat med variabel", + "scope": "Omfang", + "secrets": "Hemmeligheder", + "secret_value": "Hemmelig værdi", "select": "Vælg miljø", - "set": "Set environment", - "set_as_environment": "Set as environment", - "team_environments": "Team Environments", + "set": "Indstil miljø", + "set_as_environment": "Indstil som miljø", + "team_environments": "Arbejdsområdemiljøer", "title": "Miljøer", - "updated": "Environment updation", - "value": "Value", - "variable": "Variable", - "variables": "Variables", - "variable_list": "Variabel liste", - "properties": "Environment Properties", - "details": "Details" + "updated": "Miljø opdateret", + "value": "Værdi", + "variable": "Variabel", + "variables": "Variabler", + "variable_list": "Variabelliste", + "properties": "Miljøegenskaber", + "details": "Detaljer" }, "error": { - "authproviders_load_error": "Unable to load auth providers", - "browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.", - "check_console_details": "Tjek konsollog for at få flere oplysninger.", - "check_how_to_add_origin": "Check how you can add an origin", + "authproviders_load_error": "Kunne ikke indlæse autorisationsudbydere", + "browser_support_sse": "Denne browser ser ikke ud til at have understøttelse af Server Sent Events.", + "check_console_details": "Tjek konsolloggen for detaljer.", + "check_how_to_add_origin": "Tjek hvordan du kan tilføje en oprindelse", "curl_invalid_format": "cURL er ikke formateret korrekt", - "danger_zone": "Danger zone", - "delete_account": "Your account is currently an owner in these teams:", - "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", - "empty_profile_name": "Profile name cannot be empty", - "empty_req_name": "Tom anmodningsnavn", + "danger_zone": "Farligt område", + "delete_account": "Din konto er i øjeblikket ejer af disse arbejdsområder:", + "delete_account_description": "Du skal enten fjerne dig selv, overføre ejerskab eller slette disse arbejdsområder, før du kan slette din konto.", + "empty_profile_name": "Profilnavn kan ikke være tomt", + "empty_req_name": "Tomt anmodningsnavn", "f12_details": "(F12 for detaljer)", - "gql_prettify_invalid_query": "Kunne ikke prætificere en ugyldig forespørgsel, løse forespørgselssyntaksfejl og prøve igen", - "incomplete_config_urls": "Incomplete configuration URLs", - "incorrect_email": "Incorrect email", - "invalid_link": "Invalid link", - "invalid_link_description": "The link you clicked is invalid or expired.", - "invalid_embed_link": "The embed does not exist or is invalid.", - "json_parsing_failed": "Invalid JSON", - "json_prettify_invalid_body": "Kunne ikke pryde et ugyldigt brødtekst, løse json -syntaksfejl og prøve igen", - "network_error": "There seems to be a network error. Please try again.", - "network_fail": "Anmodningen kunne ikke sendes", - "no_collections_to_export": "No collections to export. Please create a collection to get started.", + "gql_prettify_invalid_query": "Kunne ikke forskønne en ugyldig forespørgsel, løs forespørgselssyntaksfejl og prøv igen", + "incomplete_config_urls": "Ufuldstændige konfigurationsadresser", + "incorrect_email": "Forkert e-mail", + "invalid_link": "Ugyldigt link", + "invalid_link_description": "Linket, du klikkede på, er ugyldigt eller udløbet.", + "invalid_embed_link": "Indlejringen eksisterer ikke eller er ugyldig.", + "json_parsing_failed": "Ugyldig JSON", + "json_prettify_invalid_body": "Kunne ikke forskønne en ugyldig meddelelse, løs json-syntaksfejl og prøv igen", + "network_error": "Der ser ud til at være en netværksfejl. Prøv venligst igen.", + "network_fail": "Kunne ikke sende anmodning", + "no_collections_to_export": "Ingen samlinger at eksportere. Opret venligst en samling for at komme i gang.", "no_duration": "Ingen varighed", - "no_environments_to_export": "No environments to export. Please create an environment to get started.", - "no_results_found": "No matches found", - "page_not_found": "This page could not be found", - "please_install_extension": "Please install the extension and add origin to the extension.", - "proxy_error": "Proxy error", - "same_profile_name": "Updated profile name is same as the current profile name", + "no_environments_to_export": "Ingen miljøer at eksportere. Opret venligst et miljø for at komme i gang.", + "no_results_found": "Ingen matches fundet", + "page_not_found": "Denne side kunne ikke findes", + "please_install_extension": "Installer venligst udvidelsen og tilføj oprindelse til udvidelsen.", + "proxy_error": "Proxy-fejl", + "same_profile_name": "Opdateret profilnavn er det samme som det nuværende profilnavn", "script_fail": "Kunne ikke udføre pre-request script", "something_went_wrong": "Noget gik galt", - "test_script_fail": "Could not execute post-request script", - "reading_files": "Error while reading one or more files.", - "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", - "generate_access_token": "Something went wrong while generating the access token", - "delete_access_token": "Something went wrong while deleting the access token" + "test_script_fail": "Kunne ikke udføre post-request script", + "reading_files": "Fejl under læsning af en eller flere filer.", + "fetching_access_tokens_list": "Noget gik galt under hentning af listen over tokens", + "generate_access_token": "Noget gik galt under generering af adgangstoken", + "delete_access_token": "Noget gik galt under sletning af adgangstoken" }, "export": { - "as_json": "Eksporter som JSON", + "as_json": "Eksportér som JSON", "create_secret_gist": "Opret hemmelig Gist", - "create_secret_gist_tooltip_text": "Export as secret Gist", - "failed": "Something went wrong while exporting", - "secret_gist_success": "Successfully exported as secret Gist", - "require_github": "Log ind med GitHub for at skabe hemmelig kerne", - "title": "Export", - "success": "Successfully exported", - "gist_created": "Gist skabt" + "create_secret_gist_tooltip_text": "Eksportér som hemmelig Gist", + "failed": "Noget gik galt under eksport", + "secret_gist_success": "Eksporteret som hemmelig Gist med succes", + "require_github": "Log ind med GitHub for at oprette hemmelig gist", + "title": "Eksportér", + "success": "Eksporteret med succes" }, "filter": { - "all": "All", - "none": "None", - "starred": "Starred" + "all": "Alle", + "none": "Ingen", + "starred": "Stjernemarkeret" }, "folder": { "created": "Mappe oprettet", - "edit": "Rediger mappe", - "invalid_name": "Angiv et navn til mappen", - "name_length_insufficient": "Folder name should be at least 3 characters long", + "edit": "Redigér mappe", + "invalid_name": "Angiv venligst et navn til mappen", + "name_length_insufficient": "Mappenavn skal være mindst 3 tegn langt", "new": "Ny mappe", - "renamed": "Mappen omdøbt" + "renamed": "Mappe omdøbt" }, "graphql": { - "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", - "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", - "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", + "connection_switch_confirm": "Vil du forbinde med det seneste GraphQL-endepunkt?", + "connection_switch_new_url": "Skift til en fane vil afbryde din forbindelse til den aktive GraphQL-forbindelse. Ny forbindelsesadresse er", + "connection_switch_url": "Du er forbundet til et GraphQL-endepunkt, forbindelsesadressen er", "mutations": "Mutationer", "schema": "Skema", "subscriptions": "Abonnementer", - "switch_connection": "Switch connection", - "url_placeholder": "Enter a GraphQL endpoint URL" + "switch_connection": "Skift forbindelse", + "url_placeholder": "Indtast en GraphQL-endepunktsadresse" }, "graphql_collections": { - "title": "GraphQL Collections" + "title": "GraphQL-samlinger" }, "group": { - "time": "Time", + "time": "Tid", "url": "URL" }, "header": { - "install_pwa": "Installer app", - "login": "Log på", + "install_pwa": "Installér app", + "login": "Log ind", "save_workspace": "Gem mit arbejdsområde" }, "helpers": { - "authorization": "Autorisationsoverskriften genereres automatisk, når du sender anmodningen.", - "collection_properties_authorization": " This authorization will be set for every request in this collection.", - "collection_properties_header": "This header will be set for every request in this collection.", - "generate_documentation_first": "Generer først dokumentation", - "network_fail": "Kunne ikke nå API -slutpunktet. Kontroller din netværksforbindelse, og prøv igen.", - "offline": "Du ser ud til at være offline. Data i dette arbejdsområde er muligvis ikke opdaterede.", - "offline_short": "Du ser ud til at være offline.", - "post_request_tests": "Test scripts er skrevet i JavaScript og køres efter at svaret er modtaget.", - "pre_request_script": "Forhåndsanmodnings scripts er skrevet i JavaScript og køres før anmodningen sendes.", - "script_fail": "Det ser ud til, at der er en fejl i pre-request-scriptet. Tjek fejlen nedenfor, og ret scriptet i overensstemmelse hermed.", - "test_script_fail": "There seems to be an error with test script. Please fix the errors and run tests again", - "tests": "Skriv et test script for at automatisere fejlfinding." + "authorization": "Autorisationsheaderen vil automatisk blive genereret, når du sender anmodningen.", + "collection_properties_authorization": "Denne autorisation vil blive indstillet for hver anmodning i denne samling.", + "collection_properties_header": "Denne header vil blive indstillet for hver anmodning i denne samling.", + "generate_documentation_first": "Generer dokumentation først", + "network_fail": "Kan ikke nå API-endepunktet. Tjek din netværksforbindelse eller vælg en anden Interceptor og prøv igen.", + "offline": "Du bruger Hoppscotch offline. Opdateringer vil synkronisere, når du er online, baseret på arbejdsområdeindstillinger.", + "offline_short": "Du bruger Hoppscotch offline.", + "post_request_tests": "Testscripts er skrevet i JavaScript og køres efter svaret er modtaget.", + "pre_request_script": "Pre-request scripts er skrevet i JavaScript og køres før anmodningen sendes.", + "script_fail": "Det ser ud til, at der er en fejl i pre-request scriptet. Tjek fejlen nedenfor og ret scriptet i overensstemmelse hermed.", + "test_script_fail": "Der ser ud til at være en fejl med testscriptet. Ret venligst fejlene og kør tests igen", + "tests": "Skriv et testscript for at automatisere fejlfinding." }, "hide": { - "collection": "Collapse Collection Panel", + "collection": "Skjul samlingspanel", "more": "Skjul mere", "preview": "Skjul forhåndsvisning", - "sidebar": "Skjul sidepanel" + "sidebar": "Skjul sidebjælke" }, "import": { - "collections": "Importer samlinger", - "curl": "Importer cURL", - "environments_from_gist": "Import From Gist", - "environments_from_gist_description": "Import Hoppscotch Environments From Gist", - "failed": "Import mislykkedes", - "from_file": "Import from File", - "from_gist": "Import fra Gist", - "from_gist_description": "Import from Gist URL", - "from_insomnia": "Import from Insomnia", - "from_insomnia_description": "Import from Insomnia collection", - "from_json": "Import from Hoppscotch", - "from_json_description": "Import from Hoppscotch collection file", - "from_my_collections": "Import fra Mine samlinger", - "from_my_collections_description": "Import from My Collections file", - "from_openapi": "Import from OpenAPI", - "from_openapi_description": "Import from OpenAPI specification file (YML/JSON)", - "from_postman": "Import from Postman", - "from_postman_description": "Import from Postman collection", - "from_url": "Import from URL", - "gist_url": "Indtast Gist URL", - "gql_collections_from_gist_description": "Import GraphQL Collections From Gist", - "hoppscotch_environment": "Hoppscotch Environment", - "hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", - "import_from_url_invalid_fetch": "Couldn't get data from the url", - "import_from_url_invalid_file_format": "Error while importing collections", - "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", - "import_from_url_success": "Collections Imported", - "insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", - "json_description": "Import collections from a Hoppscotch Collections JSON file", - "postman_environment": "Postman Environment", - "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importere", - "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", - "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", - "success": "Successfully imported" + "collections": "Importér samlinger", + "curl": "Importér cURL", + "environments_from_gist": "Importér fra Gist", + "environments_from_gist_description": "Importér Hoppscotch-miljøer fra Gist", + "failed": "Fejl under import: format ikke genkendt", + "from_file": "Importér fra fil", + "from_gist": "Importér fra Gist", + "from_gist_description": "Importér fra Gist-URL", + "from_insomnia": "Importér fra Insomnia", + "from_insomnia_description": "Importér fra Insomnia-samling", + "from_json": "Importér fra Hoppscotch", + "from_json_description": "Importér fra Hoppscotch-samlingsfil", + "from_my_collections": "Importér fra personlige samlinger", + "from_my_collections_description": "Importér fra personlige samlingsfil", + "from_openapi": "Importér fra OpenAPI", + "from_openapi_description": "Importér fra OpenAPI-specifikationsfil (YML/JSON)", + "from_postman": "Importér fra Postman", + "from_postman_description": "Importér fra Postman-samling", + "from_url": "Importér fra URL", + "gist_url": "Indtast Gist-URL", + "gql_collections_from_gist_description": "Importér GraphQL-samlinger fra Gist", + "hoppscotch_environment": "Hoppscotch-miljø", + "hoppscotch_environment_description": "Importér Hoppscotch-miljø JSON-fil", + "import_from_url_invalid_fetch": "Kunne ikke hente data fra URL'en", + "import_from_url_invalid_file_format": "Fejl under import af samlinger", + "import_from_url_invalid_type": "Ikke-understøttet type. Accepterede værdier er 'hoppscotch', 'openapi', 'postman', 'insomnia'", + "import_from_url_success": "Samlinger importeret", + "insomnia_environment_description": "Importér Insomnia-miljø fra en JSON/YAML-fil", + "json_description": "Importér samlinger fra en Hoppscotch-samlinger JSON-fil", + "postman_environment": "Postman-miljø", + "postman_environment_description": "Importér Postman-miljø fra en JSON-fil", + "title": "Importér", + "file_size_limit_exceeded_warning_multiple_files": "Valgte filer overstiger den anbefalede grænse på 10MB. Kun de første {files} valgte vil blive importeret", + "file_size_limit_exceeded_warning_single_file": "Den aktuelt valgte fil overstiger den anbefalede grænse på 10MB. Vælg venligst en anden fil.", + "success": "Importeret med succes" }, "inspections": { - "description": "Inspect possible errors", + "description": "Inspicér mulige fejl", "environment": { - "add_environment": "Add to Environment", - "add_environment_value": "Add value", - "empty_value": "Environment value is empty for the variable '{variable}' ", - "not_found": "Environment variable “{environment}” not found." + "add_environment": "Tilføj til miljø", + "add_environment_value": "Tilføj værdi", + "empty_value": "Miljøværdi er tom for variablen '{variable}'", + "not_found": "Miljøvariabel \"{environment}\" blev ikke fundet" }, "header": { - "cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." + "cookie": "Browseren tillader ikke Hoppscotch at indstille Cookie-headere. Brug venligst autorisationsheadere i stedet. Vores Hoppscotch Desktop App er dog live nu og understøtter cookies." }, "response": { - "401_error": "Please check your authentication credentials.", - "404_error": "Please check your request URL and method type.", - "cors_error": "Please check your Cross-Origin Resource Sharing configuration.", - "default_error": "Please check your request.", - "network_error": "Please check your network connection." + "401_error": "Tjek venligst dine autentifikationsoplysninger.", + "404_error": "Tjek venligst din anmodnings-URL og metodetype.", + "cors_error": "Tjek venligst din Cross-Origin Resource Sharing-konfiguration.", + "default_error": "Tjek venligst din anmodning.", + "network_error": "Tjek venligst din netværksforbindelse." }, - "title": "Inspector", + "title": "Inspektør", "url": { - "extension_not_installed": "Extension not installed.", - "extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", - "extention_enable_action": "Enable Browser Extension", - "extention_not_enabled": "Extension not enabled." + "extension_not_installed": "Udvidelse ikke installeret.", + "extension_unknown_origin": "Sørg for, at du har tilføjet API-endepunktets oprindelse til Hoppscotch Browser Extension-listen.", + "extention_enable_action": "Aktivér browserudvidelse", + "extention_not_enabled": "Udvidelse ikke aktiveret." } }, "layout": { - "collapse_collection": "Collapse or Expand Collections", - "collapse_sidebar": "Collapse or Expand the sidebar", - "column": "Vertical layout", + "collapse_collection": "Skjul eller udvid samlinger", + "collapse_sidebar": "Skjul eller udvid sidebjælken", + "column": "Lodret layout", "name": "Layout", - "row": "Horizontal layout" + "row": "Vandret layout" }, "modal": { - "close_unsaved_tab": "You have unsaved changes", + "close_unsaved_tab": "Du har ikke-gemte ændringer", "collections": "Samlinger", - "confirm": "Bekræfte", - "customize_request": "Customize Request", - "edit_request": "Rediger anmodning", - "import_export": "Import Eksport", - "share_request": "Share Request" + "confirm": "Bekræft", + "customize_request": "Tilpas anmodning", + "edit_request": "Redigér anmodning", + "import_export": "Import / Eksport", + "share_request": "Del anmodning" }, "mqtt": { - "already_subscribed": "You are already subscribed to this topic.", - "clean_session": "Clean Session", - "clear_input": "Clear input", - "clear_input_on_send": "Clear input on send", - "client_id": "Client ID", - "color": "Pick a color", - "communication": "Meddelelse", - "connection_config": "Connection Config", - "connection_not_authorized": "This MQTT connection does not use any authentication.", - "invalid_topic": "Please provide a topic for the subscription", - "keep_alive": "Keep Alive", + "already_subscribed": "Du abonnerer allerede på dette emne.", + "clean_session": "Ryd session", + "clear_input": "Ryd input", + "clear_input_on_send": "Ryd input ved afsendelse", + "client_id": "Klient-ID", + "color": "Vælg en farve", + "communication": "Kommunikation", + "connection_config": "Forbindelseskonfiguration", + "connection_not_authorized": "Denne MQTT-forbindelse bruger ingen godkendelse.", + "invalid_topic": "Angiv venligst et emne for abonnementet", + "keep_alive": "Hold i live", "log": "Log", - "lw_message": "Last-Will Message", + "lw_message": "Last-Will besked", "lw_qos": "Last-Will QoS", - "lw_retain": "Last-Will Retain", - "lw_topic": "Last-Will Topic", + "lw_retain": "Last-Will behold", + "lw_topic": "Last-Will emne", "message": "Besked", - "new": "New Subscription", - "not_connected": "Please start a MQTT connection first.", - "publish": "Offentliggøre", + "new": "Nyt abonnement", + "not_connected": "Start venligst en MQTT-forbindelse først.", + "publish": "Udgiv", "qos": "QoS", "ssl": "SSL", - "subscribe": "Abonner", + "subscribe": "Abonnér", "topic": "Emne", - "topic_name": "Emne navn", - "topic_title": "Udgiv / Abonner emne", - "unsubscribe": "Opsige abonnement", + "topic_name": "Emnenavn", + "topic_title": "Udgiv / Abonnér på emne", + "unsubscribe": "Afmeld abonnement", "url": "URL" }, "navigation": { - "doc": "Dokumenter", + "doc": "Docs", "graphql": "GraphQL", - "profile": "Profile", + "profile": "Profil", "realtime": "Realtid", "rest": "REST", "settings": "Indstillinger" }, "preRequest": { - "javascript_code": "JavaScript -kode", + "javascript_code": "JavaScript-kode", "learn": "Læs dokumentation", "script": "Pre-Request Script", - "snippets": "Uddrag" + "snippets": "Snippets" }, "profile": { - "app_settings": "App Settings", - "default_hopp_displayname": "Unnamed User", - "editor": "Editor", - "editor_description": "Editors can add, edit, and delete requests.", - "email_verification_mail": "A verification email has been sent to your email address. Please click on the link to verify your email address.", - "no_permission": "You do not have permission to perform this action.", - "owner": "Owner", - "owner_description": "Owners can add, edit, and delete requests, collections and team members.", - "roles": "Roles", - "roles_description": "Roles are used to control access to the shared collections.", - "updated": "Profile updated", - "viewer": "Viewer", - "viewer_description": "Viewers can only view and use requests." + "app_settings": "App-indstillinger", + "default_hopp_displayname": "Unavngivet bruger", + "editor": "Redaktør", + "editor_description": "Redaktører kan tilføje, redigere og slette anmodninger.", + "email_verification_mail": "En bekræftelses-e-mail er blevet sendt til din e-mailadresse. Klik venligst på linket for at bekræfte din e-mailadresse.", + "no_permission": "Du har ikke tilladelse til at udføre denne handling.", + "owner": "Ejer", + "owner_description": "Ejere kan tilføje, redigere og slette anmodninger, samlinger og arbejdsområdemedlemmer.", + "roles": "Roller", + "roles_description": "Roller bruges til at kontrollere adgang til de delte samlinger.", + "updated": "Profil opdateret", + "viewer": "Betragter", + "viewer_description": "Betragtere kan kun se og bruge anmodninger." }, "remove": { "star": "Fjern stjerne" }, "request": { "added": "Anmodning tilføjet", - "authorization": "Bemyndigelse", - "body": "Anmodningsorgan", + "authorization": "Autorisation", + "body": "Anmodnings meddelelse", "choose_language": "Vælg sprog", "content_type": "Indholdstype", "content_type_titles": { - "others": "Others", - "structured": "Structured", - "text": "Text" + "others": "Andre", + "structured": "Struktureret", + "text": "Tekst" }, - "different_collection": "Cannot reorder requests from different collections", - "duplicated": "Request duplicated", + "different_collection": "Kan ikke omarrangere anmodninger fra forskellige samlinger", + "duplicated": "Anmodning duplikeret", "duration": "Varighed", - "enter_curl": "Indtast cURL", - "generate_code": "Generer kode", + "enter_curl": "Indtast cURL-kommando", + "generate_code": "Generér kode", "generated_code": "Genereret kode", - "go_to_authorization_tab": "Go to Authorization tab", - "go_to_body_tab": "Go to Body tab", - "header_list": "Overskriftsliste", - "invalid_name": "Angiv et navn på anmodningen", + "go_to_authorization_tab": "Gå til autorisationsfanen", + "go_to_body_tab": "Gå til meddelelsefanen", + "header_list": "Header-liste", + "invalid_name": "Angiv venligst et navn til anmodningen", "method": "Metode", - "moved": "Request moved", + "moved": "Anmodning flyttet", "name": "Anmodningsnavn", - "new": "New Request", - "order_changed": "Request Order Updated", - "override": "Override", - "override_help": "Set Content-Type in Headers", - "overriden": "Overridden", + "new": "Ny anmodning", + "order_changed": "Anmodningsrækkefølge opdateret", + "override": "Tilsidesæt", + "override_help": "Indstil Content-Type i Headers", + "overriden": "Tilsidesat", "parameter_list": "Forespørgselsparametre", "parameters": "Parametre", "path": "Sti", "payload": "Nyttelast", "query": "Forespørgsel", - "raw_body": "Raw Request Body", - "rename": "Rename Request", + "raw_body": "Rå anmodnings meddelelse", + "rename": "Omdøb anmodning", "renamed": "Anmodning omdøbt", - "request_variables": "Request variables", - "run": "Løb", - "save": "Gemme", + "request_variables": "Anmodningsvariabler", + "run": "Kør", + "save": "Gem", "save_as": "Gem som", "saved": "Anmodning gemt", "share": "Del", - "share_description": "Share Hoppscotch with your friends", - "share_request": "Share Request", + "share_description": "Del Hoppscotch med dine venner", + "share_request": "Del anmodning", "stop": "Stop", "title": "Anmodning", "type": "Anmodningstype", "url": "URL", - "url_placeholder": "Enter a URL or paste a cURL command", + "url_placeholder": "Indtast en URL eller indsæt en cURL-kommando", "variables": "Variabler", - "view_my_links": "View my links", - "copy_link": "Kopier link" + "view_my_links": "Se mine links", + "generate_name_error": "Kunne ikke generere anmodningsnavn." }, "response": { - "audio": "Audio", - "body": "Svarorgan", - "filter_response_body": "Filter JSON response body (uses JSONPath syntax)", - "headers": "Overskrifter", + "audio": "Lyd", + "body": "Svarbesked", + "filter_response_body": "Filtrer JSON-svarbesked (bruger JSONPath-syntaks)", + "headers": "Headers", "html": "HTML", "image": "Billede", "json": "JSON", @@ -632,249 +629,249 @@ "size": "Størrelse", "status": "Status", "time": "Tid", - "title": "Respons", + "title": "Svar", "video": "Video", "waiting_for_connection": "venter på forbindelse", "xml": "XML" }, "settings": { - "accent_color": "Accent farve", + "accent_color": "Accentfarve", "account": "Konto", - "account_deleted": "Your account has been deleted", + "account_deleted": "Din konto er blevet slettet", "account_description": "Tilpas dine kontoindstillinger.", - "account_email_description": "Din primære e -mail -adresse.", + "account_email_description": "Din primære e-mailadresse.", "account_name_description": "Dette er dit visningsnavn.", - "additional": "Additional Settings", + "additional": "Yderligere indstillinger", "background": "Baggrund", "black_mode": "Sort", "choose_language": "Vælg sprog", "dark_mode": "Mørk", - "delete_account": "Delete account", - "delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.", - "expand_navigation": "Expand navigation", + "delete_account": "Slet konto", + "delete_account_description": "Når du sletter din konto, vil alle dine data blive permanent slettet. Denne handling kan ikke fortrydes.", + "expand_navigation": "Udvid navigation", "experiments": "Eksperimenter", - "experiments_notice": "Dette er en samling af eksperimenter, vi arbejder på, der kan vise sig at være nyttige, sjove, begge dele eller ingen af dem. De er ikke endelige og er muligvis ikke stabile, så hvis der sker noget alt for mærkeligt, skal du ikke gå i panik. Bare sluk for dang -tingen. Vittigheder til side,", + "experiments_notice": "Dette er en samling af eksperimenter, vi arbejder på, som måske viser sig at være nyttige, sjove, begge dele eller ingen af delene. De er ikke endelige og er måske ikke stabile, så hvis noget overdrevent mærkeligt sker, så gå ikke i panik. Bare sluk for det. Spøg til side, ", "extension_ver_not_reported": "Ikke rapporteret", - "extension_version": "Udvidelsesversion", - "extensions": "Udvidelser", - "extensions_use_toggle": "Brug browserudvidelsen til at sende anmodninger (hvis de findes)", - "follow": "Follow Us", - "interceptor": "Aflytter", + "extension_version": "Udvidelses version", + "extensions": "Browserudvidelse", + "extensions_use_toggle": "Brug browserudvidelsen til at sende anmodninger (hvis til stede)", + "follow": "Følg os", + "interceptor": "Interceptor", "interceptor_description": "Middleware mellem applikation og API'er.", "language": "Sprog", "light_mode": "Lys", - "official_proxy_hosting": "Officiel proxy er hostet af Hoppscotch.", - "profile": "Profile", - "profile_description": "Update your profile details", - "profile_email": "Email address", - "profile_name": "Profile name", + "official_proxy_hosting": "Officiel Proxy hostes af Hoppscotch.", + "profile": "Profil", + "profile_description": "Opdater dine profildetaljer", + "profile_email": "E-mailadresse", + "profile_name": "Profilnavn", "proxy": "Proxy", - "proxy_url": "Proxy -URL", - "proxy_use_toggle": "Brug proxy -middleware til at sende anmodninger", + "proxy_url": "Proxy URL", + "proxy_use_toggle": "Brug proxy-middleware til at sende anmodninger", "read_the": "Læs", "reset_default": "Nulstil til standard", - "short_codes": "Short codes", - "short_codes_description": "Short codes which were created by you.", - "sidebar_on_left": "Sidebar on left", + "short_codes": "Korte koder", + "short_codes_description": "Korte koder, som blev oprettet af dig.", + "sidebar_on_left": "Sidebjælke til venstre", + "ai_experiments": "AI-eksperimenter", "sync": "Synkroniser", "sync_collections": "Samlinger", - "sync_description": "Disse indstillinger er synkroniseret til cloud.", + "sync_description": "Disse indstillinger synkroniseres til skyen.", "sync_environments": "Miljøer", - "sync_history": "Historie", + "sync_history": "Historik", "system_mode": "System", "telemetry": "Telemetri", - "telemetry_helps_us": "Telemetri hjælper os med at tilpasse vores drift og levere den bedste oplevelse til dig.", + "telemetry_helps_us": "Telemetri hjælper os med at personliggøre vores operationer og levere den bedste oplevelse til dig.", "theme": "Tema", "theme_description": "Tilpas dit applikationstema.", - "use_experimental_url_bar": "Brug eksperimentel URL -bjælke med miljøfremhævning", + "use_experimental_url_bar": "Brug eksperimentel URL-bar med miljøfremhævning", "user": "Bruger", - "verified_email": "Verified email", - "verify_email": "Verify email" + "verified_email": "Bekræftet e-mail", + "verify_email": "Bekræft e-mail" }, "shared_requests": { - "button": "Button", - "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", - "copy_html": "Copy HTML", - "copy_link": "Copy Link", - "copy_markdown": "Copy Markdown", - "creating_widget": "Creating widget", - "customize": "Customize", - "deleted": "Shared request deleted", - "description": "Select a widget, you can change and customize this later", - "embed": "Embed", - "embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", + "button": "Knap", + "button_info": "Opret en 'Kør i Hoppscotch' knap til dit websted, blog eller en README.", + "copy_html": "Kopiér HTML", + "copy_link": "Kopiér link", + "copy_markdown": "Kopiér Markdown", + "creating_widget": "Opretter widget", + "customize": "Tilpas", + "deleted": "Delt anmodning slettet", + "description": "Vælg en widget, du kan ændre og tilpasse dette senere", + "embed": "Indlejr", + "embed_info": "Tilføj en mini 'Hoppscotch API Playground' til dit websted, blog eller dokumentation.", "link": "Link", - "link_info": "Create a shareable link to share with anyone on the internet with view access.", - "modified": "Shared request modified", - "not_found": "Shared request not found", - "open_new_tab": "Open in new tab", - "preview": "Preview", - "run_in_hoppscotch": "Run in Hoppscotch", + "link_info": "Opret et delbart link til at dele med hvem som helst på internettet med visningsadgang.", + "modified": "Delt anmodning ændret", + "not_found": "Delt anmodning ikke fundet", + "open_new_tab": "Åbn i ny fane", + "preview": "Forhåndsvis", + "run_in_hoppscotch": "Kør i Hoppscotch", "theme": { - "dark": "Dark", - "light": "Light", + "dark": "Mørk", + "light": "Lys", "system": "System", - "title": "Theme" + "title": "Tema" } }, "shortcut": { "general": { - "close_current_menu": "Luk den aktuelle menu", + "close_current_menu": "Luk nuværende menu", "command_menu": "Søg & kommandomenu", - "help_menu": "Hjælp menu", + "help_menu": "Hjælpemenu", "show_all": "Tastaturgenveje", - "title": "Generel" + "title": "Generelt" }, "miscellaneous": { - "invite": "Inviter folk til Hoppscotch", + "invite": "Invitér folk til Hoppscotch", "title": "Diverse" }, "navigation": { "back": "Gå tilbage til forrige side", - "documentation": "Gå til siden Dokumentation", - "forward": "Gå videre til næste side", - "graphql": "Gå til GraphQL -siden", - "profile": "Go to Profile page", - "realtime": "Gå til siden Realtid", - "rest": "Gå til REST side", - "settings": "Gå til siden Indstillinger", + "documentation": "Gå til dokumentationssiden", + "forward": "Gå frem til næste side", + "graphql": "Gå til GraphQL-siden", + "profile": "Gå til profilsiden", + "realtime": "Gå til realtidssiden", + "rest": "Gå til REST-siden", + "settings": "Gå til indstillingssiden", "title": "Navigation" }, "others": { - "prettify": "Prettify Editor's Content", - "title": "Others" + "prettify": "Forskøn editorens indhold", + "title": "Andre" }, "request": { - "delete_method": "Vælg SLET metode", - "get_method": "Vælg GET -metode", - "head_method": "Vælg HEAD -metode", - "import_curl": "Import cURL", + "delete_method": "Vælg DELETE-metode", + "get_method": "Vælg GET-metode", + "head_method": "Vælg HEAD-metode", + "import_curl": "Importér cURL", "method": "Metode", - "next_method": "Vælg Næste metode", - "post_method": "Vælg POST -metode", - "previous_method": "Vælg Forrige metode", - "put_method": "Vælg PUT -metode", - "rename": "Rename Request", + "next_method": "Vælg næste metode", + "post_method": "Vælg POST-metode", + "previous_method": "Vælg forrige metode", + "put_method": "Vælg PUT-metode", + "rename": "Omdøb anmodning", "reset_request": "Nulstil anmodning", - "save_request": "Save Request", - "save_to_collections": "Gem i samlinger", + "save_request": "Gem anmodning", + "save_to_collections": "Gem til samlinger", "send_request": "Send anmodning", - "share_request": "Share Request", - "show_code": "Generate code snippet", - "title": "Anmodning", - "copy_request_link": "Kopiér anmodningslink" + "share_request": "Del anmodning", + "show_code": "Generér kodesnippet", + "title": "Anmodning" }, "response": { - "copy": "Copy response to clipboard", - "download": "Download response as file", - "title": "Response" + "copy": "Kopiér svar til udklipsholder", + "download": "Download svar som fil", + "title": "Svar" }, "theme": { - "black": "Switch theme to black mode", - "dark": "Switch theme to dark mode", - "light": "Switch theme to light mode", - "system": "Switch theme to system mode", - "title": "Theme" + "black": "Skift tema til sort tilstand", + "dark": "Skift tema til mørk tilstand", + "light": "Skift tema til lys tilstand", + "system": "Skift tema til systemtilstand", + "title": "Tema" } }, "show": { "code": "Vis kode", - "collection": "Expand Collection Panel", + "collection": "Udvid samlingspanel", "more": "Vis mere", - "sidebar": "Vis sidebjælke" + "sidebar": "Udvid sidebjælke" }, "socketio": { - "communication": "Meddelelse", - "connection_not_authorized": "This SocketIO connection does not use any authentication.", - "event_name": "Begivenhedsnavn", + "communication": "Kommunikation", + "connection_not_authorized": "Denne SocketIO-forbindelse bruger ingen godkendelse.", + "event_name": "Begivenheds-/emnenavn", "events": "Begivenheder", "log": "Log", "url": "URL" }, "spotlight": { - "change_language": "Change Language", + "change_language": "Skift sprog", "environments": { - "delete": "Delete current environment", - "duplicate": "Duplicate current environment", - "duplicate_global": "Duplicate global environment", - "edit": "Edit current environment", - "edit_global": "Edit global environment", - "new": "Create new environment", - "new_variable": "Create a new environment variable", - "title": "Environments" + "delete": "Slet nuværende miljø", + "duplicate": "Duplikér nuværende miljø", + "duplicate_global": "Duplikér globalt miljø", + "edit": "Redigér nuværende miljø", + "edit_global": "Redigér globalt miljø", + "new": "Opret nyt miljø", + "new_variable": "Opret en ny miljøvariabel", + "title": "Miljøer" }, "general": { - "chat": "Chat with support", - "help_menu": "Help and support", - "open_docs": "Read Documentation", - "open_github": "Open GitHub repository", - "open_keybindings": "Keyboard shortcuts", + "chat": "Chat med support", + "help_menu": "Hjælp og support", + "open_docs": "Læs dokumentation", + "open_github": "Åbn GitHub-repository", + "open_keybindings": "Tastaturgenveje", "social": "Social", - "title": "General" + "title": "Generelt" }, "graphql": { - "connect": "Connect to server", - "disconnect": "Disconnect from server" + "connect": "Forbind til server", + "disconnect": "Afbryd forbindelse fra server" }, "miscellaneous": { - "invite": "Invite your friends to Hoppscotch", - "title": "Miscellaneous" + "invite": "Invitér dine venner til Hoppscotch", + "title": "Diverse" }, "request": { - "save_as_new": "Save as new request", - "select_method": "Select method", - "switch_to": "Switch to", - "tab_authorization": "Authorization tab", - "tab_body": "Body tab", - "tab_headers": "Headers tab", - "tab_parameters": "Parameters tab", - "tab_pre_request_script": "Pre-request script tab", - "tab_query": "Query tab", - "tab_tests": "Tests tab", - "tab_variables": "Variables tab" + "save_as_new": "Gem som ny anmodning", + "select_method": "Vælg metode", + "switch_to": "Skift til", + "tab_authorization": "Autorisationsfane", + "tab_body": "Meddelelsefane", + "tab_headers": "Headers-fane", + "tab_parameters": "Parameterfane", + "tab_pre_request_script": "Pre-request script-fane", + "tab_query": "Forespørgselsfane", + "tab_tests": "Testfane", + "tab_variables": "Variabelfane" }, "response": { - "copy": "Copy response", - "download": "Download response as file", - "title": "Response" + "copy": "Kopiér svar", + "download": "Download svar som fil", + "title": "Svar" }, "section": { "interceptor": "Interceptor", - "interface": "Interface", - "theme": "Theme", - "user": "User" + "interface": "Grænseflade", + "theme": "Tema", + "user": "Bruger" }, "settings": { - "change_interceptor": "Change Interceptor", - "change_language": "Change Language", + "change_interceptor": "Skift interceptor", + "change_language": "Skift sprog", "theme": { - "black": "Black", - "dark": "Dark", - "light": "Light", - "system": "System preference" + "black": "Sort", + "dark": "Mørk", + "light": "Lys", + "system": "Systemindstilling" } }, "tab": { - "close_current": "Close current tab", - "close_others": "Close all other tabs", - "duplicate": "Duplicate current tab", - "new_tab": "Open a new tab", - "title": "Tabs" + "close_current": "Luk nuværende fane", + "close_others": "Luk alle andre faner", + "duplicate": "Duplikér nuværende fane", + "new_tab": "Åbn en ny fane", + "title": "Faner" }, "workspace": { - "delete": "Delete current team", - "edit": "Edit current team", - "invite": "Invite people to team", - "new": "Create new team", - "switch_to_personal": "Switch to your personal workspace", - "title": "Teams" + "delete": "Slet nuværende arbejdsområde", + "edit": "Redigér nuværende arbejdsområde", + "invite": "Invitér folk til arbejdsområde", + "new": "Opret nyt arbejdsområde", + "switch_to_personal": "Skift til dit personlige arbejdsområde", + "title": "Arbejdsområder" }, "phrases": { - "try": "Try", - "import_collections": "Import collections", - "create_environment": "Create environment", - "create_workspace": "Create workspace", - "share_request": "Share request" + "try": "Prøv", + "import_collections": "Importér samlinger", + "create_environment": "Opret miljø", + "create_workspace": "Opret arbejdsområde", + "share_request": "Del anmodning" } }, "sse": { @@ -884,43 +881,43 @@ }, "state": { "bulk_mode": "Masseredigering", - "bulk_mode_placeholder": "Indlæg adskilles med ny linje\nNøgler og værdier adskilles af:\nForbered # til enhver række, du vil tilføje, men behold den deaktiveret", + "bulk_mode_placeholder": "Poster er adskilt af linjeskift\nNøgler og værdier er adskilt af :\nIndled med # for enhver række, du ønsker at tilføje, men holde deaktiveret", "cleared": "Ryddet", - "connected": "Tilsluttet", + "connected": "Forbundet", "connected_to": "Forbundet til {name}", - "connecting_to": "Opretter forbindelse til {name} ...", - "connection_error": "Failed to connect", - "connection_failed": "Connection failed", - "connection_lost": "Connection lost", - "copied_interface_to_clipboard": "Copied {language} interface type to clipboard", + "connecting_to": "Forbinder til {name}...", + "connection_error": "Kunne ikke oprette forbindelse", + "connection_failed": "Forbindelse mislykkedes", + "connection_lost": "Forbindelse tabt", + "copied_interface_to_clipboard": "Kopierede {language} interfacetype til udklipsholder", "copied_to_clipboard": "Kopieret til udklipsholder", "deleted": "Slettet", - "deprecated": "DEPRECATED", - "disabled": "handicappet", + "deprecated": "FORÆLDET", + "disabled": "Deaktiveret", "disconnected": "Afbrudt", - "disconnected_from": "Koblet fra {name}", + "disconnected_from": "Afbrudt fra {name}", "docs_generated": "Dokumentation genereret", - "download_failed": "Download failed", - "download_started": "Downloaden er startet", + "download_failed": "Download mislykkedes", + "download_started": "Download startet", "enabled": "Aktiveret", "file_imported": "Fil importeret", - "finished_in": "Færdig om {duration} ms", - "hide": "Hide", + "finished_in": "Afsluttet på {duration} ms", + "hide": "Skjul", "history_deleted": "Historik slettet", - "linewrap": "Wrap linjer", + "linewrap": "Ombryd linjer", "loading": "Indlæser...", - "message_received": "Message: {message} arrived on topic: {topic}", - "mqtt_subscription_failed": "Something went wrong while subscribing to topic: {topic}", + "message_received": "Besked: {message} ankom på emne: {topic}", + "mqtt_subscription_failed": "Noget gik galt under abonnement på emne: {topic}", "none": "Ingen", - "nothing_found": "Intet fundet til", - "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", - "published_message": "Published message: {message} to topic: {topic}", - "reconnection_error": "Failed to reconnect", - "show": "Show", - "subscribed_failed": "Failed to subscribe to topic: {topic}", - "subscribed_success": "Successfully subscribed to topic: {topic}", - "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", - "unsubscribed_success": "Successfully unsubscribed from topic: {topic}", + "nothing_found": "Intet fundet for", + "published_error": "Noget gik galt under udgivelse af besked: {topic} til emne: {message}", + "published_message": "Udgav besked: {message} til emne: {topic}", + "reconnection_error": "Kunne ikke genetablere forbindelse", + "show": "Vis", + "subscribed_failed": "Kunne ikke abonnere på emne: {topic}", + "subscribed_success": "Abonneret på emne: {topic} med succes", + "unsubscribed_failed": "Kunne ikke afmelde abonnement fra emne: {topic}", + "unsubscribed_success": "Afmeldt abonnement fra emne: {topic} med succes", "waiting_send_request": "Venter på at sende anmodning" }, "support": { @@ -929,176 +926,169 @@ "community": "Stil spørgsmål og hjælp andre", "documentation": "Læs mere om Hoppscotch", "forum": "Stil spørgsmål og få svar", - "github": "Follow us on Github", - "shortcuts": "Gennemse appen hurtigere", + "github": "Følg os på Github", + "shortcuts": "Naviger i appen hurtigere", "title": "Support", - "twitter": "Følg os på Twitter", - "team": "Kom i kontakt med teamet" + "twitter": "Følg os på Twitter" }, "tab": { - "authorization": "Bemyndigelse", - "body": "Legeme", - "close": "Close Tab", - "close_others": "Close other Tabs", + "authorization": "Autorisation", + "body": "Meddelelse", + "close": "Luk fane", + "close_others": "Luk andre faner", "collections": "Samlinger", "documentation": "Dokumentation", - "duplicate": "Duplicate Tab", - "environments": "Environments", - "headers": "Overskrifter", - "history": "Historie", + "duplicate": "Duplikér fane", + "environments": "Miljøer", + "headers": "Headers", + "history": "Historik", "mqtt": "MQTT", "parameters": "Parametre", - "pre_request_script": "Script på forhånd", + "pre_request_script": "Pre-request script", "queries": "Forespørgsler", "query": "Forespørgsel", - "schema": "Schema", - "shared_requests": "Shared Requests", - "codegen": "Generate Code", - "code_snippet": "Code snippet", - "share_tab_request": "Share tab request", + "schema": "Skema", + "shared_requests": "Delte anmodninger", + "codegen": "Generér kode", + "code_snippet": "Kodesnippet", + "share_tab_request": "Del fanens anmodning", "socketio": "Socket.IO", "sse": "SSE", - "tests": "Test", + "tests": "Tests", "types": "Typer", "variables": "Variabler", "websocket": "WebSocket" }, "team": { - "already_member": "You are already a member of this team. Contact your team owner.", - "create_new": "Opret nyt team", - "deleted": "Team slettet", - "edit": "Rediger team", + "already_member": "Du er allerede medlem af dette arbejdsområde. Kontakt din arbejdsområdeejer.", + "create_new": "Opret nyt arbejdsområde", + "deleted": "Arbejdsområde slettet", + "edit": "Redigér arbejdsområde", "email": "E-mail", - "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", - "exit": "Afslut Team", - "exit_disabled": "Kun ejeren kan ikke forlade teamet", - "failed_invites": "Failed invites", - "invalid_coll_id": "Invalid collection ID", - "invalid_email_format": "E -mailformatet er ugyldigt", - "invalid_id": "Invalid team ID. Contact your team owner.", - "invalid_invite_link": "Invalid invite link", - "invalid_invite_link_description": "The link you followed is invalid. Contact your team owner.", - "invalid_member_permission": "Giv venligst en gyldig tilladelse til teammedlemmet", - "invite": "Invite", - "invite_more": "Invite more", - "invite_tooltip": "Invite people to this workspace", - "invited_to_team": "{owner} invited you to join {team}", - "join": "Invitation accepted", - "join_team": "Join {team}", - "joined_team": "You have joined {team}", - "joined_team_description": "You are now a member of this team", - "left": "Du forlod holdet", - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to join a team.", - "logout_and_try_again": "Logout and sign in with another account", - "member_has_invite": "This email ID already has an invite. Contact your team owner.", - "member_not_found": "Member not found. Contact your team owner.", + "email_do_not_match": "E-mail matcher ikke dine kontooplysninger. Kontakt din arbejdsområdeejer.", + "exit": "Forlad arbejdsområde", + "exit_disabled": "Kun ejer kan ikke forlade arbejdsområdet", + "failed_invites": "Mislykkede invitationer", + "invalid_coll_id": "Ugyldigt samlings-ID", + "invalid_email_format": "E-mailformat er ugyldigt", + "invalid_id": "Ugyldigt arbejdsområde-ID. Kontakt din arbejdsområdeejer.", + "invalid_invite_link": "Ugyldigt invitationslink", + "invalid_invite_link_description": "Linket, du fulgte, er ugyldigt. Kontakt din arbejdsområdeejer.", + "invalid_member_permission": "Angiv venligst en gyldig tilladelse til arbejdsområdemedlemmet", + "invite": "Invitér", + "invite_more": "Invitér flere", + "invite_tooltip": "Invitér folk til dette arbejdsområde", + "invited_to_team": "{owner} inviterede dig til at tilslutte dig {workspace}", + "join": "Invitation accepteret", + "join_team": "Tilslut {workspace}", + "joined_team": "Du har tilsluttet dig {workspace}", + "joined_team_description": "Du er nu medlem af dette arbejdsområde", + "left": "Du forlod arbejdsområdet", + "login_to_continue": "Log ind for at fortsætte", + "login_to_continue_description": "Du skal være logget ind for at tilslutte dig et arbejdsområde.", + "logout_and_try_again": "Log ud og log ind med en anden konto", + "member_has_invite": "Denne e-mail-ID har allerede en invitation. Kontakt din arbejdsområdeejer.", + "member_not_found": "Medlem ikke fundet. Kontakt din arbejdsområdeejer.", "member_removed": "Bruger fjernet", "member_role_updated": "Brugerroller opdateret", "members": "Medlemmer", - "more_members": "+{count} more", - "name_length_insufficient": "Holdnavnet bør mindst være 6 tegn langt", - "name_updated": "Team name updated", - "new": "Nyt team", - "new_created": "Nyt team oprettet", - "new_name": "Mit nye hold", - "no_access": "Du har ikke redigeringsadgang til disse samlinger", - "no_invite_found": "Invitation not found. Contact your team owner.", - "no_request_found": "Request not found.", - "not_found": "Team not found. Contact your team owner.", - "not_valid_viewer": "You are not a valid viewer. Contact your team owner.", - "parent_coll_move": "Cannot move collection to a child collection", - "pending_invites": "Pending invites", + "more_members": "+{count} flere", + "name_length_insufficient": "Arbejdsområdenavn skal være mindst 6 tegn langt", + "name_updated": "Arbejdsområdenavn opdateret", + "new": "Nyt arbejdsområde", + "new_created": "Nyt arbejdsområde oprettet", + "new_name": "Mit nye arbejdsområde", + "no_access": "Du har ikke redigeringsadgang til dette arbejdsområde", + "no_invite_found": "Invitation ikke fundet. Kontakt din arbejdsområdeejer.", + "no_request_found": "Anmodning ikke fundet.", + "not_found": "Arbejdsområde ikke fundet. Kontakt din arbejdsområdeejer.", + "not_valid_viewer": "Du er ikke en gyldig betragter. Kontakt din arbejdsområdeejer.", + "parent_coll_move": "Kan ikke flytte samling til en undersamling", + "pending_invites": "Ventende invitationer", "permissions": "Tilladelser", - "same_target_destination": "Same target and destination", - "saved": "Hold reddet", - "select_a_team": "Select a team", - "success_invites": "Success invites", - "title": "Hold", - "we_sent_invite_link": "We sent an invite link to all invitees!", - "invite_sent_smtp_disabled": "Invite links generated", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", - "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", - "copy_invite_link": "Copy Invite Link", - "search_title": "Team Requests", - "join_beta": "Deltag i betaprogrammet for at få adgang til teams." + "same_target_destination": "Samme mål og destination", + "saved": "Arbejdsområde gemt", + "select_a_team": "Vælg et arbejdsområde", + "success_invites": "Vellykkede invitationer", + "title": "Arbejdsområder", + "we_sent_invite_link": "Vi sendte et invitationslink til alle inviterede!", + "invite_sent_smtp_disabled": "Invitationslinks genereret", + "we_sent_invite_link_description": "Bed alle inviterede om at tjekke deres indbakke. Klik på linket for at tilslutte sig arbejdsområdet.", + "invite_sent_smtp_disabled_description": "Afsendelse af invitations-e-mails er deaktiveret for denne instans af Hoppscotch. Brug venligst knappen Kopiér link til at kopiere og dele invitationslinket manuelt.", + "copy_invite_link": "Kopiér invitationslink", + "search_title": "Teamanmodninger" }, "team_environment": { - "deleted": "Environment Deleted", - "duplicate": "Environment Duplicated", - "not_found": "Environment not found." + "deleted": "Miljø slettet", + "duplicate": "Miljø duplikeret", + "not_found": "Miljø ikke fundet." }, "test": { - "failed": "test failed", - "javascript_code": "JavaScript -kode", + "failed": "test mislykkedes", + "javascript_code": "JavaScript-kode", "learn": "Læs dokumentation", - "passed": "test passed", - "report": "Test rapport", - "results": "Test resultater", - "script": "Manuskript", - "snippets": "Uddrag" + "passed": "test bestået", + "report": "Testrapport", + "results": "Testresultater", + "script": "Script", + "snippets": "Snippets" }, "websocket": { - "communication": "Meddelelse", + "communication": "Kommunikation", "log": "Log", "message": "Besked", "protocols": "Protokoller", "url": "URL" }, "workspace": { - "change": "Change workspace", - "personal": "My Workspace", - "other_workspaces": "My Workspaces", - "team": "Team Workspace", - "title": "Workspaces" + "change": "Skift arbejdsområde", + "personal": "Personligt arbejdsområde", + "other_workspaces": "Mine arbejdsområder", + "team": "Arbejdsområde", + "title": "Arbejdsområder" }, "site_protection": { - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", - "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + "login_to_continue": "Log ind for at fortsætte", + "login_to_continue_description": "Du skal være logget ind for at få adgang til denne Hoppscotch Enterprise-instans.", + "error_fetching_site_protection_status": "Noget gik galt under hentning af sitebeskyttelsesstatus" }, "access_tokens": { "tab_title": "Tokens", - "section_title": "Personal Access Tokens", - "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", - "last_used_on": "Last used on", - "expires_on": "Expires on", - "no_expiration": "No expiration", - "expired": "Expired", - "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", - "token_purpose": "What's this token for?", - "expiration_label": "Expiration", - "scope_label": "Scope", - "workspace_read_only_access": "Read-only access to workspace data.", - "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", - "generate_token": "Generate Token", - "invalid_label": "Please provide a label for the token", - "no_expiration_verbose": "This token will never expire!", - "token_expires_on": "This token will expire on", - "generate_new_token": "Generate new token", - "generate_modal_title": "New Personal Access Token", - "deletion_success": "The access token {label} has been deleted" + "section_title": "Personlige adgangstokens", + "section_description": "Personlige adgangstokens hjælper dig i øjeblikket med at forbinde CLI'en til din Hoppscotch-konto", + "last_used_on": "Sidst brugt den", + "expires_on": "Udløber den", + "no_expiration": "Ingen udløbsdato", + "expired": "Udløbet", + "copy_token_warning": "Sørg for at kopiere dit personlige adgangstoken nu. Du vil ikke kunne se det igen!", + "token_purpose": "Hvad er dette token til?", + "expiration_label": "Udløb", + "scope_label": "Omfang", + "workspace_read_only_access": "Skrivebeskyttet adgang til arbejdsområdedata.", + "personal_workspace_access_limitation": "Personlige adgangstokens kan ikke få adgang til dit personlige arbejdsområde.", + "generate_token": "Generér token", + "invalid_label": "Angiv venligst en etiket for tokenet", + "no_expiration_verbose": "Dette token vil aldrig udløbe!", + "token_expires_on": "Dette token udløber den", + "generate_new_token": "Generér nyt token", + "generate_modal_title": "Nyt personligt adgangstoken", + "deletion_success": "Adgangstokenet {label} er blevet slettet" }, "collection_runner": { - "collection_id": "Collection ID", - "environment_id": "Environment ID", - "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", - "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", - "include_active_environment": "Include active environment:", + "collection_id": "Samlings-ID", + "environment_id": "Miljø-ID", + "cli_collection_id_description": "Dette samlings-ID vil blive brugt af CLI-samlingsløberen for Hoppscotch.", + "cli_environment_id_description": "Dette miljø-ID vil blive brugt af CLI-samlingsløberen for Hoppscotch.", + "include_active_environment": "Inkludér aktivt miljø:", "cli": "CLI", - "ui": "Runner (coming soon)", - "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", - "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", - "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "ui": "Løber (kommer snart)", + "cli_command_generation_description_cloud": "Kopiér nedenstående kommando og kør den fra CLI'en. Angiv venligst et personligt adgangstoken.", + "cli_command_generation_description_sh": "Kopiér nedenstående kommando og kør den fra CLI'en. Angiv venligst et personligt adgangstoken og verificér den genererede SH-instans serveradresse.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Kopiér nedenstående kommando og kør den fra CLI'en. Angiv venligst et personligt adgangstoken og SH-instansens serveradresse.", + "run_collection": "Kør samling" }, - "shortcodes": { - "actions": "Actions", - "created_on": "Created on", - "deleted": "Shortcode deleted", - "method": "Method", - "not_found": "Shortcode not found", - "short_code": "Short code", - "url": "URL" + "ai_experiments": { + "generate_request_name": "Generér anmodningsnavn ved hjælp af AI", + "generate_or_modify_request_body": "Generér eller modificér anmodning meddelelse" } } diff --git a/packages/hoppscotch-common/locales/de.json b/packages/hoppscotch-common/locales/de.json index a4380339d..f26182456 100644 --- a/packages/hoppscotch-common/locales/de.json +++ b/packages/hoppscotch-common/locales/de.json @@ -1,12 +1,15 @@ { "action": { "add": "Hinzufügen", - "autoscroll": "Autoscroll", + "autoscroll": "Automatisch Scrollen", "cancel": "Abbrechen", "choose_file": "Datei auswählen", + "choose_workspace": "Arbeitsbereich auswählen", + "choose_collection": "Sammlung auswählen", + "select_workspace": "Arbeitsbereich auswählen", "clear": "Zurücksetzen", "clear_all": "Alles zurücksetzen", - "clear_history": "Clear all History", + "clear_history": "Alle Verlaufsdaten löschen", "close": "Schließen", "connect": "Verbinden", "connecting": "Verbinde", @@ -20,14 +23,14 @@ "drag_to_reorder": "Ziehen zum Umordnen", "duplicate": "Duplizieren", "edit": "Bearbeiten", - "filter": "Filter", + "filter": "Filtern", "go_back": "Zurück", - "go_forward": "Go forward", + "go_forward": "Weiter", "group_by": "Gruppiere nach", - "hide_secret": "Hide secret", - "label": "Etikett", + "hide_secret": "Geheimnis verbergen", + "label": "Bezeichnung", "learn_more": "Mehr erfahren", - "download_here": "Download here", + "download_here": "Hier herunterladen", "less": "Weniger", "more": "Mehr", "new": "Neu", @@ -36,16 +39,19 @@ "paste": "Einfügen", "prettify": "Verschönern", "properties": "Eigenschaften", + "register": "Registrieren", "remove": "Entfernen", "rename": "Umbenennen", "restore": "Wiederherstellen", + "retry": "Wiederholen", "save": "Speichern", + "save_as_example": "Als Beispiel speichern", "scroll_to_bottom": "Zum Ende scrollen", "scroll_to_top": "Zum Anfang scrollen", "search": "Suchen", "send": "Senden", "share": "Teilen", - "show_secret": "Show secret", + "show_secret": "Geheimnis anzeigen", "start": "Starten", "starting": "Startet", "stop": "Stopp", @@ -55,18 +61,41 @@ "turn_off": "Ausschalten", "turn_on": "Einschalten", "undo": "Rückgängig machen", + "verify": "Überprüfen", "yes": "Ja" }, "add": { "new": "Neue hinzufügen", "star": "Stern hinzufügen" }, + "agent": { + "registration_instruction": "Bitte registrieren Sie Hoppscotch Agent mit Ihrem Webclient, um fortzufahren.", + "enter_otp_instruction": "Bitte geben Sie den Verifizierungscode ein, der vom Hoppscotch Agent generiert wurde, und schließen Sie die Registrierung ab.", + "otp_label": "Verifizierungscode", + "processing": "Verarbeite die Anfrage...", + "not_running": "Der Hoppscotch Agent läuft nicht. Bitte starten Sie den Agenten und klicken Sie auf 'Wiederholen'.", + "not_running_title": "Agent nicht erkannt", + "registration_title": "Agent registrierung", + "verify_ssl_certs": "SSL-Zertifikate überprüfen", + "client_certs": "Client-Zertifikate", + "use_http_proxy": "HTTP-Proxy verwenden", + "proxy_capabilities": "Hoppscotch Agent unterstützt HTTP/HTTPS/SOCKS-Proxys sowie NTLM und Basic Auth für Proxys. Geben Sie den Benutzernamen und das Passwort für die Proxy-Authentifizierung in der URL an.", + "add_cert_file": "Zertifikatdatei hinzufügen", + "add_client_cert": "Client-Zertifikat hinzufügen", + "add_key_file": "Schlüssel Datei hinzufügen", + "domain": "Domain", + "cert": "Zertifikat", + "key": "Schlüssel", + "pfx_or_pkcs": "PFX/PKCS12", + "pfx_or_pkcs_file": "PFX/PKCS12-Datei", + "add_pfx_or_pkcs_file": "PFX/PKCS12-Datei hinzufügen" + }, "app": { "chat_with_us": "Chatte mit uns", "contact_us": "Kontaktiere uns", "cookies": "Cookies", "copy": "Kopieren", - "copy_interface_type": "Copy interface type", + "copy_interface_type": "Schnittstellentyp kopieren", "copy_user_id": "Kopiere Nutzer-Authentisierungstoken", "developer_option": "Entwickleroptionen", "developer_option_description": "Entwicklungswerkzeuge, die helfen Hoppscotch weiter zu entwickeln und zu warten.", @@ -89,7 +118,7 @@ "search": "Suche", "share": "Teilen", "shortcuts": "Verknüpfungen", - "social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", + "social_description": "Folge uns in den sozialen Medien für die neuesten Nachrichten und Updates", "social_links": "Social links", "spotlight": "Scheinwerfer", "status": "Status", @@ -98,18 +127,19 @@ "twitter": "Twitter", "type_a_command_search": "Gib einen Befehl ein oder suche…", "we_use_cookies": "Wir verwenden Cookies", - "updated_text": "Hoppscotch has been updated to v{version} 🎉", + "updated_text": "Hoppscotch wurde auf die Version v{version} aktualisiert 🎉", "whats_new": "Was gibt's Neues?", - "see_whats_new": "See what’s new", - "wiki": "Wiki" + "see_whats_new": "Sieh dir an, was neu ist", + "wiki": "Wiki", + "default": "standard: {value}" }, "auth": { "account_exists": "Konto existiert mit unterschiedlichen Zugangsdaten - Melde Dich an, um beide Konten zu verknüpfen", "all_sign_in_options": "Alle Anmeldeoptionen", - "continue_with_auth_provider": "Continue with {provider}", + "continue_with_auth_provider": "Fortfahren mit {provider}", "continue_with_email": "Mit E-Mail anmelden", "continue_with_github": "Mit GitHub anmelden", - "continue_with_github_enterprise": "Continue with GitHub Enterprise", + "continue_with_github_enterprise": "Fortfahren mit GitHub Enterprise", "continue_with_google": "Mit Google anmelden", "continue_with_microsoft": "Mit Microsoft anmelden", "email": "E-Mail-Adresse", @@ -126,58 +156,71 @@ }, "authorization": { "generate_token": "Token generieren", - "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", + "refresh_token": "Aktualisierungs Token", + "graphql_headers": "Autorisierungsheader werden als Teil des Payloads an connection_init gesendet", "include_in_url": "In URL einbinden", "inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "learn": "Dokumentation", "oauth": { - "redirect_auth_server_returned_error": "Auth Server returned an error state", - "redirect_auth_token_request_failed": "Request to get the auth token failed", - "redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token", - "redirect_invalid_state": "Invalid State value present in the redirect", - "redirect_no_auth_code": "No Authorization Code present in the redirect", - "redirect_no_client_id": "No Client ID defined", - "redirect_no_client_secret": "No Client Secret Defined", - "redirect_no_code_verifier": "No Code Verifier Defined", - "redirect_no_token_endpoint": "No Token Endpoint Defined", - "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", - "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", - "grant_type": "Grant Type", - "grant_type_auth_code": "Authorization Code", - "token_fetched_successfully": "Token fetched successfully", - "token_fetch_failed": "Failed to fetch token", - "validation_failed": "Validation Failed, please check the form fields", - "label_authorization_endpoint": "Authorization Endpoint", - "label_client_id": "Client ID", - "label_client_secret": "Client Secret", - "label_code_challenge": "Code Challenge", - "label_code_challenge_method": "Code Challenge Method", - "label_code_verifier": "Code Verifier", + "redirect_auth_server_returned_error": "Der Auth-Server hat einen Fehlerstatus zurückgegeben.", + "redirect_auth_token_request_failed": "Anforderung des Auth-Token ist fehlgeschlagen.", + "redirect_auth_token_request_invalid_response": "Ungültige Antwort vom Token-Endpunkt bei der Anforderung eines Auth-Tokens.", + "redirect_invalid_state": "Ungültiger Statuswert im Redirect vorhanden.", + "redirect_no_auth_code": "Kein Autorisierungscode im Redirect vorhanden.", + "redirect_no_client_id": "Keine Client-ID definiert.", + "redirect_no_client_secret": "Kein Client-Geheimnis definiert.", + "redirect_no_code_verifier": "Kein Code-Verifier definiert.", + "redirect_no_token_endpoint": "Kein Token-Endpunkt definiert.", + "something_went_wrong_on_oauth_redirect": "Beim OAuth-Redirect ist etwas schiefgegangen.", + "something_went_wrong_on_token_generation": "Beim Generieren des Tokens ist etwas schiefgegangen.", + "token_generation_oidc_discovery_failed": "Fehler bei der Token-Generierung: OpenID Connect Discovery fehlgeschlagen.", + "grant_type": "Grant-Typ", + "grant_type_auth_code": "Autorisierungscode", + "token_fetched_successfully": "Token erfolgreich abgerufen.", + "token_fetch_failed": "Abrufen des Tokens fehlgeschlagen.", + "validation_failed": "Validierung fehlgeschlagen, bitte überprüfen Sie die Formularfelder.", + "no_refresh_token_present": "Kein Aktualisierungs-Token vorhanden. Bitte führen Sie den Token-Generierungsprozess erneut aus", + "refresh_token_request_failed": "Aktualisierungs-Token-Anforderung fehlgeschlagen", + "token_refreshed_successfully": "Token erfolgreich aktualisiert", + "label_authorization_endpoint": "Autorisierungsendpunkt", + "label_client_id": "Client-ID", + "label_client_secret": "Client-Geheimnis", + "label_code_challenge": "Code-Challenge", + "label_code_challenge_method": "Code-Challenge-Methode", + "label_code_verifier": "Code-Verifier", "label_scopes": "Scopes", - "label_token_endpoint": "Token Endpoint", - "label_use_pkce": "Use PKCE", - "label_implicit": "Implicit", - "label_password": "Password", - "label_username": "Username", - "label_auth_code": "Authorization Code", - "label_client_credentials": "Client Credentials" + "label_token_endpoint": "Token-Endpunkt", + "label_use_pkce": "PKCE verwenden", + "label_implicit": "Implizit", + "label_password": "Passwort", + "label_username": "Benutzername", + "label_auth_code": "Autorisierungscode", + "label_client_credentials": "Client-Anmeldeinformationen" }, "pass_key_by": "Übertragungsart", - "pass_by_query_params_label": "Query Parameters", + "pass_by_query_params_label": "Abfrageparameter", "pass_by_headers_label": "Headers", "password": "Passwort", "save_to_inherit": "Bitte speichere diese Anfrage in einer Sammlung, um die Autorisierung zu erben", "token": "Token", "type": "Autorisierungsverfahren", - "username": "Nutzername" + "username": "Nutzername", + "aws_signature": { + "access_key": "Zugangs-Schlüssel", + "secret_key": "Geheimer Schlüssel", + "service_name": "Dienstname", + "aws_region": "AWS-Region", + "service_token": "Dienst-Token", + "advance_config": "Erweiterte Konfiguration", + "advance_config_description": "Hoppscotch weist automatisch Standardwerte für bestimmte Felder zu, wenn kein expliziter Wert angegeben wird." + } }, "collection": { "created": "Sammlung erstellt", "different_parent": "Sammlung kann nicht neu geordnet werden, weil sie ein anderes übergeordnetes Element hat", "edit": "Sammlung bearbeiten", "import_or_create": "Sammlung erstellen oder importieren", - "import_collection": "Import Collection", + "import_collection": "Sammlung importieren", "invalid_name": "Bitte gib einen gültigen Namen für die Sammlung an", "invalid_root_move": "Sammlung bereits im Stammverzeichnis", "moved": "Erfolgreich verschoben", @@ -195,8 +238,7 @@ "select": "Wähle eine Sammlung", "select_location": "Ort auswählen", "details": "Details", - "select_team": "Wähle ein Team", - "team_collections": "Teamsammlungen" + "duplicated": "Sammlung dupliziert" }, "confirm": { "close_unsaved_tab": "Bist du sicher, dass du dieses Tab schließen möchtest?", @@ -208,7 +250,8 @@ "remove_folder": "Möchtest Du diesen Ordner wirklich dauerhaft löschen?", "remove_history": "Möchtest Du wirklich den gesamten Verlauf dauerhaft löschen?", "remove_request": "Möchtest Du diese Anfrage wirklich dauerhaft löschen?", - "remove_shared_request": "Are you sure you want to permanently delete this shared request?", + "remove_response": "Möchtest Du diese Antwort wirklich dauerhaft löschen?", + "remove_shared_request": "Möchtest Du diese geteilte Anfrage wirklich dauerhaft löschen?", "remove_team": "Möchtest Du dieses Team wirklich löschen?", "remove_telemetry": "Möchtest Du die Telemetrie wirklich deaktivieren?", "request_change": "Möchtest Du diese Anfrage verwerfen? Ungespeicherte Änderungen gehen verloren.", @@ -223,26 +266,28 @@ }, "cookies": { "modal": { - "cookie_expires": "Expires", + "cookie_expires": "Läuft ab", "cookie_name": "Name", - "cookie_path": "Path", - "cookie_string": "Cookie string", - "cookie_value": "Value", - "empty_domain": "Domain is empty", - "empty_domains": "Domain list is empty", - "enter_cookie_string": "Enter cookie string", - "interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", - "managed_tab": "Managed", - "new_domain_name": "New domain name", - "no_cookies_in_domain": "No cookies set for this domain", + "cookie_path": "Pfad", + "cookie_string": "Cookie-String", + "cookie_value": "Wert", + "empty_domain": "Domain ist leer", + "empty_domains": "Domainliste ist leer", + "enter_cookie_string": "Cookie-String eingeben", + "interceptor_no_support": "Der derzeit ausgewählte Interceptor unterstützt keine Cookies. Wählen Sie einen anderen Interceptor und versuchen Sie es erneut.", + "managed_tab": "Verwaltet", + "new_domain_name": "Neuer Domainname", + "no_cookies_in_domain": "Es sind keine Cookies für diese Domain gesetzt", "raw_tab": "Raw", - "set": "Set a cookie" + "set": "Ein Cookie setzen" } }, "count": { "header": "Header {count}", "message": "Nachricht {count}", "parameter": "Parameter {count}", + "key": "Schlüssel {count}", + "description": "Beschreibung {count}", "protocol": "Protokoll {count}", "value": "Wert {count}", "variable": "Variable {count}" @@ -268,17 +313,17 @@ "pending_invites": "Es gibt keine ausstehenden Einladungen für dieses Team", "profile": "Einloggen um das Profil anzusehen", "protocols": "Protokolle sind leer", - "request_variables": "This request does not have any request variables", + "request_variables": "Diese Anfrage hat keine Anfragevariablen", "schema": "Verbinden mit einem GraphQL-Endpunkt", - "secret_environments": "Secrets are not synced to Hoppscotch", + "secret_environments": "Geheimnisse werden nicht mit dem Hoppscotch Server synchronisiert", "shared_requests": "Keine geteilten Abfragen", "shared_requests_logout": "Melde dich an, um geteilte Anfragen zu sehen oder eine neue zu erstellen", "subscription": "Keine Abonnements", "team_name": "Teamname leer", "teams": "Keine Teams", "tests": "Es gibt keine Tests für diese Anfrage", - "access_tokens": "Access tokens are empty", - "shortcodes": "Shortcodes are empty" + "access_tokens": "Zugriffs-Token sind leer", + "response": "Keine Antwort erhalten" }, "environment": { "add_to_global": "Zur Globalen Umgebung hinzufügen", @@ -304,11 +349,12 @@ "quick_peek": "Kurzinfo der Umgebung", "replace_with_variable": "Mit Variable ersetzen", "scope": "Scope", - "secrets": "Secrets", - "secret_value": "Secret value", + "secrets": "Geheimnis", + "secret_value": "Geheimwert", "select": "Umgebung auswählen", "set": "Umgebung setzen", "set_as_environment": "Als Umgebung setzen", + "short_name": "Umgebung muss mindestens 3 Zeichen haben", "team_environments": "Teamumgebungen", "title": "Umgebungen", "updated": "Umgebung aktualisiert", @@ -316,7 +362,7 @@ "variable": "Variable", "variables": "Variables", "variable_list": "Variablenliste", - "properties": "Environment Properties", + "properties": "Umgebungsparameter", "details": "Details" }, "error": { @@ -328,7 +374,7 @@ "danger_zone": "Gefahrenbereich", "delete_account": "Dein Konto ist derzeit Besitzer dieser Teams:", "delete_account_description": "Du musst dich entweder selbst entfernen, den Besitz übertragen oder diese Teams löschen, bevor du dein Konto löschen kannst.", - "empty_profile_name": "Profile name cannot be empty", + "empty_profile_name": "Profilname darf nicht leer sein", "empty_req_name": "Leerer Anfragename", "f12_details": "(F12 für Details)", "gql_prettify_invalid_query": "Eine ungültige Abfrage konnte nicht verschönert werden. Fehler in der Abfragesyntax beheben und erneut versuchen", @@ -347,31 +393,38 @@ "no_results_found": "Keine Ergebnisse gefunden", "page_not_found": "Diese Seite konnte nicht gefunden werden", "please_install_extension": "Bitte installiere die Browser-Erweiterung, um Quellen hinzuzufügen.", - "proxy_error": "Proxy error", - "same_profile_name": "Updated profile name is same as the current profile name", + "proxy_error": "Proxy fehler", + "same_profile_name": "Der aktualisierte Profilname ist identisch mit dem aktuellen Profilnamen", "script_fail": "Pre-Request-Skripte konnte nicht ausgeführt werden", "something_went_wrong": "Etwas ist schief gelaufen", "test_script_fail": "Testskripts konnten nicht ausgeführt werden", - "reading_files": "Error while reading one or more files.", - "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", - "generate_access_token": "Something went wrong while generating the access token", - "delete_access_token": "Something went wrong while deleting the access token" + "reading_files": "Fehler beim Lesen einer oder mehrerer Dateien.", + "fetching_access_tokens_list": "Beim Abrufen der Liste der Tokens ist etwas schiefgegangen.", + "generate_access_token": "Beim Generieren des Zugriffstokens ist etwas schiefgegangen.", + "delete_access_token": "Beim Löschen des Zugriffstokens ist etwas schiefgegangen." }, "export": { "as_json": "Als JSON exportieren", - "create_secret_gist_tooltip_text": "Export as secret Gist", - "secret_gist_success": "Successfully exported as secret Gist", - "success": "Successfully exported", - "create_secret_gist": "Geheimen GitHub Gist erstellen", - "failed": "Beim Exportieren ist etwas schief gelaufen", - "gist_created": "Gist erstellt", - "require_github": "Melde Dich bei GitHub an, um einen geheimen Gist zu erstellen", - "title": "Exportieren" + "create_secret_gist": "Geheimes Gist erstellen", + "create_secret_gist_tooltip_text": "Als geheimes Gist exportieren", + "failed": "Beim Exportieren ist etwas schiefgegangen", + "secret_gist_success": "Erfolgreich als geheimes Gist exportiert", + "require_github": "Melden Sie sich mit GitHub an, um ein geheimes Gist zu erstellen", + "title": "Exportieren", + "success": "Erfolgreich exportiert" + }, + "filename": { + "cookie_key_value_pairs": "Cookies", + "codegen": "{request_name} - Code", + "graphql_response": "GraphQL-Antwort", + "lens": "{request_name} - Antwort", + "realtime_response": "Echtzeit-Antwort", + "response_interface": "Antwort-Schnittstelle" }, "filter": { - "all": "All", - "none": "None", - "starred": "Starred" + "all": "Alle", + "none": "Keine", + "starred": "Favoriten" }, "folder": { "created": "Ordner erstellt", @@ -389,7 +442,7 @@ "schema": "Schema", "subscriptions": "Abonnements", "switch_connection": "Verbindung wechseln", - "url_placeholder": "Enter a GraphQL endpoint URL" + "url_placeholder": "Gib eine GraphQL-Endpunkt-URL ein" }, "graphql_collections": { "title": "GraphQL Sammlungen" @@ -438,15 +491,16 @@ "from_json_description": "Hoppscotch-Sammlung importieren", "from_my_collections": "Aus 'Meine Sammlungen' importieren", "from_my_collections_description": "Meine-Sammlungen-Datei importieren", + "from_all_collections": "Von einem anderen Arbeitsbereich importieren", + "from_all_collections_description": "Importiere jede Sammlung von einem anderen Arbeitsbereich in den aktuellen Arbeitsbereich", "from_openapi": "Aus OpenAPI importieren", "from_openapi_description": "OpenAPI-Spezifikation importieren (YAML/JSON)", "from_postman": "Aus Postman importieren", "from_postman_description": "Postman-Sammlung importieren", "from_url": "Aus URL importieren", "gist_url": "Gist-URL eingeben", - "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", - "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", - "success": "Successfully imported", + "from_har": "Von HAR importieren", + "from_har_description": "Von HAR-Datei importieren", "gql_collections_from_gist_description": "GraphQL-Sammlungen aus Gist importieren", "hoppscotch_environment": "Hoppscotch-Umgebung", "hoppscotch_environment_description": "Hoppscotch-Umgebung aus JSON-Datei importieren", @@ -458,14 +512,17 @@ "json_description": "Hoppscotch-Sammlung aus JSON-Datei importieren", "postman_environment": "Postman Umgebung", "postman_environment_description": "Postman-Umgebung aus einer JSON-Datei importieren", - "title": "Importieren" + "title": "Importieren", + "file_size_limit_exceeded_warning_multiple_files": "Die gewählten Dateien überschreiten das Limit von 10 MB. Nur die ersten {files} ausgewählten Dateien werden importiert.", + "file_size_limit_exceeded_warning_single_file": "Die aktuell gewählte Datei überschreitet das empfohlene Limit von 10 MB. Bitte wählen Sie eine andere Datei.", + "success": "Erfolgreich importiert" }, "inspections": { "description": "Mögliche Fehler überprüfen", "environment": { - "add_environment_value": "Add value", - "empty_value": "Environment value is empty for the variable '{variable}' ", "add_environment": "Zur Umgebung hinzufügen", + "add_environment_value": "Add value", + "empty_value": "Der Umgebungswert ist für die Variable '{variable}' leer", "not_found": "Umgebungsvariable “{environment}” nicht gefunden." }, "header": { @@ -499,12 +556,14 @@ "confirm": "Aktion bestätigen", "customize_request": "Anfrage anpassen", "edit_request": "Anfrage bearbeiten", + "edit_response": "Antwort bearbeiten", "import_export": "Importieren / Exportieren", + "response_name": "Antwort Name", "share_request": "Anfrage teilen" }, "mqtt": { "already_subscribed": "Du hast dieses Topic bereits abonniert.", - "clean_session": "Clean Session", + "clean_session": "Sitzung bereinigen", "clear_input": "Eingaben löschen", "clear_input_on_send": "Eingaben beim Senden löschen", "client_id": "Client ID", @@ -566,6 +625,7 @@ }, "request": { "added": "Anfrage hinzugefügt", + "add": "Anfrage hinzufügen", "authorization": "Autorisierung", "body": "Anfragekörper", "choose_language": "Sprache wählen", @@ -601,7 +661,8 @@ "raw_body": "Roher Anfragetext", "rename": "Anfrage umbenennen", "renamed": "Anfrage umbenannt", - "request_variables": "Request variables", + "request_variables": "Anfragevariablen", + "response_name_exists": "Antwortname existiert bereits", "run": "Ausführen", "save": "Speichern", "save_as": "Speichern als", @@ -613,7 +674,7 @@ "title": "Anfrage", "type": "Anfragetyp", "url": "URL", - "url_placeholder": "Enter a URL or paste a cURL command", + "url_placeholder": "Gib eine URL ein oder füge einen cURL-Befehl ein", "variables": "Variablen", "view_my_links": "Meine Links anzeigen", "copy_link": "Link kopieren" @@ -621,21 +682,29 @@ "response": { "audio": "Audio", "body": "Antworttext", + "duplicated": "Antwort dupliziert", + "duplicate_name_error": "Antwort mit demselben Namen existiert bereits", "filter_response_body": "JSON-Antwortkörper filtern (verwendet JSONPath-Syntax)", "headers": "Header", "html": "HTML", "image": "Bild", "json": "JSON", "pdf": "PDF", + "please_save_request": "Die Anfrage speichern, um ein Beispiel zu erstellen", "preview_html": "HTML-Vorschau", "raw": "Rohdaten", + "renamed": "Antwort umbenannt", "size": "Größe", "status": "Status", "time": "Zeit", "title": "Antwort", "video": "Video", "waiting_for_connection": "auf Verbindung warten", - "xml": "XML" + "xml": "XML", + "generate_data_schema": "Daten-Schema generieren", + "data_schema": "Daten-Schema", + "saved": "Antwort gespeichert", + "invalid_name": "Bitte geben Sie einen Namen für die Antwort an" }, "settings": { "accent_color": "Akzentfarbe", @@ -673,9 +742,10 @@ "proxy_use_toggle": "Verwende die Proxy-Middleware, um Anfragen zu senden", "read_the": "Lies die", "reset_default": "Zurücksetzen", - "short_codes": "Short codes", - "short_codes_description": "Short codes which were created by you.", + "short_codes": "Kurzcodes", + "short_codes_description": "Kurzcodes, die von dir erstellt wurden", "sidebar_on_left": "Seitenleiste links", + "ai_experiments": "KI-Experimente", "sync": "Synchronisieren", "sync_collections": "Sammlungen", "sync_description": "Diese Einstellungen werden mit der Cloud synchronisiert.", @@ -692,8 +762,8 @@ "verify_email": "E-Mail-Adresse bestätigen" }, "shared_requests": { - "button": "Button", - "button_info": "Erstelle einen 'Run in Hoppscotch'-Button für deine Website, deinen Blog oder eine README.", + "button": "Schaltfläche", + "button_info": "Erstelle einen 'Run in Hoppscotch'-Schaltfläche für deine Website, deinen Blog oder eine README.", "copy_html": "HTML kopieren", "copy_link": "Link kopieren", "copy_markdown": "Markdown kopieren", @@ -761,8 +831,7 @@ "send_request": "Anfrage senden", "share_request": "Anfrage teilen", "show_code": "Code-Schnipsel generieren", - "title": "Anfrage", - "copy_request_link": "Anfragelink kopieren" + "title": "Anfrage" }, "response": { "copy": "Antwort in die Zwischenablage kopieren", @@ -892,7 +961,7 @@ "connection_error": "Verbindung nicht möglich", "connection_failed": "Verbindungsaufbau fehlgeschlagen", "connection_lost": "Verbindung unterbrochen", - "copied_interface_to_clipboard": "Copied {language} interface type to clipboard", + "copied_interface_to_clipboard": "Den {language} Schnittstellentyp in die Zwischenablage kopiert", "copied_to_clipboard": "In die Zwischenablage kopiert", "deleted": "Gelöscht", "deprecated": "VERALTET", @@ -921,7 +990,9 @@ "subscribed_success": "Topic '{topic}' wurde erfolgreich abonniert.", "unsubscribed_failed": "Topic '{topic}' konnte nicht deabonniert werden.", "unsubscribed_success": "Topic '{topic}' wurde erfolgreich deabonniert.", - "waiting_send_request": "Warten auf Anfrage senden" + "waiting_send_request": "Warten auf Anfrage senden", + "loading_workspaces": "Arbeitsbereiche werden geladen", + "loading_collections_in_workspace": "Sammlungen im Arbeitsbereich werden geladen" }, "support": { "changelog": "Lese mehr über die neuesten Versionen", @@ -932,8 +1003,7 @@ "github": "Folge uns auf GitHub", "shortcuts": "Hoppscotch schneller bedienen", "title": "Hilfe", - "twitter": "Folge uns auf Twitter", - "team": "Nehme Kontakt mit dem Team auf" + "twitter": "Folge uns auf Twitter" }, "tab": { "authorization": "Autorisierung", @@ -1010,18 +1080,17 @@ "parent_coll_move": "Sammlung kann nicht in eine untergeordnete Sammlung verschoben werden", "pending_invites": "Ausstehende Einladungen", "permissions": "Berechtigungen", - "same_target_destination": "Same target and destination", + "same_target_destination": "Ziel und Zielort sind identisch", "saved": "Team gespeichert", "select_a_team": "Team auswählen", "success_invites": "Erfolgreiche Einladungen", "title": "Team", "we_sent_invite_link": "Einladungen wurden an alle E-Mails verschickt!", - "invite_sent_smtp_disabled": "Invite links generated", + "invite_sent_smtp_disabled": "Einladungslinks erstellt", "we_sent_invite_link_description": "Bitte alle eingeladenen Personen, ihren Posteingang zu überprüfen und auf den Link zu klicken, um dem Team beizutreten.", - "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", - "copy_invite_link": "Copy Invite Link", - "search_title": "Team Requests", - "join_beta": "Nimm am Beta-Programm teil, um auf Teams zuzugreifen." + "invite_sent_smtp_disabled_description": "Das Versenden von Einladungsemails ist für diese Instanz von Hoppscotch deaktiviert. Bitte verwende die Schaltfläche 'Link kopieren', um den Einladungslink manuell zu kopieren und zu teilen.", + "copy_invite_link": "Einladungslink kopieren", + "search_title": "Team-Anfragen" }, "team_environment": { "deleted": "Umgebung gelöscht", @@ -1048,57 +1117,62 @@ "workspace": { "change": "Arbeitsbereich wechseln", "personal": "Mein Arbeitsbereich", - "other_workspaces": "My Workspaces", + "other_workspaces": "Meine Arbeitsbereiche", "team": "Team Arbeitsbereich", "title": "Arbeitsbereiche" }, "site_protection": { - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", - "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + "login_to_continue": "Einloggen, um fortzufahren", + "login_to_continue_description": "Du musst eingeloggt sein, um auf diese Hoppscotch Enterprise-Instanz zuzugreifen.", + "error_fetching_site_protection_status": "Beim Abrufen des Status der Seiten­schutz­maßnahmen ist ein Fehler aufgetreten." }, "access_tokens": { "tab_title": "Tokens", - "section_title": "Personal Access Tokens", - "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", - "last_used_on": "Last used on", - "expires_on": "Expires on", - "no_expiration": "No expiration", - "expired": "Expired", - "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", - "token_purpose": "What's this token for?", - "expiration_label": "Expiration", - "scope_label": "Scope", - "workspace_read_only_access": "Read-only access to workspace data.", - "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", - "generate_token": "Generate Token", - "invalid_label": "Please provide a label for the token", - "no_expiration_verbose": "This token will never expire!", - "token_expires_on": "This token will expire on", - "generate_new_token": "Generate new token", - "generate_modal_title": "New Personal Access Token", - "deletion_success": "The access token {label} has been deleted" + "section_title": "Persönliche Zugangstoken", + "section_description": "Persönliche Zugangstoken helfen dir derzeit, das CLI mit deinem Hoppscotch-Konto zu verbinden", + "last_used_on": "Zuletzt verwendet am", + "expires_on": "Läuft ab am", + "no_expiration": "Kein Ablaufdatum", + "expired": "Abgelaufen", + "copy_token_warning": "Stelle sicher, dass du dein persönliches Zugangs-Token jetzt kopierst. Du wirst es nicht mehr sehen können!", + "token_purpose": "Wofür ist dieses Token?", + "expiration_label": "Ablaufdatum", + "scope_label": "Geltungsbereich", + "workspace_read_only_access": "Lesezugriff auf Arbeitsbereichsdaten.", + "personal_workspace_access_limitation": "Persönliche Zugangstoken können nicht auf deinen persönlichen Arbeitsbereich zugreifen.", + "generate_token": "Token generieren", + "invalid_label": "Bitte gib eine Bezeichnung für das Token an", + "no_expiration_verbose": "Dieses Token wird nie ablaufen!", + "token_expires_on": "Dieses Token verfällt am", + "generate_new_token": "Neues Token generieren", + "generate_modal_title": "Neues persönliches Zugangstoken", + "deletion_success": "Das persönliche Zugriffstoken {label} wurde gelöscht" }, "collection_runner": { - "collection_id": "Collection ID", - "environment_id": "Environment ID", - "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", - "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", - "include_active_environment": "Include active environment:", + "collection_id": "Sammlungs-ID", + "environment_id": "Umgebungs-ID", + "cli_collection_id_description": "Diese Sammlungs-ID wird vom CLI-Sammlungs-Runner für Hoppscotch verwendet.", + "cli_environment_id_description": "Diese Umgebungs-ID wird vom CLI-Sammlungs-Runner für Hoppscotch verwendet.", + "include_active_environment": "Aktive Umgebung einbeziehen:", "cli": "CLI", - "ui": "Runner (coming soon)", - "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", - "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", - "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "ui": "Runner (kommt bald)", + "cli_command_generation_description_cloud": "Kopiere den folgenden Befehl und führe ihn im CLI aus. Bitte gib ein persönliches Zugriffstoken an.", + "cli_command_generation_description_sh": "Kopiere den folgenden Befehl und führe ihn im CLI aus. Bitte gib ein persönliches Zugriffstoken an und überprüfe die generierte SH-Instanz-Server-URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Kopiere den folgenden Befehl und führe ihn im CLI aus. Bitte gib ein persönliches Zugriffstoken und die SH-Instanz-Server-URL an.", + "run_collection": "Sammlung ausführen" }, - "shortcodes": { - "actions": "Aktionen", - "created_on": "Erstellt am", - "deleted": "Shortcode gelöscht", - "method": "Methode", - "not_found": "Shortcode nicht gefunden", - "short_code": "Shortcode", - "url": "URL" + "ai_experiments": { + "generate_request_name": "Anfragenname mit KI generieren", + "generate_or_modify_request_body": "Anfragebody generieren oder ändern", + "modify_with_ai": "Mit KI ändern", + "generate": "Generieren", + "generate_or_modify_request_body_input_placeholder": "Geben Sie Ihren Prompt ein, um den Anfragebody zu ändern", + "accept_change": "Änderung akzeptieren", + "feedback_success": "Feedback erfolgreich eingereicht", + "feedback_failure": "Feedback konnte nicht eingereicht werden", + "feedback_thank_you": "Vielen Dank für Ihr Feedback!", + "feedback_cta_text_long": "Bewerten Sie die Generierung, hilft uns, uns zu verbessern", + "feedback_cta_request_name": "Hat Ihnen der generierte Name gefallen?", + "modify_request_body_error": "Fehler beim Ändern des Anfragebodys" } } diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index e4bd4437c..1b6383642 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -4,6 +4,9 @@ "autoscroll": "Autoscroll", "cancel": "Cancel", "choose_file": "Choose a file", + "choose_workspace": "Choose a workspace", + "choose_collection": "Choose a collection", + "select_workspace": "Select a workspace", "clear": "Clear", "clear_all": "Clear all", "clear_history": "Clear all History", @@ -17,6 +20,7 @@ "dismiss": "Dismiss", "dont_save": "Don't save", "download_file": "Download file", + "download_test_report": "Download test report", "drag_to_reorder": "Drag to reorder", "duplicate": "Duplicate", "edit": "Edit", @@ -36,10 +40,13 @@ "paste": "Paste", "prettify": "Prettify", "properties": "Properties", + "register": "Register", "remove": "Remove", "rename": "Rename", "restore": "Restore", + "retry": "Retry", "save": "Save", + "save_as_example": "Save as example", "scroll_to_bottom": "Scroll to bottom", "scroll_to_top": "Scroll to top", "search": "Search", @@ -55,12 +62,38 @@ "turn_off": "Turn off", "turn_on": "Turn on", "undo": "Undo", - "yes": "Yes" + "verify": "Verify", + "yes": "Yes", + "enable": "Enable", + "disable": "Disable" }, "add": { "new": "Add new", "star": "Add star" }, + "agent": { + "registration_instruction": "Please register Hoppscotch Agent with your web client to continue.", + "enter_otp_instruction": "Please enter the verification code generated by Hoppscotch Agent and complete the registration", + "otp_label": "Verification Code", + "processing": "Processing your request...", + "not_running": "The Hoppscotch Agent is not running. Please start the agent and click 'Retry'.", + "not_running_title": "Agent not detected", + "registration_title": "Agent registration", + "verify_ssl_certs": "Verify SSL Certificates", + "ca_certs": "CA Certificates", + "client_certs": "Client Certificates", + "use_http_proxy": "Use HTTP Proxy", + "proxy_capabilities": "Hoppscotch Agent supports HTTP/HTTPS/SOCKS proxies along with NTLM and Basic Auth in those proxies. Include the username and password for the proxy authentication in the URL itself.", + "add_cert_file": "Add Certificate File", + "add_client_cert": "Add Client Certificate", + "add_key_file": "Add Key File", + "domain": "Domain", + "cert": "Certificate", + "key": "Key", + "pfx_or_pkcs": "PFX/PKCS12", + "pfx_or_pkcs_file": "PFX/PKCS12 File", + "add_pfx_or_pkcs_file": "Add PFX/PKCS12 File" + }, "app": { "chat_with_us": "Chat with us", "contact_us": "Contact us", @@ -101,7 +134,8 @@ "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "What's new?", "see_whats_new": "See what’s new", - "wiki": "Wiki" + "wiki": "Wiki", + "default": "default: {value}" }, "auth": { "account_exists": "Account exists with different credential - Login to link both accounts", @@ -126,6 +160,7 @@ }, "authorization": { "generate_token": "Generate Token", + "refresh_token": "Refresh Token", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "include_in_url": "Include in URL", "inherited_from": "Inherited {auth} from parent collection {collection} ", @@ -148,6 +183,9 @@ "token_fetched_successfully": "Token fetched successfully", "token_fetch_failed": "Failed to fetch token", "validation_failed": "Validation Failed, please check the form fields", + "no_refresh_token_present": "No Refresh Token present. Please run the token generation flow again", + "refresh_token_request_failed": "Refresh token request failed", + "token_refreshed_successfully": "Token refreshed successfully", "label_authorization_endpoint": "Authorization Endpoint", "label_client_id": "Client ID", "label_client_secret": "Client Secret", @@ -170,9 +208,31 @@ "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", "type": "Authorization Type", - "username": "Username" + "username": "Username", + "advance_config": "Advanced Configuration", + "advance_config_description": "Hoppscotch automatically assigns default values to certain fields if no explicit value is provided", + "aws_signature": { + "access_key": "Access Key", + "secret_key": "Secret Key", + "service_name": "Service Name", + "aws_region": "AWS Region", + "service_token": "Service Token" + }, + "digest": { + "realm": "Realm", + "nonce": "Nonce", + "algorithm": "Algorithm", + "qop": "qop", + "nonce_count": "Nonce Count", + "client_nonce": "Client Nonce", + "opaque": "Opaque", + "disable_retry": "Disable Retrying Request", + "inspector_warning": "Agent interceptor is recommended when using Digest Authorization." + } }, "collection": { + "title": "Collection", + "run": "Run Collection", "created": "Collection created", "different_parent": "Cannot reorder collection with different parent", "edit": "Edit Collection", @@ -207,6 +267,7 @@ "remove_folder": "Are you sure you want to permanently delete this folder?", "remove_history": "Are you sure you want to permanently delete all history?", "remove_request": "Are you sure you want to permanently delete this request?", + "remove_response": "Are you sure you want to permanently delete this response?", "remove_shared_request": "Are you sure you want to permanently delete this shared request?", "remove_team": "Are you sure you want to delete this workspace?", "remove_telemetry": "Are you sure you want to opt-out of Telemetry?", @@ -242,6 +303,8 @@ "header": "Header {count}", "message": "Message {count}", "parameter": "Parameter {count}", + "key": "Key {count}", + "description": "Description {count}", "protocol": "Protocol {count}", "value": "Value {count}", "variable": "Variable {count}" @@ -276,9 +339,11 @@ "team_name": "Workspace name empty", "teams": "You don't belong to any workspaces", "tests": "There are no tests for this request", - "access_tokens": "Access tokens are empty" + "access_tokens": "Access tokens are empty", + "response": "No response received" }, "environment": { + "heading": "Environment", "add_to_global": "Add to Global", "added": "Environment addition", "create_new": "Create new environment", @@ -307,6 +372,7 @@ "select": "Select environment", "set": "Set environment", "set_as_environment": "Set as environment", + "short_name": "Environment needs to have minimum 3 characters", "team_environments": "Workspace Environments", "title": "Environments", "updated": "Environment updated", @@ -332,6 +398,7 @@ "gql_prettify_invalid_query": "Couldn't prettify an invalid query, solve query syntax errors and try again", "incomplete_config_urls": "Incomplete configuration URLs", "incorrect_email": "Incorrect email", + "invalid_file_type": "Invalid file type for `{filename}`.", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", "invalid_embed_link": "The embed does not exist or is invalid.", @@ -365,6 +432,14 @@ "title": "Export", "success": "Successfully exported" }, + "filename": { + "cookie_key_value_pairs": "Cookie", + "codegen": "{request_name} - code", + "graphql_response": "GraphQL-Response", + "lens": "{request_name} - response", + "realtime_response": "Realtime-Response", + "response_interface": "Response-Interface" + }, "filter": { "all": "All", "none": "None", @@ -376,10 +451,12 @@ "invalid_name": "Please provide a name for the folder", "name_length_insufficient": "Folder name should be at least 3 characters long", "new": "New Folder", + "run": "Run Folder", "renamed": "Folder renamed" }, "graphql": { "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", + "connection_error_http": "Failed to fetch GraphQL Schema due to network error.", "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", "mutations": "Mutations", @@ -429,18 +506,28 @@ "from_file": "Import from File", "from_gist": "Import from Gist", "from_gist_description": "Import from Gist URL", + "from_gist_import_summary": "All hoppscotch features are imported.", + "from_hoppscotch_importer_summary": "All hoppscotch features are imported.", "from_insomnia": "Import from Insomnia", "from_insomnia_description": "Import from Insomnia collection", + "from_insomnia_import_summary": "Collections and Requests will be imported.", "from_json": "Import from Hoppscotch", "from_json_description": "Import from Hoppscotch collection file", "from_my_collections": "Import from Personal Collections", "from_my_collections_description": "Import from Personal Collections file", + "from_all_collections": "Import from Another Workspace", + "from_all_collections_description": "Import any collection from Another Workspace to the current workspace", "from_openapi": "Import from OpenAPI", "from_openapi_description": "Import from OpenAPI specification file (YML/JSON)", + "from_openapi_import_summary": "Collections ( will be created from tags ), Requests and response examples will be imported.", "from_postman": "Import from Postman", "from_postman_description": "Import from Postman collection", + "from_postman_import_summary": "Collections, Requests and response examples will be imported.", "from_url": "Import from URL", "gist_url": "Enter Gist URL", + "from_har": "Import from HAR", + "from_har_description": "Import from HAR file", + "from_har_import_summary": "Requests will be imported to a default collection.", "gql_collections_from_gist_description": "Import GraphQL Collections From Gist", "hoppscotch_environment": "Hoppscotch Environment", "hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", @@ -453,9 +540,15 @@ "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", "title": "Import", - "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", - "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", - "success": "Successfully imported" + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of {sizeLimit}MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of {sizeLimit}MB. Please select another file.", + "success": "Successfully imported", + "import_summary_collections_title": "Collections", + "import_summary_requests_title": "Requests", + "import_summary_responses_title": "Responses", + "import_summary_pre_request_scripts_title": "Pre-request scripts", + "import_summary_test_scripts_title": "Test scripts", + "import_summary_not_supported_by_hoppscotch_import": "We do not support importing {featureLabel} from this source right now." }, "inspections": { "description": "Inspect possible errors", @@ -481,6 +574,9 @@ "extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", "extention_enable_action": "Enable Browser Extension", "extention_not_enabled": "Extension not enabled." + }, + "requestBody": { + "active_interceptor_doesnt_support_binary_body": "Sending binary data via the current interceptor is not supported yet." } }, "layout": { @@ -496,7 +592,9 @@ "confirm": "Confirm", "customize_request": "Customize Request", "edit_request": "Edit Request", + "edit_response": "Edit Response", "import_export": "Import / Export", + "response_name": "Response Name", "share_request": "Share Request" }, "mqtt": { @@ -563,6 +661,7 @@ }, "request": { "added": "Request added", + "add": "Add Request", "authorization": "Authorization", "body": "Request Body", "choose_language": "Choose language", @@ -570,8 +669,10 @@ "content_type_titles": { "others": "Others", "structured": "Structured", - "text": "Text" + "text": "Text", + "binary": "Binary" }, + "show_content_type": "Show Content Type", "different_collection": "Cannot reorder requests from different collections", "duplicated": "Request duplicated", "duration": "Duration", @@ -599,6 +700,7 @@ "rename": "Rename Request", "renamed": "Request renamed", "request_variables": "Request variables", + "response_name_exists": "Response name already exists", "run": "Run", "save": "Save", "save_as": "Save as", @@ -618,21 +720,29 @@ "response": { "audio": "Audio", "body": "Response Body", + "duplicated": "Response duplicated", + "duplicate_name_error": "Same name response already exists", "filter_response_body": "Filter JSON response body (uses JSONPath syntax)", "headers": "Headers", "html": "HTML", "image": "Image", "json": "JSON", "pdf": "PDF", + "please_save_request": "Save the request to create example", "preview_html": "Preview HTML", "raw": "Raw", + "renamed": "Response renamed", "size": "Size", "status": "Status", "time": "Time", "title": "Response", "video": "Video", "waiting_for_connection": "waiting for connection", - "xml": "XML" + "xml": "XML", + "generate_data_schema": "Generate Data Schema", + "data_schema": "Data Schema", + "saved": "Response saved", + "invalid_name": "Please provide a name for the response" }, "settings": { "accent_color": "Accent color", @@ -642,12 +752,16 @@ "account_email_description": "Your primary email address.", "account_name_description": "This is your display name.", "additional": "Additional Settings", + "auto_encode_mode": "Auto", + "auto_encode_mode_tooltip": "Encode the parameters in the request only if some special characters are present", "background": "Background", "black_mode": "Black", "choose_language": "Choose language", "dark_mode": "Dark", "delete_account": "Delete account", "delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.", + "disable_encode_mode_tooltip": "Never encode the parameters in the request", + "enable_encode_mode_tooltip": "Always encode the parameters in the request", "expand_navigation": "Expand navigation", "experiments": "Experiments", "experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ", @@ -656,11 +770,15 @@ "extensions": "Browser extension", "extensions_use_toggle": "Use the browser extension to send requests (if present)", "follow": "Follow us", + "general": "General", + "general_description": " General settings used in the application", "interceptor": "Interceptor", "interceptor_description": "Middleware between application and APIs.", "language": "Language", "light_mode": "Light", "official_proxy_hosting": "Official Proxy is hosted by Hoppscotch.", + "query_parameters_encoding": "Query Parameters Encoding", + "query_parameters_encoding_description": "Configure encoding for query parameters in requests", "profile": "Profile", "profile_description": "Update your profile details", "profile_email": "Email address", @@ -918,7 +1036,9 @@ "subscribed_success": "Successfully subscribed to topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_success": "Successfully unsubscribed from topic: {topic}", - "waiting_send_request": "Waiting to send request" + "waiting_send_request": "Waiting to send request", + "loading_workspaces": "Loading workspaces", + "loading_collections_in_workspace": "Loading collections in workspace" }, "support": { "changelog": "Read more about latest releases", @@ -957,10 +1077,13 @@ "tests": "Tests", "types": "Types", "variables": "Variables", - "websocket": "WebSocket" + "websocket": "WebSocket", + "all_tests": "All Tests", + "passed": "Passed", + "failed": "Failed" }, "team": { - "already_member": "You are already a member of this workspace. Contact your workspace owner.", + "already_member": "This email is associated with an existing user.", "create_new": "Create new workspace", "deleted": "Workspace deleted", "edit": "Edit Workspace", @@ -987,7 +1110,7 @@ "login_to_continue": "Login to continue", "login_to_continue_description": "You need to be logged in to join a workspace.", "logout_and_try_again": "Logout and sign in with another account", - "member_has_invite": "This email ID already has an invite. Contact your workspace owner.", + "member_has_invite": "User already has an invite. Please ask them to check their inbox or revoke and resend the invite.", "member_not_found": "Member not found. Contact your workspace owner.", "member_removed": "User removed", "member_role_updated": "User roles updated", @@ -1011,12 +1134,13 @@ "select_a_team": "Select a workspace", "success_invites": "Success invites", "title": "Workspaces", - "we_sent_invite_link": "We sent an invite link to all invitees!", + "we_sent_invite_link": "Invitations are on the way", "invite_sent_smtp_disabled": "Invite links generated", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the workspace.", + "we_sent_invite_link_description": " New invitees will receive a link to join the workspace, existing members and pending invitees won't receive a new link.", "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", "copy_invite_link": "Copy Invite Link", - "search_title": "Team Requests" + "search_title": "Team Requests", + "user_not_found": "User not found in the instance." }, "team_environment": { "deleted": "Environment Deleted", @@ -1024,6 +1148,8 @@ "not_found": "Environment not found." }, "test": { + "requests": "Requests", + "selection": "Selection", "failed": "test failed", "javascript_code": "JavaScript Code", "learn": "Read documentation", @@ -1031,7 +1157,14 @@ "report": "Test Report", "results": "Test Results", "script": "Script", - "snippets": "Snippets" + "snippets": "Snippets", + "run": "Run", + "run_again": "Run again", + "stop": "Stop", + "new_run": "New Run", + "iterations": "Iterations", + "duration": "Duration", + "avg_resp": "Avg. Response Time" }, "websocket": { "communication": "Communication", @@ -1081,14 +1214,37 @@ "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", "include_active_environment": "Include active environment:", "cli": "CLI", - "ui": "Runner (coming soon)", + "delay": "Delay", + "negative_delay": "Delay cannot be negative", + "ui": "Runner", + "running_collection": "Running collection", + "run_config": "Run Configuration", + "advanced_settings": "Advanced Settings", + "stop_on_error": "Stop run if an error occurs", + "persist_responses": "Persist responses", + "collection_not_found": "Collection not found. May be deleted or moved.", + "empty_collection": "Collection is empty. Add requests to run.", + "no_response_persist": "The collection runner is presently configured not to persist responses. This setting prevents showing the response data. To modify this behavior, initiate a new run configuration.", + "select_request": "Select a request to see response and test results", "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "run_collection": "Run collection", + "no_passed_tests": "No tests passed", + "no_failed_tests": "No tests failed" }, "ai_experiments": { "generate_request_name": "Generate Request Name Using AI", - "generate_or_modify_request_body": "Generate or Modify Request Body" + "generate_or_modify_request_body": "Generate or Modify Request Body", + "modify_with_ai": "Modify with AI", + "generate": "Generate", + "generate_or_modify_request_body_input_placeholder": "Enter your prompt to modify request body", + "accept_change": "Accept Change", + "feedback_success": "Feedback submitted successfully", + "feedback_failure": "Failed to submit feedback", + "feedback_thank_you": "Thank you for your feedback!", + "feedback_cta_text_long": "Rate the generation, helps us to improve", + "feedback_cta_request_name": "Did you like the generated name?", + "modify_request_body_error": "Failed to modify request body" } } diff --git a/packages/hoppscotch-common/locales/es.json b/packages/hoppscotch-common/locales/es.json index 48828ca74..b5351785b 100644 --- a/packages/hoppscotch-common/locales/es.json +++ b/packages/hoppscotch-common/locales/es.json @@ -1,17 +1,17 @@ { "action": { - "add": "Add", + "add": "Añadir", "autoscroll": "Desplazamiento automático", "cancel": "Cancelar", "choose_file": "Seleccionar archivo", "clear": "Limpiar", "clear_all": "Limpiar todo", - "clear_history": "Clear all History", + "clear_history": "Borrar todo el historial", "close": "Cerrar", "connect": "Conectar", "connecting": "Conectando", "copy": "Copiar", - "create": "Create", + "create": "Crear", "delete": "Borrar", "disconnect": "Desconectar", "dismiss": "Descartar", @@ -24,10 +24,10 @@ "go_back": "Volver", "go_forward": "Adelante", "group_by": "Agrupar por", - "hide_secret": "Hide secret", + "hide_secret": "Ocultar secreto", "label": "Etiqueta", "learn_more": "Aprender más", - "download_here": "Download here", + "download_here": "Descargar aquí", "less": "Menos", "more": "Más", "new": "Nuevo", @@ -44,8 +44,8 @@ "scroll_to_top": "Desplazar hacia arriba", "search": "Buscar", "send": "Enviar", - "share": "Share", - "show_secret": "Show secret", + "share": "Compartir", + "show_secret": "Mostrar secreto", "start": "Comenzar", "starting": "Iniciando", "stop": "Detener", @@ -100,16 +100,16 @@ "we_use_cookies": "Usamos cookies", "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "¿Qué hay de nuevo?", - "see_whats_new": "See what’s new", + "see_whats_new": "Novedades", "wiki": "Wiki" }, "auth": { "account_exists": "La cuenta existe con una credencial diferente - Inicia sesión para vincular ambas cuentas", "all_sign_in_options": "Todas las opciones de inicio de sesión", - "continue_with_auth_provider": "Continue with {provider}", + "continue_with_auth_provider": "Continuar con {provider}", "continue_with_email": "Continuar con correo electrónico", "continue_with_github": "Continuar con GitHub", - "continue_with_github_enterprise": "Continue with GitHub Enterprise", + "continue_with_github_enterprise": "Continuar con GitHub Enterprise", "continue_with_google": "Continuar con Google", "continue_with_microsoft": "Continuar con Microsoft", "email": "Correo electrónico", @@ -142,30 +142,30 @@ "redirect_no_token_endpoint": "No se ha definido ningún punto final de token", "something_went_wrong_on_oauth_redirect": "Algo ha ido mal durante la redirección OAuth", "something_went_wrong_on_token_generation": "Algo salió mal en la generación del token", - "token_generation_oidc_discovery_failed": "Fallo en la generación del token: OpenID Connect Discovery Failed", - "grant_type": "Grant Type", - "grant_type_auth_code": "Authorization Code", - "token_fetched_successfully": "Token fetched successfully", - "token_fetch_failed": "Failed to fetch token", - "validation_failed": "Validation Failed, please check the form fields", - "label_authorization_endpoint": "Authorization Endpoint", - "label_client_id": "Client ID", - "label_client_secret": "Client Secret", - "label_code_challenge": "Code Challenge", - "label_code_challenge_method": "Code Challenge Method", - "label_code_verifier": "Code Verifier", - "label_scopes": "Scopes", - "label_token_endpoint": "Token Endpoint", - "label_use_pkce": "Use PKCE", - "label_implicit": "Implicit", - "label_password": "Password", - "label_username": "Username", - "label_auth_code": "Authorization Code", - "label_client_credentials": "Client Credentials" + "token_generation_oidc_discovery_failed": "Fallo en la generación del token: Error en el descubrimiento de OpenID Connect", + "grant_type": "Tipo de autorización", + "grant_type_auth_code": "Código de autorización", + "token_fetched_successfully": "Token recuperado correctamente", + "token_fetch_failed": "Fallo al recuperar el token", + "validation_failed": "Fallo de validación, comprueba los campos del formulario", + "label_authorization_endpoint": "Punto final de autorización", + "label_client_id": "ID de cliente", + "label_client_secret": "Secreto de cliente", + "label_code_challenge": "Código de desafío", + "label_code_challenge_method": "Método código de desafío", + "label_code_verifier": "Verificador de código", + "label_scopes": "Ámbitos", + "label_token_endpoint": "Punto final de token", + "label_use_pkce": "Utilizar PKCE", + "label_implicit": "Implícito", + "label_password": "Contraseña", + "label_username": "Nombre de usuario", + "label_auth_code": "Código de autorización", + "label_client_credentials": "Credenciales del cliente" }, "pass_key_by": "Pasar por", - "pass_by_query_params_label": "Query Parameters", - "pass_by_headers_label": "Headers", + "pass_by_query_params_label": "Parámetros de consulta", + "pass_by_headers_label": "Cabeceras", "password": "Contraseña", "save_to_inherit": "Por favor, guarda esta solicitud en cualquier colección para heredar la autorización", "token": "Token", @@ -177,8 +177,8 @@ "different_parent": "No se puede reordenar la colección con un padre diferente", "edit": "Editar colección", "import_or_create": "Importar o crear una colección", - "import_collection": "Import Collection", - "invalid_name": "Proporciona un nombre válido para la colección.", + "import_collection": "Importar colección", + "invalid_name": "El nombre para la colección no es válido", "invalid_root_move": "La colección ya está en la raíz", "moved": "Movido con éxito", "my_collections": "Mis colecciones", @@ -194,7 +194,7 @@ "save_to_collection": "Guardar en la colección", "select": "Seleccionar colección", "select_location": "Seleccionar ubicación", - "details": "Details", + "details": "Detalles", "select_team": "Seleccionar equipo", "team_collections": "Colecciones de equipos" }, @@ -214,7 +214,7 @@ "request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.", "save_unsaved_tab": "¿Deseas guardar los cambios realizados en esta pestaña?", "sync": "¿Estás seguro de que deseas sincronizar este espacio de trabajo?", - "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" + "delete_access_token": "¿Estás seguro de que deseas eliminar el token de acceso {tokenLabel}?" }, "context_menu": { "add_parameters": "Añadir a parámetros", @@ -252,11 +252,11 @@ "generate_message": "Importar cualquier colección de Hoppscotch para generar documentación de la API sobre la marcha." }, "empty": { - "authorization": "Esta solicitud no utiliza ninguna autorización.", + "authorization": "Esta solicitud no utiliza ninguna autorización", "body": "Esta solicitud no tiene cuerpo", "collection": "Colección vacía", "collections": "No hay colecciones", - "documentation": "Conectarse a un punto final de GraphQL para ver la documentación", + "documentation": "Es necesario conectarse a un punto final de GraphQL para ver la documentación", "endpoint": "El punto final no puede estar vacío", "environments": "No hay entornos", "folder": "Carpeta vacía", @@ -268,16 +268,16 @@ "pending_invites": "No hay invitaciones pendientes para este equipo", "profile": "Iniciar sesión para ver tu perfil", "protocols": "No hay protocolos", - "request_variables": "This request does not have any request variables", + "request_variables": "Esta solicitud no tiene variables de solicitud", "schema": "Conectarse a un punto final de GraphQL", - "secret_environments": "Secrets are not synced to Hoppscotch", + "secret_environments": "Los secretos no están sincronizados con Hoppscotch", "shared_requests": "No hay solicitudes compartidas", "shared_requests_logout": "Iniciar sesión para ver sus solicitudes compartidas o crear una nueva", "subscription": "No hay suscripciones", "team_name": "Nombre del equipo vacío", "teams": "No hay equipos", "tests": "No hay pruebas para esta solicitud", - "access_tokens": "Access tokens are empty", + "access_tokens": "No hay tokens de acceso disponibles", "shortcodes": "Aún no se han creado Shortcodes" }, "environment": { @@ -304,8 +304,8 @@ "quick_peek": "Vistazo rápido al entorno", "replace_with_variable": "Sustituir por variable", "scope": "Ámbito", - "secrets": "Secrets", - "secret_value": "Secret value", + "secrets": "Secretos", + "secret_value": "Valor de secreto", "select": "Seleccionar entorno", "set": "Establecer entorno", "set_as_environment": "Establecer como entorno", @@ -316,8 +316,8 @@ "variable": "Variable", "variables": "Variables", "variable_list": "Lista de variables", - "properties": "Environment Properties", - "details": "Details" + "properties": "Propiedades del entorno", + "details": "Detalles" }, "error": { "authproviders_load_error": "No se han podido cargar los proveedores de autenticación", @@ -328,7 +328,7 @@ "danger_zone": "Zona de peligro", "delete_account": "Tu cuenta es actualmente propietaria en estos equipos:", "delete_account_description": "Para poder eliminar tu cuenta, debes darte de baja, transferir la propiedad o eliminar estos equipos.", - "empty_profile_name": "Profile name cannot be empty", + "empty_profile_name": "El nombre del perfil no puede estar vacío", "empty_req_name": "Nombre de solicitud vacío", "f12_details": "(F12 para más detalles)", "gql_prettify_invalid_query": "No se puede aplicar embellecedor a una consulta no válida, resuelve los errores de sintaxis de la consulta y vuelve a intentarlo", @@ -336,36 +336,36 @@ "incorrect_email": "Correo electrónico incorrecto", "invalid_link": "Enlace no válido", "invalid_link_description": "El enlace que has pulsado no es válido o ha caducado.", - "invalid_embed_link": "The embed does not exist or is invalid.", + "invalid_embed_link": "La inserción no existe o no es válida.", "json_parsing_failed": "JSON no válido", "json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo", "network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.", "network_fail": "No se pudo enviar la solicitud", - "no_collections_to_export": "No hay colecciones para exportar. Crea una colección para empezar.", + "no_collections_to_export": "No hay colecciones para exportar. Por favor, crea una colección para empezar.", "no_duration": "Sin duración", "no_environments_to_export": "No hay entornos para exportar. Por favor, crea un entorno para empezar.", "no_results_found": "No se han encontrado coincidencias", "page_not_found": "No se ha podido encontrar esta página", - "please_install_extension": "Please install the extension and add origin to the extension.", + "please_install_extension": "Por favor, instala la extensión y añade el origen a la extensión.", "proxy_error": "Error de proxy", - "same_profile_name": "Updated profile name is same as the current profile name", + "same_profile_name": "El nombre del perfil actualizado es el mismo que el nombre del perfil actual", "script_fail": "No se pudo ejecutar el script de solicitud previa", "something_went_wrong": "Algo salió mal", "test_script_fail": "No se ha podido ejecutar la secuencia de comandos posterior a la solicitud", - "reading_files": "Error while reading one or more files.", - "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", - "generate_access_token": "Something went wrong while generating the access token", - "delete_access_token": "Something went wrong while deleting the access token" + "reading_files": "Error al leer uno o más archivos.", + "fetching_access_tokens_list": "Algo ha ido mal al obtener la lista de tokens", + "generate_access_token": "Algo ha ido mal al generar el token de acceso", + "delete_access_token": "Algo ha ido mal al borrar el token de acceso" }, "export": { "as_json": "Exportar como JSON", "create_secret_gist": "Crear un Gist secreto", - "create_secret_gist_tooltip_text": "Export as secret Gist", + "create_secret_gist_tooltip_text": "Exportar como Gist secreto", "failed": "Algo ha ido mal al exportar", - "secret_gist_success": "Successfully exported as secret Gist", + "secret_gist_success": "Exportado con éxito como Gist secreto", "require_github": "Iniciar sesión con GitHub para crear un Gist secreto", "title": "Exportar", - "success": "Successfully exported", + "success": "Exportado con éxito", "gist_created": "Gist creado" }, "filter": { @@ -382,14 +382,14 @@ "renamed": "Carpeta renombrada" }, "graphql": { - "connection_switch_confirm": "¿Deseas conectarte con el endpoint GraphQL más reciente?", + "connection_switch_confirm": "¿Deseas conectarte con el punto final de GraphQL más reciente?", "connection_switch_new_url": "Al cambiar a una pestaña se desconectará de la conexión GraphQL activa. La nueva URL de conexión es", - "connection_switch_url": "Estás conectado a un endpoint GraphQL cuya URL de conexión es", + "connection_switch_url": "Estás conectado a un punto final de GraphQL cuya URL de conexión es", "mutations": "Mutaciones", "schema": "Esquema", "subscriptions": "Suscripciones", "switch_connection": "Cambiar conexión", - "url_placeholder": "Enter a GraphQL endpoint URL" + "url_placeholder": "Introduce una URL de punto final de GraphQL" }, "graphql_collections": { "title": "Colecciones de GraphQL" @@ -409,7 +409,7 @@ "collection_properties_header": "Este encabezado se establecerá para cada solicitud de esta colección.", "generate_documentation_first": "Generar la documentación primero", "network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.", - "offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.", + "offline": "Pareces estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.", "offline_short": "Pareces estar desconectado.", "post_request_tests": "Los scripts de prueba están escritos en JavaScript y se ejecutan después de recibir la respuesta.", "pre_request_script": "Los scripts previos a la solicitud están escritos en JavaScript y se ejecutan antes de que se envíe la solicitud.", @@ -426,12 +426,12 @@ "import": { "collections": "Importar colecciones", "curl": "Importar cURL", - "environments_from_gist": "Importar desde Gist", - "environments_from_gist_description": "Importar entornos Hoppscotch desde Gist", + "environments_from_gist": "Importar desde GitHub Gist", + "environments_from_gist_description": "Importar entornos Hoppscotch desde GitHub Gist", "failed": "Importación fallida", "from_file": "Importar desde archivo", - "from_gist": "Importar desde Gist", - "from_gist_description": "Importar desde URL de Gist", + "from_gist": "Importar desde GitHub Gist", + "from_gist_description": "Importar desde URL de GitHub Gist", "from_insomnia": "Importar desde Insomnia", "from_insomnia_description": "Importar desde una colección de Insomnia", "from_json": "Importar de Hoppscotch", @@ -443,8 +443,8 @@ "from_postman": "Importar desde Postman", "from_postman_description": "Importar desde una colección de Postman", "from_url": "Importar desde una URL", - "gist_url": "Introduce la URL de Gist", - "gql_collections_from_gist_description": "Importar colecciones GraphQL desde Gist", + "gist_url": "Introduce la URL del GitHub Gist", + "gql_collections_from_gist_description": "Importar colecciones GraphQL desde GitHub Gist", "hoppscotch_environment": "Entorno de Hoppscotch", "hoppscotch_environment_description": "Importar archivo JSON del entorno de Hoppscotch", "import_from_url_invalid_fetch": "No se han podido obtener datos de la url", @@ -456,16 +456,16 @@ "postman_environment": "Entorno de Postman", "postman_environment_description": "Importar entorno de Postman desde un archivo JSON", "title": "Importar", - "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", - "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", - "success": "Successfully imported" + "file_size_limit_exceeded_warning_multiple_files": "Los archivos seleccionados exceden el límite recomendado de 10MB. Sólo se importarán los primeros {files} seleccionados.", + "file_size_limit_exceeded_warning_single_file": "El archivo seleccionado supera el límite recomendado de 10 MB. Por favor, selecciona otro archivo.", + "success": "Importado con éxito" }, "inspections": { "description": "Inspeccionar posibles errores", "environment": { "add_environment": "Añadir al Entorno", "add_environment_value": "Add value", - "empty_value": "Environment value is empty for the variable '{variable}' ", + "empty_value": "El valor de la variable de entorno '{variable}' está vacío ", "not_found": "No se ha encontrado la variable de entorno \"{environment}\"." }, "header": { @@ -513,7 +513,7 @@ "connection_config": "Configuración de conexión", "connection_not_authorized": "Esta conexión MQTT no utiliza ninguna autenticación.", "invalid_topic": "Indica un tema para la suscripción", - "keep_alive": "Mantenerse vivo", + "keep_alive": "Mantenerse activo", "log": "Registro", "lw_message": "Mensaje de última voluntad", "lw_qos": "QoS de última voluntad", @@ -582,7 +582,7 @@ "generate_code": "Generar código", "generated_code": "Código generado", "go_to_authorization_tab": "Ir a la pestaña Autorización", - "go_to_body_tab": "Ir a la pestaña de cuerpo", + "go_to_body_tab": "Ir a la pestaña de cuerpo de solicitud", "header_list": "Lista de encabezados", "invalid_name": "Proporciona un nombre para la solicitud.", "method": "Método", @@ -601,7 +601,7 @@ "raw_body": "cuerpo sin procesar", "rename": "Renombrar solicitud", "renamed": "Solicitud renombrada", - "request_variables": "Request variables", + "request_variables": "Variables de solicitud", "run": "Ejecutar", "save": "Guardar", "save_as": "Guardar como", @@ -613,7 +613,7 @@ "title": "Solicitud", "type": "Tipo de solicitud", "url": "URL", - "url_placeholder": "Enter a URL or paste a cURL command", + "url_placeholder": "Introduce una URL o pega un comando cURL", "variables": "Variables", "view_my_links": "Ver mis enlaces", "copy_link": "Copiar enlace" @@ -709,7 +709,7 @@ "not_found": "Solicitud compartida no encontrada", "open_new_tab": "Abrir en una nueva pestaña", "preview": "Vista previa", - "run_in_hoppscotch": "Correr en Hoppscotch", + "run_in_hoppscotch": "Ejecutar en Hoppscotch", "theme": { "dark": "Oscuro", "light": "Claro", @@ -862,19 +862,19 @@ "title": "Pestañas" }, "workspace": { - "delete": "Borrar el equipo actual", - "edit": "Editar el equipo actual", - "invite": "Invitar al equipo", - "new": "Crear un nuevo equipo", + "delete": "Borrar el espacio de trabajo actual", + "edit": "Editar el espacio de trabajo actual", + "invite": "Invitar al espacio de trabajo", + "new": "Crear un nuevo espacio de trabajo", "switch_to_personal": "Cambia a tu espacio de trabajo personal", - "title": "Equipos" + "title": "Espacio de trabajo" }, "phrases": { - "try": "Try", - "import_collections": "Import collections", - "create_environment": "Create environment", - "create_workspace": "Create workspace", - "share_request": "Share request" + "try": "Probar", + "import_collections": "Importar colecciones", + "create_environment": "Crear entorno", + "create_workspace": "Crear espacio de trabajo", + "share_request": "Compartir solicitud" } }, "sse": { @@ -964,64 +964,64 @@ "websocket": "WebSocket" }, "team": { - "already_member": "Ya eres miembro de este equipo. Ponte en contacto con el propietario de tu equipo.", - "create_new": "Crear nuevo equipo", - "deleted": "Equipo eliminado", - "edit": "Editar equipo", + "already_member": "Ya eres miembro de este espacio de trabajo. Ponte en contacto con el propietario del espacio de trabajo.", + "create_new": "Crear nuevo espacio de trabajo", + "deleted": "Espacio de trabajo eliminado", + "edit": "Editar espacio de trabajo", "email": "Correo electrónico", - "email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.", - "exit": "Salir del equipo", - "exit_disabled": "Solo el propietario puede salir del equipo", + "email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario del espacio de trabajo.", + "exit": "Salir del espacio de trabajo", + "exit_disabled": "Sólo el propietario no puede salir del espacio de trabajo", "failed_invites": "Invitaciones fallidas", "invalid_coll_id": "Identificador de colección no válido", "invalid_email_format": "El formato de correo electrónico no es válido", - "invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.", + "invalid_id": "Identificador de espacio de trabajo inválido. Ponte en contacto con el propietario del espacio de trabajo.", "invalid_invite_link": "Enlace de invitación inválido", - "invalid_invite_link_description": "El enlace que has seguido no es válido. Ponte en contacto con el propietario de tu equipo.", - "invalid_member_permission": "Proporcionar un permiso válido al miembro del equipo", + "invalid_invite_link_description": "El enlace que has seguido no es válido. Ponte en contacto con el propietario del espacio de trabajo.", + "invalid_member_permission": "Por favor, proporciona un permiso válido al miembro del espacio de trabajo", "invite": "Invitar", "invite_more": "Invitar a más", - "invite_tooltip": "Invite a personas a este espacio de trabajo", - "invited_to_team": "{owner} te ha invitado al equipo {team}", + "invite_tooltip": "Invitar a personas a este espacio de trabajo", + "invited_to_team": "{owner} te ha invitado al espacio de trabajo {team}", "join": "Invitación aceptada", - "join_team": "Entrar a {team}", - "joined_team": "Haz entrado a {team}", - "joined_team_description": "Ahora eres miembro de este equipo", - "left": "Saliste del equipo", + "join_team": "Entrar al espacio de trabajo {team}", + "joined_team": "Haz entrado al espacio de trabajo {team}", + "joined_team_description": "Ahora eres miembro de este espacio de trabajo", + "left": "Saliste del espacio de trabajo", "login_to_continue": "Iniciar sesión para continuar", - "login_to_continue_description": "Tienes que estar conectado para unirte a un equipo.", + "login_to_continue_description": "Tienes que estar conectado para unirte a un espacio de trabajo.", "logout_and_try_again": "Cerrar la sesión e iniciar sesión con otra cuenta", - "member_has_invite": "Este Identificador de correo electrónico ya tiene una invitación. Ponte en contacto con el propietario de tu equipo.", - "member_not_found": "Miembro no encontrado. Ponte en contacto con el propietario de tu equipo.", - "member_removed": "Usuario eliminado", + "member_has_invite": "Este identificador de correo electrónico ya tiene una invitación. Ponte en contacto con el propietario del espacio de trabajo.", + "member_not_found": "Miembro no encontrado. Ponte en contacto con el propietario del espacio de trabajo.", + "member_removed": "Miembro eliminado", "member_role_updated": "Funciones de usuario actualizadas", "members": "Miembros", "more_members": "+{count} más", - "name_length_insufficient": "El nombre del equipo debe tener al menos 6 caracteres", - "name_updated": "Nombre de equipo actualizado", - "new": "Nuevo equipo", - "new_created": "Nuevo equipo creado", - "new_name": "Mi nuevo equipo", + "name_length_insufficient": "El nombre del espacio de trabajo debe tener al menos 6 caracteres", + "name_updated": "Nombre de espacio de trabajo actualizado", + "new": "Nuevo espacio de trabajo", + "new_created": "Nuevo espacio de trabajo creado", + "new_name": "Mi nuevo espacio de trabajo", "no_access": "No tienes acceso de edición a estas colecciones.", - "no_invite_found": "No se ha encontrado la invitación. Ponte en contacto con el propietario de tu equipo.", + "no_invite_found": "No se ha encontrado la invitación. Ponte en contacto con el propietario del espacio de trabajo.", "no_request_found": "Solicitud no encontrada.", - "not_found": "Equipo no encontrado. Ponte en contacto con el propietario de tu equipo.", - "not_valid_viewer": "No eres un espectador válido. Ponte en contacto con el propietario de tu equipo.", + "not_found": "Espacio de trabajo no encontrado. Ponte en contacto con el propietario del espacio de trabajo.", + "not_valid_viewer": "No eres un espectador válido. Ponte en contacto con el propietario del espacio de trabajo.", "parent_coll_move": "No se puede mover la colección a una colección hija", "pending_invites": "Invitaciones pendientes", "permissions": "Permisos", "same_target_destination": "Mismo objetivo y destino", - "saved": "Equipo guardado", - "select_a_team": "Seleccionar un equipo", - "success_invites": "Invitaciones con éxito", - "title": "Equipos", + "saved": "Espacio de trabajo guardado", + "select_a_team": "Seleccionar un espacio de trabajo", + "success_invites": "Invitaciones realizadas con éxito", + "title": "Espacios de trabajo", "we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!", "invite_sent_smtp_disabled": "Invite links generated", - "we_sent_invite_link_description": "Pide a todos los invitados que revisen su bandeja de entrada. Tienen que hacer clic en el enlace para unirse al equipo.", - "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", - "copy_invite_link": "Copy Invite Link", - "search_title": "Team Requests", - "join_beta": "Únete al programa beta para acceder a los equipos." + "we_sent_invite_link_description": "Pide a todos los invitados que revisen su bandeja de entrada. Tienen que hacer clic en el enlace para unirse al espacio de trabajo.", + "invite_sent_smtp_disabled_description": "El envío de correos electrónicos de invitación está deshabilitado para esta instancia de Hoppscotch. Utiliza el botón Copiar enlace para copiar y compartir el enlace de invitación manualmente.", + "copy_invite_link": "Copiar enlace de invitación", + "search_title": "Solicitudes del espacio de trabajo", + "join_beta": "Únete al programa beta para acceder a los espacio de trabajos." }, "team_environment": { "deleted": "Entorno eliminado", @@ -1053,44 +1053,44 @@ "title": "Espacios de trabajo" }, "site_protection": { - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", - "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + "login_to_continue": "Iniciar sesión para continuar", + "login_to_continue_description": "Debes iniciar sesión para acceder a esta instancia de Hoppscotch Enterprise.", + "error_fetching_site_protection_status": "Algo ha fallado al obtener el estado de protección del sitio web" }, "access_tokens": { "tab_title": "Tokens", - "section_title": "Personal Access Tokens", - "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", - "last_used_on": "Last used on", - "expires_on": "Expires on", - "no_expiration": "No expiration", - "expired": "Expired", - "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", - "token_purpose": "What's this token for?", - "expiration_label": "Expiration", - "scope_label": "Scope", - "workspace_read_only_access": "Read-only access to workspace data.", - "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", - "generate_token": "Generate Token", - "invalid_label": "Please provide a label for the token", - "no_expiration_verbose": "This token will never expire!", - "token_expires_on": "This token will expire on", - "generate_new_token": "Generate new token", - "generate_modal_title": "New Personal Access Token", - "deletion_success": "The access token {label} has been deleted" + "section_title": "Tokens de acceso personal", + "section_description": "Los tokens de acceso personal actualmente te ayudan a conectar el CLI a tu cuenta Hoppscotch", + "last_used_on": "Utilizado por última vez en", + "expires_on": "Expira en", + "no_expiration": "Sin expiración", + "expired": "Expirado", + "copy_token_warning": "Asegúrate de copiar ahora tu token de acceso personal. No podrás volver a verlo.", + "token_purpose": "¿Para qué es este token?", + "expiration_label": "Expiración", + "scope_label": "Ámbito", + "workspace_read_only_access": "Acceso de sólo lectura a los datos del espacio de trabajo.", + "personal_workspace_access_limitation": "Los tokens de acceso personal no pueden acceder a tu espacio de trabajo personal.", + "generate_token": "Generar token", + "invalid_label": "Proporciona un nombre válido para el token", + "no_expiration_verbose": "Este token no expirará nunca.", + "token_expires_on": "Este token expirará el", + "generate_new_token": "Generar nuevo token", + "generate_modal_title": "Nuevo token de acceso personal", + "deletion_success": "El token de acceso {label} ha sido eliminado" }, "collection_runner": { - "collection_id": "Collection ID", - "environment_id": "Environment ID", - "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", - "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", - "include_active_environment": "Include active environment:", + "collection_id": "ID de colección", + "environment_id": "ID de entorno", + "cli_collection_id_description": "Este ID de colección será utilizado por el CLI collection runner para Hoppscotch.", + "cli_environment_id_description": "Este ID de entorno será utilizado por el CLI collection runner para Hoppscotch.", + "include_active_environment": "Incluir un entorno activo:", "cli": "CLI", - "ui": "Runner (coming soon)", - "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", - "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", - "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "ui": "Runner (próximamente)", + "cli_command_generation_description_cloud": "Copia el siguiente comando y ejecútalo desde la CLI. Por favor, especifica un token de acceso personal.", + "cli_command_generation_description_sh": "Copia el siguiente comando y ejecútalo desde la CLI. Por favor, especifica un token de acceso personal y verifica la URL generada del servidor de instancias SH.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copia el siguiente comando y ejecútalo desde la CLI. Por favor, especifica un token de acceso personal y la URL del servidor de instancias SH.", + "run_collection": "Ejecutar colección" }, "shortcodes": { "actions": "Acciones", @@ -1098,7 +1098,7 @@ "deleted": "Código corto eliminado", "method": "Método", "not_found": "Shortcode no encontrado", - "short_code": "Short code", + "short_code": "Shortcode", "url": "URL" } } diff --git a/packages/hoppscotch-common/locales/ru.json b/packages/hoppscotch-common/locales/ru.json index 8a03b5ae5..b074b5899 100644 --- a/packages/hoppscotch-common/locales/ru.json +++ b/packages/hoppscotch-common/locales/ru.json @@ -24,13 +24,13 @@ "go_back": "Вернуться", "go_forward": "Вперёд", "group_by": "Сгруппировать по", - "hide_secret": "Hide secret", + "hide_secret": "Спрятать секрет", "label": "Название", "learn_more": "Узнать больше", "download_here": "Download here", "less": "Меньше", "more": "Больше", - "new": "Создать новый", + "new": "Создать", "no": "Нет", "open_workspace": "Открыть пространство", "paste": "Вставить", @@ -45,7 +45,7 @@ "search": "Поиск", "send": "Отправить", "share": "Поделиться", - "show_secret": "Show secret", + "show_secret": "Показать секрет", "start": "Начать", "starting": "Запускаю", "stop": "Стоп", @@ -58,7 +58,7 @@ "yes": "Да" }, "add": { - "new": "Добавить новое", + "new": "Добавить", "star": "Добавить в избранное" }, "app": { @@ -81,8 +81,8 @@ "join_discord_community": "Присоединяйтесь к нашему сообществу Discord", "keyboard_shortcuts": "Горячие клавиши", "name": "Hoppscotch", - "new_version_found": "Найдена новая версия. Перезагрузите для обновления.", - "open_in_hoppscotch": "Open in Hoppscotch", + "new_version_found": "Найдена новая версия. Перезагрузите для обновления. Все данные уже сохранены, ничего не потеряется", + "open_in_hoppscotch": "Открыть в Hoppscotch", "options": "Настройки", "proxy_privacy_policy": "Политика конфиденциальности прокси", "reload": "Перезагрузить", @@ -98,9 +98,9 @@ "twitter": "Twitter", "type_a_command_search": "Введите команду или выполните поиск…", "we_use_cookies": "Мы используем куки", - "updated_text": "Hoppscotch has been updated to v{version} 🎉", + "updated_text": "Hoppscotch был обновлен до v{version} 🎉", "whats_new": "Что нового?", - "see_whats_new": "See what’s new", + "see_whats_new": "Узнать что нового", "wiki": "Узнать больше" }, "auth": { @@ -112,7 +112,7 @@ "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Продолжить с Google", "continue_with_microsoft": "Продолжить с Microsoft", - "email": "Электронное письмо", + "email": "Электронная почта", "logged_out": "Успешно вышли. Будем скучать!", "login": "Авторизоваться", "login_success": "Успешный вход в систему", @@ -128,7 +128,7 @@ "generate_token": "Сгенерировать токен", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "include_in_url": "Добавить в URL", - "inherited_from": "Унаследован тип аутентификации {auth} из родительской коллекции {collection}", + "inherited_from": "Унаследован тип аутентификации {auth} из родительской коллекции \"{collection}\"", "learn": "Узнать больше", "oauth": { "redirect_auth_server_returned_error": "Auth Server returned an error state", @@ -141,7 +141,7 @@ "redirect_no_code_verifier": "No Code Verifier Defined", "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", - "something_went_wrong_on_token_generation": "Something went wrong on token generation", + "something_went_wrong_on_token_generation": "Что-то пошло не так в процессе генерации токена доступа", "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", "grant_type": "Grant Type", "grant_type_auth_code": "Authorization Code", @@ -163,11 +163,11 @@ "label_auth_code": "Authorization Code", "label_client_credentials": "Client Credentials" }, - "pass_key_by": "Pass by", - "pass_by_query_params_label": "Query Parameters", "pass_by_headers_label": "Headers", + "pass_by_query_params_label": "Query Parameters", + "pass_key_by": "Pass by", "password": "Пароль", - "save_to_inherit": "Чтобы унаследовать аутентификации, нужно сохранить запрос в коллекции", + "save_to_inherit": "Cохраните этот запрос в любой коллекции, чтобы унаследовать авторизацию", "token": "Токен", "type": "Метод авторизации", "username": "Имя пользователя" @@ -183,7 +183,7 @@ "moved": "Перемещено успешно", "my_collections": "Мои коллекции", "name": "Новая коллекция", - "name_length_insufficient": "Имя коллекции должно иметь 3 или более символов", + "name_length_insufficient": "Имя коллекции должно составлять не менее 3 символов", "new": "Создать коллекцию", "order_changed": "Порядок коллекции обновлён", "properties": "Параметры коллекции", @@ -194,7 +194,7 @@ "save_to_collection": "Сохранить в коллекцию", "select": "Выбрать коллекцию", "select_location": "Выберите местоположение", - "details": "Details" + "details": "Подробности" }, "confirm": { "close_unsaved_tab": "Вы уверены, что хотите закрыть эту вкладку?", @@ -209,10 +209,10 @@ "remove_shared_request": "Вы уверены, что хотите навсегда удалить этот запрос?", "remove_team": "Вы уверены, что хотите удалить эту команду?", "remove_telemetry": "Вы действительно хотите отказаться от телеметрии?", - "request_change": "Вы уверены, что хотите сбросить текущий запрос, все не сохранённые данные будт утеряны?", + "request_change": "Вы уверены, что хотите сбросить текущий запрос, все несохранённые данные будт утеряны?", "save_unsaved_tab": "Вы хотите сохранить изменения в этой вкладке?", "sync": "Вы уверены, что хотите синхронизировать это рабочее пространство?", - "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" + "delete_access_token": "Вы уверены, что хотите удалить токен доступа {tokenLabel}?" }, "context_menu": { "add_parameters": "Добавить в список параметров", @@ -253,8 +253,8 @@ "authorization": "Этот запрос не использует авторизацию", "body": "У этого запроса нет тела", "collection": "Коллекция пуста", - "collections": "Коллекции пустые", - "documentation": "Подключите GraphQL endpoint, чтобы увидеть документацию.", + "collections": "Коллекции пусты", + "documentation": "Подключите GraphQL endpoint, чтобы увидеть документацию", "endpoint": "Endpoint не может быть пустым", "environments": "Переменных окружения нет", "folder": "Папка пуста", @@ -263,19 +263,19 @@ "invites": "Вы еще никого не приглашали", "members": "В этой команде еще нет участников", "parameters": "Этот запрос не содержит параметров", - "pending_invites": "Пока что нет ожидающих заявок на вступление в команду", + "pending_invites": "Пока нет заявок, ожидающих вступления в команду", "profile": "Войдите, чтобы просмотреть свой профиль", - "protocols": "Протоколы пустые", + "protocols": "Протоколы пусты", "request_variables": "Этот запрос не содержит никаких переменных", "schema": "Подключиться к конечной точке GraphQL", "secret_environments": "Секреты хранятся только на этом устройстве и не синхронизируются с сервером", "shared_requests": "Вы еще не делились запросами с другими", "shared_requests_logout": "Нужно войти, чтобы делиться запросами и управлять ими", "subscription": "Нет подписок", - "team_name": "Название команды пусто", + "team_name": "Название команды пустое", "teams": "Команды пустые", "tests": "Для этого запроса нет тестов", - "access_tokens": "Access tokens are empty" + "access_tokens": "Токенов еще нет" }, "environment": { "add_to_global": "Добавить в глобальное окружение", @@ -317,8 +317,8 @@ "details": "Details" }, "error": { - "authproviders_load_error": "Unable to load auth providers", - "browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.", + "authproviders_load_error": "Не получается загрузить список возможных вариантов входа. Проверьте сеть, возможно отключен VPN", + "browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером", "check_console_details": "Подробности смотрите в журнале консоли.", "check_how_to_add_origin": "Инструкция как это сделать", "curl_invalid_format": "cURL неправильно отформатирован", @@ -330,11 +330,11 @@ "f12_details": "(F12 для подробностей)", "gql_prettify_invalid_query": "Не удалось отформатировать, т.к. в запросе есть синтаксические ошибки. Устраните их и повторите попытку.", "incomplete_config_urls": "Не заполнены URL конфигурации", - "incorrect_email": "Не корректный Email", - "invalid_link": "Не корректная ссылка", - "invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.", + "incorrect_email": "Некорректный Email", + "invalid_link": "Некорректная ссылка", + "invalid_link_description": "Ссылка, по которой вы перешли, недействительна, либо срок её действия истёк", "invalid_embed_link": "The embed does not exist or is invalid.", - "json_parsing_failed": "Не корректный JSON", + "json_parsing_failed": "Некорректный JSON", "json_prettify_invalid_body": "Не удалось определить формат строки, устраните синтаксические ошибки и повторите попытку.", "network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.", "network_fail": "Не удалось отправить запрос", @@ -344,15 +344,15 @@ "no_results_found": "Совпадения не найдены", "page_not_found": "Эта страница не найдена", "please_install_extension": "Ничего страшного. Просто нужно установить специальное расширение в браузере.", - "proxy_error": "Proxy error", + "proxy_error": "Ошибка в прокси", "same_profile_name": "Задано имя пользователя такое же как и было", "script_fail": "Не удалось выполнить сценарий предварительного запроса", "something_went_wrong": "Что-то пошло не так", "test_script_fail": "Не удалось выполнить тестирование запроса", "reading_files": "Произошла ошибка при чтении файла или нескольких файлов", "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", - "generate_access_token": "Something went wrong while generating the access token", - "delete_access_token": "Something went wrong while deleting the access token" + "generate_access_token": "Что-то пошло не так в процессе генерации токена доступа", + "delete_access_token": "Что-то пошло не так в процессе удаления токена доступа" }, "export": { "as_json": "Экспорт как JSON", @@ -400,16 +400,16 @@ "save_workspace": "Сохранить мою рабочую область" }, "helpers": { - "authorization": "Заголовок авторизации будет автоматически сгенерирован при отправке запроса.", - "collection_properties_authorization": "Этот заголовок авторизации будет подставляться при каждом запросе в этой коллекции.", - "collection_properties_header": "Этот заголовок будет подставляться при каждом запросе в этой коллекции.", + "authorization": "Заголовок авторизации будет автоматически сгенерирован при отправке запроса", + "collection_properties_authorization": "Этот заголовок авторизации будет подставляться при каждом запросе в этой коллекции", + "collection_properties_header": "Этот заголовок будет подставляться при каждом запросе в этой коллекции", "generate_documentation_first": "Сначала создайте документацию", - "network_fail": "Невозможно достичь конечной точки API. Проверьте подключение к сети и попробуйте еще раз.", - "offline": "Кажется, вы не в сети. Данные в этой рабочей области могут быть устаревшими.", - "offline_short": "Кажется, вы не в сети.", - "post_request_tests": "Сценарии тестирования написаны на JavaScript и запускаются после получения ответа.", - "pre_request_script": "Скрипты предварительного запроса написаны на JavaScript и запускаются перед отправкой запроса.", - "script_fail": "Похоже, в скрипте предварительного запроса есть сбой. Проверьте ошибку ниже и исправьте скрипт соответствующим образом.", + "network_fail": "Невозможно достичь конечной точки API. Проверьте подключение к сети и попробуйте еще раз", + "offline": "Кажется, вы не в сети. Данные в этой рабочей области могут быть устаревшими", + "offline_short": "Кажется, вы не в сети", + "post_request_tests": "Сценарии тестирования написаны на JavaScript и запускаются после получения ответа", + "pre_request_script": "Скрипты предварительного запроса написаны на JavaScript и запускаются перед отправкой запроса", + "script_fail": "Похоже, в скрипте предварительного запроса есть сбой. Проверьте ошибку ниже и исправьте скрипт соответствующим образом", "test_script_fail": "Похоже, что скрипт тестирования содержит ошибку. Пожалуйста исправьте её и попробуйте снова", "tests": "Напишите тестовый сценарий для автоматизации отладки." }, @@ -428,7 +428,7 @@ "from_file": "Импортировать из одного или нескольких файлов", "from_gist": "Импорт из Gist", "from_gist_description": "Импортировать через Gist URL", - "from_insomnia": "Импортировать с Insomnia", + "from_insomnia": "Импортировать из Insomnia", "from_insomnia_description": "Импортировать из коллекции Insomnia", "from_json": "Импортировать из Hoppscotch", "from_json_description": "Импортировать из файла коллекции Hoppscotch", @@ -450,10 +450,10 @@ "insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", "json_description": "Импортировать из коллекции Hoppscotch", "postman_environment": "Postman Environment", - "postman_environment_description": "Import Postman Environment from a JSON file", + "postman_environment_description": "Импортировать переменные окружения Postman из JSON файла", "title": "Импортировать", "file_size_limit_exceeded_warning_multiple_files": "Выбранные файлы превышают рекомендованный лимит в 10MB. Были импортированы только первые {files}", - "file_size_limit_exceeded_warning_single_file": "Размер выбранного в данный момент файла превышает рекомендуемый лимит в 10 МБ. Пожалуйста, выберите другой файл.", + "file_size_limit_exceeded_warning_single_file": "Размер выбранного в данный момент файла превышает рекомендуемый лимит в 10 МБ. Пожалуйста, выберите другой файл", "success": "Успешно импортировано" }, "inspections": { @@ -462,24 +462,24 @@ "add_environment": "Добавить переменную", "add_environment_value": "Заполнить значение", "empty_value": "Значение переменной окружения '{variable}' пустое", - "not_found": "Переменная окружения “{environment}” не задана." + "not_found": "Переменная окружения “{environment}” не задана" }, "header": { - "cookie": "Из-за ограничений безопасности в веб версии нельзя задать Cookie параметры. Пожалуйста, используйте Hoppscotch Desktop приложение или используйте заголовок Authorization вместо этого." + "cookie": "Из-за ограничений безопасности в веб версии нельзя задать Cookie параметры. Пожалуйста, используйте приложение Hoppscotch Desktop или используйте заголовок Authorization для веб-версии" }, "response": { - "401_error": "Please check your authentication credentials.", - "404_error": "Please check your request URL and method type.", - "cors_error": "Please check your Cross-Origin Resource Sharing configuration.", - "default_error": "Please check your request.", - "network_error": "Please check your network connection." + "401_error": "Проверьте данные для аутентификации", + "404_error": "Проверьте параметры запроса и метод HTTP", + "cors_error": "Проверьте настройки CORS (Cross-Origin Resource Sharing)", + "default_error": "Проверьте параметры запроса", + "network_error": "Проверьте сетевое подключение" }, "title": "Помощник", "url": { - "extension_not_installed": "Расширение не установлено.", + "extension_not_installed": "Расширение не установлено", "extension_unknown_origin": "Убедитесь, что текущий домен добавлен в список доверенных ресурсов в расширении браузера", "extention_enable_action": "Подключить расширение", - "extention_not_enabled": "Расширение в браузере не подключено." + "extention_not_enabled": "Расширение в браузере не подключено" } }, "layout": { @@ -490,10 +490,10 @@ "row": "Горизонтальная развертка" }, "modal": { - "close_unsaved_tab": "У вас есть не сохранённые изменения", + "close_unsaved_tab": "У вас есть несохранённые изменения", "collections": "Коллекции", "confirm": "Подтвердите действие", - "customize_request": "Customize Request", + "customize_request": "Настроить вид запроса", "edit_request": "Изменить запрос", "import_export": "Импорт Экспорт", "share_request": "Поделиться запросом" @@ -507,7 +507,7 @@ "color": "Выбрать цвет", "communication": "Коммуникация", "connection_config": "Конфигурация соединения", - "connection_not_authorized": "Это соединение MQTT не использует какую-либо авторизацию.", + "connection_not_authorized": "Это соединение MQTT не использует какую-либо авторизацию", "invalid_topic": "Пожалуйста выберите topic для подписки", "keep_alive": "Поддерживать соединение", "log": "Лог", @@ -517,7 +517,7 @@ "lw_topic": "Last-Will Topic", "message": "Сообщение", "new": "Новая подписка", - "not_connected": "Пожалуйста, сначала запустите MQTT соединение.", + "not_connected": "Пожалуйста, сначала запустите MQTT соединение", "publish": "Публиковать", "qos": "QoS", "ssl": "SSL", @@ -539,23 +539,23 @@ "preRequest": { "javascript_code": "Код JavaScript", "learn": "Читать документацию", - "script": "Предворительный скрипт запроса", + "script": "Предварительный скрипт запроса", "snippets": "Готовый код" }, "profile": { "app_settings": "Настройки приложения", "default_hopp_displayname": "Безымянный", "editor": "Редактор", - "editor_description": "Редакторы могут добавлять, редактировать, а так же удалять запросы.", - "email_verification_mail": "На вашу электронную почту отправлено письмо для подтверждения. Перейдите по ссылке из письма, чтобы подтвердить свой электронный адрес.", - "no_permission": "У Вас недостаточно прав, чтобы выполнить это действие.", + "editor_description": "Редакторы могут добавлять, редактировать, а так же удалять запросы", + "email_verification_mail": "На вашу электронную почту отправлено письмо для подтверждения. Перейдите по ссылке из письма, чтобы подтвердить свой электронный адрес", + "no_permission": "У Вас недостаточно прав, чтобы выполнить это действие", "owner": "Владелец", - "owner_description": "Владелец может добавлять, редактировать, и удалять запросы, коллекии, а так же участников.", + "owner_description": "Владелец может добавлять, редактировать и удалять запросы, коллекции, а так же участников", "roles": "Роли", - "roles_description": "Роли позволяют настраивать доступ конкретным людям к публичным коллекциям.", + "roles_description": "Роли позволяют настраивать доступ конкретным людям к публичным коллекциям", "updated": "Профиль обновлен", "viewer": "Читатель", - "viewer_description": "Могут только просматривать и использовать запросы." + "viewer_description": "Могут только просматривать и использовать запросы" }, "remove": { "star": "Удалить звезду" @@ -609,7 +609,7 @@ "title": "Запрос", "type": "Тип запроса", "url": "URL", - "url_placeholder": "Enter a URL or paste a cURL command", + "url_placeholder": "Введите URL или вставьте команду из cURL", "variables": "Переменные", "view_my_links": "Посмотреть мои ссылки" }, @@ -623,7 +623,7 @@ "json": "JSON", "pdf": "PDF", "preview_html": "Предварительный просмотр HTML", - "raw": "Необработанное", + "raw": "RAW", "size": "Размер", "status": "Статус", "time": "Время", @@ -641,24 +641,24 @@ "account_name_description": "Это ваше отображаемое имя.", "additional": "Additional Settings", "background": "Задний фон", - "black_mode": "Темная тема", + "black_mode": "Чёрная", "choose_language": "Выберите язык", - "dark_mode": "Темный", + "dark_mode": "Тёмная", "delete_account": "Удалить аккаунт", "delete_account_description": "Удаление аккаунта нельзя отменить", - "expand_navigation": "Раскрыть панель навигации", + "expand_navigation": "Раскрыть боковую панель", "experiments": "Эксперименты", - "experiments_notice": "Это набор экспериментов, над которыми мы работаем, которые могут оказаться полезными, интересными, и тем, и другим, или ни тем, ни другим. Они не окончательные и могут быть нестабильными, поэтому, если произойдет что-то слишком странное, не паникуйте. Просто выключи эту чертову штуку. Шутки в сторону,", + "experiments_notice": "Это набор экспериментов, над которыми мы работаем, которые могут оказаться полезными, интересными тебе и мне, а может и никому. Они не завершены и могут быть нестабильными, поэтому, если произойдет что-то слишком странное, не паникуйте. Просто выключи эту чертову штуку. Шутки в сторону,", "extension_ver_not_reported": "Не сообщается", "extension_version": "Версия расширения", "extensions": "Расширения", "extensions_use_toggle": "Используйте расширение браузера для отправки запросов (если есть)", "follow": "Follow Us", "interceptor": "Перехватчик", - "interceptor_description": "Промежуточное ПО между приложением и API.", + "interceptor_description": "Промежуточное ПО между приложением и API", "language": "Язык", - "light_mode": "Свет", - "official_proxy_hosting": "Официальный прокси-сервер размещен на Hoppscotch.", + "light_mode": "Светлая", + "official_proxy_hosting": "Официальный прокси-сервер размещен на Hoppscotch", "profile": "Профиль", "profile_description": "Обновить настройки профиля", "profile_email": "Адрес электронной почты", @@ -673,12 +673,12 @@ "sidebar_on_left": "Панель слева", "sync": "Синхронизировать", "sync_collections": "Коллекции", - "sync_description": "Эти настройки синхронизируются с облаком.", + "sync_description": "Эти настройки синхронизируются с облаком", "sync_environments": "Среды", "sync_history": "История", "system_mode": "Система", "telemetry": "Телеметрия", - "telemetry_helps_us": "Телеметрия помогает нам персонализировать наши операции и предоставлять вам лучший опыт.", + "telemetry_helps_us": "Телеметрия помогает нам персонализировать наши операции и предоставлять вам лучший опыт", "theme": "Тема", "theme_description": "Настройте тему своего приложения.", "use_experimental_url_bar": "Использовать экспериментальную строку URL с выделением среды", @@ -688,23 +688,23 @@ }, "shared_requests": { "button": "Кнопка", - "button_info": "Создать кнопку 'Run in Hoppscotch' на свой сайт, блог или README.", + "button_info": "Добавить кнопку 'Run in Hoppscotch' на свой сайт, блог или README", "copy_html": "Копировать HTML код", "copy_link": "Копировать ссылку", "copy_markdown": "Копировать Markdown", - "creating_widget": "Создание виджет", + "creating_widget": "Создание виджета", "customize": "Настроить", "deleted": "Запрос удален", - "description": "Выберите вид как вы поделитесь запросом, позже вы сможете дополнительно его настроить", + "description": "Выберите, каким образом вы хотите поделиться запросом. Вы сможете добавить дополнительные настройки кастомизации позже", "embed": "Встраиваемое окно", - "embed_info": "Добавьте небольшую площадку 'Hoppscotch API Playground' на свой веб-сайт, блог или документацию.", + "embed_info": "Добавьте небольшую площадку 'Hoppscotch API Playground' на свой веб-сайт, блог или документацию", "link": "Ссылка", - "link_info": "Создайте общедоступную ссылку, которой можно поделиться с любым пользователем, имеющим доступ к просмотру.", + "link_info": "Создайте общедоступную ссылку, которой можно поделиться с любым пользователем", "modified": "Запрос изменен", - "not_found": "Такой ссылке не нашлось", + "not_found": "Такой ссылки не нашлось", "open_new_tab": "Открыть в новом окне", - "preview": "Preview", - "run_in_hoppscotch": "Run in Hoppscotch", + "preview": "Как это будет выглядеть:", + "run_in_hoppscotch": "Запустить в Hoppscotch", "theme": { "dark": "Темная", "light": "Светлая", @@ -753,7 +753,7 @@ "reset_request": "Сбросить запрос", "save_request": "Сохранить запрос", "save_to_collections": "Сохранить в коллекции", - "send_request": "Послать запрос", + "send_request": "Отправить запрос", "share_request": "Поделиться запросом", "show_code": "Сгенерировать фрагмент кода из запроса", "title": "Запрос" @@ -864,7 +864,7 @@ "title": "Команды" }, "phrases": { - "try": "Попробовать", + "try": "Попробуй", "import_collections": "Импортировать коллекцию", "create_environment": "Создать окружение", "create_workspace": "Создать пространство", @@ -880,7 +880,7 @@ "bulk_mode": "Множественное редактирование", "bulk_mode_placeholder": "Каждый параметр должен начинаться с новой строки\nКлючи и значения разделяются двоеточием\nИспользуйте # для комментария", "cleared": "Очищено", - "connected": "Связаны", + "connected": "Подключено", "connected_to": "Подключено к {name}", "connecting_to": "Подключение к {name} ...", "connection_error": "Ошибка подключения", @@ -891,7 +891,7 @@ "deleted": "Удалено", "deprecated": "УСТАРЕЛО", "disabled": "Отключено", - "disconnected": "Отключен", + "disconnected": "Нет подключения", "disconnected_from": "Отключено от {name}", "docs_generated": "Документация создана", "download_failed": "Download failed", @@ -901,7 +901,7 @@ "finished_in": "Завершено через {duration} мс", "hide": "Скрыть", "history_deleted": "История удалена", - "linewrap": "Обернуть линии", + "linewrap": "Переносить строки", "loading": "Загрузка...", "message_received": "Сообщение: {message} получено по топику: {topic}", "mqtt_subscription_failed": "Что-то пошло не так, при попытке подписаться на топик: {topic}", @@ -919,12 +919,12 @@ }, "support": { "changelog": "Узнать больше о последних выпусках", - "chat": "Вопросов? Поболтай с нами!", + "chat": "Есть вопрос? Давай поболтаем!", "community": "Задавайте вопросы и помогайте другим", "documentation": "Узнать больше о Hoppscotch", "forum": "Задавайте вопросы и получайте ответы", "github": "Подпишитесь на нас на Github", - "shortcuts": "Просматривайте приложение быстрее", + "shortcuts": "Работайте с приложением ещё быстрее", "title": "Служба поддержки", "twitter": "Следуйте за нами на Twitter" }, @@ -946,8 +946,8 @@ "query": "Запрос", "schema": "Схема", "shared_requests": "Запросы в общем доступе", - "codegen": "Generate Code", - "code_snippet": "Code snippet", + "codegen": "Сгенерировать код", + "code_snippet": "Фрагмент кода", "share_tab_request": "Поделиться запросом", "socketio": "Socket.IO", "sse": "SSE", @@ -975,10 +975,10 @@ "invite": "Пригласить", "invite_more": "Пригласить больше", "invite_tooltip": "Пригласить людей в Ваше рабочее пространство", - "invited_to_team": "{owner} приглашает Вас присоединиться к команде {team}", + "invited_to_team": "{owner} приглашает Вас присоединиться к пространству {workspace}", "join": "Приглашение принято", - "join_team": "Присоединиться к {team}", - "joined_team": "Вы присоединились к команде {team}", + "join_team": "Присоединиться к {workspace}", + "joined_team": "Вы присоединились к команде {workspace}", "joined_team_description": "Теперь Вы участник этой команды", "left": "Вы покинули команду", "login_to_continue": "Войдите для продолжения", @@ -990,7 +990,7 @@ "member_role_updated": "Роли пользователей обновлены", "members": "Участники", "more_members": "+{count}", - "name_length_insufficient": "Название команды должно быть не менее 6 символов.", + "name_length_insufficient": "Название команды должно составлять не менее 6 символов", "name_updated": "Название команды обновлено", "new": "Новая команда", "new_created": "Создана новая команда", @@ -1001,7 +1001,7 @@ "not_found": "Команда не найдена, свяжитесь с владельцем команды", "not_valid_viewer": "У Вас нет прав просматривать это. Свяжитесь с руководителем команды.", "parent_coll_move": "Не удалось переместить коллекцию в дочернюю", - "pending_invites": "Ожидающие приглашения", + "pending_invites": "Ждут добавления", "permissions": "Разрешения", "same_target_destination": "Таже цель и конечная точка", "saved": "Команда сохранена", @@ -1009,10 +1009,10 @@ "success_invites": "Принятые приглашения", "title": "Команды", "we_sent_invite_link": "Мы отправили все приглашения!", - "invite_sent_smtp_disabled": "Invite links generated", + "invite_sent_smtp_disabled": "Ссылка-приглашение сгенерирована", "we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду.", "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", - "copy_invite_link": "Copy Invite Link", + "copy_invite_link": "Скопировать ссылку-приглашение", "search_title": "Team Requests" }, "team_environment": { @@ -1025,8 +1025,8 @@ "javascript_code": "Код JavaScript", "learn": "Читать документацию", "passed": "Тест пройден", - "report": "Отчет об испытаниях", - "results": "Результаты теста", + "report": "Отчет", + "results": "Результаты", "script": "Скрипт", "snippets": "Фрагменты" }, @@ -1050,38 +1050,38 @@ "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" }, "access_tokens": { - "tab_title": "Tokens", - "section_title": "Personal Access Tokens", - "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", - "last_used_on": "Last used on", - "expires_on": "Expires on", - "no_expiration": "No expiration", - "expired": "Expired", - "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", - "token_purpose": "What's this token for?", - "expiration_label": "Expiration", - "scope_label": "Scope", - "workspace_read_only_access": "Read-only access to workspace data.", - "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", - "generate_token": "Generate Token", - "invalid_label": "Please provide a label for the token", - "no_expiration_verbose": "This token will never expire!", - "token_expires_on": "This token will expire on", - "generate_new_token": "Generate new token", - "generate_modal_title": "New Personal Access Token", - "deletion_success": "The access token {label} has been deleted" + "tab_title": "Токены", + "section_title": "Персональные токены доступа", + "section_description": "С помощью персональных токенов доступа можно подключиться к вашей учётной записи Hoppscotch через CLI", + "last_used_on": "Последнее использование", + "expires_on": "Истекает", + "no_expiration": "Бессрочный", + "expired": "Истёк", + "copy_token_warning": "Обязательно скопируйте и сохраните свой персональный токен доступа. Вы больше с ним не увидитесь!", + "token_purpose": "Для чего будет использоваться токен?", + "expiration_label": "Время жизни", + "scope_label": "Ограничения", + "workspace_read_only_access": "Доступ к данным рабочей области только для чтения", + "personal_workspace_access_limitation": "Личные токены доступа не позволяют получить доступ к вашему личному рабочему пространству", + "generate_token": "Сгенерировать токен", + "invalid_label": "Придумайте название для токена", + "no_expiration_verbose": "Токен будет жить вечно!", + "token_expires_on": "Токен истечёт", + "generate_new_token": "Сгенерировать токен", + "generate_modal_title": "Новый персональный токен доступа", + "deletion_success": "Токен доступа {label} удалён" }, "collection_runner": { - "collection_id": "Collection ID", - "environment_id": "Environment ID", - "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", - "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "collection_id": "ID коллекции", + "environment_id": "ID окружения", + "cli_collection_id_description": "Этот ID будет использован для запуска коллекций через CLI в Hoppscoth", + "cli_environment_id_description": "Этот ID окружения будет использован для запуска коллекций через CLI в Hoppscoth", "include_active_environment": "Include active environment:", "cli": "CLI", - "ui": "Runner (coming soon)", - "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", - "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", - "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", - "run_collection": "Run collection" + "ui": "Runner (в разработке)", + "cli_command_generation_description_cloud": "Скопируйте команду и запустите её в CLI. Заполните параметр своим токеном", + "cli_command_generation_description_sh": "Скопируйте команду и запустите её в CLI. Заполните параметр своим токеном и проверьте, что сгенерирован верный URL сервера", + "cli_command_generation_description_sh_with_server_url_placeholder": "Скопируйте команду и запустите её в CLI. Заполните параметр своим токеном и определите URL сервера", + "run_collection": "Запустить коллекцию" } } diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index ba2742093..5d39873b0 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -1,7 +1,7 @@ { "name": "@hoppscotch/common", "private": true, - "version": "2024.7.0", + "version": "2024.11.0", "scripts": { "dev": "pnpm exec npm-run-all -p -l dev:*", "test": "vitest --run", @@ -22,14 +22,15 @@ }, "dependencies": { "@apidevtools/swagger-parser": "10.1.0", - "@codemirror/autocomplete": "6.13.0", - "@codemirror/commands": "6.3.3", + "@codemirror/autocomplete": "6.18.1", + "@codemirror/commands": "6.7.0", "@codemirror/lang-javascript": "6.2.2", "@codemirror/lang-json": "6.0.1", "@codemirror/lang-xml": "6.1.0", "@codemirror/language": "6.10.1", - "@codemirror/legacy-modes": "6.3.3", - "@codemirror/lint": "6.5.0", + "@codemirror/legacy-modes": "6.4.1", + "@codemirror/lint": "6.8.2", + "@codemirror/merge": "6.7.2", "@codemirror/search": "6.5.6", "@codemirror/state": "6.4.1", "@codemirror/view": "6.25.1", @@ -37,48 +38,52 @@ "@hoppscotch/data": "workspace:^", "@hoppscotch/httpsnippet": "3.0.6", "@hoppscotch/js-sandbox": "workspace:^", - "@hoppscotch/ui": "0.2.0", + "@hoppscotch/ui": "0.2.2", "@hoppscotch/vue-toasted": "0.1.0", "@lezer/highlight": "1.2.0", - "@unhead/vue": "1.8.8", - "@urql/core": "4.2.0", + "@noble/curves": "1.6.0", + "@scure/base": "1.1.9", + "@shopify/lang-jsonc": "1.0.0", + "@unhead/vue": "1.11.10", + "@urql/core": "5.0.6", "@urql/devtools": "2.0.3", - "@urql/exchange-auth": "2.1.6", - "@urql/exchange-graphcache": "6.4.0", - "@vitejs/plugin-legacy": "4.1.1", - "@vueuse/core": "10.7.0", - "acorn-walk": "8.3.0", - "axios": "1.6.2", + "@urql/exchange-auth": "2.2.0", + "@vueuse/core": "11.1.0", + "acorn-walk": "8.3.4", + "aws4fetch": "1.0.20", + "axios": "1.7.7", "buffer": "6.0.3", - "cookie-es": "1.0.0", + "cookie-es": "1.2.2", "dioc": "3.0.2", "esprima": "4.0.1", "events": "3.3.0", - "fp-ts": "2.16.1", - "globalthis": "1.0.3", - "graphql": "16.8.1", + "fp-ts": "2.16.9", + "globalthis": "1.0.4", + "graphql": "16.9.0", "graphql-language-service-interface": "2.10.2", "graphql-tag": "2.12.6", "insomnia-importers": "3.6.0", - "io-ts": "2.2.20", + "io-ts": "2.2.21", + "js-md5": "0.8.3", "js-yaml": "4.1.0", - "jsonpath-plus": "7.2.0", + "jsonc-parser": "3.3.1", + "jsonpath-plus": "10.0.0", "lodash-es": "4.17.21", - "lossless-json": "3.0.2", - "minisearch": "6.3.0", + "lossless-json": "4.0.2", + "minisearch": "7.1.0", "nprogress": "0.2.0", "paho-mqtt": "1.1.0", "path": "0.12.7", - "postman-collection": "4.3.0", + "postman-collection": "4.5.0", "process": "0.11.10", - "qs": "6.11.2", - "quicktype-core": "23.0.79", + "qs": "6.13.0", + "quicktype-core": "23.0.170", "rxjs": "7.8.1", - "set-cookie-parser": "2.6.0", + "set-cookie-parser": "2.7.0", "set-cookie-parser-es": "1.0.5", - "socket.io-client-v2": "npm:socket.io-client@2.4.0", + "socket.io-client-v2": "npm:socket.io-client@2.5.0", "socket.io-client-v3": "npm:socket.io-client@3.1.3", - "socket.io-client-v4": "npm:socket.io-client@4.4.1", + "socket.io-client-v4": "npm:socket.io-client@4.8.0", "socketio-wildcard": "2.0.0", "splitpanes": "3.1.5", "stream-browserify": "3.0.0", @@ -86,83 +91,84 @@ "tern": "0.24.3", "timers": "0.1.1", "tippy.js": "6.3.7", - "url": "0.11.3", + "url": "0.11.4", "util": "0.12.5", - "uuid": "9.0.1", - "verzod": "0.2.2", - "vue": "3.3.9", - "vue-i18n": "9.8.0", - "vue-pdf-embed": "1.2.1", - "vue-router": "4.2.5", - "vue-tippy": "6.3.1", + "uuid": "10.0.0", + "verzod": "0.2.3", + "vue": "3.5.12", + "vue-i18n": "10.0.4", + "vue-pdf-embed": "2.1.0", + "vue-router": "4.4.5", + "vue-tippy": "6.5.0", "vuedraggable-es": "4.1.1", "wonka": "6.3.4", - "workbox-window": "7.0.0", - "xml-formatter": "3.6.0", + "workbox-window": "7.1.0", + "xml-formatter": "3.6.3", "yargs-parser": "21.1.1", - "zod": "3.22.4" + "zod": "3.23.8" }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", - "@graphql-codegen/add": "5.0.0", - "@graphql-codegen/cli": "5.0.0", - "@graphql-codegen/typed-document-node": "5.0.1", - "@graphql-codegen/typescript": "4.0.1", - "@graphql-codegen/typescript-operations": "4.0.1", - "@graphql-codegen/typescript-urql-graphcache": "3.0.0", + "@graphql-codegen/add": "5.0.3", + "@graphql-codegen/cli": "5.0.3", + "@graphql-codegen/typed-document-node": "5.0.10", + "@graphql-codegen/typescript": "4.1.0", + "@graphql-codegen/typescript-operations": "4.3.0", + "@graphql-codegen/typescript-urql-graphcache": "3.1.0", "@graphql-codegen/urql-introspection": "3.0.0", "@graphql-typed-document-node/core": "3.2.0", - "@iconify-json/lucide": "1.1.144", + "@iconify-json/lucide": "1.2.9", "@intlify/vite-plugin-vue-i18n": "7.0.0", "@relmify/jest-fp-ts": "2.1.1", - "@rushstack/eslint-patch": "1.6.0", - "@types/har-format": "1.2.15", + "@rushstack/eslint-patch": "1.10.4", + "@types/har-format": "1.2.16", "@types/js-yaml": "4.0.9", "@types/lodash-es": "4.17.12", "@types/lossless-json": "1.0.4", "@types/nprogress": "0.2.3", "@types/paho-mqtt": "1.0.10", "@types/postman-collection": "3.5.10", - "@types/qs": "6.9.12", + "@types/qs": "6.9.16", "@types/splitpanes": "2.2.6", - "@types/uuid": "9.0.7", + "@types/uuid": "10.0.0", "@types/yargs-parser": "21.0.3", - "@typescript-eslint/eslint-plugin": "7.3.1", - "@typescript-eslint/parser": "7.3.1", - "@vitejs/plugin-vue": "4.5.1", - "@vue/compiler-sfc": "3.3.10", - "@vue/eslint-config-typescript": "12.0.0", - "@vue/runtime-core": "3.3.10", - "autoprefixer": "10.4.16", + "@typescript-eslint/eslint-plugin": "8.9.0", + "@typescript-eslint/parser": "8.9.0", + "@urql/exchange-graphcache": "6.5.1", + "@vitejs/plugin-vue": "5.1.4", + "@vue/compiler-sfc": "3.5.12", + "@vue/eslint-config-typescript": "13.0.0", + "@vue/runtime-core": "3.5.12", + "autoprefixer": "10.4.20", "cross-env": "7.0.3", - "dotenv": "16.3.1", + "dotenv": "16.4.5", "eslint": "8.57.0", - "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-vue": "9.24.0", - "glob": "10.3.10", + "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-vue": "9.29.0", + "glob": "11.0.0", + "jsdom": "25.0.1", "npm-run-all": "4.1.5", "openapi-types": "12.1.3", - "postcss": "8.4.31", - "prettier": "3.1.0", - "prettier-plugin-tailwindcss": "0.5.7", + "postcss": "8.4.47", + "prettier": "3.3.3", + "prettier-plugin-tailwindcss": "0.6.8", "rollup-plugin-polyfill-node": "0.13.0", - "sass": "1.69.5", - "tailwindcss": "3.3.5", - "typescript": "5.3.2", + "sass": "1.79.5", + "tailwindcss": "3.4.14", + "typescript": "5.3.3", "unplugin-fonts": "1.1.1", - "unplugin-icons": "0.17.4", - "unplugin-vue-components": "0.25.2", - "vite": "4.5.0", - "vite-plugin-checker": "0.6.2", + "unplugin-icons": "0.19.3", + "unplugin-vue-components": "0.27.4", + "vite": "5.4.9", + "vite-plugin-checker": "0.6.4", "vite-plugin-fonts": "0.7.0", - "vite-plugin-html-config": "1.0.11", - "vite-plugin-inspect": "0.7.42", - "vite-plugin-pages": "0.31.0", - "vite-plugin-pages-sitemap": "1.6.1", - "vite-plugin-pwa": "0.17.3", - "vite-plugin-vue-layouts": "0.8.0", - "vitest": "0.34.6", + "vite-plugin-html-config": "2.0.2", + "vite-plugin-pages": "0.32.3", + "vite-plugin-pages-sitemap": "1.7.1", + "vite-plugin-pwa": "0.20.5", + "vite-plugin-vue-layouts": "0.11.0", + "vitest": "2.1.3", "vue-tsc": "1.8.24" } } diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 8ff1054de..2b1ed9970 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -1,17 +1,23 @@ /* eslint-disable */ -/* prettier-ignore */ // @ts-nocheck // Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 export {} +/* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + '(chore': fix broken runner for user collection) + '(feat': collection runner config in modal) + '(fix': run again function) AccessTokens: typeof import('./components/accessTokens/index.vue')['default'] AccessTokensGenerateModal: typeof import('./components/accessTokens/GenerateModal.vue')['default'] AccessTokensList: typeof import('./components/accessTokens/List.vue')['default'] AccessTokensOverview: typeof import('./components/accessTokens/Overview.vue')['default'] + AiexperimentsMergeView: typeof import('./components/aiexperiments/MergeView.vue')['default'] + AiexperimentsModifyBodyModal: typeof import('./components/aiexperiments/ModifyBodyModal.vue')['default'] AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default'] + AppAnnouncement: (typeof import("./components/app/Announcement.vue"))["default"] AppBanner: typeof import('./components/app/Banner.vue')['default'] AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default'] AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default'] @@ -23,7 +29,6 @@ declare module 'vue' { AppLogo: typeof import('./components/app/Logo.vue')['default'] AppOptions: typeof import('./components/app/Options.vue')['default'] AppPaneLayout: typeof import('./components/app/PaneLayout.vue')['default'] - AppPWAPrompt: typeof import('./components/app/PWAPrompt.vue')['default'] AppShare: typeof import('./components/app/Share.vue')['default'] AppShortcuts: typeof import('./components/app/Shortcuts.vue')['default'] AppShortcutsEntry: typeof import('./components/app/ShortcutsEntry.vue')['default'] @@ -40,6 +45,8 @@ declare module 'vue' { AppSpotlightSearch: typeof import('./components/app/SpotlightSearch.vue')['default'] AppSupport: typeof import('./components/app/Support.vue')['default'] AppWhatsNewDialog: typeof import('./components/app/WhatsNewDialog.vue')['default'] + ButtonPrimary: (typeof import("./../../hoppscotch-ui/src/components/button/Primary.vue"))["default"] + ButtonSecondary: (typeof import("./../../hoppscotch-ui/src/components/button/Secondary.vue"))["default"] Collections: typeof import('./components/collections/index.vue')['default'] CollectionsAdd: typeof import('./components/collections/Add.vue')['default'] CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default'] @@ -48,6 +55,8 @@ declare module 'vue' { CollectionsEdit: typeof import('./components/collections/Edit.vue')['default'] CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default'] CollectionsEditRequest: typeof import('./components/collections/EditRequest.vue')['default'] + CollectionsEditResponse: typeof import('./components/collections/EditResponse.vue')['default'] + CollectionsExampleResponse: typeof import('./components/collections/ExampleResponse.vue')['default'] CollectionsGraphql: typeof import('./components/collections/graphql/index.vue')['default'] CollectionsGraphqlAdd: typeof import('./components/collections/graphql/Add.vue')['default'] CollectionsGraphqlAddFolder: typeof import('./components/collections/graphql/AddFolder.vue')['default'] @@ -63,7 +72,7 @@ declare module 'vue' { CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default'] CollectionsProperties: typeof import('./components/collections/Properties.vue')['default'] CollectionsRequest: typeof import('./components/collections/Request.vue')['default'] - CollectionsRunner: typeof import('./components/collections/Runner.vue')['default'] + CollectionsRunner: (typeof import("./components/collections/Runner.vue"))["default"] CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default'] CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default'] CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default'] @@ -104,9 +113,10 @@ declare module 'vue' { HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'] + HoppSmartAutoComplete: (typeof import("@hoppscotch/ui"))["HoppSmartAutoComplete"] HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox'] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] - HoppSmartExpand: typeof import('@hoppscotch/ui')['HoppSmartExpand'] + HoppSmartExpand: (typeof import("@hoppscotch/ui"))["HoppSmartExpand"] HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip'] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection'] @@ -127,16 +137,33 @@ declare module 'vue' { HoppSmartTree: typeof import('@hoppscotch/ui')['HoppSmartTree'] HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow'] HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows'] + HoppTestEnv: (typeof import("@hoppscotch/ui"))["HoppTestEnv"] + HoppTestRunnerModal: (typeof import("@hoppscotch/ui"))["HoppTestRunnerModal"] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default'] + HttpAuthorizationAkamaiEG: typeof import('./components/http/authorization/AkamaiEG.vue')['default'] HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default'] + HttpAuthorizationASAP: typeof import('./components/http/authorization/ASAP.vue')['default'] + HttpAuthorizationAWSSign: typeof import('./components/http/authorization/AWSSign.vue')['default'] HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default'] + HttpAuthorizationDigest: typeof import('./components/http/authorization/Digest.vue')['default'] + HttpAuthorizationHAWK: typeof import('./components/http/authorization/HAWK.vue')['default'] + HttpAuthorizationNTLM: typeof import('./components/http/authorization/NTLM.vue')['default'] + HttpAuthorizationOAuth2: typeof import('./components/http/authorization/OAuth2.vue')['default'] HttpBody: typeof import('./components/http/Body.vue')['default'] + HttpBodyBinary: typeof import('./components/http/BodyBinary.vue')['default'] HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default'] HttpCodegen: typeof import('./components/http/Codegen.vue')['default'] HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default'] + HttpCollectionRunner: (typeof import("./components/http/CollectionRunner.vue"))["default"] + HttpExampleLenseBodyRenderer: typeof import('./components/http/example/LenseBodyRenderer.vue')['default'] + HttpExampleResponse: typeof import('./components/http/example/Response.vue')['default'] + HttpExampleResponseMeta: typeof import('./components/http/example/ResponseMeta.vue')['default'] + HttpExampleResponseRequest: typeof import('./components/http/example/ResponseRequest.vue')['default'] + HttpExampleResponseTab: typeof import('./components/http/example/ResponseTab.vue')['default'] HttpHeaders: typeof import('./components/http/Headers.vue')['default'] HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default'] - HttpOAuth2Authorization: typeof import('./components/http/OAuth2Authorization.vue')['default'] + HttpKeyValue: typeof import('./components/http/KeyValue.vue')['default'] + HttpOAuth2Authorization: (typeof import("./components/http/OAuth2Authorization.vue"))["default"] HttpParameters: typeof import('./components/http/Parameters.vue')['default'] HttpPreRequestScript: typeof import('./components/http/PreRequestScript.vue')['default'] HttpRawBody: typeof import('./components/http/RawBody.vue')['default'] @@ -146,20 +173,38 @@ declare module 'vue' { HttpRequestTab: typeof import('./components/http/RequestTab.vue')['default'] HttpRequestVariables: typeof import('./components/http/RequestVariables.vue')['default'] HttpResponse: typeof import('./components/http/Response.vue')['default'] + HttpResponseInterface: typeof import('./components/http/ResponseInterface.vue')['default'] HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default'] + HttpRunner: (typeof import("./components/http/Runner.vue"))["default"] + HttpSaveResponseName: typeof import('./components/http/SaveResponseName.vue')['default'] HttpSidebar: typeof import('./components/http/Sidebar.vue')['default'] HttpTabHead: typeof import('./components/http/TabHead.vue')['default'] + HttpTestEnv: typeof import('./components/http/test/Env.vue')['default'] + HttpTestFolder: typeof import('./components/http/test/Folder.vue')['default'] + HttpTestRequest: typeof import('./components/http/test/Request.vue')['default'] + HttpTestResponse: typeof import('./components/http/test/Response.vue')['default'] HttpTestResult: typeof import('./components/http/TestResult.vue')['default'] HttpTestResultEntry: typeof import('./components/http/TestResultEntry.vue')['default'] HttpTestResultEnv: typeof import('./components/http/TestResultEnv.vue')['default'] + HttpTestResultFolder: typeof import('./components/http/test/ResultFolder.vue')['default'] HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default'] + HttpTestResultRequest: typeof import('./components/http/test/ResultRequest.vue')['default'] + HttpTestRunner: typeof import('./components/http/test/Runner.vue')['default'] + HttpTestRunnerConfig: typeof import('./components/http/test/RunnerConfig.vue')['default'] + HttpTestRunnerMeta: typeof import('./components/http/test/RunnerMeta.vue')['default'] + HttpTestRunnerModal: typeof import('./components/http/test/RunnerModal.vue')['default'] + HttpTestRunnerResult: typeof import('./components/http/test/RunnerResult.vue')['default'] HttpTests: typeof import('./components/http/Tests.vue')['default'] + HttpTestSelector: (typeof import("./components/http/test/Selector.vue"))["default"] + HttpTestSelectRequest: (typeof import("./components/http/test/SelectRequest.vue"))["default"] + HttpTestTestResult: typeof import('./components/http/test/TestResult.vue')['default'] HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default'] IconLucideActivity: typeof import('~icons/lucide/activity')['default'] + IconLucideAlertCircle: (typeof import("~icons/lucide/alert-circle"))["default"] IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] - IconLucideBrush: typeof import('~icons/lucide/brush')['default'] + IconLucideBrush: (typeof import("~icons/lucide/brush"))["default"] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] @@ -169,16 +214,26 @@ declare module 'vue' { IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] - IconLucideRss: typeof import('~icons/lucide/rss')['default'] + IconLucidePlay: (typeof import("~icons/lucide/play"))["default"] + IconLucidePlaySquare: (typeof import("~icons/lucide/play-square"))["default"] + IconLucideRss: (typeof import("~icons/lucide/rss"))["default"] IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default'] + IconLucideVerified: (typeof import("~icons/lucide/verified"))["default"] IconLucideX: typeof import('~icons/lucide/x')['default'] ImportExportBase: typeof import('./components/importExport/Base.vue')['default'] ImportExportImportExportList: typeof import('./components/importExport/ImportExportList.vue')['default'] ImportExportImportExportSourcesList: typeof import('./components/importExport/ImportExportSourcesList.vue')['default'] + ImportExportImportExportStepsAllCollectionImport: typeof import('./components/importExport/ImportExportSteps/AllCollectionImport.vue')['default'] ImportExportImportExportStepsFileImport: typeof import('./components/importExport/ImportExportSteps/FileImport.vue')['default'] + ImportExportImportExportStepsImportSummary: typeof import('./components/importExport/ImportExportSteps/ImportSummary.vue')['default'] ImportExportImportExportStepsMyCollectionImport: typeof import('./components/importExport/ImportExportSteps/MyCollectionImport.vue')['default'] ImportExportImportExportStepsUrlImport: typeof import('./components/importExport/ImportExportSteps/UrlImport.vue')['default'] + InterceptorsAgentModalNativeCACertificates: typeof import('./components/interceptors/agent/ModalNativeCACertificates.vue')['default'] + InterceptorsAgentModalNativeClientCertificates: typeof import('./components/interceptors/agent/ModalNativeClientCertificates.vue')['default'] + InterceptorsAgentModalNativeClientCertsAdd: typeof import('./components/interceptors/agent/ModalNativeClientCertsAdd.vue')['default'] + InterceptorsAgentRegistrationModal: typeof import('./components/interceptors/agent/RegistrationModal.vue')['default'] + InterceptorsAgentRootExt: typeof import('./components/interceptors/agent/RootExt.vue')['default'] InterceptorsErrorPlaceholder: typeof import('./components/interceptors/ErrorPlaceholder.vue')['default'] InterceptorsExtensionSubtitle: typeof import('./components/interceptors/ExtensionSubtitle.vue')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] @@ -192,30 +247,59 @@ declare module 'vue' { LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default'] LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default'] LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default'] - ModalsNativeCACertificates: typeof import('./../../hoppscotch-selfhost-desktop/src/components/modals/NativeCACertificates.vue')['default'] - ModalsNativeClientCertificates: typeof import('./../../hoppscotch-selfhost-desktop/src/components/modals/NativeClientCertificates.vue')['default'] - ModalsNativeClientCertsAdd: typeof import('./../../hoppscotch-selfhost-desktop/src/components/modals/NativeClientCertsAdd.vue')['default'] + ProfileShortcode: (typeof import("./components/profile/Shortcode.vue"))["default"] + ProfileShortcodes: (typeof import("./components/profile/Shortcodes.vue"))["default"] ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default'] RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default'] RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default'] RealtimeLog: typeof import('./components/realtime/Log.vue')['default'] RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default'] RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default'] + SettingsAgent: typeof import('./components/settings/Agent.vue')['default'] SettingsExtension: typeof import('./components/settings/Extension.vue')['default'] - SettingsNativeInterceptor: typeof import('./../../hoppscotch-selfhost-desktop/src/components/settings/NativeInterceptor.vue')['default'] SettingsProxy: typeof import('./components/settings/Proxy.vue')['default'] Share: typeof import('./components/share/index.vue')['default'] ShareCreateModal: typeof import('./components/share/CreateModal.vue')['default'] ShareCustomizeModal: typeof import('./components/share/CustomizeModal.vue')['default'] ShareModal: typeof import('./components/share/Modal.vue')['default'] ShareRequest: typeof import('./components/share/Request.vue')['default'] + ShareRequestModal: (typeof import("./components/share/RequestModal.vue"))["default"] + ShareShareRequestModal: (typeof import("./components/share/ShareRequestModal.vue"))["default"] ShareTemplatesButton: typeof import('./components/share/templates/Button.vue')['default'] ShareTemplatesEmbeds: typeof import('./components/share/templates/Embeds.vue')['default'] ShareTemplatesLink: typeof import('./components/share/templates/Link.vue')['default'] SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default'] + SmartAnchor: (typeof import("./../../hoppscotch-ui/src/components/smart/Anchor.vue"))["default"] + SmartAutoComplete: (typeof import("./../../hoppscotch-ui/src/components/smart/AutoComplete.vue"))["default"] SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default'] + SmartCheckbox: (typeof import("./../../hoppscotch-ui/src/components/smart/Checkbox.vue"))["default"] SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default'] + SmartConfirmModal: (typeof import("./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue"))["default"] + SmartEncodingPicker: typeof import('./components/smart/EncodingPicker.vue')['default'] SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default'] + SmartExpand: (typeof import("./../../hoppscotch-ui/src/components/smart/Expand.vue"))["default"] + SmartFileChip: (typeof import("./../../hoppscotch-ui/src/components/smart/FileChip.vue"))["default"] + SmartInput: (typeof import("./../../hoppscotch-ui/src/components/smart/Input.vue"))["default"] + SmartIntersection: (typeof import("./../../hoppscotch-ui/src/components/smart/Intersection.vue"))["default"] + SmartItem: (typeof import("./../../hoppscotch-ui/src/components/smart/Item.vue"))["default"] + SmartLink: (typeof import("./../../hoppscotch-ui/src/components/smart/Link.vue"))["default"] + SmartModal: (typeof import("./../../hoppscotch-ui/src/components/smart/Modal.vue"))["default"] + SmartPicture: (typeof import("./../../hoppscotch-ui/src/components/smart/Picture.vue"))["default"] + SmartPlaceholder: (typeof import("./../../hoppscotch-ui/src/components/smart/Placeholder.vue"))["default"] + SmartProgressRing: (typeof import("./../../hoppscotch-ui/src/components/smart/ProgressRing.vue"))["default"] + SmartRadio: (typeof import("./../../hoppscotch-ui/src/components/smart/Radio.vue"))["default"] + SmartRadioGroup: (typeof import("./../../hoppscotch-ui/src/components/smart/RadioGroup.vue"))["default"] + SmartSelectWrapper: (typeof import("./../../hoppscotch-ui/src/components/smart/SelectWrapper.vue"))["default"] + SmartSlideOver: (typeof import("./../../hoppscotch-ui/src/components/smart/SlideOver.vue"))["default"] + SmartSpinner: (typeof import("./../../hoppscotch-ui/src/components/smart/Spinner.vue"))["default"] + SmartTab: (typeof import("./../../hoppscotch-ui/src/components/smart/Tab.vue"))["default"] + SmartTable: (typeof import("./../../hoppscotch-ui/src/components/smart/Table.vue"))["default"] + SmartTabs: (typeof import("./../../hoppscotch-ui/src/components/smart/Tabs.vue"))["default"] + SmartToggle: (typeof import("./../../hoppscotch-ui/src/components/smart/Toggle.vue"))["default"] + SmartTree: (typeof import("./../../hoppscotch-ui/src/components/smart/Tree.vue"))["default"] + SmartTreeBranch: (typeof import("./../../hoppscotch-ui/src/components/smart/TreeBranch.vue"))["default"] + SmartWindow: (typeof import("./../../hoppscotch-ui/src/components/smart/Window.vue"))["default"] + SmartWindows: (typeof import("./../../hoppscotch-ui/src/components/smart/Windows.vue"))["default"] TabPrimary: typeof import('./components/tab/Primary.vue')['default'] TabSecondary: typeof import('./components/tab/Secondary.vue')['default'] Teams: typeof import('./components/teams/index.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/accessTokens/List.vue b/packages/hoppscotch-common/src/components/accessTokens/List.vue index 220738cdc..5fe799029 100644 --- a/packages/hoppscotch-common/src/components/accessTokens/List.vue +++ b/packages/hoppscotch-common/src/components/accessTokens/List.vue @@ -13,7 +13,7 @@ import { useI18n } from "@composables/i18n" -import { useColorMode } from "@vueuse/core" +import { useColorMode } from "@composables/theming" import { computed } from "vue" import { shortDateTime } from "~/helpers/utils/date" diff --git a/packages/hoppscotch-common/src/components/aiexperiments/MergeView.vue b/packages/hoppscotch-common/src/components/aiexperiments/MergeView.vue new file mode 100644 index 000000000..05efd5d26 --- /dev/null +++ b/packages/hoppscotch-common/src/components/aiexperiments/MergeView.vue @@ -0,0 +1,82 @@ + + + diff --git a/packages/hoppscotch-common/src/components/aiexperiments/ModifyBodyModal.vue b/packages/hoppscotch-common/src/components/aiexperiments/ModifyBodyModal.vue new file mode 100644 index 000000000..910ba57d9 --- /dev/null +++ b/packages/hoppscotch-common/src/components/aiexperiments/ModifyBodyModal.vue @@ -0,0 +1,161 @@ + + + diff --git a/packages/hoppscotch-common/src/components/app/ActionHandler.vue b/packages/hoppscotch-common/src/components/app/ActionHandler.vue index 89aaa64a0..9253b0edc 100644 --- a/packages/hoppscotch-common/src/components/app/ActionHandler.vue +++ b/packages/hoppscotch-common/src/components/app/ActionHandler.vue @@ -2,6 +2,11 @@ + diff --git a/packages/hoppscotch-common/src/components/app/Inspection.vue b/packages/hoppscotch-common/src/components/app/Inspection.vue index e44bd75c8..9338f66c5 100644 --- a/packages/hoppscotch-common/src/components/app/Inspection.vue +++ b/packages/hoppscotch-common/src/components/app/Inspection.vue @@ -42,6 +42,7 @@ > {{ inspector.text.text }}
- -
-

- {{ t("app.updated_text", { version: version }) }} -

+
+

+ {{ t("app.updated_text", { version: version }) }} +

+ +
@@ -44,3 +51,41 @@ const openWhatsNew = () => { if (props.notesUrl) platform.io.openExternalLink(props.notesUrl) } + + diff --git a/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue b/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue index a5b07eeb8..aecc5b1b5 100644 --- a/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue +++ b/packages/hoppscotch-common/src/components/app/spotlight/Entry.vue @@ -80,11 +80,10 @@ const props = defineProps<{ active: boolean }>() -const formattedShortcutKeys = computed( - () => - props.entry.meta?.keyboardShortcut?.map( - (key) => SPECIAL_KEY_CHARS[key] ?? capitalize(key) - ) +const formattedShortcutKeys = computed(() => + props.entry.meta?.keyboardShortcut?.map( + (key) => SPECIAL_KEY_CHARS[key] ?? capitalize(key) + ) ) const emit = defineEmits<{ diff --git a/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue b/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue index 062b47459..0ba4cb15b 100644 --- a/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue +++ b/packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue @@ -7,9 +7,9 @@ {{ request.method.toUpperCase() }} diff --git a/packages/hoppscotch-common/src/components/collections/Add.vue b/packages/hoppscotch-common/src/components/collections/Add.vue index 2a39b7d4b..4d2a81d42 100644 --- a/packages/hoppscotch-common/src/components/collections/Add.vue +++ b/packages/hoppscotch-common/src/components/collections/Add.vue @@ -69,6 +69,10 @@ watch( ) const addNewCollection = () => { + if (props.loadingState) { + return + } + if (!editingName.value) { toast.error(t("collection.invalid_name")) return diff --git a/packages/hoppscotch-common/src/components/collections/AddFolder.vue b/packages/hoppscotch-common/src/components/collections/AddFolder.vue index e8f3aaf15..30cec3b09 100644 --- a/packages/hoppscotch-common/src/components/collections/AddFolder.vue +++ b/packages/hoppscotch-common/src/components/collections/AddFolder.vue @@ -69,10 +69,15 @@ watch( ) const addFolder = () => { + if (props.loadingState) { + return + } + if (editingName.value.trim() === "") { toast.error(t("folder.invalid_name")) return } + emit("add-folder", editingName.value) } diff --git a/packages/hoppscotch-common/src/components/collections/AddRequest.vue b/packages/hoppscotch-common/src/components/collections/AddRequest.vue index 02870f2ef..1b4f19875 100644 --- a/packages/hoppscotch-common/src/components/collections/AddRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/AddRequest.vue @@ -3,32 +3,93 @@ v-if="show" dialog :title="t('request.new')" - @close="$emit('hide-modal')" + @close="hideModal" > - + @@ -39,6 +100,14 @@ import { useI18n } from "@composables/i18n" import { useToast } from "@composables/toast" import { useService } from "dioc/vue" import { RESTTabService } from "~/services/tab/rest" +import { + useRequestNameGeneration, + useSubmitFeedback, +} from "~/composables/ai-experiments" +import { HoppRESTRequest } from "@hoppscotch/data" +import IconSparkle from "~icons/lucide/sparkles" +import IconThumbsUp from "~icons/lucide/thumbs-up" +import IconThumbsDown from "~icons/lucide/thumbs-down" const toast = useToast() const t = useI18n() @@ -47,10 +116,12 @@ const props = withDefaults( defineProps<{ show: boolean loadingState: boolean + requestContext: HoppRESTRequest | null }>(), { show: false, loadingState: false, + requestContext: null, } ) @@ -61,21 +132,47 @@ const emit = defineEmits<{ const editingName = ref("") +const { + generateRequestName, + isGenerateRequestNamePending, + canDoRequestNameGeneration, + lastTraceID, +} = useRequestNameGeneration(editingName) + +watch( + () => props.show, + (newVal) => { + if (!newVal) { + submittedFeedback.value = false + lastTraceID.value = null + } + } +) + +const submittedFeedback = ref(false) +const { submitFeedback, isSubmitFeedbackPending } = useSubmitFeedback() + const tabs = useService(RESTTabService) watch( () => props.show, (show) => { if (show) { - editingName.value = tabs.currentActiveTab.value.document.request.name + if (tabs.currentActiveTab.value.document.type === "request") + editingName.value = tabs.currentActiveTab.value.document.request.name } } ) const addRequest = () => { + if (props.loadingState) { + return + } + if (editingName.value.trim() === "") { toast.error(`${t("error.empty_req_name")}`) return } + emit("add-request", editingName.value) } diff --git a/packages/hoppscotch-common/src/components/collections/Collection.vue b/packages/hoppscotch-common/src/components/collections/Collection.vue index 60860a1b0..3de4eb428 100644 --- a/packages/hoppscotch-common/src/components/collections/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/Collection.vue @@ -62,7 +62,7 @@